From 779f3c0c1a0be0858326823b03ab33efba04451a Mon Sep 17 00:00:00 2001 From: Kevin Breit Date: Mon, 9 Jul 2018 04:08:37 -0500 Subject: [PATCH] Allow module to claim devices into an organization (#42448) - Before this, it allowed claiming devices into a network only - Make integration tests a block - Note, API doesn't allow unclaiming in an organization, only net - Added an integration test for claiming into an org - Requires unclaiming manually - There is a bug in the API which isn't showing claimed devices --- .../modules/network/meraki/meraki_device.py | 96 ++-- .../targets/meraki_device/tasks/main.yml | 413 +++++++++--------- 2 files changed, 263 insertions(+), 246 deletions(-) diff --git a/lib/ansible/modules/network/meraki/meraki_device.py b/lib/ansible/modules/network/meraki/meraki_device.py index 7ac4c9969ab2d8..09e25f8721a9f7 100644 --- a/lib/ansible/modules/network/meraki/meraki_device.py +++ b/lib/ansible/modules/network/meraki/meraki_device.py @@ -211,6 +211,14 @@ def is_device_valid(meraki, serial, data): return False +def get_org_devices(meraki, org_id): + path = meraki.construct_path('get_all_org', org_id=org_id) + response = meraki.request(path, method='GET') + if meraki.status != 200: + meraki.fail_json(msg='Failed to query all devices belonging to the organization') + return response + + def temp_get_nets(meraki, org_name, net_name): org_id = meraki.get_org_id(org_name) path = meraki.construct_path('get_all', function='network', org_id=org_id) @@ -263,24 +271,19 @@ def main(): meraki.params['follow_redirects'] = 'all' - query_urls = {'device': '/networks/{net_id}/devices', - } - - query_device_urls = {'device': '/networks/{net_id}/devices/', - } - - claim_device_urls = {'device': '/networks/{net_id}/devices/claim', - } - - update_device_urls = {'device': '/networks/{net_id}/devices/', - } - - delete_device_urls = {'device': '/networks/{net_id}/devices/', - } + query_urls = {'device': '/networks/{net_id}/devices'} + query_org_urls = {'device': '/organizations/{org_id}/deviceStatuses'} + query_device_urls = {'device': '/networks/{net_id}/devices/'} + claim_device_urls = {'device': '/networks/{net_id}/devices/claim'} + bind_org_urls = {'device': '/organizations/{org_id}/claim'} + update_device_urls = {'device': '/networks/{net_id}/devices/'} + delete_device_urls = {'device': '/networks/{net_id}/devices/'} meraki.url_catalog['get_all'].update(query_urls) + meraki.url_catalog['get_all_org'] = query_org_urls meraki.url_catalog['get_device'] = query_device_urls meraki.url_catalog['create'] = claim_device_urls + meraki.url_catalog['bind_org'] = bind_org_urls meraki.url_catalog['update'] = update_device_urls meraki.url_catalog['delete'] = delete_device_urls @@ -297,15 +300,19 @@ def main(): # manipulate or modify the state as needed (this is going to be the # part where your module will do what it needs to do) - nets = temp_get_nets(meraki, meraki.params['org_name'], meraki.params['net_name']) + + org_id = meraki.params['org_id'] + if org_id is None: + org_id = meraki.get_org_id(meraki.params['org_name']) + net_id = meraki.params['net_id'] + if net_id is None: + if meraki.params['net_name']: + nets = temp_get_nets(meraki, meraki.params['org_name'], meraki.params['net_name']) + net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets) if meraki.params['state'] == 'query': if meraki.params['net_name'] or meraki.params['net_id']: device = [] - if meraki.params['net_name']: - net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets) - elif meraki.params['net_id']: - net_id = meraki.params['net_id'] if meraki.params['serial']: path = meraki.construct_path('get_device', net_id=net_id) + meraki.params['serial'] request = meraki.request(path, method='GET') @@ -341,24 +348,16 @@ def main(): request = meraki.request(path, method='GET') meraki.result['data'] = request else: - devices = [] - for net in nets: # Gather all devices in all networks - path = meraki.construct_path('get_all', net_id=net['id']) - request = meraki.request(path, method='GET') - devices.append(request) + path = meraki.construct_path('get_all_org', org_id=org_id) + devices = meraki.request(path, method='GET') if meraki.params['serial']: - for network in devices: - for dev in network: - if dev['serial'] == meraki.params['serial']: - meraki.result['data'] = [dev] + for device in devices: + if device['serial'] == meraki.params['serial']: + meraki.result['data'] = device else: meraki.result['data'] = devices elif meraki.params['state'] == 'present': device = [] - if meraki.params['net_name']: - net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets) - elif meraki.params['net_id']: - net_id = meraki.params['net_id'] if meraki.params['hostname']: query_path = meraki.construct_path('get_all', net_id=net_id) device_list = meraki.request(query_path, method='GET') @@ -380,21 +379,28 @@ def main(): meraki.result['data'] = updated_device meraki.result['changed'] = True else: - query_path = meraki.construct_path('get_all', net_id=net_id) - device_list = meraki.request(query_path, method='GET') - if is_device_valid(meraki, meraki.params['serial'], device_list) is False: - payload = {'serial': meraki.params['serial']} - path = meraki.construct_path('create', net_id=net_id) - created_device = [] - created_device.append(meraki.request(path, method='POST', payload=json.dumps(payload))) - meraki.result['data'] = created_device - meraki.result['changed'] = True + if net_id is None: + device_list = get_org_devices(meraki, org_id) + if is_device_valid(meraki, meraki.params['serial'], device_list) is False: + payload = {'serial': meraki.params['serial']} + path = meraki.construct_path('bind_org', org_id=org_id) + created_device = [] + created_device.append(meraki.request(path, method='POST', payload=json.dumps(payload))) + meraki.result['data'] = created_device + meraki.result['changed'] = True + else: + query_path = meraki.construct_path('get_all', net_id=net_id) + device_list = meraki.request(query_path, method='GET') + if is_device_valid(meraki, meraki.params['serial'], device_list) is False: + if net_id: + payload = {'serial': meraki.params['serial']} + path = meraki.construct_path('create', net_id=net_id) + created_device = [] + created_device.append(meraki.request(path, method='POST', payload=json.dumps(payload))) + meraki.result['data'] = created_device + meraki.result['changed'] = True elif meraki.params['state'] == 'absent': device = [] - if meraki.params['net_name']: - net_id = meraki.get_net_id(net_name=meraki.params['net_name'], data=nets) - elif meraki.params['net_id']: - net_id = meraki.params['net_id'] query_path = meraki.construct_path('get_all', net_id=net_id) device_list = meraki.request(query_path, method='GET') if is_device_valid(meraki, meraki.params['serial'], device_list) is True: diff --git a/test/integration/targets/meraki_device/tasks/main.yml b/test/integration/targets/meraki_device/tasks/main.yml index 2368162509fc6d..0a831e24213d26 100644 --- a/test/integration/targets/meraki_device/tasks/main.yml +++ b/test/integration/targets/meraki_device/tasks/main.yml @@ -1,202 +1,213 @@ --- -- name: Claim a device - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial: '{{serial}}' - state: present - delegate_to: localhost - register: claim_device - -- debug: - msg: '{{claim_device}}' - -- assert: - that: - - claim_device.changed == true - -- name: Query all devices - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - state: query - delegate_to: localhost - register: query_all - -- debug: - msg: '{{query_all}}' - -- assert: - that: - - query_all.changed == False - -- name: Query all devices in one network by network ID - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_id: '{{test_net_id}}' - state: query - delegate_to: localhost - register: query_one_net_id - -- debug: - msg: '{{query_one_net_id}}' - -- name: Query all devices in one network - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - state: query - delegate_to: localhost - register: query_one_net - -- debug: - msg: '{{query_one_net}}' - -- name: Query device by serial - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - serial: '{{serial}}' - state: query - delegate_to: localhost - register: query_serial_no_net - -- debug: - msg: '{{query_serial_no_net}}' - -- name: Query device by serial - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial: '{{serial}}' - state: query - delegate_to: localhost - register: query_serial - -- debug: - msg: '{{query_serial}}' - -- assert: - that: - - query_serial.changed == False - -- name: Query uplink information for a device - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial_uplink: '{{serial}}' - state: query - delegate_to: localhost - register: query_serial_uplink - -- debug: - msg: '{{query_serial_uplink}}' - -- name: Query LLDP/CDP information about a device - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial_lldp_cdp: '{{serial}}' - lldp_cdp_timespan: 6000 - state: query - delegate_to: localhost - register: query_serial_lldp_cdp - -- debug: - msg: '{{query_serial_lldp_cdp}}' - -- name: Query a device by hostname - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - hostname: test-hostname - state: query - delegate_to: localhost - register: query_hostname - -- debug: - msg: '{{query_hostname}}' - -- name: Query a device by model - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - model: MR26 - state: query - delegate_to: localhost - register: query_model - -- debug: - msg: '{{query_model}}' - -- name: Update a device - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial: '{{serial}}' - name: mr26 - address: 1060 W. Addison St., Chicago, IL - lat: 41.948038 - lng: -87.65568 - tags: recently-added - state: present - move_map_marker: True - delegate_to: localhost - register: update_device - -- debug: - msg: '{{update_device}}' - -# - assert: -# that: -# - update_device.changed == true -# - '"1060 W. Addison St., Chicago, IL" in update_device.data.0.address' - -- name: Update a device with idempotency - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial: '{{serial}}' - name: mr26 - address: 1060 W. Addison St., Chicago, IL - lat: 41.948038 - lng: -87.65568 - tags: recently-added - state: present - move_map_marker: True - delegate_to: localhost - register: update_device_idempotent - -- debug: - msg: '{{update_device_idempotent}}' - -- assert: - that: - - update_device_idempotent.changed == False - -- name: Remove a device - meraki_device: - auth_key: '{{auth_key}}' - org_name: '{{test_org_name}}' - net_name: '{{test_net_name}}' - serial: '{{serial}}' - state: absent - delegate_to: localhost - register: delete_device - -- debug: - msg: '{{delete_device}}' - -- assert: - that: - - delete_device.changed == true \ No newline at end of file +- block: + - name: Claim a device into an organization + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + serial: '{{serial}}' + state: present + delegate_to: localhost + register: claim_device_org + + - assert: + that: + - claim_device_org.changed == true + + - name: Query status of all devices in an organization + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + state: query + delegate_to: localhost + register: query_device_org + + - debug: + msg: '{{query_device_org}}' + + - name: Claim a device into a network + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial: '{{serial}}' + state: present + delegate_to: localhost + register: claim_device + + - debug: + msg: '{{claim_device}}' + + - assert: + that: + - claim_device.changed == true + + - name: Query all devices in one network by network ID + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_id: '{{test_net_id}}' + state: query + delegate_to: localhost + register: query_one_net_id + + - debug: + msg: '{{query_one_net_id}}' + + - name: Query all devices in one network + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + state: query + delegate_to: localhost + register: query_one_net + + - debug: + msg: '{{query_one_net}}' + + - name: Query device by serial + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + serial: '{{serial}}' + state: query + delegate_to: localhost + register: query_serial_no_net + + - debug: + msg: '{{query_serial_no_net}}' + + - name: Query device by serial + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial: '{{serial}}' + state: query + delegate_to: localhost + register: query_serial + + - debug: + msg: '{{query_serial}}' + + - assert: + that: + - query_serial.changed == False + + - name: Query uplink information for a device + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial_uplink: '{{serial}}' + state: query + delegate_to: localhost + register: query_serial_uplink + + - debug: + msg: '{{query_serial_uplink}}' + + - name: Query LLDP/CDP information about a device + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial_lldp_cdp: '{{serial}}' + lldp_cdp_timespan: 6000 + state: query + delegate_to: localhost + register: query_serial_lldp_cdp + + - debug: + msg: '{{query_serial_lldp_cdp}}' + + - name: Query a device by hostname + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + hostname: test-hostname + state: query + delegate_to: localhost + register: query_hostname + + - debug: + msg: '{{query_hostname}}' + + - name: Query a device by model + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + model: MR26 + state: query + delegate_to: localhost + register: query_model + + - debug: + msg: '{{query_model}}' + + - name: Update a device + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial: '{{serial}}' + name: mr26 + address: 1060 W. Addison St., Chicago, IL + lat: 41.948038 + lng: -87.65568 + tags: recently-added + state: present + move_map_marker: True + delegate_to: localhost + register: update_device + + - debug: + msg: '{{update_device}}' + + # - assert: + # that: + # - update_device.changed == true + # - '"1060 W. Addison St., Chicago, IL" in update_device.data.0.address' + + - name: Update a device with idempotency + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial: '{{serial}}' + name: mr26 + address: 1060 W. Addison St., Chicago, IL + lat: 41.948038 + lng: -87.65568 + tags: recently-added + state: present + move_map_marker: True + delegate_to: localhost + register: update_device_idempotent + + - debug: + msg: '{{update_device_idempotent}}' + + - assert: + that: + - update_device_idempotent.changed == False + + always: + - name: Remove a device + meraki_device: + auth_key: '{{auth_key}}' + org_name: '{{test_org_name}}' + net_name: '{{test_net_name}}' + serial: '{{serial}}' + state: absent + delegate_to: localhost + register: delete_device + + - debug: + msg: '{{delete_device}}' + + - assert: + that: + - delete_device.changed == true \ No newline at end of file