From 82ed910d24c70695f0bc036997829f0be0ba2377 Mon Sep 17 00:00:00 2001 From: "sonali.kesali" Date: Fri, 2 Aug 2024 00:19:29 +0530 Subject: [PATCH 001/121] accesspoint_update_feature adding integration test code for accesspoint manager --- .../defaults/main.yml | 2 + .../meta/main.yml | 1 + .../tasks/main.yml | 34 ++++++ .../test_accesspoint_workflow_manager.yml | 111 ++++++++++++++++++ .../vars_accesspoint_workflow_manager.yml | 36 ++++++ 5 files changed, 184 insertions(+) create mode 100644 tests/integration/ccc_accesspoint_workflow_management/defaults/main.yml create mode 100644 tests/integration/ccc_accesspoint_workflow_management/meta/main.yml create mode 100644 tests/integration/ccc_accesspoint_workflow_management/tasks/main.yml create mode 100644 tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml create mode 100644 tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml diff --git a/tests/integration/ccc_accesspoint_workflow_management/defaults/main.yml b/tests/integration/ccc_accesspoint_workflow_management/defaults/main.yml new file mode 100644 index 0000000000..5f709c5aac --- /dev/null +++ b/tests/integration/ccc_accesspoint_workflow_management/defaults/main.yml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/tests/integration/ccc_accesspoint_workflow_management/meta/main.yml b/tests/integration/ccc_accesspoint_workflow_management/meta/main.yml new file mode 100644 index 0000000000..32cf5dda7e --- /dev/null +++ b/tests/integration/ccc_accesspoint_workflow_management/meta/main.yml @@ -0,0 +1 @@ +dependencies: [] diff --git a/tests/integration/ccc_accesspoint_workflow_management/tasks/main.yml b/tests/integration/ccc_accesspoint_workflow_management/tasks/main.yml new file mode 100644 index 0000000000..d842bc1747 --- /dev/null +++ b/tests/integration/ccc_accesspoint_workflow_management/tasks/main.yml @@ -0,0 +1,34 @@ +--- +- name: collect ccc test cases + find: + paths: "{{ role_path }}/tests" + patterns: "{{ testcase }}.yml" + connection: local + register: ccc_cases + tags: sanity + +- debug: + msg: "CCC Cases: {{ ccc_cases }}" + +- set_fact: + test_cases: + files: "{{ ccc_cases.files }}" + tags: sanity + +- debug: + msg: "Test Cases: {{ test_cases }}" + +- name: set test_items + set_fact: + test_items: "{{ test_cases.files | map(attribute='path') | list }}" + tags: sanity + +- debug: + msg: "Test Items: {{ test_items }}" + +- name: run test cases (connection=httpapi) + include_tasks: "{{ test_case_to_run }}" + loop: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run + tags: sanity diff --git a/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml b/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml new file mode 100644 index 0000000000..135a03a339 --- /dev/null +++ b/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml @@ -0,0 +1,111 @@ +--- +- debug: msg="Starting accesspoint workflow manager test" +- debug: msg="accesspoint Path {{ role_path }}" + +- block: + - name: accesspoint workflow manager + include_vars: + file: "{{ role_path }}/vars/vars_accesspoint_workflow_manager.yml" + name: vars_map + vars: + dnac_login: &dnac_login + dnac_host: "{{ dnac_host }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_port: "{{ dnac_port }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log: true + dnac_log_level: DEBUG + config_verify: true + + # - debug: + # msg: "{{ vars_map. }}" + # - debug: + # msg: "{{ vars_map. }}" + # - debug: + # msg: "{{ vars_map. }}" + +############################################# +# Clean Up # +############################################# + + # - name: Delete wlc + # cisco.dnac.accesspoint_workflow_manager: + # <<: *dnac_login + # state: deleted + # config: + # - "{{ item }}" + # loop: "{{ vars_map.delete_wlc }}" + # register: result_deleted_wlc + +########################################### + # PROVISION ACCESSPOINT # +########################################### + + - name: Provision accesspoint + cisco.dnac.accesspoint_workflow_manager: + <<: *dnac_login + state: merged + config: + - "{{ item }}" + loop: "{{ vars_map.provision_accesspoint }}" + register: result_provision_accesspoint + + # - name: Debug item + # debug: + # var: item + # loop: "{{ result_provision_accesspoint.results }}" + # when: result_provision_accesspoint is defined + + - name: Assert Provision accesspoint + assert: + that: + - item.changed == false + - "'AP - AP34B8.8315.7C6C does not need any update' in item.ap_update_msg" + loop: "{{ result_provision_accesspoint.results }}" + when: result_provision_accesspoint is defined + + +############################################# +# UPDATE ACCESSPOINT CONFIGURATION # +############################################# + + - name: Update accesspoint configuration + cisco.dnac.accesspoint_workflow_manager: + <<: *dnac_login + state: merged + config: + - "{{ item }}" + loop: "{{ vars_map.update_accesspoint_config }}" + register: result_update_accesspoint_config + + # - name: Debug item + # debug: + # var: item + # loop: "{{ result_update_accesspoint_config.results }}" + # when: result_update_accesspoint_config is defined + + - name: Assert Update accesspoint configuration + assert: + that: + - item.changed == true + - "'AP Configuration - LTTS-test1 updated Successfully' in item.ap_update_msg" + loop: "{{ result_update_accesspoint_config.results }}" + when: result_update_accesspoint_config is defined + +############################################# +# POST Clean Up # +############################################# + + # - name: Delete wlc + # cisco.dnac.accesspoint_workflow_manager: + # <<: *dnac_login + # state: deleted + # config: + # - "{{ item }}" + # loop: "{{ vars_map.delete_wlc }}" + # register: result_deleted_wlc + + diff --git a/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml new file mode 100644 index 0000000000..2942ff3a27 --- /dev/null +++ b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml @@ -0,0 +1,36 @@ +--- +provision_accesspoint: + - mac_address: "34:5d:a8:3b:d8:e0" + rf_profile: "HIGH" + site: + floor: + name: "FLOOR1" + parent_name: "Global/USA/New York/BLDNYC" + +update_accesspoint_config: + - mac_address: "90:e9:5e:03:f3:40" + rf_profile: "HIGH" + site: + floor: + name: "FLOOR1" + parent_name: "Global/USA/New York/BLDNYC" + ap_name: "LTTS-test1" + led_status: "Enabled" + led_brightness_level: 3 + ap_mode: "Local" + location: "LTTS/Cisco/Chennai" + failover_priority: "Low" + 2.4ghz_radio: + admin_status: "Enabled" + antenna_name: "C-ANT9104-2.4GHz" + radio_role_assignment: "Client-Serving" + channel_number: 3 + powerlevel: 3 + 5ghz_radio: + admin_status: "Enabled" + antenna_name: "AIR-ANT2513P4M-N-5GHz" + radio_role_assignment: "Client-Serving" + channel_number: 48 + powerlevel: 3 + channel_width: "20 MHz" + From 4b52cab3d6dabd029779505d67c8ba18573ae38f Mon Sep 17 00:00:00 2001 From: "sonali.kesali" Date: Fri, 2 Aug 2024 00:24:35 +0530 Subject: [PATCH 002/121] accesspoint_update_feature modified integration test code for accesspoint manager --- .../vars/vars_accesspoint_workflow_manager.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml index 2942ff3a27..f11eb9dba5 100644 --- a/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml +++ b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml @@ -9,11 +9,6 @@ provision_accesspoint: update_accesspoint_config: - mac_address: "90:e9:5e:03:f3:40" - rf_profile: "HIGH" - site: - floor: - name: "FLOOR1" - parent_name: "Global/USA/New York/BLDNYC" ap_name: "LTTS-test1" led_status: "Enabled" led_brightness_level: 3 From 969cfc31669d3be02ab9ddcc7698f3b1b551eae8 Mon Sep 17 00:00:00 2001 From: "sonali.kesali" Date: Fri, 2 Aug 2024 00:30:47 +0530 Subject: [PATCH 003/121] accesspoint_update_feature new modified integration test code for accesspoint manager --- playbooks/ansible.cfg | 4 ++++ .../tests/test_accesspoint_workflow_manager.yml | 4 +--- .../vars/vars_accesspoint_workflow_manager.yml | 3 +-- 3 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 playbooks/ansible.cfg diff --git a/playbooks/ansible.cfg b/playbooks/ansible.cfg new file mode 100644 index 0000000000..88795f7abc --- /dev/null +++ b/playbooks/ansible.cfg @@ -0,0 +1,4 @@ +[defaults] +interpreter_python = /Users/skesali/myenv/bin/python +collections_paths = /Users/skesali/.ansible/collections + diff --git a/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml b/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml index 135a03a339..377414b7fc 100644 --- a/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml +++ b/tests/integration/ccc_accesspoint_workflow_management/tests/test_accesspoint_workflow_manager.yml @@ -106,6 +106,4 @@ # config: # - "{{ item }}" # loop: "{{ vars_map.delete_wlc }}" - # register: result_deleted_wlc - - + # register: result_deleted_wlc \ No newline at end of file diff --git a/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml index f11eb9dba5..b5a33cf96e 100644 --- a/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml +++ b/tests/integration/ccc_accesspoint_workflow_management/vars/vars_accesspoint_workflow_manager.yml @@ -27,5 +27,4 @@ update_accesspoint_config: radio_role_assignment: "Client-Serving" channel_number: 48 powerlevel: 3 - channel_width: "20 MHz" - + channel_width: "20 MHz" \ No newline at end of file From d9722ad31070a8274addedd92f078e572dfdc629 Mon Sep 17 00:00:00 2001 From: Madhan Date: Fri, 2 Aug 2024 07:07:22 +0530 Subject: [PATCH 004/121] Add default code review template --- .github/PULL_REQUEST_TEMPLATE.md | 37 ++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..7d71e1b664 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ +# Default Code Review Template + +## Description +Please include a summary of the changes and the related issue. Also, include relevant motivation and context. + +## Type of Change +- [ ] Bug fix +- [ ] New feature +- [ ] Breaking change +- [ ] Documentation update + +## Checklist +- [ ] My code follows the style guidelines of this project +- [ ] I have performed a self-review of my own code +- [ ] I have commented my code, particularly in hard-to-understand areas +- [ ] I have made corresponding changes to the documentation +- [ ] My changes generate no new warnings +- [ ] I have added tests that prove my fix is effective or that my feature works +- [ ] New and existing unit tests pass locally with my changes +- [ ] Any dependent changes have been merged and published in downstream modules +- [ ] All the sanity checks have been completed and the sanity test cases have been executed + +## Ansible Best Practices +- [ ] Tasks are idempotent (can be run multiple times without changing state) +- [ ] Variables and secrets are handled securely (e.g., using `ansible-vault` or environment variables) +- [ ] Playbooks are modular and reusable +- [ ] Handlers are used for actions that need to run on change + +## Documentation +- [ ] All options and parameters are documented clearly. +- [ ] Examples are provided and tested. +- [ ] Notes and limitations are clearly stated. + +## Screenshots (if applicable) + +## Notes to Reviewers + From 2ee9d3ec6699c77857e55d199dc01ba250bc2bb1 Mon Sep 17 00:00:00 2001 From: md-rafeek <168404052+md-rafeek@users.noreply.github.com> Date: Fri, 2 Aug 2024 16:49:18 +0530 Subject: [PATCH 005/121] Update ansible.cfg --- playbooks/ansible.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playbooks/ansible.cfg b/playbooks/ansible.cfg index 88795f7abc..8c0498e7c5 100644 --- a/playbooks/ansible.cfg +++ b/playbooks/ansible.cfg @@ -1,4 +1,4 @@ [defaults] -interpreter_python = /Users/skesali/myenv/bin/python -collections_paths = /Users/skesali/.ansible/collections +interpreter_python = /Users//myenv/bin/python +collections_paths = /Users//.ansible/collections From 6722456a384ab731ae12c0dc1d07acebc09bbdd7 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Mon, 5 Aug 2024 13:20:36 +0530 Subject: [PATCH 006/121] have add the code for version routing in the dnac.py --- plugins/module_utils/dnac.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/module_utils/dnac.py b/plugins/module_utils/dnac.py index f44223c1ae..60891fca70 100644 --- a/plugins/module_utils/dnac.py +++ b/plugins/module_utils/dnac.py @@ -290,6 +290,7 @@ def get_dnac_params(self, params): "dnac_port": params.get("dnac_port"), "dnac_username": params.get("dnac_username"), "dnac_password": params.get("dnac_password"), + "dnac_version":params.get("dnac_version"), "dnac_verify": params.get("dnac_verify"), "dnac_debug": params.get("dnac_debug"), "dnac_log": params.get("dnac_log"), From 95638f320a0493e32210a8f826338e41c8c9e41e Mon Sep 17 00:00:00 2001 From: Abinash Date: Mon, 5 Aug 2024 17:18:18 +0000 Subject: [PATCH 007/121] Fix for unreacable revices, ap devices and unmanaged devices backup --- .../device_configs_backup_workflow_manager.py | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/plugins/modules/device_configs_backup_workflow_manager.py b/plugins/modules/device_configs_backup_workflow_manager.py index 2adfbfdb9c..31b4e1a0ff 100644 --- a/plugins/modules/device_configs_backup_workflow_manager.py +++ b/plugins/modules/device_configs_backup_workflow_manager.py @@ -300,15 +300,42 @@ def get_device_ids_list(self): ) self.log("Response collected from the API 'get_device_list' is {0}".format(str(response)), "DEBUG") device_list = response.get("response") - self.log("Length of the device list fetched from the API 'get_device_list' is {0}".format(str(device_list)), "INFO") - if len(device_list) == 0: + original_dev_len = len(device_list) + if original_dev_len == 0: msg = "Couldn't find any devices in the inventory that match the given parameters." self.log(msg, "CRITICAL") self.module.fail_json(msg=msg) - dev_id_list = [id.get("id") for id in device_list] + changed_dev_list = [] + for dev_info in device_list: + if dev_info.get("collectionStatus") != "Managed": + msg = "Device backup of device with IP address {0} \ + is not possible due to collection status not being in Managed state".format(dev_info.get("managementIpAddress")) + self.log(msg, "WARNING") + + elif dev_info.get("family") != "Unified AP": + msg = "Device backup of device with IP address {0} \ + is not possible due to device being an Unified AP".format(dev_info.get("managementIpAddress")) + self.log(msg, "WARNING") + + elif dev_info.get("reachabilityStatus") != "Reachable": + msg = "Device backup of device with IP address {0} \ + is not possible due to device being not reachable".format(dev_info.get("managementIpAddress")) + self.log(msg, "WARNING") + else: + changed_dev_list.append(dev_info) + + if len(changed_dev_list) == 0: + msg = "No device IDs got collected either due to devices \ + being an Unified Ap or device not being in Managed state or device not being reachable" + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + + dev_id_list = [id.get("id") for id in changed_dev_list] + dev_len = len(dev_id_list) self.log("Device Ids list collected is {0}".format(dev_id_list), "INFO") + self.log("Backup of {0} devices out of {1} devices is possible".format(dev_len, original_dev_len), "INFO") return dev_id_list def password_generator(self): From 6e00a110b510ec611ee41241fcd53921c2557eb0 Mon Sep 17 00:00:00 2001 From: Madhan Date: Tue, 6 Aug 2024 07:05:09 +0530 Subject: [PATCH 008/121] Code Review template --- .github/PULL_REQUEST_TEMPLATE.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7d71e1b664..7835113143 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,5 +1,3 @@ -# Default Code Review Template - ## Description Please include a summary of the changes and the related issue. Also, include relevant motivation and context. From 97d9c01e860cace678ff8d0115abed5278c789c2 Mon Sep 17 00:00:00 2001 From: Abinash Date: Tue, 6 Aug 2024 06:23:30 +0000 Subject: [PATCH 009/121] Fix for unreachable devices, ap devices and unmanaged devices backup --- .../device_configs_backup_workflow_manager.py | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/plugins/modules/device_configs_backup_workflow_manager.py b/plugins/modules/device_configs_backup_workflow_manager.py index 31b4e1a0ff..9eb0a1d342 100644 --- a/plugins/modules/device_configs_backup_workflow_manager.py +++ b/plugins/modules/device_configs_backup_workflow_manager.py @@ -279,7 +279,7 @@ def get_device_ids_list(self): Args: self: The instance of the class containing the 'config' attribute to be validated. Returns: - dev_id_list: The list of device ids based on the parameters passed by the user + device_ids: The list of device ids based on the parameters passed by the user Example: Stored paramters like management ip address/ family can be used to fetch the device ids list @@ -301,42 +301,43 @@ def get_device_ids_list(self): self.log("Response collected from the API 'get_device_list' is {0}".format(str(response)), "DEBUG") device_list = response.get("response") self.log("Length of the device list fetched from the API 'get_device_list' is {0}".format(str(device_list)), "INFO") - original_dev_len = len(device_list) - if original_dev_len == 0: - msg = "Couldn't find any devices in the inventory that match the given parameters." + original_valid_device_count = len(device_list) + if original_valid_device_count == 0: + msg = "No devices found in the inventory matching the given parameters." self.log(msg, "CRITICAL") self.module.fail_json(msg=msg) - changed_dev_list = [] + valid_devices = [] for dev_info in device_list: + ip_address = dev_info.get("managementIpAddress") if dev_info.get("collectionStatus") != "Managed": msg = "Device backup of device with IP address {0} \ - is not possible due to collection status not being in Managed state".format(dev_info.get("managementIpAddress")) + is not possible due to collection status not being in Managed state".format(ip_address) self.log(msg, "WARNING") elif dev_info.get("family") != "Unified AP": msg = "Device backup of device with IP address {0} \ - is not possible due to device being an Unified AP".format(dev_info.get("managementIpAddress")) + is not possible due to device being an Unified AP".format(ip_address) self.log(msg, "WARNING") elif dev_info.get("reachabilityStatus") != "Reachable": msg = "Device backup of device with IP address {0} \ - is not possible due to device being not reachable".format(dev_info.get("managementIpAddress")) + is not possible due to device being not reachable".format(ip_address) self.log(msg, "WARNING") else: - changed_dev_list.append(dev_info) + valid_devices.append(dev_info) - if len(changed_dev_list) == 0: - msg = "No device IDs got collected either due to devices \ - being an Unified Ap or device not being in Managed state or device not being reachable" + if len(valid_devices) == 0: + msg = "No device IDs were collected because the devices are either Unified APs \ + not in the Managed state, or not reachable." self.log(msg, "CRITICAL") self.module.fail_json(msg=msg) - dev_id_list = [id.get("id") for id in changed_dev_list] - dev_len = len(dev_id_list) - self.log("Device Ids list collected is {0}".format(dev_id_list), "INFO") - self.log("Backup of {0} devices out of {1} devices is possible".format(dev_len, original_dev_len), "INFO") - return dev_id_list + device_ids = [id.get("id") for id in valid_devices] + valid_device_count = len(device_ids) + self.log("Collected device IDs: {0}".format(device_ids), "INFO") + self.log("Backup of {0} devices out of {1} devices is possible".format(valid_device_count, original_valid_device_count), "INFO") + return device_ids def password_generator(self): """ From 7c0ebc4af5dd2dcb172bb9aee8e1290b34ae8255 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 6 Aug 2024 12:23:49 +0530 Subject: [PATCH 010/121] sanity error solved --- plugins/module_utils/dnac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/module_utils/dnac.py b/plugins/module_utils/dnac.py index 60891fca70..12c3b8fdc9 100644 --- a/plugins/module_utils/dnac.py +++ b/plugins/module_utils/dnac.py @@ -290,7 +290,7 @@ def get_dnac_params(self, params): "dnac_port": params.get("dnac_port"), "dnac_username": params.get("dnac_username"), "dnac_password": params.get("dnac_password"), - "dnac_version":params.get("dnac_version"), + "dnac_version": params.get("dnac_version"), "dnac_verify": params.get("dnac_verify"), "dnac_debug": params.get("dnac_debug"), "dnac_log": params.get("dnac_log"), From e92fb7a8a799ded24542d2b53b37dd3d66ad1425 Mon Sep 17 00:00:00 2001 From: Abinash Date: Tue, 6 Aug 2024 10:08:50 +0000 Subject: [PATCH 011/121] Adding fix for the issues related to typographical error, unused variables, error message clarity and un-rpovisioning example --- plugins/modules/provision_workflow_manager.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/plugins/modules/provision_workflow_manager.py b/plugins/modules/provision_workflow_manager.py index e5d888ad6b..26d6d632df 100644 --- a/plugins/modules/provision_workflow_manager.py +++ b/plugins/modules/provision_workflow_manager.py @@ -182,6 +182,21 @@ managed_ap_locations: - Global/USA/RTP/BLD11/BLD11_FLOOR1 +- name: Unprovision a device from a site + cisco.dnac.provision_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log: True + state: deleted + config_verify: True + config: + - management_ip_address: 204.1.2.2 + """ RETURN = r""" @@ -261,7 +276,7 @@ def validate_input(self, state=None): """ if not self.config: - self.msg = "config not available in playbook for validattion" + self.msg = "config not available in playbook for validation" self.status = "success" return self @@ -756,8 +771,8 @@ def get_wireless_params(self): ] if not (wireless_params[0].get("managedAPLocations") and isinstance(wireless_params[0].get("managedAPLocations"), list)): - msg = "Managed AP locations must be passed as a list of sites. For example, [Global/USA/RTP/BLD11/BLD11_FLOOR1,\ - Global/USA/RTP/BLD11/BLD11_FLOOR2]" + msg = "Missing Managed AP Locations: Please specify the intended location(s) for the wireless device \ + within the site hierarchy." self.log(msg, "CRITICAL") self.module.fail_json(msg=msg, response=[]) @@ -849,7 +864,7 @@ def perform_wireless_reprovision(self): ) self.log("Wireless provisioning response collected from 'provision_update' API is: {0}".format(str(response)), "DEBUG") execution_id = response.get("executionId") - provision_info = self.get_execution_status_wireless(execution_id=execution_id) + self.get_execution_status_wireless(execution_id=execution_id) self.result["changed"] = True self.result['msg'] = "Wireless device with IP address {0} got re-provisioned successfully".format(self.validated_config[0]["management_ip_address"]) self.result['diff'] = self.validated_config @@ -905,7 +920,7 @@ class instance for further use. ) self.log("Reprovisioning response collected from 're_provision_wired_device' API is: {0}".format(response), "DEBUG") task_id = response.get("taskId") - provision_info = self.get_task_status(task_id=task_id) + self.get_task_status(task_id=task_id) self.result["changed"] = True self.result['msg'] = "Re-Provision done Successfully" self.result['diff'] = self.validated_config @@ -979,7 +994,7 @@ class instance for further use. ) self.log("Wireless provisioning response collected from 'provision' API is: {0}".format(str(response)), "DEBUG") execution_id = response.get("executionId") - provision_info = self.get_execution_status_wireless(execution_id=execution_id) + self.get_execution_status_wireless(execution_id=execution_id) self.result["changed"] = True self.result['msg'] = "Wireless device with IP {0} got provisioned successfully".format(self.validated_config[0]["management_ip_address"]) self.result['diff'] = self.validated_config @@ -1000,7 +1015,7 @@ class instance for further use. return self task_id = response.get("taskId") - provision_info = self.get_task_status(task_id=task_id) + self.get_task_status(task_id=task_id) self.result["changed"] = True self.result['msg'] = "Provision done Successfully" self.result['diff'] = self.validated_config From 2cff61b13f1be2e5c150029184e8bd53c0a33240 Mon Sep 17 00:00:00 2001 From: md-rafeek <168404052+md-rafeek@users.noreply.github.com> Date: Tue, 6 Aug 2024 15:51:43 +0530 Subject: [PATCH 012/121] Delete playbooks/ansible.cfg Unwanted file --- playbooks/ansible.cfg | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 playbooks/ansible.cfg diff --git a/playbooks/ansible.cfg b/playbooks/ansible.cfg deleted file mode 100644 index 8c0498e7c5..0000000000 --- a/playbooks/ansible.cfg +++ /dev/null @@ -1,4 +0,0 @@ -[defaults] -interpreter_python = /Users//myenv/bin/python -collections_paths = /Users//.ansible/collections - From 50a2949974b21d5031245ec555e56bac85e391f7 Mon Sep 17 00:00:00 2001 From: Madhan Date: Wed, 7 Aug 2024 10:08:09 +0530 Subject: [PATCH 013/121] Adding log message --- plugins/modules/user_role_workflow_manager.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index ad21c28c19..dc41a0b09f 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1328,6 +1328,8 @@ def get_current_config(self, input_config): current_role_id = {} if "role_name" in input_config and input_config["role_name"] is not None: + self.log("Retrieving role details for role_name: {0}".format(str(input_config["role_name"])), "DEBUG") + response_role = self.get_role() response_role = self.camel_to_snake_case(response_role) roles = response_role.get("response", {}).get("roles", []) @@ -1337,9 +1339,13 @@ def get_current_config(self, input_config): current_role_configuration = role role_exists = True + self.log("Role retrieval result - role_exists: {0}, current_role_configuration: {1}".format( + str(role_exists), str(current_role_configuration)), "DEBUG") return role_exists, current_role_configuration if "username" in input_config or "email" in input_config: + self.log("Retrieving user details for username: {0}, email: {1}".format( + str(input_config.get("username")), str(input_config.get("email"))), "DEBUG") response_user = self.get_user() response_role = self.get_role() response_user = self.camel_to_snake_case(response_user) @@ -1356,6 +1362,9 @@ def get_current_config(self, input_config): current_user_configuration = user user_exists = True + self.log("User retrieval result - user_exists: {0}, current_user_configuration: {1}".format( + str(user_exists), str(current_user_configuration)), "DEBUG") + if input_config.get("role_list"): for role_name in input_config["role_list"]: for role in roles: @@ -1366,6 +1375,7 @@ def get_current_config(self, input_config): if role.get("name").lower() == "observer-role": current_role_id[role.get("name").lower()] = role.get("role_id") + self.log("Role ID retrieval result - current_role_id: {0}".format(str(current_role_id)), "DEBUG") return user_exists, current_user_configuration, current_role_id def create_user(self, user_params): From 76e08688a4b904f70fee02bdfaeb228fc7a2ce25 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 7 Aug 2024 11:43:35 +0530 Subject: [PATCH 014/121] minor bugs and enhancement fixed --- plugins/modules/user_role_workflow_manager.py | 16 +++++++--------- .../fixtures/user_role_workflow_manager.json | 2 +- .../dnac/test_user_role_workflow_manager.py | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index dc41a0b09f..be29af9693 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -883,7 +883,7 @@ def validate_input_yml(self, user_role_details): """ self.log("Validating the Playbook Yaml File..", "INFO") - if user_role_details is None or isinstance(user_role_details, dict): + if user_role_details is None or not isinstance(user_role_details, list): self.msg = "Configuration is not available in the playbook for validation or user/role details are not type list" self.log(self.msg, "ERROR") self.status = "failed" @@ -1056,13 +1056,12 @@ def valid_user_config_parameters(self, user_config): """ self.log("Validating user configuration parameters...", "INFO") error_messages = [] + regex_name_validation = re.compile(r"^[A-Za-z0-9_-]+$") - first_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") - self.validate_string_field(user_config.get("first_name"), first_name_regex, "first_name: 'first_name' must only contain letters, numbers, \ + self.validate_string_field(user_config.get("first_name"), regex_name_validation, "first_name: 'first_name' must only contain letters, numbers, \ underscores, and hyphens and should not contain spaces or other special characters.", error_messages) - last_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") - self.validate_string_field(user_config.get("last_name"), last_name_regex, "last_name: 'last_name' must only contain letters, numbers, \ + self.validate_string_field(user_config.get("last_name"), regex_name_validation, "last_name: 'last_name' must only contain letters, numbers, \ underscores, and hyphens and should not contain spaces or other special characters.", error_messages) email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") @@ -1073,10 +1072,9 @@ def valid_user_config_parameters(self, user_config): password_regex = re.compile(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$") if user_config.get("password"): self.validate_string_field(user_config.get("password"), password_regex, "password: 'Password' does not meet complexity requirements\ - for password: {0}".format(user_config.get("email")), error_messages) + for password: {0}".format(user_config.get("password")), error_messages) - username_regex = re.compile(r"^[A-Za-z0-9_-]+$") - self.validate_string_field(user_config.get("username"), username_regex, "username: 'Username' must only contain letters, numbers, \ + self.validate_string_field(user_config.get("username"), regex_name_validation, "username: 'Username' must only contain letters, numbers, \ underscores, and hyphens and should not contain spaces or other special characters.", error_messages) if user_config.get("role_list"): @@ -2238,7 +2236,7 @@ def role_requires_update(self, current_role, desired_role): if current_description != desired_description: self.log("Updating description from {0} to {1}.".format(current_description, desired_description), "DEBUG") update_role_params["description"] = desired_description - update_needed = True + update_required = True elif "description" not in update_role_params: update_role_params["description"] = current_description else: diff --git a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json index baf89c9e5c..31eb957cb9 100644 --- a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json @@ -460,7 +460,7 @@ } }, "user_invalid_param_not_correct_formate_responce":{ - "message": "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., last_name: 'last_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: ajith.andrewexample.com, username: 'Username' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters." + "message": "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., last_name: 'last_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: Ajith123, username: 'Username' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters." }, "user_invalid_param_not_type_list_response":{ "message": "Invalid parameter(s) found in playbook: Super-Admin-Role : is not a valid list" diff --git a/tests/unit/modules/dnac/test_user_role_workflow_manager.py b/tests/unit/modules/dnac/test_user_role_workflow_manager.py index ef7e4f9430..78fa303a89 100644 --- a/tests/unit/modules/dnac/test_user_role_workflow_manager.py +++ b/tests/unit/modules/dnac/test_user_role_workflow_manager.py @@ -395,7 +395,7 @@ def test_user_role_workflow_manager_user_invalid_param_not_correct_formate(self) special characters., last_name: 'last_name' must only contain letters, numbers, underscores, \ and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, \ password: 'Password' does not meet complexity requirements for password: \ -ajith.andrewexample.com, username: 'Username' must only contain letters, numbers, underscores, \ +Ajith123, username: 'Username' must only contain letters, numbers, underscores, \ and hyphens and should not contain spaces or other special characters." ) From eb8d811b1693e4aa04c3baf8bc0c71b608b237d9 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 7 Aug 2024 12:21:37 +0530 Subject: [PATCH 015/121] Bugs Fixed --- plugins/modules/user_role_workflow_manager.py | 20 +++++++++---------- .../fixtures/user_role_workflow_manager.json | 4 ++-- .../dnac/test_user_role_workflow_manager.py | 12 +++++------ 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index be29af9693..3f265b4a90 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1005,8 +1005,8 @@ def valid_role_config_parameters(self, role_config): role_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") self.validate_string_field(role_config.get("role_name"), role_name_regex, - "Role name: 'role_name' must only contain letters, numbers, underscores,\ - and hyphens and should not contain spaces or other special characters.", error_messages) + "Role name: 'role_name' must only contain letters, numbers, underscores " + "and hyphens and should not contain spaces or other special characters.", error_messages) if role_config.get("description"): self.validate_string_parameter("description", role_config["description"], error_messages) @@ -1058,11 +1058,11 @@ def valid_user_config_parameters(self, user_config): error_messages = [] regex_name_validation = re.compile(r"^[A-Za-z0-9_-]+$") - self.validate_string_field(user_config.get("first_name"), regex_name_validation, "first_name: 'first_name' must only contain letters, numbers, \ - underscores, and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("first_name"), regex_name_validation, "first_name: 'first_name' must only contain letters, numbers, " + "underscores and hyphens and should not contain spaces or other special characters.", error_messages) - self.validate_string_field(user_config.get("last_name"), regex_name_validation, "last_name: 'last_name' must only contain letters, numbers, \ - underscores, and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("last_name"), regex_name_validation, "last_name: 'last_name' must only contain letters, numbers, " + "underscores and hyphens and should not contain spaces or other special characters.", error_messages) email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") if user_config.get("email"): @@ -1071,11 +1071,11 @@ def valid_user_config_parameters(self, user_config): password_regex = re.compile(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$") if user_config.get("password"): - self.validate_string_field(user_config.get("password"), password_regex, "password: 'Password' does not meet complexity requirements\ - for password: {0}".format(user_config.get("password")), error_messages) + self.validate_string_field(user_config.get("password"), password_regex, "password: 'Password' does not meet complexity requirements " + "for password: {0}".format(user_config.get("password")), error_messages) - self.validate_string_field(user_config.get("username"), regex_name_validation, "username: 'Username' must only contain letters, numbers, \ - underscores, and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("username"), regex_name_validation, "username: 'Username' must only contain letters, numbers, " + "underscores and hyphens and should not contain spaces or other special characters.", error_messages) if user_config.get("role_list"): param_spec = dict(type="list", elements="str") diff --git a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json index 31eb957cb9..9f089b87e9 100644 --- a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json @@ -460,7 +460,7 @@ } }, "user_invalid_param_not_correct_formate_responce":{ - "message": "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., last_name: 'last_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: Ajith123, username: 'Username' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters." + "message": "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., last_name: 'last_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: Ajith123, username: 'Username' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." }, "user_invalid_param_not_type_list_response":{ "message": "Invalid parameter(s) found in playbook: Super-Admin-Role : is not a valid list" @@ -1372,7 +1372,7 @@ } }, "role_invalid_param_rolename_not_correct_formate_responce":{ - "message": "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores, and hyphens and should not contain spaces or other special characters." + "message": "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." }, "invalid_param_type_list_missing_response":{ "message": "Configuration is not available in the playbook for validation or user/role details are not type list" diff --git a/tests/unit/modules/dnac/test_user_role_workflow_manager.py b/tests/unit/modules/dnac/test_user_role_workflow_manager.py index 78fa303a89..dea7f32d8b 100644 --- a/tests/unit/modules/dnac/test_user_role_workflow_manager.py +++ b/tests/unit/modules/dnac/test_user_role_workflow_manager.py @@ -391,11 +391,11 @@ def test_user_role_workflow_manager_user_invalid_param_not_correct_formate(self) self.assertEqual( result.get("msg"), "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, \ -numbers, underscores, and hyphens and should not contain spaces or other \ -special characters., last_name: 'last_name' must only contain letters, numbers, underscores, \ +numbers, underscores and hyphens and should not contain spaces or other \ +special characters., last_name: 'last_name' must only contain letters, numbers, underscores \ and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, \ -password: 'Password' does not meet complexity requirements for password: \ -Ajith123, username: 'Username' must only contain letters, numbers, underscores, \ +password: 'Password' does not meet complexity requirements for password: \ +Ajith123, username: 'Username' must only contain letters, numbers, underscores \ and hyphens and should not contain spaces or other special characters." ) @@ -695,8 +695,8 @@ def test_user_role_workflow_manager_role_invalid_param_rolename_not_correct_form print(result) self.assertEqual( result.get("msg"), - "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores,\ - and hyphens and should not contain spaces or other special characters." + "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores \ +and hyphens and should not contain spaces or other special characters." ) def test_user_role_workflow_manager_invalid_param_type_list_missing(self): From 62439725a2c4cd1ef85e06c2c6261db09031d1b3 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Wed, 7 Aug 2024 18:58:16 +0530 Subject: [PATCH 016/121] swim UT test cases and fixtures started --- playbooks/swim_workflow_manager.yml | 82 ++++++---- .../dnac/fixtures/swim_workflow_manager.json | 140 ++++++++++++++++++ .../dnac/test_swim_workflow_manager.py | 103 +++++++++++++ 3 files changed, 295 insertions(+), 30 deletions(-) create mode 100644 tests/unit/modules/dnac/fixtures/swim_workflow_manager.json create mode 100644 tests/unit/modules/dnac/test_swim_workflow_manager.py diff --git a/playbooks/swim_workflow_manager.yml b/playbooks/swim_workflow_manager.yml index c4f027c469..38b75b7f19 100644 --- a/playbooks/swim_workflow_manager.yml +++ b/playbooks/swim_workflow_manager.yml @@ -21,34 +21,56 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 + state : "merged" config: - - import_image_details: - type: "{{ item.type }}" - url_details: - payload: - - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin - third_party: False - tagging_details: - image_name: cat9k_iosxe.17.12.02.SPA.bin - device_role: ALL - device_image_family_name: Cisco Catalyst 9300 Switch - site_name: "{{item.site_name}}" - tagging: True - # image_distribution_details: - # image_name: cat9k_iosxe.17.12.02.SPA.bin - # site_name: "{{item.site_name}}" - # device_role: "{{ item.device_role }}" - # device_family_name: "{{ item.device_family_name }}" - # device_series_name: "Catalyst 9300 Series" - image_activation_details: - image_name: cat9k_iosxe.17.12.02.SPA.bin - site_name: "{{item.site_name}}" - device_role: "{{ item.device_role }}" - device_family_name: "{{ item.device_family_name }}" - device_series_name: "Catalyst 9300 Series" - scehdule_validate: False - distribute_if_needed: True - - with_items: "{{ image_details }}" - tags: - - swim + - import_image_details: + type: remote + url_details: + payload: + - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin + third_party: False + tagging_details: + image_name: cat9k_iosxe.17.12.02.SPA.bin + device_role: ALL + device_image_family_name: Cisco Catalyst 9300 Switch + tagging: True + + + + + + + + + + + # - import_image_details: + # type: "{{ item.type }}" + # url_details: + # payload: + # - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin + # third_party: False + # tagging_details: + # image_name: cat9k_iosxe.17.12.02.SPA.bin + # device_role: ALL + # device_image_family_name: Cisco Catalyst 9300 Switch + # site_name: "{{item.site_name}}" + # tagging: True + # # image_distribution_details: + # # image_name: cat9k_iosxe.17.12.02.SPA.bin + # # site_name: "{{item.site_name}}" + # # device_role: "{{ item.device_role }}" + # # device_family_name: "{{ item.device_family_name }}" + # # device_series_name: "Catalyst 9300 Series" + # image_activation_details: + # image_name: cat9k_iosxe.17.12.02.SPA.bin + # site_name: "{{item.site_name}}" + # device_role: "{{ item.device_role }}" + # device_family_name: "{{ item.device_family_name }}" + # device_series_name: "Catalyst 9300 Series" + # scehdule_validate: False + # distribute_if_needed: True + + # with_items: "{{ image_details }}" + # tags: + # - swim diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json new file mode 100644 index 0000000000..239b077f3f --- /dev/null +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -0,0 +1,140 @@ +{ + "playbook_import_image_url_tag_golden_load": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + }, + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_serial_number": "FJC2327U0S2" + }, + "image_activation_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "schedule_validate": false, + "activate_lower_image_version": false, + "distribute_if_needed": true, + "device_serial_number": "FJC2327U0S2" + } + } + ], + + "playbook_import_image_from_local_tag_golden": [ + { + "import_image_details": { + "type": "local", + "local_image_details": { + "file_path": "/Users/Downloads/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + } + } + ], + + "playbook_tag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + } + } + ], + + "playbook_untag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": false + } + } + ], + + "playbook_Distribute_image_associate_site_role": [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "site_name": "Global/USA/San Francisco/BGL_18", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches" + } + } + ], + + "playbook_Activate_image_associate_site_role": [ + { + "image_activation_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "site_name": "Global/USA/San Francisco/BGL_18", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches", + "schedule_validate": false, + "activate_lower_image_version": true, + "distribute_if_needed": true + } + } + ], + "playbook_import_image_url_tag_golden": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "tagging": true + } + } + ], + "get_software_image_details": { + "response": [], + "version": "1.0" + }, + + "import_software_image_via_url": {"response": {"taskId": "01912ce2-73b4-7bbe-a6f3-428e4809602a", "url": "/api/v1/task/01912ce2-73b4-7bbe-a6f3-428e4809602a"}, "version": "1.0"}, + "get_software_image_details_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, + "tag_as_golden_image": {"response": {"taskId": "01912ce2-f6f0-777c-8ec6-b404471af00d", "url": "/api/v1/task/01912ce2-f6f0-777c-8ec6-b404471af00d"}, "version": "1.0"}, + "import_image_url_tag_golden_response":{ + "message": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + } +} + \ No newline at end of file diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py new file mode 100644 index 0000000000..06061faa17 --- /dev/null +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -0,0 +1,103 @@ +# Copyright (c) 2024 Cisco and/or its affiliates. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Make coding more python3-ish + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.cisco.dnac.plugins.modules import swim_workflow_manager +from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData + + +class TestswimWorkflowManager(TestDnacModule): + + module = swim_workflow_manager + + test_data = loadPlaybookData("swim_workflow_manager") + + playbook_import_image_url_tag_golden_load = test_data.get("playbook_import_image_url_tag_golden_load") + playbook_import_image_from_local_tag_golden = test_data.get("playbook_import_image_from_local_tag_golden") + playbook_tag_image_as_golden_and_load_on_device = test_data.get("playbook_tag_image_as_golden_and_load_on_device") + playbook_untag_image_as_golden_and_load_on_device = test_data.get("playbook_untag_image_as_golden_and_load_on_device") + playbook_Distribute_image_associate_site_role = test_data.get("playbook_Distribute_image_associate_site_role") + playbook_Activate_image_associate_site_role = test_data.get("playbook_Activate_image_associate_site_role") + playbook_import_image_url_tag_golden = test_data.get("playbook_import_image_url_tag_golden") + + + def setUp(self): + super(TestswimWorkflowManager, self).setUp() + + self.mock_dnac_init = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__") + self.run_dnac_init = self.mock_dnac_init.start() + self.run_dnac_init.side_effect = [None] + self.mock_dnac_exec = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK._exec" + ) + self.run_dnac_exec = self.mock_dnac_exec.start() + + def tearDown(self): + super(TestswimWorkflowManager, self).tearDown() + self.mock_dnac_exec.stop() + self.mock_dnac_init.stop() + + def load_fixtures(self, response=None, device=""): + """ + Load fixtures for user. + """ + if "playbook_import_image_url_tag_golden" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details"), + self.test_data.get("import_software_image_via_url"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_device_family_identifiers"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_golden_tag_status_of_an_image"), + self.test_data.get("tag_as_golden_image"), + self.test_data.get("import_image_url_tag_golden_response") + ] + # elif "user_update_needed" in self._testMethodName: + # self.run_dnac_exec.side_effect = [ + # self.test_data.get("update_needed_get_user_response"), + # self.test_data.get("update_user_needed_get_role_response"), + # self.test_data.get("update_needed_user_response") + # ] + + def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): + """ + Test case for user role workflow manager when creating a user. + + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_import_image_url_tag_golden + ) + ) + result = self.execute_module(changed=True, failed=False) + print(result) + self.assertEqual( + result.get('response'), + "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + ) From 48019894b936367c9537dac479ccdeff4f49fbeb Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 09:57:53 -0700 Subject: [PATCH 017/121] demo test commit --- .../ccc_site_management/vars/vars_site_management.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/ccc_site_management/vars/vars_site_management.yml b/tests/integration/ccc_site_management/vars/vars_site_management.yml index 8ad35c6177..5cb360e62a 100644 --- a/tests/integration/ccc_site_management/vars/vars_site_management.yml +++ b/tests/integration/ccc_site_management/vars/vars_site_management.yml @@ -39,6 +39,7 @@ design_sites: + - site: building: name: BLD10 From 781baed742f185935d813e2ccbc452bfd09d54e0 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 10:05:59 -0700 Subject: [PATCH 018/121] demo test commit --- .../ccc_site_management/vars/vars_site_management.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/ccc_site_management/vars/vars_site_management.yml b/tests/integration/ccc_site_management/vars/vars_site_management.yml index 5cb360e62a..82df1088dc 100644 --- a/tests/integration/ccc_site_management/vars/vars_site_management.yml +++ b/tests/integration/ccc_site_management/vars/vars_site_management.yml @@ -39,7 +39,6 @@ design_sites: - - site: building: name: BLD10 @@ -49,6 +48,7 @@ design_sites: longitude: -78.8829258991226 country: United States site_type: building + - site: building: name: BLD11 From 96f39f113236666bbf5e471866c91dd7cd7c17f0 Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Thu, 8 Aug 2024 00:20:23 +0530 Subject: [PATCH 019/121] Write the code for the creation/updation/deletion of fabric sites and zone and also for the updation of authentication profile template in Cisco Catalyst Center and write the documentation and docstring for each API. Also write the playbook for the same and do the first level testing as well. --- .../fabric_sites_zones_workflow_manager.yml | 37 + .../fabric_sites_zones_workflow_manager.py | 1747 +++++++++++++++++ 2 files changed, 1784 insertions(+) create mode 100644 playbooks/fabric_sites_zones_workflow_manager.yml create mode 100644 plugins/modules/fabric_sites_zones_workflow_manager.py diff --git a/playbooks/fabric_sites_zones_workflow_manager.yml b/playbooks/fabric_sites_zones_workflow_manager.yml new file mode 100644 index 0000000000..9f49706fb9 --- /dev/null +++ b/playbooks/fabric_sites_zones_workflow_manager.yml @@ -0,0 +1,37 @@ +--- +- name: Configure fabric site/zones and authentication profile template in Cisco Catalyst Center + hosts: localhost + connection: local + gather_facts: no + vars_files: + - "input_fabric_sites_zones.yml" + - "credentials.yml" + tasks: + - name: Configure fabric sites/zones and authentication profile template in Cisco Catalyst Center. + cisco.dnac.fabric_sites_zones_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: DEBUG + dnac_log: true + config_verify: True + state: merged + config: + - fabric_sites: + - site_name: "{{item.fabric_sites.site_name}}" + site_type: "{{item.fabric_sites.site_type}}" + authentication_profile: "{{item.fabric_sites.authentication_profile}}" + is_pub_sub_enabled: "{{item.fabric_sites.is_pub_sub_enabled}}" + update_authentication_profile: + authentication_order: "{{item.fabric_sites.update_authentication_profile.authentication_order}}" + dot1x_fallback_timeout: "{{item.fabric_sites.update_authentication_profile.dot1x_fallback_timeout}}" + wake_on_lan: "{{item.fabric_sites.update_authentication_profile.wake_on_lan}}" + number_of_hosts: "{{item.fabric_sites.update_authentication_profile.number_of_hosts}}" + + with_items: "{{ fabric_sites}}" + tags: + - fabric_site_zones_testing diff --git a/plugins/modules/fabric_sites_zones_workflow_manager.py b/plugins/modules/fabric_sites_zones_workflow_manager.py new file mode 100644 index 0000000000..c950ec6ac9 --- /dev/null +++ b/plugins/modules/fabric_sites_zones_workflow_manager.py @@ -0,0 +1,1747 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright (c) 2022, Cisco Systems +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +__author__ = ("Abhishek Maheshwari, Madhan Sankaranarayanan") + +DOCUMENTATION = r""" +--- +module: fabric_sites_zones_workflow_manager +short_description: Configure the fabric site(s)/zone(s) and also updating the authentication profile template for + Cisco Catalyst Center Platform. +description: +- Creating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. +- Updating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. +- Creating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. +- Updating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. +- Deletes the fabric site(s) from Cisco Catalyst Center. +- Deletes the fabric zone(s) from Cisco Catalyst Center. +- Configure the authentication profile template for fabric site/zone in Cisco Catalyst Center. +version_added: '6.17.0' +extends_documentation_fragment: + - cisco.dnac.workflow_manager_params +author: Abhishek Maheshwari (@abmahesh) + Madhan Sankaranarayanan (@madhansansel) +options: + config_verify: + description: Set to True to verify the Cisco Catalyst Center config after applying the playbook config. + type: bool + default: False + state: + description: The state of Cisco Catalyst Center after module completion. + type: str + choices: [ merged, deleted ] + default: merged + config: + description: A list containing detailed configurations for creating, updating, or deleting fabric sites or zones + in a Software-Defined Access (SDA) environment. It also includes specifications for updating the authentication + profile template for these sites. Each element in the list represents a specific operation to be performed on + the SDA infrastructure, such as the addition, modification, or removal of fabric sites/zones, and modifications + to authentication profiles. + type: list + elements: dict + required: True + suboptions: + fabric_sites: + description: A dictionary that holds the detailed configuration for setting up or modifying REST Endpoints that will + receive Audit logs and Events from the Cisco Catalyst Center Platform. This dictionary is essential for specifying + the attributes and parameters required to manage the lifecycle of fabric sites and zones, as well as the associated + authentication profiles. + type: dict + suboptions: + site_name: + description: The full hierarchical name of the site within the SDA environment. This name uniquely identifies the site + for operations such as creation, update, or deletion of fabric sites or zones, as well as for updating the + authentication profile template. This parameter is mandatory for any operation related to fabric site/zone management. + type: str + required: True + site_type: + description: Specifies the type of site to be managed within the SDA environment. The acceptable values are 'fabric_site' + and 'fabric_zone'. The default value is 'fabric_site', indicating the configuration of a broader network area, whereas + 'fabric_zone' typically refers to a more specific segment within the site. + type: str + required: True + authentication_profile: + description: The authentication profile applied to the specified fabric. The profile determines the security posture and + controls applied to network access within the site. Possible values include 'Closed Authentication', 'Low Impact', + 'No Authentication', and 'Open Authentication'. This setting is critical during the creation or update of a fabric site + or when updating the authentication profile template. + type: str + is_pub_sub_enabled: + description: A boolean flag that indicates whether the pub/sub mechanism is enabled for control nodes in the fabric site. + This feature is only relevant during the creation or update of fabric sites, not fabric zones. When set to True, + pub/sub facilitates more efficient communication and control within the site. The default is True for fabric sites, + and this setting is not applicable for fabric zones. + type: bool + update_authentication_profile: + description: A dictionary containing the specific details required to update the authentication profile template associated + with the fabric site. This includes advanced settings that fine-tune the authentication process and security controls + within the site. + type: dict + suboptions: + authentication_order: + description: Specifies the primary method of authentication for the site. The available methods are 'dot1x' (IEEE 802.1X) + and 'mac' (MAC-based authentication). This setting determines the order in which authentication mechanisms are attempted. + type: str + dot1x_fallback_timeout: + description: The timeout duration, in seconds, for falling back from 802.1X authentication. This value must be within the + range of 3 to 120 seconds. It defines the period a device waits before attempting an alternative authentication method + if 802.1X fails. + type: int + wake_on_lan: + description: A boolean value indicating whether the Wake-on-LAN feature is enabled. Wake-on-LAN allows the network to + remotely wake up devices that are in a low-power state. + type: bool + number_of_hosts: + description: Specifies the number of hosts allowed per port. This can either be 'Single' for one device per port or + 'Unlimited' for multiple devices. This setting helps in controlling the network access and maintaining security. + type: str + enable_bpu_guard: + description: A boolean setting that enables or disables BPDU Guard. BPDU Guard provides a security mechanism by disabling + a port when a BPDU (Bridge Protocol Data Unit) is received, protecting against potential network loops. This setting + defaults to true and is only applicable when the authentication profile is set to "Closed Authentication". + type: bool + + +requirements: +- dnacentersdk >= 2.7.1 +- python >= 3.9 + +notes: + - To ensure the module operates correctly for scaled sets, which involve creating or updating fabric sites/zones and handling + the updation of authentication profile template, please provide valid input in the playbook. If any failure is encountered, + the module willhalt execution without proceeding to further operations. + - SDK Method used are + ccc_fabric_sites.FabricSitesZones.get_site + ccc_fabric_sites.FabricSitesZones.get_fabric_sites + ccc_fabric_sites.FabricSitesZones.get_fabric_zones + ccc_fabric_sites.FabricSitesZones.add_fabric_site + ccc_fabric_sites.FabricSitesZones.update_fabric_site + ccc_fabric_sites.FabricSitesZones.add_fabric_zone + ccc_fabric_sites.FabricSitesZones.update_fabric_zone + ccc_fabric_sites.FabricSitesZones.get_authentication_profiles + ccc_fabric_sites.FabricSitesZones.update_authentication_profile + ccc_fabric_sites.FabricSitesZones.delete_fabric_site_by_id + ccc_fabric_sites.FabricSitesZones.delete_fabric_zone_by_id + +""" + +EXAMPLES = r""" +- name: Create fabric site for sda with given name. + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: merged + config: + - fabric sites: + site_name: "Global/Test_SDA/Bld1" + authentication_profile: "Closed Authentication" + is_pub_sub_enabled: False + +- name: Update fabric site for sda with given name. + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: merged + config: + - fabric sites: + site_name: "Global/Test_SDA/Bld1" + authentication_profile: "Open Authentication" + +- name: Create fabric zone for sda with given name. + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: merged + config: + - fabric sites: + site_name: "Global/Test_SDA/Bld1/Floor1" + site_type: "fabric_zone" + authentication_profile: "Closed Authentication" + +- name: Update fabric zone for sda with given name. + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: merged + config: + - fabric sites: + site_name: "Global/Test_SDA/Bld1/Floor1" + site_type: "fabric_zone" + authentication_profile: "Open Authentication" + +- name: Update/customise authentication profile template for fabric site/zone. + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: merged + config: + - fabric_sites: + site_name: "Global/Test_SDA/Bld1" + site_type: "fabric_zone" + authentication_profile: "Open Authentication" + is_pub_sub_enabled: False + update_authentication_profile: + authentication_order: "dot1x" + dot1x_fallback_timeout: 28 + wake_on_lan: False + number_of_hosts: "Single" + +- name: Deleting/removing fabric site from sda from Cisco Catalyst Center + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: deleted + config: + - fabric_sites: + site_name: "Global/Test_SDA/Bld1" + +- name: Deleting/removing fabric zone from sda from Cisco Catalyst Center + cisco.dnac.events_and_notifications_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: False + state: deleted + config: + - fabric_sites: + site_name: "Global/Test_SDA/Bld1/Floor1" + site_type: "fabric_zone" + +""" + +RETURN = r""" + +dnac_response: + description: A dictionary or list with the response returned by the Cisco Catalyst Center Python SDK + returned: always + type: dict + sample: > + { + "response": { + "taskId": "string", + "url": "string" + }, + "version": "string" + } +""" + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( + DnacBase, + validate_list_of_dicts, +) +import time + + +class FabricSitesZones(DnacBase): + """Class containing member attributes for fabric sites and zones workflow manager module""" + + def __init__(self, module): + super().__init__(module) + self.supported_states = ["merged", "deleted"] + self.create_site, self.update_site, self.no_update_site = [], [], [] + self.create_zone, self.update_zone, self.no_update_zone = [], [], [] + self.update_auth_profile, self.no_update_profile = [], [] + self.delete_site, self.delete_zone, self.absent_site, self.absent_zone = [], [], [], [] + + def validate_input(self): + """ + Validate the fields provided in the playbook. + Checks the configuration provided in the playbook against a predefined specification + to ensure it adheres to the expected structure and data types. + Parameters: + self: The instance of the class containing the 'config' attribute to be validated. + Returns: + The method returns an instance of the class with updated attributes: + - self.msg: A message describing the validation result. + - self.status: The status of the validation (either 'success' or 'failed'). + - self.validated_config: If successful, a validated version of the 'config' parameter. + Example: + To use this method, create an instance of the class and call 'validate_input' on it. + If the validation succeeds, 'self.status' will be 'success' and 'self.validated_config' + will contain the validated configuration. If it fails, 'self.status' will be 'failed', and + 'self.msg' will describe the validation issues. + """ + + temp_spec = { + 'fabric_sites': { + 'type': 'list', + 'elements': 'dict', + 'site_name': {'type': 'str'}, + 'site_type': {'type': 'str', 'default': 'fabric_site'}, + 'authentication_profile': {'type': 'str'}, + 'is_pub_sub_enabled': {'type': 'bool', 'default': False}, + 'update_authentication_profile': { + 'elements': 'dict', + 'site_name_hierarchy': {'type': 'str'}, + 'authentication_profile': {'type': 'str'}, + 'authentication_order': {'type': 'str'}, + 'dot1x_fallback_timeout': {'type': 'int'}, + 'wake_on_lan': {'type': 'bool'}, + 'number_of_hosts': {'type': 'str'}, + 'enable_bpu_guard': {'type': 'bool'} + } + }, + } + + # Validate device params + valid_temp, invalid_params = validate_list_of_dicts( + self.config, temp_spec + ) + + if invalid_params: + self.msg = "The playbook contains invalid parameters: {0}".format(invalid_params) + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + self.validated_config = valid_temp + self.msg = "Successfully validated playbook configuration parameters using 'validate_input': {0}".format(str(valid_temp)) + self.log(self.msg, "INFO") + self.status = "success" + + return self + + def get_site_id(self, site_name): + """ + Retrieves the site IDs for a given site name from the Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site_name (str): The complete name of site for which the site ID need to be retrieved. + Returns: + str: A site ID corresponding to the provided site name. + Description: + This function invokes an API to fetch the details of each site from the Cisco Catalyst Center. If the + site is found, its site ID is extracted and added to the list of site IDs. + The function logs messages for successful API responses, missing site, and any errors + encountered during the process. The final site ID is returned. + """ + + try: + response = self.dnac._exec( + family="sites", + function='get_site', + op_modifies=True, + params={"name": site_name}, + ) + self.log("Received API response from 'get_site': {0}".format(str(response)), "DEBUG") + response = response.get('response') + + if not response: + self.status = "failed" + self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) + self.log(self.msg, "ERROR") + self.check_return_status() + site_id = response[0].get("id") + + if not site_id: + self.status = "failed" + self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) + self.log(self.msg, "ERROR") + self.check_return_status() + + except Exception as e: + self.status = "failed" + self.msg = """Error while getting the details of Site with given name '{0}' present in + Cisco Catalyst Center: {1}""".format(site_name, str(e)) + self.log(self.msg, "ERROR") + self.check_return_status() + + return site_id + + def get_fabric_site_detail(self, site_name, site_id): + """ + Retrieves the detailed information of a fabric site from the Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site_name (str): The complete name of the site for which the details need to be retrieved. + site_id (str): The unique identifier of the site in the Cisco Catalyst Center. + Returns: + dict or None: A dictionary containing the details of the fabric site if found. + Returns None if the site is not a fabric site or if an error occurs. + Description: + This function invokes an API to fetch the details of a specified fabric site from the Cisco Catalyst Center + based on the provided site ID. It logs the API response, checks if the response contains data, + and extracts the first site's details. If the site is not found or is not a fabric site, it returns None. + In case of any exceptions during the API call, it logs an error message, sets the status to "failed", + and invokes the check_return_status method to handle the failure. + """ + + try: + response = self.dnac._exec( + family="sda", + function='get_fabric_sites', + op_modifies=True, + params={"site_id": site_id}, + ) + response = response.get("response") + self.log("Received API response from 'get_fabric_sites' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") + + if not response: + self.log("Given site '{0}' is not a fabric site in Cisco Catalyst Center.".format(site_name), "INFO") + return None + site_detail = response[0] + return site_detail + except Exception as e: + self.status = "failed" + self.msg = """Error while getting the details of Site with given name '{0}' present in + Cisco Catalyst Center: {1}""".format(site_name, str(e)) + self.log(self.msg, "ERROR") + self.check_return_status() + + return None + + def get_fabric_zone_detail(self, site_name, site_id): + """ + Retrieves the detailed information of a fabric zone from the Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site_name (str): The complete name of the site for which the fabric zone details need to be retrieved. + site_id (str): The unique identifier of the site in the Cisco Catalyst Center. + Returns: + dict or None: A dictionary containing the details of the fabric zone if found, + or None if the site is not a fabric zone or an error occurs. + Description: + This function sends an API request to the Cisco Catalyst Center to fetch the details of a fabric zone + specified by the given site ID. It logs the API response and checks if the response contains data. + If the site is not recognized as a fabric zone, the function returns None. + In the event of an exception, an error message is logged, the status is set to "failed", + and the check_return_status method is called to handle the failure. + """ + + try: + response = self.dnac._exec( + family="sda", + function='get_fabric_zones', + op_modifies=True, + params={"site_id": site_id}, + ) + response = response.get("response") + self.log("Received API response from 'get_fabric_zones' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") + + if not response: + self.log("Given site '{0}' is not a fabric zone in Cisco Catalyst Center.".format(site_name), "INFO") + return None + + site_detail = response[0] + return site_detail + + except Exception as e: + self.status = "failed" + self.msg = """Error while getting the details of fabric zone '{0}' present in + Cisco Catalyst Center: {1}""".format(site_name, str(e)) + self.log(self.msg, "ERROR") + self.check_return_status() + + return None + + def get_have(self, config): + """ + Retrieves the current state of fabric sites and zones from the Cisco Catalyst Center based on the given configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): A configuration dictionary containing details about the fabric sites and zones. + The key "fabric_sites" should contain a list of dictionaries. + Returns: + self (object): The instance of the class with the updated `have` attribute containing the current state + of fabric sites and zones. + Description: + This function processes the provided configuration to determine the current state of fabric sites + and zones in the Cisco Catalyst Center. It iterates over the "fabric_sites" list in the configuration, + extracting the site name and type. For each site, it retrieves the corresponding site or zone ID + and details using the `get_site_id`, `get_fabric_site_detail`, and `get_fabric_zone_detail` methods. + The `have` attribute of the instance is updated with this dictionary, representing the current state + of the system. The function logs the final state and returns the instance for further use. + """ + + have = {} + + if config.get("fabric_sites"): + fabric_sites = config.get("fabric_sites") + fabric_sites_ids, fabric_zone_ids = [], [] + + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) + + if site_type == "fabric_site": + site_detail = self.get_fabric_site_detail(site_name, site_id) + if site_detail: + fabric_id = site_detail.get("siteId") + fabric_sites_ids.append(fabric_id) + else: + zone_detail = self.get_fabric_zone_detail(site_name, site_id) + if zone_detail: + fabric_id = zone_detail.get("siteId") + fabric_zone_ids.append(fabric_id) + + if fabric_sites_ids: + self.log("Fabric site with site ids present in Cisco Catalyst Center are : {0}".format(fabric_sites_ids), "DEBUG") + have["fabric_sites_ids"] = fabric_sites_ids + else: + have["fabric_sites_ids"] = [] + + if fabric_zone_ids: + self.log("Fabric zone with site ids present in Cisco Catalyst Center are : {0}".format(fabric_zone_ids), "DEBUG") + have["fabric_zone_ids"] = fabric_zone_ids + else: + have["fabric_zone_ids"] = [] + + self.have = have + self.log("Current State (have): {0}".format(str(have)), "INFO") + + return self + + def get_want(self, config): + """ + Collects and validates the desired state configuration for fabric sites and zones from the given playbook configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): A dictionary containing the configuration for the desired state of fabric sites and zones. + It should include a key "fabric_sites" with a list of dictionaries. + Returns: + self (object): The instance of the class with the updated `want` attribute containing the validated desired state + of fabric sites and zones and updating authentication profile template. + Description: + This function processes the provided playbook configuration to determine the desired state of fabric sites + and zones in the Cisco Catalyst Center. + The validated site information is stored in the `want` dictionary under the key "fabric_sites". + The `want` attribute of the instance is updated with this dictionary, representing the desired state + of the system. The function returns the instance for further processing or method chaining. + """ + + want = {} + + if config.get("fabric_sites"): + site_detail = config.get("fabric_sites") + fabric_site_info = [] + + for site in site_detail: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + + if not site_name: + self.status = "failed" + self.msg = ( + "Required parameter 'site_name' must be given in the playbook in order to perform any fabric site/zone " + "operation including creation/updation/deletion in Cisco Catalyst Center." + ) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + if site_type not in ["fabric_site", "fabric_zone"]: + self.status = "failed" + self.msg = ( + "Invalid fabric site_type '{0}' given in the playbook. Please select one of the type 'fabric_site/fabric_zone' " + "for the creation/updation of fabric site/zone in Cisco Catalyst Center." + ).format(site_type) + self.log(self.msg, "ERROR") + return self + fabric_site_info.append(site) + + want["fabric_sites"] = fabric_site_info + + self.want = want + self.msg = "Successfully collected all parameters from the playbook for creating/updating the fabric sites/zones." + self.status = "success" + self.log("Desired State (want): {0}".format(str(self.want)), "INFO") + + return self + + def create_fabric_site(self, site): + """ + Creates a fabric site in the Cisco Catalyst Center using the provided site configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site (dict): A dictionary containing the details of the fabric site to be created. + Returns: + self (object): The instance of the class with updated status and result attributes reflecting the outcome + of the fabric site creation operation. + Description: + This function creates a fabric site in the Cisco Catalyst Center based on the configuration provided + in the `site` dictionary. + The function constructs the payload for the API request, which includes the site ID, authentication profile, + and an optional flag for PubSub enablement. The payload is then sent to the `add_fabric_site` API endpoint. + After the API call, the function monitors the status of the task using the `get_task_details` method. + If the task encounters an error, the function logs the error and sets the status to "failed". If the task completes + successfully and contains the necessary data, the status is set to "success", and the site is marked as created. + """ + + try: + fabric_site_payload = [] + site_name = site.get("site_name") + auth_profile = site.get("authentication_profile") + if not auth_profile: + self.status = "failed" + self.msg = ( + "Required parameter 'authentication_profile'is missing needed for creation of fabric sites in Cisco Catalyst Center. " + "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" + ", 'No Authentication', 'Open Authentication'] in the playbook." + ) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + site_payload = { + "siteId": self.get_site_id(site_name), + "authenticationProfileName": site.get("authentication_profile"), + "isPubSubEnabled": site.get("is_pub_sub_enabled", False) + } + fabric_site_payload.append(site_payload) + self.log("Requested payload for creating fabric site '{0}' is: {1}".format(site_name, str(site_payload)), "INFO") + + response = self.dnac._exec( + family="sda", + function='add_fabric_site', + op_modifies=True, + params={'payload': fabric_site_payload} + ) + self.log("Received API response from 'add_fabric_site' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the creation of fabric site as the 'add_fabric_site' response is empty." + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to create the Fabric site '{0}' because of {1}.".format(site_name, failure_reason) + else: + self.msg = "Unable to create the Fabric site '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + self.create_site.append(site_name) + self.log("Fabric site '{0}' created successfully in the Cisco Catalyst Center".format(site_name), "INFO") + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "An exception occured while creating the fabric site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def fabric_site_needs_update(self, site, site_in_ccc): + """ + Determines if a fabric site in Cisco Catalyst Center needs to be updated based on the provided configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site (dict): A dictionary containing the desired configuration of the fabric site. + site_in_ccc (dict): A dictionary containing the current configuration of the fabric site as + present in the Cisco Catalyst Center. + Returns: + bool: True if the fabric site requires an update, False otherwise. + Description: + This function compares the desired configuration (`site`) of a fabric site with its current + configuration (`site_in_ccc`) in the Cisco Catalyst Center. + The function returns True, indicating that the fabric site needs to be updated. Otherwise, it returns False, + indicating no update is needed. + """ + + if site.get("authentication_profile") and site.get("authentication_profile") != site_in_ccc.get("authenticationProfileName"): + return True + + if site.get("is_pub_sub_enabled") is not None and site.get("is_pub_sub_enabled") != site_in_ccc.get("isPubSubEnabled"): + return True + + def update_fabric_site(self, site, site_in_ccc): + """ + Updates a fabric site in the Cisco Catalyst Center based on the provided configuration and current state. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + site (dict): A dictionary containing the desired configuration for the fabric site. + site_in_ccc (dict): A dictionary containing the current configuration of the fabric site + in the Cisco Catalyst Center. + Returns: + self (object): The instance of the class with updated status and result attributes reflecting the outcome + of the fabric site update operation. + Description: + This method updates a fabric site in the Cisco Catalyst Center. The constructed payload includes the site ID, + authentication profile name, and PubSub enablement status and payload is sent to the `update_fabric_site` + API endpoint. + After initiating the update, the method tracks the status of the update task using `get_task_details`. + It checks for task errors or successful completion, updating the status and logging messages accordingly. + If the task fails, an appropriate error message is logged, and the status is set to "failed". + """ + + try: + update_site_params = [] + site_name = site.get("site_name") + + if site.get("is_pub_sub_enabled") is None: + pub_sub_enable = site_in_ccc.get("isPubSubEnabled") + else: + pub_sub_enable = site.get("is_pub_sub_enabled") + + site_payload = { + "id": site_in_ccc.get("id"), + "siteId": site_in_ccc.get("siteId"), + "authenticationProfileName": site.get("authentication_profile") or site_in_ccc.get("authenticationProfileName"), + "isPubSubEnabled": pub_sub_enable + } + update_site_params.append(site_payload) + self.log("Requested payload for updating fabric site '{0}' is: {1}".format(site_name, str(site_payload)), "INFO") + + response = self.dnac._exec( + family="sda", + function='update_fabric_site', + op_modifies=True, + params={'payload': update_site_params} + ) + self.log("Received API response from 'update_fabric_site' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the updation of fabric site as the 'update_fabric_site' response is empty." + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to update the Fabric site '{0}' because of {1}.".format(site_name, failure_reason) + else: + self.msg = "Unable to update the Fabric site '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + self.update_site.append(site_name) + self.log("Fabric site '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "An exception occured while updating the fabric site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def create_fabric_zone(self, zone): + """ + Creates a fabric zone in the Cisco Catalyst Center based on the provided configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + zone (dict): A dictionary containing the desired configuration for the fabric zone. + Returns: + self (object): The instance of the class with updated status and result attributes reflecting the outcome + of the fabric zone creation operation. + Description: + This method creates a fabric zone in the Cisco Catalyst Center and payload is sent to the `add_fabric_zone` + API endpoint. The method logs the requested payload and the API response. + After initiating the creation, the method monitors the task's status using `get_task_details`. + It checks for task errors or successful completion. If the task fails, an appropriate error message + is logged, and the status is set to "failed". If the task succeeds, the status is set to "success", + and the site name is added to the list of successfully created zones. + The function returns the class instance (`self`) with the updated attributes. + """ + + try: + fabric_zone_payload = [] + site_name = zone.get("site_name") + + zone_payload = { + "siteId": self.get_site_id(site_name), + "authenticationProfileName": zone.get("authentication_profile"), + } + fabric_zone_payload.append(zone_payload) + self.log("Requested payload for creating fabric zone '{0}' is: {1}".format(site_name, zone_payload), "INFO") + + response = self.dnac._exec( + family="sda", + function='add_fabric_zone', + op_modifies=True, + params={'payload': fabric_zone_payload} + ) + self.log("Received API response from 'add_fabric_zone' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the creation of fabric zone as the 'add_fabric_zone' response is empty." + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to create the Fabric zone '{0}' because of {1}.".format(site_name, failure_reason) + else: + self.msg = "Unable to create the Fabric zone '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + self.create_zone.append(site_name) + self.log("Fabric zone '{0}' created successfully in the Cisco Catalyst Center.".format(site_name), "INFO") + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "An exception occured while creating the fabric zone '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def update_fabric_zone(self, zone, zone_in_ccc): + """ + Updates an existing fabric zone in the Cisco Catalyst Center with the provided configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + zone (dict): A dictionary containing the desired updates for the fabric zone. + zone_in_ccc (dict): A dictionary containing the current configuration of the fabric zone + in the Cisco Catalyst Center. + Returns: + self (object): The instance of the class with updated status and result attributes reflecting the outcome + of the fabric zone update operation. + Description: + This method updates the configuration of a fabric zone in the Cisco Catalyst Center. + The constructed payload is sent to the `update_fabric_zone` API endpoint. The method logs the + requested payload and the API response. + After initiating the update, the method monitors the task's status using `get_task_details`. It checks + for task errors or successful completion. + The function returns the class instance (`self`) with the updated attributes. + """ + + try: + update_zone_params = [] + site_name = zone.get("site_name") + + zone_payload = { + "id": zone_in_ccc.get("id"), + "siteId": zone_in_ccc.get("siteId"), + "authenticationProfileName": zone.get("authentication_profile") or zone_in_ccc.get("authenticationProfileName") + } + update_zone_params.append(zone_payload) + self.log("Requested payload for updating fabric zone '{0}' is: {1}".format(site_name, zone_payload), "INFO") + + response = self.dnac._exec( + family="sda", + function='update_fabric_zone', + op_modifies=True, + params={'payload': update_zone_params} + ) + self.log("Received API response from 'update_fabric_zone' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the updation of fabric zone as the 'update_fabric_zone' response is empty." + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to update the Fabric zone '{0}' because of {1}.".format(site_name, failure_reason) + else: + self.msg = "Unable to update the Fabric zone '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + self.log("Fabric zone '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") + self.update_zone.append(site_name) + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "An exception occured while updating the fabric zone '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def validate_auth_profile_parameters(self, auth_profile_dict): + """ + Validates the parameters provided for updating the authentication profile template. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + auth_profile_dict (dict): A dictionary containing the parameters for the authentication profile. + Returns: + self (objetc): The instance of the class with updated status and result attributes if invalid parameters are found. + Description: + This method checks the validity of the provided parameters for the authentication profile template. It validates + the "authentication_order" to ensure it is either "dot1x" or "mac". For "dot1x_fallback_timeout", it ensures the + value is an integer within the range of 3 to 120. The "number_of_hosts" must be either "Single" or "Unlimited". + If any invalid parameters are found, they are added to the `invalid_auth_profile_list`. Corresponding error messages + are logged, and the status is set to "failed". The method also logs warnings for any exceptions encountered during + the validation process. + """ + + invalid_auth_profile_list = [] + auth_order = auth_profile_dict.get("authentication_order") + fall_timeout = auth_profile_dict.get("dot1x_fallback_timeout") + number_of_hosts = auth_profile_dict.get("number_of_hosts") + + if auth_order and auth_order not in ["dot1x", "mac"]: + invalid_auth_profile_list.append("authentication_order") + msg = ( + "Invalid authentication_order '{0}'given in the playbook for the updation of authentication profile template. " + "Please provide one of the following authentication_order ['dot1x', 'mac'] in the playbook." + ).format(auth_order) + self.log(msg, "ERROR") + + if fall_timeout: + try: + timeout = int(fall_timeout) + if timeout not in range(3, 121): + invalid_auth_profile_list.append("dot1x_fallback_timeout") + msg = ( + "Invalid 'dot1x_fallback_timeout' '{0}' given in the playbook. Please select the timeout within the range " + " of numbers (3, 120) and provide it in the playbook." + ).format(timeout) + self.log(msg, "ERROR") + except Exception as e: + invalid_auth_profile_list.append("dot1x_fallback_timeout") + msg = ( + "Invalid 'dot1x_fallback_timeout' '{0}' string given in the playbook, unable to convert it into the integer. " + "Please select the timeout within the range of numbers (3, 120) and provide it in the playbook." + ).format(fall_timeout) + self.log(msg, "WARNING") + + if number_of_hosts and number_of_hosts.title() not in ["Single", "Unlimited"]: + invalid_auth_profile_list.append("number_of_hosts") + msg = ( + "Invalid number_of_hosts '{0}'given in the playbook for the updation of authentication profile template. " + "Please provide one of the following number_of_hosts ['Single', 'Unlimited'] in the playbook." + ).format(auth_order) + self.log(msg, "ERROR") + + if invalid_auth_profile_list: + self.status = "failed" + self.msg = ( + "Following invalid input parameter '{0}' given in the playbook due to this unable to " + "perform the action for updating the authentication profile template based on user input." + ).format(invalid_auth_profile_list) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + + return self + + def get_authentication_profile(self, fabric_id, auth_profile, site_name): + """ + Retrieves the details of an authentication profile for a given fabric and site from the Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + fabric_id (str): The ID of the fabric to which the authentication profile belongs. + auth_profile (str): The name of the authentication profile to retrieve. + site_name (str): The name of the site associated with the authentication profile. + Returns: + dict or None: A dictionary containing the details of the authentication profile if found, or None if no profile is associated + with the site or if an error occurs. + Description: + This method sends a request to the Cisco Catalyst Center to fetch the authentication profile details based on the provided + `fabric_id` and `auth_profile` name. The `site_name` is used for logging purposes to provide context in the logs. + If the response contains authentication profile details, these details are returned. If no profile is found or if an error + occurs during the request, the method logs an appropriate message and returns `None`. + """ + + try: + response = self.dnac._exec( + family="sda", + function='get_authentication_profiles', + op_modifies=True, + params={ + "fabric_id": fabric_id, + "authentication_profile_name": auth_profile + } + ) + response = response.get("response") + self.log("Received API response from 'get_authentication_profiles' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") + + if not response: + self.log("No Authentication profile asssociated to this site '{0}' in Cisco Catalyst Center.".format(site_name), "INFO") + return None + + profile_details = response[0] + return profile_details + except Exception as e: + self.status = "failed" + self.msg = ( + "Error while getting the details of authentication profiles for the site '{0}' present in " + "Cisco Catalyst Center: {1}" + ).format(site_name, str(e)) + self.log(self.msg, "ERROR") + self.check_return_status() + + return None + + def auth_profile_needs_update(self, auth_profile_dict, auth_profile_in_ccc): + """ + Determines if the authentication profile requires an update by comparing it with the existing profile in Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + auth_profile_dict (dict): A dictionary containing the desired authentication profile settings to compare. + auth_profile_in_ccc (dict): A dictionary containing the current authentication profile settings from Cisco Catalyst Center. + Returns: + bool: Returns `True` if any of the settings in `auth_profile_dict` differ from those in `auth_profile_in_ccc` and an update + is needed. Returns `False` if the settings match and no update is required. + Description: + This method compares the provided authentication profile settings (`auth_profile_dict`) with the current settings retrieved from + the Cisco Catalyst Center (`auth_profile_in_ccc`). It considers the possibility of an additional setting "enable_bpu_guard" if + the current profile is "Closed Authentication". + It iterates through a mapping of profile settings and checks if any of the settings require an update. If any discrepancies are + found, the method returns `True`. If all settings match, it returns `False`. + """ + + profile_key_mapping = { + "authentication_order": "authenticationOrder", + "dot1x_fallback_timeout": "dot1xToMabFallbackTimeout", + "wake_on_lan": "wakeOnLan", + "number_of_hosts": "numberOfHosts" + } + if auth_profile_in_ccc.get("authenticationProfileName") == "Closed Authentication": + profile_key_mapping["enable_bpu_guard"] = "isBpduGuardEnabled" + + for key, ccc_key in profile_key_mapping.items(): + if auth_profile_dict.get(key) is None: + continue + if key == "dot1x_fallback_timeout": + if int(auth_profile_dict.get(key)) != int(auth_profile_in_ccc.get(ccc_key)): + return True + elif auth_profile_dict.get(key) != auth_profile_in_ccc.get(ccc_key): + return True + + return False + + def collect_authentication_params(self, auth_profile_dict, auth_profile_in_ccc): + """ + Collects and prepares the updated authentication profile parameters based on the provided dictionary and the current profile in + Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + auth_profile_dict (dict): A dictionary containing the desired authentication profile settings. + auth_profile_in_ccc (dict): A dictionary containing the current authentication profile settings from Cisco Catalyst Center. + Returns: + list: A list containing a single dictionary with the updated authentication profile parameters. + Description: + This method prepares the updated parameters for an authentication profile by combining desired settings from `auth_profile_dict` with + the current settings from `auth_profile_in_ccc`. + It creates a dictionary with the ID, fabric ID, profile name, and updated settings for authentication order, dot1x fallback timeout, + number of hosts, and Wake-on-LAN. If the profile is "Closed Authentication," it also includes the BPDU guard setting. + The method returns a list containing the updated parameters in a dictionary, which can be used for further processing or API requests. + """ + + updated_params = [] + profile_name = auth_profile_in_ccc.get("authenticationProfileName") + authentications_params_dict = { + "id": auth_profile_in_ccc.get("id"), + "fabricId": auth_profile_in_ccc.get("fabricId"), + "authenticationProfileName": profile_name, + "authenticationOrder": auth_profile_dict.get("authentication_order") or auth_profile_in_ccc.get("authenticationOrder"), + "dot1xToMabFallbackTimeout": int(auth_profile_dict.get("dot1x_fallback_timeout")) or auth_profile_in_ccc.get("dot1xToMabFallbackTimeout"), + "numberOfHosts": auth_profile_dict.get("number_of_hosts") or auth_profile_in_ccc.get("numberOfHosts"), + } + + if auth_profile_dict.get("wake_on_lan") is None: + authentications_params_dict["wakeOnLan"] = auth_profile_in_ccc.get("wakeOnLan") + else: + authentications_params_dict["wakeOnLan"] = auth_profile_dict.get("wake_on_lan") + + if profile_name == "Closed Authentication": + if auth_profile_dict.get("enable_bpu_guard") is None: + auth_profile_dict["isBpduGuardEnabled"] = auth_profile_in_ccc.get("isBpduGuardEnabled", True) + else: + auth_profile_dict["isBpduGuardEnabled"] = auth_profile_dict.get("enable_bpu_guard") + + updated_params.append(authentications_params_dict) + + return updated_params + + def update_authentication_profile_template(self, profile_update_params, site_name): + """ + Updates the authentication profile template for a specified site in Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + profile_update_params (dict): A dictionary containing the parameters to update the authentication profile. + site_name (str): The name of the site where the authentication profile is being updated. + Returns: + self (object): Returns the current instance of the class with updated status and message attributes. + Description: + This method sends a request to update the authentication profile template for the specified site using the + provided parameters. It first logs the requested payload and sends it to the API for processing. + It then monitors the task status by polling until the update is complete. If the update is successful, + it logs a success message and appends the site name to the list of updated profiles. If an error occurs or + the task fails, it logs an error message and updates the status to "failed". + """ + + try: + self.log("Requested payload for updating authentication profile for site {0}: {1}".format(site_name, profile_update_params), "INFO") + response = self.dnac._exec( + family="sda", + function='update_authentication_profile', + op_modifies=True, + params={'payload': profile_update_params} + ) + self.log("Received API response from 'update_authentication_profile'for site {0}: {1}".format(site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the updation of authentication profile for site '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to update the authentication profile for site '{0}' because of {1}.".format(site_name, failure_reason) + else: + self.msg = "Unable to update the authentication profile for site '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + self.update_auth_profile.append(site_name) + self.log("Authentication profile for the site '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "An exception occured while updating the authentication profile for site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def delete_fabric_site_zone(self, fabric_id, site_name, site_type): + """ + Deletes a fabric site or fabric zone from Cisco Catalyst Center. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + fabric_id (str): The ID of the fabric site or fabric zone to be deleted. + site_name (str): The name of the fabric site or fabric zone to be deleted. + site_type (str): The type of the entity to be deleted. Should be either "fabric_site" or "fabric_zone". + Returns: + self (object): Returns the current instance of the class with updated status and message attributes. + Description: + This method sends a request to delete a fabric site or fabric zone based on the provided `fabric_id` and `site_type`. + It determines the appropriate API function to call based on the `site_type`, either "delete_fabric_site_by_id" or + "delete_fabric_zone_by_id". It returns the class instance for further processing or chaining. + """ + + try: + if site_type == "fabric_site": + api_name = "delete_fabric_site_by_id" + type_name = "fabric site" + else: + api_name = "delete_fabric_zone_by_id" + type_name = "fabric zone" + + response = self.dnac._exec( + family="sda", + function=api_name, + op_modifies=True, + params={"id": fabric_id}, + ) + self.log("Received API response from '{0}' for the site {1}: {2}".format(api_name, site_name, str(response)), "DEBUG") + response = response.get("response") + + if not response: + self.status = "failed" + self.msg = "Unable to fetch the task Id for the deletion of {0}: '{1}'.".format(type_name, site_name) + self.log(self.msg, "ERROR") + return self + + task_id = response.get("taskId") + + while True: + task_details = self.get_task_details(task_id) + + if task_details.get("isError"): + self.status = "failed" + failure_reason = task_details.get("failureReason") + if failure_reason: + self.msg = "Unable to delete {0} '{1}' because of {2}.".format(type_name, site_name, failure_reason) + else: + self.msg = "Unable to delete {0} '{1}'.".format(type_name, site_name) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + break + elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + self.status = "success" + if site_type == "fabric_site": + self.delete_site.append(site_name) + else: + self.delete_zone.append(site_name) + self.log("{0} '{1}' deleted successfully from the Cisco Catalyst Center".format(type_name.title(), site_name), "INFO") + break + time.sleep(1) + except Exception as e: + self.status = "failed" + self.msg = "Exception occurred while deleting {0} '{1}' due to: {2}".format(type_name, site_name, str(e)) + self.log(self.msg, "ERROR") + + return self + + def update_site_zones_profile_messages(self): + """ + Updates and logs messages based on the status of fabric sites, fabric zones, and authentication profile templates. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Returns: + self (object): Returns the current instance of the class with updated `result` and `msg` attributes. + Description: + This method aggregates status messages related to the creation, update, or deletion of fabric sites, fabric zones, + and authentication profile templates. + It checks various instance variables (`create_site`, `update_site`, `no_update_site`, `create_zone`, `update_zone`, + `no_update_zone`, `update_auth_profile`, `no_update_profile`, `delete_site`, `absent_site`, `delete_zone`, `absent_zone`) + to determine the status and generates corresponding messages. + The method also updates the `result["response"]` attribute with the concatenated status messages. + """ + + self.result["changed"] = False + result_msg_list = [] + + if self.create_site: + create_site_msg = "Fabric site(s) '{0}' created successfully in Cisco Catalyst Center.".format(self.create_site) + result_msg_list.append(create_site_msg) + + if self.update_site: + update_site_msg = "Fabric site(s) '{0}' updated successfully in Cisco Catalyst Center.".format(self.update_site) + result_msg_list.append(update_site_msg) + + if self.no_update_site: + no_update_site_msg = "Fabric site(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_site) + result_msg_list.append(no_update_site_msg) + + if self.create_zone: + create_zone_msg = "Fabric zone(s) '{0}' created successfully in Cisco Catalyst Center.".format(self.create_zone) + result_msg_list.append(create_zone_msg) + + if self.update_zone: + update_zone_msg = "Fabric zone(s) '{0}' updated successfully in Cisco Catalyst Center.".format(self.update_zone) + result_msg_list.append(update_zone_msg) + + if self.no_update_zone: + no_update_zone_msg = "Fabric zone(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_zone) + result_msg_list.append(no_update_zone_msg) + + if self.update_auth_profile: + update_auth_msg = """Authentication profile template for site(s) '{0}' updated successfully in Cisco Catalyst + Center.""".format(self.update_auth_profile) + result_msg_list.append(update_auth_msg) + + if self.no_update_profile: + no_update_auth_msg = "Authentication profile template for site(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_profile) + result_msg_list.append(no_update_auth_msg) + + if self.delete_site: + delete_site_msg = "Fabric site(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format(self.delete_site) + result_msg_list.append(delete_site_msg) + + if self.absent_site: + absent_site_msg = "Unable to delete fabric site(s) '{0}' as they are not present in Cisco Catalyst Center.".format(self.absent_site) + result_msg_list.append(absent_site_msg) + + if self.delete_zone: + delete_zone_msg = "Fabric zone(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format(self.delete_zone) + result_msg_list.append(delete_zone_msg) + + if self.absent_zone: + absent_zone_msg = "Unable to delete fabric zone(s) '{0}' as they are not present in Cisco Catalyst Center.".format(self.absent_zone) + result_msg_list.append(absent_zone_msg) + + if self.create_site or self.update_site or self.create_zone or self.update_zone or self.delete_site or self.update_auth_profile: + self.result["changed"] = True + + self.msg = " ".join(result_msg_list) + self.log(self.msg, "INFO") + self.result["response"] = self.msg + + return self + + def get_diff_merged(self, config): + """ + Creates, updates, or deletes fabric sites and zones based on the provided configuration, and manages + authentication profile updates. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): A dictionary containing the configuration for fabric sites and zones and updating + authentication profile template. + Returns: + self (object): Returns the current instance of the class with updated attributes based on the operations performed. + Description: + This method processes the provided configuration to manage fabric sites and zones in Cisco Catalyst Center. + 1. Fabric Sites + - If 'fabric_sites' is present in the configuration, it iterates over the list of sites. + - Checks if the site needs to be created or updated based on its type ("fabric_site" or "fabric_zone"). + - Creates or updates the site as necessary. If the site does not need any updates, it logs this information. + 2. Authentication Profile + - If an `update_authentication_profile` parameter is provided, it validates and updates the authentication + profile template associated with the site. + - Ensures that the authentication profile is valid and performs updates if needed. + - If no update is necessary or if the profile is not present, it logs the appropriate messages. + """ + + # Create/Update Fabric sites/zones in Cisco Catalyst Center + if config.get('fabric_sites'): + fabric_sites = self.want.get('fabric_sites') + + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) + auth_profile = site.get("authentication_profile") + + if auth_profile and auth_profile not in ["Closed Authentication", "Low Impact", "No Authentication", "Open Authentication"]: + self.status = "failed" + self.msg = ( + "Invalid authentication_profile '{0}'given in the playbook for the creation of fabric site. " + "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" + ", 'No Authentication', 'Open Authentication'] in the playbook." + ).format(auth_profile) + + if site_type == "fabric_site": + # Check whether site is already fabric or not. + if site_id not in self.have.get("fabric_sites_ids"): + # Create the fabric site in Cisco Catalyst Center + self.create_fabric_site(site).check_return_status() + else: + # Check whether fabric site needs any update or not + site_in_ccc = self.get_fabric_site_detail(site_name, site_id) + require_update = self.fabric_site_needs_update(site, site_in_ccc) + if require_update: + self.update_fabric_site(site, site_in_ccc).check_return_status() + else: + self.status = "success" + self.no_update_site.append(site_name) + self.log("Fabric site '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") + else: + # Check whether site zone is already fabric or not. + if site_id not in self.have.get("fabric_zone_ids"): + # Create the fabric site in Cisco Catalyst Center + self.create_fabric_zone(site).check_return_status() + else: + # Check whether fabric site needs any update or not + zone_in_ccc = self.get_fabric_zone_detail(site_name, site_id) + if auth_profile and auth_profile != zone_in_ccc.get("authenticationProfileName"): + self.update_fabric_zone(site, zone_in_ccc).check_return_status() + else: + self.status = "success" + self.no_update_zone.append(site_name) + self.log("Fabric zone '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") + + # Updating/customising the default parameters for authentication profile template + if site.get("update_authentication_profile"): + if not auth_profile: + self.status = "failed" + self.msg = ( + "Required parameter 'authentication_profile' is missing needed for updation of Authentication Profile template. " + "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" + ", 'Open Authentication'] in the playbook." + ) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + if auth_profile == "No Authentication": + self.status = "success" + msg = ( + "Unable to update 'authentication_profile' for the site '{0}' as for the profile template 'No Authentication' updating " + "authentication_profile is not supported. Please provide one of the following authentication_profile ['Closed Authentication'" + ", 'Low Impact', 'Open Authentication'] in the playbook." + ).format(site_name) + self.log(msg, "INFO") + self.no_update_profile.append(site_name) + return self + + # With the given site id collect the fabric site/zone id + if site_type == "fabric_site": + site_detail = self.get_fabric_site_detail(site_name, site_id) + fabric_id = site_detail.get("id") + else: + zone_detail = self.get_fabric_zone_detail(site_name, site_id) + fabric_id = zone_detail.get("id") + + # Validate the playbook input parameter for updating the authentication profile + auth_profile_dict = site.get("update_authentication_profile") + self.validate_auth_profile_parameters(auth_profile_dict).check_return_status() + validate_msg = ( + "All the given parameter(s) '{0}' in the playbook for the updation of authentication " + " profile in SDA fabric site/zone are validated successfully." + ).format(auth_profile_dict) + self.log(validate_msg, "INFO") + auth_profile_in_ccc = self.get_authentication_profile(fabric_id, auth_profile, site_name) + + if not auth_profile_in_ccc: + self.status = "success" + msg = ( + "There is no authentication template profile associated to the site '{0}' " + "in the Cisco Catalyst Center so unable to update the profile parameters." + ).format(site_name) + self.log(self.msg, "INFO") + self.no_update_profile.append(site_name) + return self + + profile_needs_update = self.auth_profile_needs_update(auth_profile_dict, auth_profile_in_ccc) + if not profile_needs_update: + self.status = "success" + msg = ( + "Authentication profile for the site '{0}' does not need any update in the " + "Cisco Catalyst Center." + ).format(site_name) + self.log(msg, "INFO") + self.no_update_profile.append(site_name) + return self + + # Collect the authentication profile parameters for the update operation + profile_update_params = self.collect_authentication_params(auth_profile_dict, auth_profile_in_ccc) + self.update_authentication_profile_template(profile_update_params, site_name).check_return_status() + + return self + + def get_diff_deleted(self, config): + """ + Deletes fabric sites and zones from the Cisco Catalyst Center based on the provided configuration. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): A dictionary containing the configuration for fabric sites and zones. It may include: + - 'fabric_sites' - List of dictionaries, where each dictionary represents a fabric site or zone. + - 'site_name' - The name of the site or zone to be deleted. + - 'site_type'- Type of the site or zone, either "fabric_site" or "fabric_zone". Defaults to "fabric_site". + Returns: + self (object): Returns the current instance of the class with updated attributes based on the deletion operations performed. + Description: + This method processes the provided configuration to manage the deletion of fabric sites and zones in Cisco Catalyst Center. + - For Fabric Sites + - Verifies if the site exists in Cisco Catalyst Center. + - Deletes the site if it exists; otherwise, logs a message indicating the site is not present. + - For Fabric Zones + - Verifies if the zone exists in Cisco Catalyst Center. + - Deletes the zone if it exists; otherwise, logs a message indicating the zone is not present. + """ + + # Delete Fabric sites/zones from the Cisco Catalyst Center + if not config.get('fabric_sites'): + self.status = "success" + self.msg = "Unable to delete any fabric site/zone or authentication profile template as input is not given in the playbook." + + fabric_sites = self.want.get('fabric_sites') + + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + if not site_name: + self.status = "failed" + self.msg = "Unable to delete fabric site/zone as required parameter 'site_name' is not given in the playbook." + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + site_id = self.get_site_id(site_name) + + if site_type == "fabric_site": + # Check whether fabric site is present in Cisco Catalyst Center. + if site_id in self.have.get("fabric_sites_ids"): + site_detail = self.get_fabric_site_detail(site_name, site_id) + fabric_id = site_detail.get("id") + # Delete the fabric site from the Cisco Catalyst Center + self.delete_fabric_site_zone(fabric_id, site_name, site_type).check_return_status() + else: + self.status = "success" + self.absent_site.append(site_name) + self.log("Unable to delete fabric site '{0}' as it is not present in the Cisco Catalyst Center.".format(site_name), "INFO") + else: + # Check whether fabric zone is present in Cisco Catalyst Center. + if site_id in self.have.get("fabric_zone_ids"): + site_detail = self.get_fabric_zone_detail(site_name, site_id, ) + fabric_id = site_detail.get("id") + # Delete the fabric zone from the Cisco Catalyst Center + self.delete_fabric_site_zone(fabric_id, site_name, site_type).check_return_status() + else: + self.status = "success" + self.absent_zone.append(site_name) + self.log("Unable to delete fabric zone '{0}' as it is not present in the Cisco Catalyst Center.".format(site_name), "INFO") + + return self + + def verify_diff_merged(self, config): + """ + Verify the addition/update status of fabric site/zones in Cisco Catalyst Center. + Parameters: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): The configuration details to be verified. + Returns: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Description: + This method verifies whether the specified configurations have been successfully added/updated + in Cisco Catalyst Center as desired. + """ + + self.get_have(config) + self.log("Current State (have): {0}".format(str(self.have)), "INFO") + self.log("Desired State (want): {0}".format(str(self.want)), "INFO") + + if config.get('fabric_sites'): + fabric_sites = self.want.get('fabric_sites') + verify_site_list, verify_auth_list = [], [] + site_name_list, auth_name_list = [], [] + auth_flag = False + + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) + + if site_type == "fabric_site": + if site_id not in self.have.get("fabric_sites_ids"): + verify_site_list.append(site_name) + else: + site_name_list.append(site_name) + else: + if site_id not in self.have.get("fabric_zone_ids"): + verify_site_list.append(site_name) + else: + site_name_list.append(site_name) + + # Verifying updating/customising the default parameters for authentication profile template + if site.get("update_authentication_profile"): + auth_flag = True + # With the given site id collect the fabric site/zone id + if site_type == "fabric_site": + site_detail = self.get_fabric_site_detail(site_name, site_id) + fabric_id = site_detail.get("id") + auth_name_list.append(site_name) + else: + zone_detail = self.get_fabric_zone_detail(site_name, site_id) + fabric_id = zone_detail.get("id") + auth_name_list.append(site_name) + + if not fabric_id: + verify_auth_list.append(site_name) + + if not verify_site_list: + self.status = "success" + msg = ( + "Requested fabric site(s)/zone(s) '{0}' have been successfully added/updated to the Cisco Catalyst Center " + "and their addition/updation has been verified." + ).format(site_name_list) + self.log(msg, "INFO") + else: + msg = ( + "Playbook's input does not match with Cisco Catalyst Center, indicating that the fabric site(s) '{0}' " + " addition/updation task may not have executed successfully." + ).format(verify_site_list) + self.log(msg, "INFO") + + if not auth_flag: + return self + + if not verify_auth_list: + self.status = "success" + msg = ( + "Authentication template profile for the site(s) '{0}' have been successfully updated to the Cisco Catalyst Center " + "and their updation has been verified." + ).format(auth_name_list) + self.log(msg, "INFO") + else: + msg = ( + "Playbook's input does not match with Cisco Catalyst Center, indicating that the Authentication template " + "profile for the site(s) '{0}' updation task may not have executed successfully." + ).format(verify_auth_list) + self.log(msg, "INFO") + + return self + + def verify_diff_deleted(self, config): + """ + Verify the deletion status of fabric sites/zones fromt the Cisco Catalyst Center. + Parameters: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + config (dict): The configuration details to be verified. + Returns: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Description: + This method checks the deletion status of a configuration in Cisco Catalyst Center. + It validates whether the specified fabric site/zone deleted from Cisco Catalyst Center. + """ + + self.get_have(config) + self.log("Current State (have): {0}".format(str(self.have)), "INFO") + self.log("Desired State (want): {0}".format(str(self.want)), "INFO") + + fabric_sites = self.want.get('fabric_sites') + verify_site_list, site_name_list = [], [] + + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) + + if site_type == "fabric_site": + # Check whether fabric site is present in Cisco Catalyst Center. + if site_id in self.have.get("fabric_sites_ids"): + verify_site_list.append(site_name) + else: + site_name_list.append(site_name) + else: + # Check whether fabric zone is present in Cisco Catalyst Center. + if site_id in self.have.get("fabric_zone_ids"): + verify_site_list.append(site_name) + else: + site_name_list.append(site_name) + + if not verify_site_list: + self.status = "success" + msg = ( + "Requested fabric site(s)/zones(s) '{0}' have been successfully deleted from the Cisco Catalyst " + "Center and their deletion has been verified." + ).format(site_name_list) + self.log(msg, "INFO") + else: + msg = ( + "Playbook's input does not match with Cisco Catalyst Center, indicating that fabric site(s)/zones(s)" + " '{0}' deletion task may not have executed successfully." + ).format(verify_site_list) + + return self + + +def main(): + """ main entry point for module execution + """ + + element_spec = {'dnac_host': {'required': True, 'type': 'str'}, + 'dnac_port': {'type': 'str', 'default': '443'}, + 'dnac_username': {'type': 'str', 'default': 'admin', 'aliases': ['user']}, + 'dnac_password': {'type': 'str', 'no_log': True}, + 'dnac_verify': {'type': 'bool', 'default': 'True'}, + 'dnac_version': {'type': 'str', 'default': '2.2.3.3'}, + 'dnac_debug': {'type': 'bool', 'default': False}, + 'dnac_log_level': {'type': 'str', 'default': 'WARNING'}, + "dnac_log_file_path": {"type": 'str', "default": 'dnac.log'}, + "dnac_log_append": {"type": 'bool', "default": True}, + 'dnac_log': {'type': 'bool', 'default': False}, + 'validate_response_schema': {'type': 'bool', 'default': True}, + 'config_verify': {'type': 'bool', "default": False}, + 'dnac_api_task_timeout': {'type': 'int', "default": 1200}, + 'dnac_task_poll_interval': {'type': 'int', "default": 2}, + 'config': {'required': True, 'type': 'list', 'elements': 'dict'}, + 'state': {'default': 'merged', 'choices': ['merged', 'deleted']} + } + + module = AnsibleModule(argument_spec=element_spec, + supports_check_mode=False) + + ccc_fabric_sites = FabricSitesZones(module) + state = ccc_fabric_sites.params.get("state") + + if state not in ccc_fabric_sites.supported_states: + ccc_fabric_sites.status = "invalid" + ccc_fabric_sites.msg = "State {0} is invalid".format(state) + ccc_fabric_sites.check_return_status() + + ccc_fabric_sites.validate_input().check_return_status() + config_verify = ccc_fabric_sites.params.get("config_verify") + + for config in ccc_fabric_sites.validated_config: + ccc_fabric_sites.reset_values() + ccc_fabric_sites.get_want(config).check_return_status() + ccc_fabric_sites.get_have(config).check_return_status() + ccc_fabric_sites.get_diff_state_apply[state](config).check_return_status() + if config_verify: + ccc_fabric_sites.verify_diff_state_apply[state](config).check_return_status() + + # Invoke the API to check the status and log the output of each site/zone and authentication profile update on console. + ccc_fabric_sites.update_site_zones_profile_messages().check_return_status() + + module.exit_json(**ccc_fabric_sites.result) + + +if __name__ == '__main__': + main() From 558a71442cd501c69e2960a9351496493b11dfff Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Thu, 8 Aug 2024 00:42:27 +0530 Subject: [PATCH 020/121] Added the limitation of the module in the notes section of documentation --- plugins/modules/fabric_sites_zones_workflow_manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/modules/fabric_sites_zones_workflow_manager.py b/plugins/modules/fabric_sites_zones_workflow_manager.py index c950ec6ac9..c57adfc793 100644 --- a/plugins/modules/fabric_sites_zones_workflow_manager.py +++ b/plugins/modules/fabric_sites_zones_workflow_manager.py @@ -116,6 +116,8 @@ - To ensure the module operates correctly for scaled sets, which involve creating or updating fabric sites/zones and handling the updation of authentication profile template, please provide valid input in the playbook. If any failure is encountered, the module willhalt execution without proceeding to further operations. + - When deleting fabric sites, make sure to provide the input to remove the fabric zones associated with them in the + playbook. Fabric sites cannot be deleted until all underlying fabric zones have been removed. - SDK Method used are ccc_fabric_sites.FabricSitesZones.get_site ccc_fabric_sites.FabricSitesZones.get_fabric_sites From 56aa266c6ebe2e38eca1428c99954dad6070cb2b Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 17:06:04 -0700 Subject: [PATCH 021/121] added sda_extranet_policies_workflow_manager --- .../sda_extranet_policies_workflow_manager.py | 1373 +++++++++++++++++ 1 file changed, 1373 insertions(+) create mode 100644 plugins/modules/sda_extranet_policies_workflow_manager.py diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py new file mode 100644 index 0000000000..e114394be0 --- /dev/null +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -0,0 +1,1373 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2024, Cisco Systems +# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) + +"""Ansible module to perform Network Compliance Operations on devices in Cisco Catalyst Center.""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +__author__ = ("Rugvedi Kapse, Madhan Sankaranarayanan") + +DOCUMENTATION = r""" +--- +module: sda_extranet_policies_workflow_manager +short_description: SDA Extranet Policies Module provides functionality for managing SDA Extranet Policy in Cisco Catalyst Center. +description: +version_added: "6.17.0" +- Manage extranet policy operations such as add/update/delete. +- API to create a new extranet policy. +- API to update an existing or edit an existing extranet policy. +- API for deletion of an existing extranet policy using the policy name. +extends_documentation_fragment: + - cisco.dnac.workflow_manager_params +author: Rugvedi Kapse (@rukapse) + Madhan Sankaranarayanan (@madhansansel) +options: + config_verify: + description: Set to True to verify the Cisco Catalyst Center config after applying the playbook config. + type: bool + default: False + state: + description: State of Cisco Catalyst Center after module completion. + type: str + choices: [ merged, deleted ] + default: merged + config: + description: List of Extranet Policy Details for Creating, Updating, or Deleting Operations. + type: list + elements: dict + required: True + suboptions: + extranet_policy_name: + description: Name of the SDA Extranet Policy. + This parameter allows you to specify the desired name when creating a new extranet policy. + The same name can be used to update or delete the policy. + Note - This parameter is required when creating, updating or deleting extranet policy. + type: str + provider_virtual_network: + description: Specifies the Provider Virtual Network containing shared services resources that subscribers need to access. + If a virtual network is already defined as a Provider, it cannot be assigned as a provider again. + Ensure the default route is present in the Global Routing Table if INFRA_VN is defined as the Provider. + For Subscriber Virtual Networks with multiple Providers having overlapping routes, traffic will be + load-balanced across those Provider Virtual Networks. + This parameter is required when creating or updating extranet policy. + type: str + subscriber_virtual_networks: + description: Specifies a list of Subscriber Virtual Networks that require access to the Provider Virtual Network + containing shared services resources. + A Virtual Network previously defined as a Provider cannot be selected as a subscriber. + This parameter is required when creating or updating extranet policy. + type: list + elements: str + fabric_sites: + description: Specifies the Fabric Site(s) where this Extranet Policy will be applied. + The Provider Virtual Network must be added to a Fabric Site before applying the policy. + Fabric Site(s) connected to the same SD-Access Transit must have consistent Extranet Policies. + Selecting a Fabric Site connected to an SD-Access Transit will automatically select all other Sites connected to that Transit. + type: list + elements: str + + +requirements: +- dnacentersdk == 2.7.0 +- python >= 3.9 +notes: + - SDK Methods used are + sites.Sites.get_site + sda.SDA.get_fabric_sites + sda.SDA.get_extranet_policies + sda.SDA.add_extranet_policy + sda.SDA.update_extranet_policy + sda.SDA.delete_extranet_policy_by_id + task.Task.get_task_by_id + + - Paths used are + get /dna/intent/api/v1/site + get /dna/intent/api/v1/sda/fabricSites + get /dna/intent/api/v1/sda/extranetPolicies + post /dna/intent/api/v1/sda/extranetPolicies + put /dna/intent/api/v1/sda/extranetPolicies + delete dna/intent/api/v1/sda/extranetPolicies/${id} + get /dna/intent/api/v1/task/{taskId} + +""" + +EXAMPLES = r""" +- name: Create Extranet Policy + cisco.dnac.sda_extranet_policies_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: True + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_3"] + +- name: Create Extranet Policy with Fabric Site(s) specified + cisco.dnac.sda_extranet_policies_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: True + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_3"] + fabric_sites: ["Global/Test_Extranet_Polcies/USA", "Global/Test_Extranet_Polcies/India"] + +- name: Update existing Extranet Policy + cisco.dnac.sda_extranet_policies_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: True + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_4"] + +- name: Update existing Extranet Policy with Fabric Site(s) specified + cisco.dnac.sda_extranet_policies_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: True + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + fabric_sites: ["Global/Test_Extranet_Polcies/USA", "Global/Test_Extranet_Polcies/India"] + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_4"] + +- name: Delete Extranet Policy + cisco.dnac.sda_extranet_policies_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log_level: "{{dnac_log_level}}" + dnac_log: True + state: deleted + config: + - extranet_policy_name: "test_extranet_policy_1" +""" + +RETURN = r""" +#Case_1: Response when task is successful +sample_response_2: + description: A dictionary with the response returned by the Cisco Catalyst Center Python SDK + returned: always + type: dict + sample: > + { + "status": "string", + "changed": bool, + "msg": "string" + "response": { + "taskId": "string", + "url": "string" + }, + "version": "string" + } + +#Case_3: Response when Error Occurs +sample_response_3: + description: A dictionary with the response returned by the Cisco Catalyst Center Python SDK + returned: always + type: dict + sample: > + { + "changed": bool, + "msg": "string" + } +""" + +import time +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( + DnacBase, + validate_list_of_dicts +) + +class SDAExtranetPolicies(DnacBase): + """ + A class for managing Extranet Policies within the Cisco DNA Center using the SDA API. + """ + def __init__(self, module): + """ + Initialize an instance of the class. + Parameters: + - module: The module associated with the class instance. + Returns: + The method does not return a value. + """ + super().__init__(module) + + def validate_input(self): + """ + Validates the input configuration parameters for the playbook. + Returns: + object: An instance of the class with updated attributes: + - self.msg: A message describing the validation result. + - self.status: The status of the validation (either "success" or "failed"). + - self.validated_config: If successful, a validated version of the "config" parameter. + + Description: + This method validates the fields provided in the playbook against a predefined specification. + It checks if the required fields are present and if their data types match the expected types. + If any parameter is found to be invalid, it logs an error message and sets the validation status to "failed". + If the validation is successful, it logs a success message and returns an instance of the class + with the validated configuration. + """ + # Check if configuration is available + if not self.config: + self.status = "success" + self.msg = "Configuration is not available in the playbook for validation" + self.log(self.msg, "ERROR") + return self + + # Expected schema for configuration parameters + temp_spec = { + "extranet_policy_name": {"type": "str", "required": True}, + "fabric_sites": {"type": "list", "elements": "str", "required": False}, + "provider_virtual_network": {"type": "str", "required": False}, + "subscriber_virtual_networks": {"type": "list", "elements": "str", "required": False}, + } + + # Validate params + valid_temp, invalid_params = validate_list_of_dicts( + self.config, temp_spec + ) + + if invalid_params: + self.msg = "Invalid parameters in playbook: {0}".format(invalid_params) + self.update_result("failed", False, self.msg, "ERROR") + return self + + # Set the validated configuration and update the result with success status + self.validated_config = valid_temp + self.msg = "Successfully validated playbook configuration parameters using 'validated_input': {0}".format(str(valid_temp)) + self.update_result("success", False, self.msg, "INFO") + return self + + + def update_result(self, status, changed, msg, log_level, data=None): + """ + Update the result of the operation with the provided status, message, and log level. + Parameters: + - status (str): The status of the operation ("success" or "failed"). + - changed (bool): Indicates whether the operation caused changes. + - msg (str): The message describing the result of the operation. + - log_level (str): The log level at which the message should be logged ("INFO", "ERROR", "CRITICAL", etc.). + - data (dict, optional): Additional data related to the operation result. + Returns: + self (object): An instance of the class. + Note: + - If the status is "failed", the "failed" key in the result dictionary will be set to True. + - If data is provided, it will be included in the result dictionary. + """ + # Update the result attributes with the provided values + self.status = status + self.result["status"] = status + self.result["msg"] = msg + self.result["changed"] = changed + + # Log the message at the specified log level + self.log(msg, log_level) + + # If the status is "failed", set the "failed" key to True + if status == "failed": + self.result["failed"] = True + + # If additional data is provided, include it in the result dictionary + if data: + self.result["data"] = data + + return self + + def exit_while_loop(self, start_time, task_id, task_name, response): + """ + Check if the elapsed time exceeds the specified timeout period and exit the while loop if it does. + Parameters: + - start_time (float): The time when the while loop started. + - task_id (str): ID of the task being monitored. + - task_name (str): Name of the task being monitored. + - response (dict): Response received from the task status check. + Returns: + bool: True if the elapsed time exceeds the timeout period, False otherwise. + """ + # If the elapsed time exceeds the timeout period + if time.time() - start_time > self.params.get("dnac_api_task_timeout"): + if response.get("data"): + # If there is data in the response, include it in the error message + self.msg = "Task {0} with task id {1} has not completed within the timeout period. Task Status: {2} ".format( + task_name, task_id, response.get("data")) + else: + # If there is no data in the response, generate a generic error message + self.msg = "Task {0} with task id {1} has not completed within the timeout period.".format( + task_name, task_id) + + # Update the result with failure status and log the error message + self.update_result("failed", False, self.msg, "ERROR") + return True + + return False + + def get_fabric_ids_list(self, site_details): + """ + Extracts a list of fabric IDs from the provided site details. + Parameters: + - site_details (dict): A dictionary containing site information. Each key-value pair + represents a site, where the value is another dictionary that + includes a 'fabric_id'. + Returns: + list: A list of fabric IDs extracted from the site details. + Description: + This method iterates over the values in the provided site_details dictionary, extracts + the 'fabric_id' from each value, and appends it to a list. The resulting list of fabric IDs + is then returned. + """ + # Initialize an empty list to store fabric IDs + fabric_ids_list = [] + + # Iterate over each site's information in the site details + for site_info in site_details.values(): + fabric_ids_list.append(site_info['fabric_id']) + return fabric_ids_list + + def validate_merged_parameters(self, config): + """ + Validate that the required parameters are present in the configuration for performing + Add or Update Extranet Policy operations. + Parameters: + - config (dict): A dictionary containing the configuration parameters to be validated. + Returns: + None: This function does not return a value. It logs messages and raises exceptions + if required parameters are missing. + Description: + This method checks the provided configuration for the presence of the required parameters: + 'provider_virtual_network' and 'subscriber_virtual_networks'. If any of these parameters + are missing, it logs an error message and raises an exception to halt execution. If all + required parameters are present, it logs a success message indicating successful validation. + """ + # Check for provider_virtual_network + provider_virtual_network = config.get("provider_virtual_network") + if provider_virtual_network is None: + msg = ("Missing required parameter: 'provider_virtual_network'. " + "(extranet_policy_name, provider_virtual_network, and subscriber_virtual_networks) - " + "are the required parameters for performing Add or Update Extranet Policy operations.") + self.log(msg, "ERROR") + self.module.fail_json(msg) + + # Check for subscriber_virtual_networks + subscriber_virtual_networks = config.get("subscriber_virtual_networks") + if subscriber_virtual_networks is None: + msg = ( + "Missing required parameter: 'subscriber_virtual_networks'. " + "(extranet_policy_name, provider_virtual_network, and subscriber_virtual_networks) - " + "are the required parameters for performing Add or Update Extranet Policy operations." + ) + self.log(msg, "ERROR") + self.module.fail_json(msg) + + self.log( + "Successfully validated that the required parameters — (extranet_policy_name, " + "provider_virtual_network, and subscriber_virtual_networks) are provided", + "INFO" + ) + + + def get_add_extranet_policy_params(self, config, site_details=None): + """ + Generate parameters required for adding an Extranet Policy based on the provided configuration and site details. + Parameters: + - config (dict): A dictionary containing the configuration parameters. + - site_details (dict, optional): A dictionary containing site details. Default is None. + Returns: + dict: A dictionary containing the parameters for adding an Extranet Policy. + Description: + This method constructs a dictionary of parameters required for adding an Extranet Policy. + It includes the 'extranetPolicyName', 'providerVirtualNetworkName', and 'subscriberVirtualNetworkNames' + from the configuration. If 'fabric_sites' are provided in the configuration and site details are available, + it also includes the 'fabricIds' obtained from the site details. + """ + # Initialize the parameters dictionary with basic required parameters + add_extranet_policy_params = { + "extranetPolicyName": config.get("extranet_policy_name"), + "providerVirtualNetworkName": config.get("provider_virtual_network"), + "subscriberVirtualNetworkNames": config.get("subscriber_virtual_networks") + } + + # Check if 'fabric_sites' are provided and site details are available + if config.get("fabric_sites") and site_details: + add_extranet_policy_params["fabricIds"] = self.get_fabric_ids_list(site_details) + + return add_extranet_policy_params + + def get_update_extranet_policy_params(self, config, extranet_policy_id, site_details=None): + """ + Generate parameters required for updating an Extranet Policy based on the provided configuration, + policy ID, and site details. + Parameters: + config (dict): A dictionary containing the configuration parameters. + extranet_policy_id (str): The ID of the Extranet Policy to be updated. + site_details (dict, optional): A dictionary containing site details. Default is None. + Returns: + dict: A dictionary containing the parameters for updating an Extranet Policy. + Description: + This method constructs a dictionary of parameters required for updating an Extranet Policy. + It includes the 'id' of the policy, 'extranetPolicyName', 'providerVirtualNetworkName', and + 'subscriberVirtualNetworkNames' from the configuration. If 'fabric_sites' are provided in the + configuration and site details are available, it also includes the 'fabricIds' obtained from the + site details. + """ + # Initialize the parameters dictionary with basic required parameters + update_extranet_policy_params = { + "id": extranet_policy_id, + "extranetPolicyName": config.get("extranet_policy_name"), + "providerVirtualNetworkName": config.get("provider_virtual_network"), + "subscriberVirtualNetworkNames": config.get("subscriber_virtual_networks") + } + + # Check if 'fabric_sites' are provided and site details are available + if config.get("fabric_sites") and site_details: + update_extranet_policy_params["fabricIds"] = self.get_fabric_ids_list(site_details) + + return update_extranet_policy_params + + def get_delete_extranet_policy_params(self, extranet_policy_id): + """ + Generate parameters required for deleting an Extranet Policy based on the provided policy ID. + Parameters: + extranet_policy_id (str): The unique identifier of the Extranet Policy to be deleted. + Returns: + dict: A dictionary containing the parameters for deleting an Extranet Policy. + Description: + This method constructs a dictionary of parameters required for deleting an Extranet Policy. + It includes the 'id' of the policy, which is necessary for identifying the specific policy + to be deleted. + """ + # Create a dictionary with the extranet policy ID + delete_extranet_policy_params = { + "id": extranet_policy_id + } + + return delete_extranet_policy_params + + def validate_site_exists(self, site_name): + """ + Checks the existence of a site in Cisco Catalyst Center. + Parameters: + site_name (str): The name of the site to be checked. + Returns: + tuple: A tuple containing two values: + - site_exists (bool): Indicates whether the site exists (True) or not (False). + - site_id (str or None): The ID of the site if it exists, or None if the site is not found. + Description: + This method queries Cisco Catalyst Center to determine if a site with the provided name exists. + If the site is found, it sets "site_exists" to True and retrieves the site"s ID. + If the site does not exist, "site_exists" is set to False, and "site_id" is None. + If an exception occurs during the site lookup, an error message is logged, and the module fails. + """ + site_exists = False + site_id = None + response = None + + # Attempt to retrieve site information from Catalyst Center + try: + response = self.dnac._exec( + family="sites", + function="get_site", + op_modifies=True, + params={"name": site_name}, + ) + self.log("Response received post 'get_site' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response["response"]: + site = response.get("response") + site_id = site[0].get("id") + site_exists = True + else: + self.log("No response received from the 'get_site' API call.", "WARNING") + + except Exception as e: + # Log an error message and fail if an exception occurs + self.log("An error occurred while retrieving site details for Site '{0}' using 'get_site' API call: {1}".format(site_name, str(e)), "ERROR") + + if not site_exists: + self.msg = "An error occurred while retrieving site details for Site '{0}'. Please verify that the site exists.".format(site_name) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + return (site_exists, site_id) + + def get_site_details(self, fabric_sites): + """ + Retrieve details for each site in the provided fabric sites list. + Parameters: + - fabric_sites (list): A list of site names to be validated and detailed. + Returns: + dict: A dictionary containing the details for each site, including existence and site ID. + Description: + This method takes a list of fabric sites and checks if each site exists using the validate_site_exists method. + It constructs a dictionary where each key is a site name and the value is another dictionary containing + 'site_exists' (a boolean indicating if the site exists) and 'site_id' (the unique identifier of the site). + """ + # Initialize an empty dictionary to store site details + site_details = {} + + # Iterate over each site in the provided fabric sites list + for site in fabric_sites: + # Validate if the site exists and retrieve its ID + (site_exists, site_id) = self.validate_site_exists(site) + site_details[site] = { + "site_exists": site_exists, + "site_id": site_id, + } + + return site_details + + def get_fabric_sites(self, site_name, site_id): + """ + Retrieve the fabric ID for a given site using the SDA 'get_fabric_sites' API call. + Parameters: + - site_name (str): The name of the site. + - site_id (str): The unique identifier of the site. + Returns: + str: The fabric ID if found, otherwise None. + Description: + This method calls the SDA 'get_fabric_sites' API to retrieve the fabric ID for a specified site. It logs the response, + processes the response to extract the fabric ID, and handles any exceptions that occur during the API call. + """ + try: + # Call the SDA 'get_fabric_sites' API with the provided site ID + response = self.dnac._exec( + family="sda", + function="get_fabric_sites", + op_modifies=True, + params={"siteId": site_id}, + ) + self.log("Response received post SDA - 'get_fabric_sites' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response["response"]: + fabric_id = response.get("response")[0]["id"] + return fabric_id + else: + self.log("No response received from the SDA - 'get_fabric_sites' API call.", "WARNING") + return None + except Exception as e: + # Log an error message and fail if an exception occurs + self.msg = ( + "An error occurred while retrieving fabric Site 'Id' for Site '{0}' using SDA - " + "'get_fabric_sites' API call: {1}".format(site_name, str(e)) + ) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def get_fabric_sites_ids(self, site_details): + """ + Retrieve and update fabric IDs for a list of sites. + Parameters: + - site_details (dict): A dictionary where each key is a site name and the value is another dictionary + containing site information, including "site_id". + Returns: + dict: The updated dictionary with fabric IDs added to each site's information. + Description: + This method iterates through the provided `site_details` dictionary, retrieves the fabric ID for each site + by calling the `get_fabric_sites` method, and logs the retrieved fabric IDs along with site details. + It updates the `site_details` dictionary to include the fabric ID for each site and logs the updated + information. + """ + for site_name, site_info in site_details.items(): + site_id = site_info["site_id"] + # Get the fabric ID using the site name and site ID + fabric_id = self.get_fabric_sites(site_name, site_id) + self.log("Fabric Id: {0} collected for the fabric_site: {1} with siteId: {2}".format(fabric_id, site_name, site_id)) + site_info["fabric_id"] = fabric_id + self.log("Updated 'site_details' with the fabric_ids of each site. {0}".format(site_details)) + return site_details + + def get_extranet_policies(self, extranet_policy_name): + """ + Retrieve extranet policies for a given policy name using the SDA 'get_extranet_policies' API call. + Parameters: + - extranet_policy_name (str): The name of the extranet policy to retrieve. + Returns: + dict or None: The response dictionary containing policy details if found, otherwise None. + Description: + This method calls the SDA 'get_extranet_policies' API to retrieve details for the specified extranet + policy name. It logs the response received from the API call and processes it. If the API call is successful + and returns data, the first item in the response is returned. If no data is received or an exception occurs, + appropriate warnings or error messages are logged. + """ + try: + # Execute the API call to get extranet policie + response = self.dnac._exec( + family="sda", + function="get_extranet_policies", + op_modifies=True, + params={"extranetPolicyName": extranet_policy_name}, + ) + self.log("Response received post SDA - 'get_extranet_policies' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response.get("response"): + response = response.get("response")[0] + return response + else: + self.log("No response received from the SDA - 'get_extranet_policies' API call.", "WARNING") + return None + except Exception as e: + # Log an error message and fail if an exception occurs + self.msg = ( + "An error occurred while retrieving Extranet Policy Details: '{0}' using SDA - " + "'get_extranet_policies' API call: {1}".format(extranet_policy_name, str(e)) + ) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def validate_extranet_policy_exists(self, config): + """ + Check if an extranet policy exists and retrieve its details. + Parameters: + - config (dict): A dictionary containing configuration details, including the key "extranet_policy_name". + Returns: + tuple: A tuple containing: + - bool: `True` if the extranet policy exists, otherwise `False`. + - str or None: The ID of the extranet policy if it exists, otherwise `None`. + - dict or None: The details of the extranet policy if it exists, otherwise `None`. + Description: + This method verifies the existence of an extranet policy based on the name provided in the `config` dictionary. + It calls the `get_extranet_policies` method to retrieve policy details. If the policy is found, it sets + `extranet_policy_exists` to `True` and extracts the policy ID and details. The method returns a tuple containing + the existence status, policy ID, and policy details. + """ + # Initialize variables to default values + extranet_policy_exists = False + extranet_policy_id = None + + extranet_policy_name = config.get("extranet_policy_name") + extranet_policy_details = self.get_extranet_policies(extranet_policy_name) + + # Check if the policy details were retrieved successfully + if extranet_policy_details: + extranet_policy_exists = True + extranet_policy_id = extranet_policy_details["id"] + + return (extranet_policy_exists, extranet_policy_id, extranet_policy_details) + + def compare_extranet_policies(self, extranet_policy_details, update_extranet_policy_params): + """ + Compare the details of two extranet policies to check if they are equivalent. + Parameters: + - extranet_policy_details (dict): A dictionary containing the current details of the extranet policy. + - update_extranet_policy_params (dict): A dictionary containing the updated policy parameters to compare against. + Returns: + bool: `True` if all values for the keys match between the two dictionaries, `False` otherwise. + Description: + This method compares the details of two extranet policies by iterating over each key in the `extranet_policy_details` + dictionary and checking if the corresponding values in the `update_extranet_policy_params` dictionary match. + Lists are compared regardless of order, while other values are compared directly. The method returns `True` if + all values are equivalent, and `False` if any values differ. + """ + # Iterate over each key in the extranet policy details and compare the details + for key in extranet_policy_details: + value1 = extranet_policy_details.get(key) + value2 = update_extranet_policy_params.get(key) + + if isinstance(value1, list) and isinstance(value2, list): + # Compare lists regardless of order + if sorted(value1) != sorted(value2): + return False + else: + # Compare values directly + if value1 != value2: + return False + + return True + + def get_task_status(self, task_id, task_name): + """ + Retrieve the status of a task by its ID. + Parameters: + - task_id (str): The ID of the task whose status is to be retrieved. + - task_name (str): The name of the task. + Returns: + response (dict): The response containing the status of the task. + Note: + This method makes an API call to retrieve the task status and logs the status information. + If an error occurs during the API call, it will be caught and logged. + """ + # Make an API call to retrieve the task status + try: + response = self.dnac_apply["exec"]( + family="task", + function="get_task_by_id", + params=dict(task_id=task_id), + op_modifies=True, + ) + self.log("Response received post 'get_task_by_id' API Call for the Task {0} with Task id {1} " + "is {2}".format(task_name, str(task_id), str(response)), "DEBUG") + + if response["response"]: + response = response["response"] + else: + self.log("No response received from the 'get_task_by_id' API call.", "CRITICAL") + return response + + # Log the error if an exception occurs during the API call + except Exception as e: + self.msg = "Error occurred while retrieving 'get_task_by_id' for Task {0} with Task id {1}. Error: {2}".format(task_name, task_id, str(e)) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def add_extranet_policy(self, add_extranet_policy_params): + """ + Add a new extranet policy using the SDA 'add_extranet_policy' API call. + Parameters: + - add_extranet_policy_params (dict): A dictionary containing the parameters for the new extranet policy to be added. + Returns: + str or None: The task ID if the policy is added successfully, otherwise `None`. + Description: + This method sends a request to add a new extranet policy using the SDA 'add_extranet_policy' API. It logs the + response from the API call and processes it to extract the task ID. If the policy is added successfully, the + task ID is returned. If the API call does not return a response or an exception occurs, appropriate warnings or + error messages are logged. + """ + try: + # Execute the API call to add a new extranet policy + response = self.dnac._exec( + family="sda", + function="add_extranet_policy", + op_modifies=True, + params={"payload": [add_extranet_policy_params]}, + ) + self.log("Response received post SDA - 'add_extranet_policy' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response["response"]: + self.result.update(dict(response=response["response"])) + self.log("Task Id for the 'add_extranet_policy' task is {0}".format(response["response"].get("taskId")), "INFO") + # Return the task ID + return response["response"].get("taskId") + else: + self.log("No response received from the SDA - 'add_extranet_policy' API call.", "WARNING") + return None + except Exception as e: + # Log an error message and fail if an exception occurs + self.msg = ( + "An error occurred while Adding Extranet Policy to the Cisco Catalyst Center. " + "add_extranet_policy_params: {0} Error: {1}".format(add_extranet_policy_params, str(e)) + ) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def get_add_extranet_policy_status(self, task_id): + """ + Monitor the status of the 'Add Extranet Policy' task until completion or failure. + Parameters: + - task_id (str): The unique identifier of the task to monitor. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method continuously polls the status of an ongoing task identified by `task_id`. It retrieves task status + using `get_task_status` and handles various outcomes, including errors, timeouts, and successful completion. + If the task encounters errors or fails, it logs appropriate error messages and updates the result. If the task + completes successfully, it logs a success message and updates the result accordingly. The method will break + out of the loop either on successful completion, encountering an error, or when a timeout condition is met. + """ + task_name = "Add Extranet Policy" + start_time = time.time() + + while True: + response = self.get_task_status(task_id, task_name) + + # Check if response returned + if not response: + self.msg = "Error retrieving Task status for the task_name {0} task_id {1}".format(task_name, task_id) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if the elapsed time exceeds the timeout + if self.exit_while_loop(start_time, task_id, task_name, response): + break + + # Handle error if task execution encounters an error + if response.get("isError"): + if response.get("failureReason"): + failure_reason = response.get("failureReason") + self.msg = ( + "An error occurred while performing {0} task for add_extranet_policy_params: {1}. " + "The operation failed due to the following reason: {2}".format( + task_name, self.want.get("add_extranet_policy_params"), failure_reason + ) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + else: + self.msg = ( + "An error occurred while performing {0} task for add_extranet_policy_params: {1}. " + .format(task_name, self.want.get("add_extranet_policy_params")) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if task completed successfully + if not response.get("isError") and response.get("progress") == "TASK_PROVISION": + if "processcfs_complete=true" in response.get("data").lower(): + extranet_policy_name = self.want.get("add_extranet_policy_params").get("extranetPolicyName") + self.msg = "Extranet Policy - '{0}' has been successfully added to the Cisco Catalyst Center.".format(extranet_policy_name) + self.update_result("success", True, self.msg, "INFO") + break + return self + + def update_extranet_policy(self, update_extranet_policy_params): + """ + Update an existing extranet policy using the SDA 'update_extranet_policy' API call. + Parameters: + - update_extranet_policy_params (dict): A dictionary containing the parameters for updating the extranet policy. + Returns: + str or None: The task ID if the update request is processed successfully, otherwise `None`. + Description: + This method sends a request to update an existing extranet policy using the SDA 'update_extranet_policy' API. + It logs the response from the API call and processes it to extract the task ID. If the API call is successful and + returns a response, the method updates the result with the response details and returns the task ID. If no response + is received or if an exception occurs, appropriate warnings or error messages are logged. + """ + try: + # Execute the API call to update the extranet policy with the provided parameters + response = self.dnac._exec( + family="sda", + function="update_extranet_policy", + op_modifies=True, + params={"payload": [update_extranet_policy_params]}, + ) + self.log("Response received post SDA - 'update_extranet_policy' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response["response"]: + self.result.update(dict(response=response["response"])) + self.log("Task Id for the 'update_extranet_policy' task is {0}".format(response["response"].get("taskId")), "INFO") + # Return the task ID + return response["response"].get("taskId") + else: + self.log("No response received from the SDA - 'update_extranet_policy' API call.", "WARNING") + return None + except Exception as e: + # Log an error message and fail if an exception occurs + self.msg = ( + "An error occurred while Updating Extranet Policy. " + "update_extranet_policy_params: {0}. Error - {1} ".format(update_extranet_policy_params, str(e)) + ) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def get_update_extranet_policy_status(self, task_id): + """ + Monitor the status of the 'Update Extranet Policy' task until completion or failure. + Parameters: + - task_id (str): The unique identifier of the task to monitor. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method continuously polls the status of an ongoing update task identified by `task_id`. It retrieves the + task status using `get_task_status` and handles different outcomes such as errors, timeouts, and successful + completion. The method logs appropriate messages based on the status of the task and updates the result with + success or failure information. The method exits the loop upon encountering an error, exceeding the timeout, + or successful completion of the task. + """ + task_name = "Update Extranet Policy" + start_time = time.time() + + while True: + response = self.get_task_status(task_id, task_name) + + # Check if response returned + if not response: + self.msg = "Error retrieving Task status for the task_name {0} task_id {1}".format(task_name, task_id) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if the elapsed time exceeds the timeout + if self.exit_while_loop(start_time, task_id, task_name, response): + break + + # Handle error if task execution encounters an error + if response.get("isError"): + if response.get("failureReason"): + failure_reason = response.get("failureReason") + self.msg = ( + "An error occurred while performing {0} task for update_extranet_policy_params: {1}. " + "The operation failed due to the following reason: {2}".format( + task_name, self.want.get("update_extranet_policy_params"), failure_reason + ) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + else: + self.msg = ( + "An error occurred while performing {0} task for update_extranet_policy_params: {1}. " + .format(task_name, self.want.get("update_extranet_policy_params")) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if task completed successfully + if not response.get("isError") and response.get("progress") == "TASK_MODIFY_PUT": + if "processcfs_complete=true" in response.get("data").lower(): + extranet_policy_name = self.want.get("update_extranet_policy_params").get("extranetPolicyName") + self.msg = "Extranet Policy - '{0}' has been successfully updated!".format(extranet_policy_name) + self.update_result("success", True, self.msg, "INFO") + break + + return self + + + def delete_extranet_policy(self, delete_extranet_policy_params): + """ + Delete an extranet policy using the SDA 'delete_extranet_policy_by_id' API call. + Parameters: + - delete_extranet_policy_params (dict): A dictionary containing the parameters for deleting the extranet policy, + including the policy ID or other identifying details. + Returns: + str or None: The task ID if the delete request is processed successfully, otherwise `None`. + Description: + This method sends a request to delete an extranet policy using the SDA 'delete_extranet_policy_by_id' API. + It logs the response from the API call and processes it to extract the task ID. If the API call is successful and + returns a response, the method updates the result with the response details and returns the task ID. If no response + is received or if an exception occurs, appropriate warnings or error messages are logged. + """ + try: + # Execute the API call to delete the extranet policy with the provided parameters + response = self.dnac._exec( + family="sda", + function="delete_extranet_policy_by_id", + op_modifies=True, + params=delete_extranet_policy_params, + ) + self.log("Response received post SDA - 'delete_extranet_policy_by_id' API call: {0}".format(str(response)), "DEBUG") + + # Process the response if available + if response["response"]: + self.result.update(dict(response=response["response"])) + self.log("Task Id for the 'delete_extranet_policy_by_id' task is {0}".format(response["response"].get("taskId")), "INFO") + # Return the task ID + return response["response"].get("taskId") + else: + self.log("No response received from the SDA - 'delete_extranet_policy_by_id' API call.", "WARNING") + return None + except Exception as e: + # Log an error message and fail if an exception occurs + self.msg = ( + "An error occurred while Deleting Extranet Policy. " + "delete_extranet_policy_params: {0}. Error - {1} ".format(delete_extranet_policy_params, str(e)) + ) + self.update_result("failed", False, self.msg, "ERROR") + self.check_return_status() + + def get_delete_extranet_policy_status(self, task_id): + """ + Monitor the status of the 'Delete Extranet Policy' task until completion or failure. + Parameters: + - task_id (str): The unique identifier of the task to monitor. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method continuously polls the status of an ongoing delete task identified by `task_id`. It uses the + `get_task_status` method to check the task status and handles various outcomes, such as errors, timeouts, + and successful completion. The method logs appropriate messages based on the task's status and updates the + result with success or failure information. The monitoring loop exits upon encountering an error, exceeding + the timeout, or successfully completing the task. + """ + task_name = "Delete Extranet Policy" + start_time = time.time() + + while True: + response = self.get_task_status(task_id, task_name) + + # Check if response returned + if not response: + self.msg = "Error retrieving Task status for the task_name {0} task_id {1}".format(task_name, task_id) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if the elapsed time exceeds the timeout + if self.exit_while_loop(start_time, task_id, task_name, response): + break + + # Handle error if task execution encounters an error + if response.get("isError"): + if response.get("failureReason"): + failure_reason = response.get("failureReason") + self.msg = ( + "An error occurred while performing {0} task for delete_extranet_policy_params: {1}. " + "The operation failed due to the following reason: {2}".format( + task_name, self.want.get("delete_extranet_policy_params"), failure_reason + ) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + else: + self.msg = ( + "An error occurred while performing {0} task for " + "delete_extranet_policy_params: {1}. ".format( + task_name, self.want.get("delete_extranet_policy_params") + ) + ) + self.update_result("failed", False, self.msg, "ERROR") + break + + # Check if task completed successfully + if not response.get("isError") and response.get("progress") == "TASK_TERMINATE": + if "processcfs_complete=true" in response.get("data").lower(): + extranet_policy_name = self.want.get("extranet_policy_name") + self.msg = "Extranet Policy - '{0}' has been successfully deleted!".format(extranet_policy_name) + self.update_result("success", True, self.msg, "INFO") + break + + return self + + def get_have(self, config): + """ + Retrieve the current state of the extranet policy based on the provided configuration. + Parameters: + - config (dict): Configuration dictionary containing site details. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method checks if the extranet policy specified in the `config` exists. It uses the + `validate_extranet_policy_exists` method to determine if the policy exists and to retrieve its details. + The method logs the current state of the extranet policy and updates the instance attribute `have` with + information about the existence, ID, and details of the extranet policy. It returns the instance for + method chaining. + """ + have = {} + + # check if given site exits, if exists store current site info + (extranet_policy_exists, extranet_policy_id, extranet_policy_details) = self.validate_extranet_policy_exists(config) + + self.log("Current Extranet Policy details (have): {0}".format(str(extranet_policy_details)), "DEBUG") + + have["extranet_policy_exists"] = extranet_policy_exists + have["extranet_policy_id"] = extranet_policy_id + have["current_extranet_policy"] = extranet_policy_details + + self.have = have + self.log("Current State (have): {0}".format(str(self.have)), "INFO") + + return self + + def get_want(self, config, state): + """ + Generate the desired state parameters for API calls based on the provided configuration and state. + Parameters: + - config (dict): Configuration dictionary containing site and policy details. + - state (str): Desired state, which can be 'merged' or 'delete'. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method determines the parameters required for API calls based on the desired state and configuration. + It checks if the extranet policy exists and sets the appropriate parameters for creating, updating, or deleting + the policy. For the 'merged' state, it prepares parameters for updating the policy if it exists or creating + it if it does not. For the 'delete' state, it prepares parameters for deleting the policy if it exists. The + method logs the created parameters and updates the instance attribute `want` with these parameters. It returns + the instance for method chaining. + """ + #Initialize want + want = {} + site_details = {} + self.log("Creating Parameters for API Calls with state: {0}".format(state)) + + + extranet_policy_name = config.get("extranet_policy_name") + + # Identify if policy already exists or needs to be created + extranet_policy_exists = self.have.get("extranet_policy_exists") + extranet_policy_id = self.have.get("extranet_policy_id") + extranet_policy_details = self.have.get("current_extranet_policy") + + if state == "merged": + self.validate_merged_parameters(config) + fabric_sites = config.get("fabric_sites") + if fabric_sites: + self.log("Attempting to get the 'SiteId' for the provided fabric sites: {0}".format(fabric_sites), "DEBUG") + site_details = self.get_site_details(fabric_sites) + self.log("Attempting to get the fabric 'Id' for the provided fabric sites: {0}".format(fabric_sites), "DEBUG") + site_details = self.get_fabric_sites_ids(site_details) + + if extranet_policy_exists: + self.log( + "Extranet Policy - '{0}' exists in the Cisco Catalyst Center, " + "therefore setting 'update_extranet_policy_params'.".format(extranet_policy_name), + "DEBUG" + ) + want = dict( + update_extranet_policy_params=self.get_update_extranet_policy_params(config, extranet_policy_id, site_details), + ) + if self.compare_extranet_policies(extranet_policy_details, want["update_extranet_policy_params"]): + self.msg = ( + "Extranet Policy - '{0}' is already same as the update requested, " + "and hence an update operation is not required.".format(extranet_policy_name) + ) + self.update_result("ok", False, self.msg, "INFO") + self.check_return_status() + return self + else: + self.log( + "Extranet Policy - '{0}' does not exist in the Cisco Catalyst Center, " + "therefore setting 'add_extranet_policy_params'.".format(extranet_policy_name), + "DEBUG" + ) + want = dict( + add_extranet_policy_params=self.get_add_extranet_policy_params(config, site_details), + ) + else: + if extranet_policy_exists: + self.log( + "State is delete and Extranet Policy - '{0}' exists in the Cisco Catalyst Center, " + "therefore setting 'delete_extranet_policy_params'.".format(extranet_policy_name), + "DEBUG" + ) + want = dict( + extranet_policy_name=extranet_policy_name, + delete_extranet_policy_params=self.get_delete_extranet_policy_params(extranet_policy_id) + ) + else: + self.msg = ( + "Extranet Policy - '{0}' does not exist in the Cisco Catalyst Center and " + "hence delete operation not required.".format(extranet_policy_name) + ) + self.update_result("ok", False, self.msg, "INFO") + self.check_return_status() + return self + + self.want = want + self.log("Desired State (want): {0}".format(str(self.want)), "INFO") + return self + + + def get_diff_merged(self): + """ + Executes actions based on the desired state parameters and checks their status. + Parameters: + - None + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method iterates through a map of action parameters to their corresponding functions for execution and status + checking. For each action parameter present in the desired state (`want`), the associated action function is called + to perform the action, and the corresponding status function is used to check the result. It ensures that all actions + specified in the desired state are executed and their statuses are verified. The method returns the instance for method + chaining. + """ + action_map = { + "add_extranet_policy_params": (self.add_extranet_policy, self.get_add_extranet_policy_status), + "update_extranet_policy_params": (self.update_extranet_policy, self.get_update_extranet_policy_status), + } + + for action_param, (action_func, status_func) in action_map.items(): + # Execute the action and check its status + if self.want.get(action_param): + result_task_id = action_func(self.want.get(action_param)) + status_func(result_task_id).check_return_status() + return self + + def get_diff_deleted(self): + """ + Executes deletion actions based on the desired state parameters and checks their status. + Parameters: + - None + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method iterates through a map of deletion action parameters to their corresponding functions for execution and + status checking. For each deletion action parameter present in the desired state (`want`), the associated action + function is called to perform the deletion, and the corresponding status function is used to check the result. + It ensures that all deletion actions specified in the desired state are executed and their statuses are verified. + The method returns the instance for method chaining. + """ + action_map = { + "delete_extranet_policy_params": (self.delete_extranet_policy, self.get_delete_extranet_policy_status) + + } + for action_param, (action_func, status_func) in action_map.items(): + # Execute the action and check its status + if self.want.get(action_param): + result_task_id = action_func(self.want.get(action_param)) + status_func(result_task_id).check_return_status() + return self + + def verify_diff_merged(self, config): + """ + Verifies the results of the merged state operations by comparing the state before and after the operations. + Parameters: + - config (dict): Configuration dictionary containing site and policy details. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method performs verification of operations related to the 'merged' state. It first retrieves the state before + performing any operations and then compares it with the state after the operations. For add and update operations, + it logs the states before and after the operations and verifies the success based on the presence or absence of + the extranet policy and whether any changes were detected. It ensures that the operations have been performed as + expected and logs appropriate messages based on the results. + """ + pre_operation_state = self.have.copy() + desired_state = self.want + self.get_have(config) + post_operation_state = self.have.copy() + extranet_policy_name = config.get("extranet_policy_name") + + if desired_state.get("add_extranet_policy_params"): + self.log("State before performing ADD Extranet Policy operation: {0}".format(str(pre_operation_state)), "INFO") + self.log("Desired State: {}".format(str(desired_state)), "INFO") + self.log("State after performing ADD Extranet Policy operation: {0}".format(str(post_operation_state)), "INFO") + + if post_operation_state["extranet_policy_exists"]: + self.log("Verified the success of ADD Extranet Policy - '{0}' operation.".format(extranet_policy_name), "INFO") + else: + self.log( + "The ADD Extranet Policy - '{0}' operation may not have been successful " + "since the Extranet Policy does not exist in the Cisco Catalyst Center.".format(extranet_policy_name), + "WARNING" + ) + + if self.want.get("update_extranet_policy_params"): + self.log("State before performing UPDATE Extranet Policy operation: {0}".format(str(pre_operation_state)), "INFO") + self.log("Desired State: {}".format(str(desired_state)), "INFO") + self.log("State after performing UPDATE Extranet Policy operation - '{0}'".format(str(post_operation_state)), "INFO") + + if not self.compare_extranet_policies(pre_operation_state["current_extranet_policy"], post_operation_state["current_extranet_policy"] ): + self.log("Verified the success of UPDATE Extranet Policy - '{0}' operation.".format(extranet_policy_name), "INFO") + else: + self.log( + "The UPDATE Extranet Policy - '{0}' operation may not have been performed or " + "may not have been successful because no change was detected in the Extranet Policy " + "in the Cisco Catalyst Center".format(extranet_policy_name), + "WARNING" + ) + return self + + def verify_diff_deleted(self, config): + """ + Verifies the results of the delete state operation by comparing the state before and after the delete operation. + Parameters: + - config (dict): Configuration dictionary containing site and policy details. + Returns: + self: The instance of the class, allowing for method chaining. + Description: + This method performs verification of the delete operation by comparing the state before and after the operation. + It introduces a delay to allow the deletion to process and then retrieves the state. It checks if the extranet policy + no longer exists and logs the result of the delete operation. It ensures that the delete operation was successful + by verifying the absence of the extranet policy and logs appropriate messages based on the outcome. + """ + pre_operation_state = self.have.copy() + desired_state = self.want + time.sleep(10) + self.get_have(config) + post_operation_state = self.have.copy() + extranet_policy_name = config.get("extranet_policy_name") + + self.log("State before performing DELETE Extranet Policy operation: {0}".format(str(pre_operation_state)), "INFO") + self.log("Desired State: {}".format(str(desired_state)), "INFO") + self.log("State after performing DELETE Extranet Policy operation: {0}".format(str(post_operation_state)), "INFO") + + if not post_operation_state["extranet_policy_exists"]: + self.log("Verified the success of DELETE Extranet Policy - '{0}' operation".format(extranet_policy_name), "INFO") + else: + self.log( + "The DELETE Extranet Policy - '{0}' operation may not have been successful since " + "the policy still exists in the Cisco Catalyst Center.".format(extranet_policy_name), + "WARNING" + ) + return self + +def main(): + """ main entry point for module execution + """ + + # Define the specification for the module"s arguments + element_spec = {"dnac_host": {"required": True, "type": "str"}, + "dnac_port": {"type": "str", "default": "443"}, + "dnac_username": {"type": "str", "default": "admin", "aliases": ["user"]}, + "dnac_password": {"type": "str", "no_log": True}, + "dnac_verify": {"type": "bool", "default": "True"}, + "dnac_version": {"type": "str", "default": "2.2.3.3"}, + "dnac_debug": {"type": "bool", "default": False}, + "dnac_log_level": {"type": "str", "default": "WARNING"}, + "dnac_log_file_path": {"type": "str", "default": "dnac.log"}, + "dnac_log_append": {"type": "bool", "default": True}, + "dnac_log": {"type": "bool", "default": False}, + "validate_response_schema": {"type": "bool", "default": True}, + "config_verify": {"type": "bool", "default": False}, + "dnac_api_task_timeout": {"type": "int", "default": 1200}, + "dnac_task_poll_interval": {"type": "int", "default": 2}, + "config": {"required": True, "type": "list", "elements": "dict"}, + 'state': {'default': 'merged', 'choices': ['merged', 'deleted']} + } + + # Initialize the Ansible module with the provided argument specifications + module = AnsibleModule(argument_spec=element_spec, + supports_check_mode=False) + + # Initialize the NetworkCompliance object with the module + ccc_sda_extranet_policies = SDAExtranetPolicies(module) + + # Get the state parameter from the provided parameters + state = ccc_sda_extranet_policies.params.get("state") + + # Check if the state is valid + if state not in ccc_sda_extranet_policies.supported_states: + ccc_sda_extranet_policies.status = "invalid" + ccc_sda_extranet_policies.msg = "State {0} is invalid".format(state) + ccc_sda_extranet_policies.check_return_status() + + # Validate the input parameters and check the return status + ccc_sda_extranet_policies.validate_input().check_return_status() + + # Get the config_verify parameter from the provided parameters + config_verify = ccc_sda_extranet_policies.params.get("config_verify") + + # Iterate over the validated configuration parameters + for config in ccc_sda_extranet_policies.validated_config: + ccc_sda_extranet_policies.reset_values() + ccc_sda_extranet_policies.get_have(config).check_return_status() + ccc_sda_extranet_policies.get_want(config, state).check_return_status() + ccc_sda_extranet_policies.get_diff_state_apply[state]().check_return_status() + if config_verify: + ccc_sda_extranet_policies.verify_diff_state_apply[state](config).check_return_status() + + module.exit_json(**ccc_sda_extranet_policies.result) + + +if __name__ == '__main__': + main() From f2227c412ac69c01f1a1d31ce48e301655039443 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 17:21:14 -0700 Subject: [PATCH 022/121] Added sample playbook for sda_extranet_policies_workflow_manager --- ...sda_extranet_policies_workflow_manager.yml | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 playbooks/sda_extranet_policies_workflow_manager.yml diff --git a/playbooks/sda_extranet_policies_workflow_manager.yml b/playbooks/sda_extranet_policies_workflow_manager.yml new file mode 100644 index 0000000000..706722f81d --- /dev/null +++ b/playbooks/sda_extranet_policies_workflow_manager.yml @@ -0,0 +1,71 @@ +--- +- name: Testing + hosts: dnac_servers + gather_facts: no + + vars_files: + - "credentials.yml" + + vars: + dnac_login: &dnac_login + dnac_host: "{{ dnac_host }}" + dnac_username: "{{ dnac_username }}" + dnac_password: "{{ dnac_password }}" + dnac_verify: "{{ dnac_verify }}" + dnac_port: "{{ dnac_port }}" + dnac_version: "{{ dnac_version }}" + dnac_debug: "{{ dnac_debug }}" + dnac_log: true + dnac_log_level: INFO + dnac_log_append: False + config_verify: true + + tasks: + - name: Create Extranet Policy + cisco.dnac.network_compliance_workflow_manager: + <<: *dnac_login + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_3"] + + + - name: Create Extranet Policy with Fabric Site(s) specified + cisco.dnac.network_compliance_workflow_manager: + <<: *dnac_login + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_3"] + fabric_sites: ["Global/Test_Extranet_Polcies/USA", "Global/Test_Extranet_Polcies/India"] + + + - name: Update existing Extranet Policy + cisco.dnac.network_compliance_workflow_manager: + <<: *dnac_login + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_4"] + + + - name: Update existing Extranet Policy with Fabric Site(s) specified + cisco.dnac.network_compliance_workflow_manager: + <<: *dnac_login + state: merged + config: + - extranet_policy_name: "test_extranet_policy_1" + provider_virtual_network: "VN_1" + subscriber_virtual_networks: ["VN_2", "VN_4"] + fabric_sites: ["Global/Test_Extranet_Polcies/USA", "Global/Test_Extranet_Polcies/India"] + + + - name: Delete Extranet Policy + cisco.dnac.network_compliance_workflow_manager: + <<: *dnac_login + state: deleted + config: + - extranet_policy_name: "test_extranet_policy_1" From 77f2eb40c856b62b685e097ff0fbe34fd0a420c1 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 17:30:55 -0700 Subject: [PATCH 023/121] sanity bug fixes' --- .../sda_extranet_policies_workflow_manager.py | 90 +++++++++---------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py index e114394be0..82371e6a5b 100644 --- a/plugins/modules/sda_extranet_policies_workflow_manager.py +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -13,7 +13,7 @@ --- module: sda_extranet_policies_workflow_manager short_description: SDA Extranet Policies Module provides functionality for managing SDA Extranet Policy in Cisco Catalyst Center. -description: +description: version_added: "6.17.0" - Manage extranet policy operations such as add/update/delete. - API to create a new extranet policy. @@ -39,31 +39,31 @@ elements: dict required: True suboptions: - extranet_policy_name: - description: Name of the SDA Extranet Policy. - This parameter allows you to specify the desired name when creating a new extranet policy. + extranet_policy_name: + description: Name of the SDA Extranet Policy. + This parameter allows you to specify the desired name when creating a new extranet policy. The same name can be used to update or delete the policy. Note - This parameter is required when creating, updating or deleting extranet policy. type: str provider_virtual_network: - description: Specifies the Provider Virtual Network containing shared services resources that subscribers need to access. - If a virtual network is already defined as a Provider, it cannot be assigned as a provider again. - Ensure the default route is present in the Global Routing Table if INFRA_VN is defined as the Provider. - For Subscriber Virtual Networks with multiple Providers having overlapping routes, traffic will be + description: Specifies the Provider Virtual Network containing shared services resources that subscribers need to access. + If a virtual network is already defined as a Provider, it cannot be assigned as a provider again. + Ensure the default route is present in the Global Routing Table if INFRA_VN is defined as the Provider. + For Subscriber Virtual Networks with multiple Providers having overlapping routes, traffic will be load-balanced across those Provider Virtual Networks. This parameter is required when creating or updating extranet policy. type: str subscriber_virtual_networks: - description: Specifies a list of Subscriber Virtual Networks that require access to the Provider Virtual Network - containing shared services resources. + description: Specifies a list of Subscriber Virtual Networks that require access to the Provider Virtual Network + containing shared services resources. A Virtual Network previously defined as a Provider cannot be selected as a subscriber. This parameter is required when creating or updating extranet policy. type: list elements: str fabric_sites: - description: Specifies the Fabric Site(s) where this Extranet Policy will be applied. - The Provider Virtual Network must be added to a Fabric Site before applying the policy. - Fabric Site(s) connected to the same SD-Access Transit must have consistent Extranet Policies. + description: Specifies the Fabric Site(s) where this Extranet Policy will be applied. + The Provider Virtual Network must be added to a Fabric Site before applying the policy. + Fabric Site(s) connected to the same SD-Access Transit must have consistent Extranet Policies. Selecting a Fabric Site connected to an SD-Access Transit will automatically select all other Sites connected to that Transit. type: list elements: str @@ -89,7 +89,7 @@ post /dna/intent/api/v1/sda/extranetPolicies put /dna/intent/api/v1/sda/extranetPolicies delete dna/intent/api/v1/sda/extranetPolicies/${id} - get /dna/intent/api/v1/task/{taskId} + get /dna/intent/api/v1/task/{taskId} """ @@ -217,6 +217,7 @@ validate_list_of_dicts ) + class SDAExtranetPolicies(DnacBase): """ A class for managing Extranet Policies within the Cisco DNA Center using the SDA API. @@ -278,7 +279,6 @@ def validate_input(self): self.update_result("success", False, self.msg, "INFO") return self - def update_result(self, status, changed, msg, log_level, data=None): """ Update the result of the operation with the provided status, message, and log level. @@ -345,14 +345,14 @@ def get_fabric_ids_list(self, site_details): """ Extracts a list of fabric IDs from the provided site details. Parameters: - - site_details (dict): A dictionary containing site information. Each key-value pair - represents a site, where the value is another dictionary that + - site_details (dict): A dictionary containing site information. Each key-value pair + represents a site, where the value is another dictionary that includes a 'fabric_id'. Returns: list: A list of fabric IDs extracted from the site details. Description: - This method iterates over the values in the provided site_details dictionary, extracts - the 'fabric_id' from each value, and appends it to a list. The resulting list of fabric IDs + This method iterates over the values in the provided site_details dictionary, extracts + the 'fabric_id' from each value, and appends it to a list. The resulting list of fabric IDs is then returned. """ # Initialize an empty list to store fabric IDs @@ -365,17 +365,17 @@ def get_fabric_ids_list(self, site_details): def validate_merged_parameters(self, config): """ - Validate that the required parameters are present in the configuration for performing + Validate that the required parameters are present in the configuration for performing Add or Update Extranet Policy operations. Parameters: - config (dict): A dictionary containing the configuration parameters to be validated. Returns: - None: This function does not return a value. It logs messages and raises exceptions + None: This function does not return a value. It logs messages and raises exceptions if required parameters are missing. Description: - This method checks the provided configuration for the presence of the required parameters: - 'provider_virtual_network' and 'subscriber_virtual_networks'. If any of these parameters - are missing, it logs an error message and raises an exception to halt execution. If all + This method checks the provided configuration for the presence of the required parameters: + 'provider_virtual_network' and 'subscriber_virtual_networks'. If any of these parameters + are missing, it logs an error message and raises an exception to halt execution. If all required parameters are present, it logs a success message indicating successful validation. """ # Check for provider_virtual_network @@ -404,7 +404,6 @@ def validate_merged_parameters(self, config): "INFO" ) - def get_add_extranet_policy_params(self, config, site_details=None): """ Generate parameters required for adding an Extranet Policy based on the provided configuration and site details. @@ -415,8 +414,8 @@ def get_add_extranet_policy_params(self, config, site_details=None): dict: A dictionary containing the parameters for adding an Extranet Policy. Description: This method constructs a dictionary of parameters required for adding an Extranet Policy. - It includes the 'extranetPolicyName', 'providerVirtualNetworkName', and 'subscriberVirtualNetworkNames' - from the configuration. If 'fabric_sites' are provided in the configuration and site details are available, + It includes the 'extranetPolicyName', 'providerVirtualNetworkName', and 'subscriberVirtualNetworkNames' + from the configuration. If 'fabric_sites' are provided in the configuration and site details are available, it also includes the 'fabricIds' obtained from the site details. """ # Initialize the parameters dictionary with basic required parameters @@ -451,7 +450,7 @@ def get_update_extranet_policy_params(self, config, extranet_policy_id, site_det """ # Initialize the parameters dictionary with basic required parameters update_extranet_policy_params = { - "id": extranet_policy_id, + "id": extranet_policy_id, "extranetPolicyName": config.get("extranet_policy_name"), "providerVirtualNetworkName": config.get("provider_virtual_network"), "subscriberVirtualNetworkNames": config.get("subscriber_virtual_networks") @@ -696,7 +695,7 @@ def compare_extranet_policies(self, extranet_policy_details, update_extranet_pol bool: `True` if all values for the keys match between the two dictionaries, `False` otherwise. Description: This method compares the details of two extranet policies by iterating over each key in the `extranet_policy_details` - dictionary and checking if the corresponding values in the `update_extranet_policy_params` dictionary match. + dictionary and checking if the corresponding values in the `update_extranet_policy_params` dictionary match. Lists are compared regardless of order, while other values are compared directly. The method returns `True` if all values are equivalent, and `False` if any values differ. """ @@ -824,7 +823,7 @@ def get_add_extranet_policy_status(self, task_id): # Handle error if task execution encounters an error if response.get("isError"): - if response.get("failureReason"): + if response.get("failureReason"): failure_reason = response.get("failureReason") self.msg = ( "An error occurred while performing {0} task for add_extranet_policy_params: {1}. " @@ -924,7 +923,7 @@ def get_update_extranet_policy_status(self, task_id): # Handle error if task execution encounters an error if response.get("isError"): - if response.get("failureReason"): + if response.get("failureReason"): failure_reason = response.get("failureReason") self.msg = ( "An error occurred while performing {0} task for update_extranet_policy_params: {1}. " @@ -943,7 +942,7 @@ def get_update_extranet_policy_status(self, task_id): break # Check if task completed successfully - if not response.get("isError") and response.get("progress") == "TASK_MODIFY_PUT": + if not response.get("isError") and response.get("progress") == "TASK_MODIFY_PUT": if "processcfs_complete=true" in response.get("data").lower(): extranet_policy_name = self.want.get("update_extranet_policy_params").get("extranetPolicyName") self.msg = "Extranet Policy - '{0}' has been successfully updated!".format(extranet_policy_name) @@ -952,7 +951,6 @@ def get_update_extranet_policy_status(self, task_id): return self - def delete_extranet_policy(self, delete_extranet_policy_params): """ Delete an extranet policy using the SDA 'delete_extranet_policy_by_id' API call. @@ -1027,7 +1025,7 @@ def get_delete_extranet_policy_status(self, task_id): # Handle error if task execution encounters an error if response.get("isError"): - if response.get("failureReason"): + if response.get("failureReason"): failure_reason = response.get("failureReason") self.msg = ( "An error occurred while performing {0} task for delete_extranet_policy_params: {1}. " @@ -1048,7 +1046,7 @@ def get_delete_extranet_policy_status(self, task_id): break # Check if task completed successfully - if not response.get("isError") and response.get("progress") == "TASK_TERMINATE": + if not response.get("isError") and response.get("progress") == "TASK_TERMINATE": if "processcfs_complete=true" in response.get("data").lower(): extranet_policy_name = self.want.get("extranet_policy_name") self.msg = "Extranet Policy - '{0}' has been successfully deleted!".format(extranet_policy_name) @@ -1103,15 +1101,14 @@ def get_want(self, config, state): method logs the created parameters and updates the instance attribute `want` with these parameters. It returns the instance for method chaining. """ - #Initialize want + # Initialize want want = {} site_details = {} - self.log("Creating Parameters for API Calls with state: {0}".format(state)) - - - extranet_policy_name = config.get("extranet_policy_name") + self.log("Creating Parameters for API Calls with state: {0}".format(state)) + # Identify if policy already exists or needs to be created + extranet_policy_name = config.get("extranet_policy_name") extranet_policy_exists = self.have.get("extranet_policy_exists") extranet_policy_id = self.have.get("extranet_policy_id") extranet_policy_details = self.have.get("current_extranet_policy") @@ -1138,7 +1135,7 @@ def get_want(self, config, state): self.msg = ( "Extranet Policy - '{0}' is already same as the update requested, " "and hence an update operation is not required.".format(extranet_policy_name) - ) + ) self.update_result("ok", False, self.msg, "INFO") self.check_return_status() return self @@ -1157,7 +1154,7 @@ def get_want(self, config, state): "State is delete and Extranet Policy - '{0}' exists in the Cisco Catalyst Center, " "therefore setting 'delete_extranet_policy_params'.".format(extranet_policy_name), "DEBUG" - ) + ) want = dict( extranet_policy_name=extranet_policy_name, delete_extranet_policy_params=self.get_delete_extranet_policy_params(extranet_policy_id) @@ -1166,7 +1163,7 @@ def get_want(self, config, state): self.msg = ( "Extranet Policy - '{0}' does not exist in the Cisco Catalyst Center and " "hence delete operation not required.".format(extranet_policy_name) - ) + ) self.update_result("ok", False, self.msg, "INFO") self.check_return_status() return self @@ -1175,7 +1172,6 @@ def get_want(self, config, state): self.log("Desired State (want): {0}".format(str(self.want)), "INFO") return self - def get_diff_merged(self): """ Executes actions based on the desired state parameters and checks their status. @@ -1236,7 +1232,7 @@ def verify_diff_merged(self, config): self: The instance of the class, allowing for method chaining. Description: This method performs verification of operations related to the 'merged' state. It first retrieves the state before - performing any operations and then compares it with the state after the operations. For add and update operations, + performing any operations and then compares it with the state after the operations. For add and update operations, it logs the states before and after the operations and verifies the success based on the presence or absence of the extranet policy and whether any changes were detected. It ensures that the operations have been performed as expected and logs appropriate messages based on the results. @@ -1266,7 +1262,7 @@ def verify_diff_merged(self, config): self.log("Desired State: {}".format(str(desired_state)), "INFO") self.log("State after performing UPDATE Extranet Policy operation - '{0}'".format(str(post_operation_state)), "INFO") - if not self.compare_extranet_policies(pre_operation_state["current_extranet_policy"], post_operation_state["current_extranet_policy"] ): + if not self.compare_extranet_policies(pre_operation_state["current_extranet_policy"], post_operation_state["current_extranet_policy"]): self.log("Verified the success of UPDATE Extranet Policy - '{0}' operation.".format(extranet_policy_name), "INFO") else: self.log( @@ -1311,10 +1307,10 @@ def verify_diff_deleted(self, config): ) return self + def main(): """ main entry point for module execution """ - # Define the specification for the module"s arguments element_spec = {"dnac_host": {"required": True, "type": "str"}, "dnac_port": {"type": "str", "default": "443"}, From c2718c288a67c40874ea19c61514006791fa202a Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 17:37:47 -0700 Subject: [PATCH 024/121] sanity bug fixes --- .../sda_extranet_policies_workflow_manager.py | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py index 82371e6a5b..a6eb93dd82 100644 --- a/plugins/modules/sda_extranet_policies_workflow_manager.py +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -1106,7 +1106,7 @@ def get_want(self, config, state): site_details = {} self.log("Creating Parameters for API Calls with state: {0}".format(state)) - + # Identify if policy already exists or needs to be created extranet_policy_name = config.get("extranet_policy_name") extranet_policy_exists = self.have.get("extranet_policy_exists") @@ -1128,14 +1128,12 @@ def get_want(self, config, state): "therefore setting 'update_extranet_policy_params'.".format(extranet_policy_name), "DEBUG" ) - want = dict( - update_extranet_policy_params=self.get_update_extranet_policy_params(config, extranet_policy_id, site_details), - ) + want = dict(update_extranet_policy_params=self.get_update_extranet_policy_params(config, extranet_policy_id, site_details)) if self.compare_extranet_policies(extranet_policy_details, want["update_extranet_policy_params"]): self.msg = ( "Extranet Policy - '{0}' is already same as the update requested, " "and hence an update operation is not required.".format(extranet_policy_name) - ) + ) self.update_result("ok", False, self.msg, "INFO") self.check_return_status() return self @@ -1145,9 +1143,7 @@ def get_want(self, config, state): "therefore setting 'add_extranet_policy_params'.".format(extranet_policy_name), "DEBUG" ) - want = dict( - add_extranet_policy_params=self.get_add_extranet_policy_params(config, site_details), - ) + want = dict(add_extranet_policy_params=self.get_add_extranet_policy_params(config, site_details)) else: if extranet_policy_exists: self.log( @@ -1155,15 +1151,13 @@ def get_want(self, config, state): "therefore setting 'delete_extranet_policy_params'.".format(extranet_policy_name), "DEBUG" ) - want = dict( - extranet_policy_name=extranet_policy_name, - delete_extranet_policy_params=self.get_delete_extranet_policy_params(extranet_policy_id) - ) + want = dict(extranet_policy_name=extranet_policy_name, + delete_extranet_policy_params=self.get_delete_extranet_policy_params(extranet_policy_id)) else: self.msg = ( "Extranet Policy - '{0}' does not exist in the Cisco Catalyst Center and " "hence delete operation not required.".format(extranet_policy_name) - ) + ) self.update_result("ok", False, self.msg, "INFO") self.check_return_status() return self From cc902ff1d45895271b889b452e392904d876a9f5 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 17:47:05 -0700 Subject: [PATCH 025/121] sanity bug fixes --- .../modules/sda_extranet_policies_workflow_manager.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py index a6eb93dd82..b2c861e4db 100644 --- a/plugins/modules/sda_extranet_policies_workflow_manager.py +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -15,10 +15,10 @@ short_description: SDA Extranet Policies Module provides functionality for managing SDA Extranet Policy in Cisco Catalyst Center. description: version_added: "6.17.0" -- Manage extranet policy operations such as add/update/delete. -- API to create a new extranet policy. -- API to update an existing or edit an existing extranet policy. -- API for deletion of an existing extranet policy using the policy name. + - Manage extranet policy operations such as add/update/delete. + - API to create a new extranet policy. + - API to update an existing or edit an existing extranet policy. + - API for deletion of an existing extranet policy using the policy name. extends_documentation_fragment: - cisco.dnac.workflow_manager_params author: Rugvedi Kapse (@rukapse) @@ -1150,7 +1150,7 @@ def get_want(self, config, state): "State is delete and Extranet Policy - '{0}' exists in the Cisco Catalyst Center, " "therefore setting 'delete_extranet_policy_params'.".format(extranet_policy_name), "DEBUG" - ) + ) want = dict(extranet_policy_name=extranet_policy_name, delete_extranet_policy_params=self.get_delete_extranet_policy_params(extranet_policy_id)) else: From ee9b538fa8d040accf4f8e953f37d56c6f30b5c0 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Wed, 7 Aug 2024 18:00:24 -0700 Subject: [PATCH 026/121] sanity bug fixes --- plugins/modules/sda_extranet_policies_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py index b2c861e4db..883836fa5a 100644 --- a/plugins/modules/sda_extranet_policies_workflow_manager.py +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -14,11 +14,11 @@ module: sda_extranet_policies_workflow_manager short_description: SDA Extranet Policies Module provides functionality for managing SDA Extranet Policy in Cisco Catalyst Center. description: -version_added: "6.17.0" - Manage extranet policy operations such as add/update/delete. - API to create a new extranet policy. - API to update an existing or edit an existing extranet policy. - API for deletion of an existing extranet policy using the policy name. +version_added: "6.17.0" extends_documentation_fragment: - cisco.dnac.workflow_manager_params author: Rugvedi Kapse (@rukapse) From e1feffadc3a8ba5378a2d36c71e8f8accad642ac Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Thu, 8 Aug 2024 11:48:26 +0530 Subject: [PATCH 027/121] UT code for site workflow manager --- .../dnac/fixtures/site_workflow_manager.json | 489 ++++++++++++++++++ .../dnac/test_site_workflow_manager.py | 221 ++++++++ 2 files changed, 710 insertions(+) create mode 100644 tests/unit/modules/dnac/fixtures/site_workflow_manager.json create mode 100644 tests/unit/modules/dnac/test_site_workflow_manager.py diff --git a/tests/unit/modules/dnac/fixtures/site_workflow_manager.json b/tests/unit/modules/dnac/fixtures/site_workflow_manager.json new file mode 100644 index 0000000000..4bfbbc7e18 --- /dev/null +++ b/tests/unit/modules/dnac/fixtures/site_workflow_manager.json @@ -0,0 +1,489 @@ +{ + "playbook_config_site": [ + { + "type": "area", + "site": { + "area": { + "name": "Mysore", + "parentName": "Global" + } + } + }, + { + "type": "building", + "site": { + "building": { + "name": "Mod-x", + "address": "1234 Elm Street", + "parentName": "Global/Mysore", + "latitude": 37.338, + "longitude": -121.832, + "country": "India" + } + } + }, + { + "type": "floor", + "site": { + "floor": { + "name": "Mezzanine", + "parentName": "Global/Mysore/Mod-x", + "rfModel": "Cubes And Walled Offices", + "width": 100, + "length": 100, + "height": 15, + "floorNumber": 1 + } + } + } + ], + + "playbook_config_invalid_param": { + "type": "floor" + }, + + "playbook_update_site": + [ + { + "type": "area", + "site": { + "area": { + "name": "Mysore", + "parentName": "Global" + } + } + }, + { + "type": "building", + "site": { + "building": { + "name": "Mod-x", + "address": "1234 Elm Street", + "parentName": "Global/Mysore", + "latitude": "38.338", + "longitude": "-121.832", + "country": "India" + } + } + }, + { + "type": "floor", + "site": { + "floor": { + "name": "Mezzanine", + "parentName": "Global/Mysore/Mod-x", + "rfModel": "Cubes And Walled Offices", + "width": 104, + "length": 104, + "height": 15, + "floorNumber": 1 + } + } + } + ], + + "get_site_area": { + "response": + [{ + "parentId": "3cf15665-7b7b-4b29-82a9-75b8d094b602", + "additionalInfo": [ + { + "nameSpace": "Location", + "attributes": { + "addressInheritedFrom": "bc0ede8b-ef59-4c0e-a420-e570b72331db", + "type": "area" + } + } + ], + "name": "Mysore", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "bc0ede8b-ef59-4c0e-a420-e570b72331db", + "siteHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/bc0ede8b-ef59-4c0e-a420-e570b72331db", + "siteNameHierarchy": "Global/Mysore" + }] + + }, + + + "get_site_building": + { + "response": + [{ + "parentId": "bc0ede8b-ef59-4c0e-a420-e570b72331db", + "additionalInfo": [ + { + "nameSpace": "Location", + "attributes": { + "country": "India", + "address": "1234 Elm Street", + "latitude": "37.338", + "longitude": "-121.832", + "addressInheritedFrom": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "type": "building" + } + } + ], + "name": "Mod-x", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "siteHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/bc0ede8b-ef59-4c0e-a420-e570b72331db/bc5bae84-3991-4684-8d4f-8fa332fd554c", + "siteNameHierarchy": "Global/Mysore/Mod-x" + }] + }, + + "get_site_floor": + { + "response": [ + { + "parentId": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "additionalInfo": [ + { + "nameSpace": "mapGeometry", + "attributes": { + "offsetX": "0.0", + "offsetY": "0.0", + "length": "100.0", + "width": "100.0", + "height": "15.0" + } + }, + { + "nameSpace": "Location", + "attributes": { + "address": "1234 Elm Street", + "addressInheritedFrom": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "type": "floor" + } + }, + { + "nameSpace": "mapsSummary", + "attributes": { + "rfModel": "101101", + "floorIndex": "1" + } + } + ], + "name": "Mezzanine", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "383bd9aa-1634-4f8d-8803-487d47e45d71", + "siteHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/bc0ede8b-ef59-4c0e-a420-e570b72331db/bc5bae84-3991-4684-8d4f-8fa332fd554c/383bd9aa-1634-4f8d-8803-487d47e45d71", + "siteNameHierarchy": "Global/Mysore/Mod-x/Mezzanine" + } + ] + }, + + + "create_site_area_response": { + "executionId": "8057b09c-a528-4d6c-ae8a-a05e6bfffe89", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/8057b09c-a528-4d6c-ae8a-a05e6bfffe89", + "message": "The request has been accepted for execution" + }, + + "get_business_api_execution_details_response":{ + "status": "SUCCESS" + }, + + "currentExecution": { + "bapiKey": "50b5-89fd-4c7a-930a", + "bapiName": "Create Site", + "bapiExecutionId": "8057b09c-a528-4d6c-ae8a-a05e6bfffe89", + "startTime": "Tue Aug 06 05:10:21 UTC 2024", + "startTimeEpoch": 1722921021794, + "endTime": "Tue Aug 06 05:10:32 UTC 2024", + "endTimeEpoch": 1722921032740, + "timeDuration": 10946, + "status": "SUCCESS", + "runtimeInstanceId": "DNACP_Runtime_4720e362-ab2e-4697-85d3-02775fa0cb3c" + }, + + + "update_site_building": { + "executionId": "0ebad7d7-e2ad-471d-bd0e-f86d58d07c8d", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/0ebad7d7-e2ad-471d-bd0e-f86d58d07c8d", + "message": "The request has been accepted for execution" + }, + + "building_updation_execution": + { + "bapiKey": "eeb7-eb4b-4bd8-a1dd", + "bapiName": "Update Site", + "bapiExecutionId": "0ebad7d7-e2ad-471d-bd0e-f86d58d07c8d", + "startTime": "Wed Aug 07 13:09:53 UTC 2024", + "startTimeEpoch": 1723036193875, + "endTime": "Wed Aug 07 13:09:59 UTC 2024", + "endTimeEpoch": 1723036199347, + "timeDuration": 5472, + "status": "SUCCESS", + "runtimeInstanceId": "DNACP_Runtime_4720e362-ab2e-4697-85d3-02775fa0cb3c" + }, + + + "update_site_floor_response": { + "executionId": "237403c9-5f0f-4ad5-b0d5-42ea0cb4f137", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/237403c9-5f0f-4ad5-b0d5-42ea0cb4f137", + "message": "The request has been accepted for execution" + }, + + "floor_updation_execution": + { + "bapiKey": "eeb7-eb4b-4bd8-a1dd", + "bapiName": "Update Site", + "bapiExecutionId": "237403c9-5f0f-4ad5-b0d5-42ea0cb4f137", + "startTime": "Wed Aug 07 13:10:01 UTC 2024", + "startTimeEpoch": 1723036201195, + "endTime": "Wed Aug 07 13:10:01 UTC 2024", + "endTimeEpoch": 1723036201876, + "timeDuration": 681, + "status": "SUCCESS", + "runtimeInstanceId": "DNACP_Runtime_4720e362-ab2e-4697-85d3-02775fa0cb3c" + + }, + + + "get_site_updated_floor" : + { + "response": [ + { + "parentId": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "additionalInfo": [ + { + "nameSpace": "mapsSummary", + "attributes": { + "rfModel": "101101", + "floorIndex": "1" + } + }, + { + "nameSpace": "mapGeometry", + "attributes": { + "offsetX": "0.0", + "offsetY": "0.0", + "width": "104.0", + "length": "104.0", + "height": "15.0" + } + }, + { + "nameSpace": "Location", + "attributes": { + "address": "1234 Elm Street", + "addressInheritedFrom": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "type": "floor" + } + } + ], + "name": "Mezzanine", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "484b0a4f-81c0-41c1-82eb-1ef0ffe99b60", + "siteHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/45513959-fc87-4d15-af59-fba6ec5353b3/b8773b63-3652-4d39-a4ef-3c6a5aa657b0/484b0a4f-81c0-41c1-82eb-1ef0ffe99b60", + "siteNameHierarchy": "Global/Mysore/Mod-x/Mezzanine" + } + ] + }, + + + "get_site_updated_building": + { + "response": + [{ + "parentId": "bc0ede8b-ef59-4c0e-a420-e570b72331db", + "additionalInfo": [ + { + "nameSpace": "Location", + "attributes": { + "country": "India", + "address": "1234 Elm Street", + "latitude": "38.338", + "longitude": "-121.832", + "addressInheritedFrom": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "type": "building" + } + } + ], + "name": "Mod-x", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "bc5bae84-3991-4684-8d4f-8fa332fd554c", + "siteHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/bc0ede8b-ef59-4c0e-a420-e570b72331db/bc5bae84-3991-4684-8d4f-8fa332fd554c", + "siteNameHierarchy": "Global/Mysore/Mod-x" + }] + }, + + + "get_membership": { + "site": { + "response": [ + { + "parentId": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "groupTypeList": ["SITE"], + "additionalInfo": [ + { + "nameSpace": "com.wireless.managingwlc", + "attributes": { + "secondaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "anchorWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "tertiaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "primaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3" + } + }, + { + "nameSpace": "Location", + "attributes": { + "address": "1234 Elm Street", + "addressInheritedFrom": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "type": "floor" + } + }, + { + "nameSpace": "mapGeometry", + "attributes": { + "offsetX": "0.0", + "offsetY": "0.0", + "width": "104.0", + "length": "104.0", + "height": "15.0" + } + }, + { + "nameSpace": "mapsSummary", + "attributes": { + "rfModel": "101101", + "floorIndex": "1" + } + }, + { + "nameSpace": "System Settings", + "attributes": { + "group.count.total": "0", + "hasChild": "FALSE", + "group.count.direct": "0", + "group.hierarchy.groupType": "SITE", + "member.count.total": "0", + "member.count.direct": "0" + } + } + ], + "groupHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/45513959-fc87-4d15-af59-fba6ec5353b3/b8773b63-3652-4d39-a4ef-3c6a5aa657b0/484b0a4f-81c0-41c1-82eb-1ef0ffe99b60", + "groupNameHierarchy": "Global/Mysore/Mod-x/Mezzanine", + "name": "Mezzanine", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "484b0a4f-81c0-41c1-82eb-1ef0ffe99b60" + }, + { + "parentId": "45513959-fc87-4d15-af59-fba6ec5353b3", + "groupTypeList": ["SITE"], + "additionalInfo": [ + { + "nameSpace": "com.wireless.managingwlc", + "attributes": { + "secondaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "anchorWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "tertiaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3", + "primaryWlcInheritedFrom": "45513959-fc87-4d15-af59-fba6ec5353b3" + } + }, + { + "nameSpace": "Location", + "attributes": { + "country": "United States", + "address": "1234 Elm Street", + "latitude": "38.338", + "addressInheritedFrom": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "type": "building", + "longitude": "-121.832" + } + }, + { + "nameSpace": "System Settings", + "attributes": { + "group.count.total": "1", + "hasChild": "TRUE", + "group.count.direct": "1", + "group.hierarchy.groupType": "SITE", + "member.count.total": "0", + "member.count.direct": "0" + } + } + ], + "groupHierarchy": "3cf15665-7b7b-4b29-82a9-75b8d094b602/45513959-fc87-4d15-af59-fba6ec5353b3/b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "groupNameHierarchy": "Global/Mysore/Mod-x", + "name": "Mod-x", + "instanceTenantId": "6486ce96ff1f0d0c8be622f4", + "id": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0" + } + ], + "version": "1.0" + }, + "device": [ + { + "response": [], + "version": "1.0", + "siteId": "45513959-fc87-4d15-af59-fba6ec5353b3", + "message": "Site doesn’t not have device member with given device family or serial number as input" + }, + { + "response": [], + "version": "1.0", + "siteId": "484b0a4f-81c0-41c1-82eb-1ef0ffe99b60", + "message": "Site doesn’t not have device member with given device family or serial number as input" + }, + { + "response": [], + "version": "1.0", + "siteId": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", + "message": "Site doesn’t not have device member with given device family or serial number as input" + } + ] + }, + + + "get_single_floor_deletion_response": + { + "delete_site": { + "executionId": "41f2c39e-2545-49d3-8a25-103441f8baa2", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/41f2c39e-2545-49d3-8a25-103441f8baa2", + "message": "The request has been accepted for execution" + } + }, + + "delete_site_execution_detail": + { + "bapiKey": "f083-cb13-484a-8fae", + "bapiName": "Delete Site", + "bapiExecutionId": "41f2c39e-2545-49d3-8a25-103441f8baa2", + "startTime": "Tue Aug 06 11:42:23 UTC 2024", + "startTimeEpoch": 1722944543211, + "endTime": "Tue Aug 06 11:42:26 UTC 2024", + "endTimeEpoch": 1722944546152, + "timeDuration": 2941, + "status": "SUCCESS", + "bapiSyncResponse": "{\"status\":true,\"message\":\"Floor deleted successfully.\"}", + "bapiSyncResponseJson": { + "status": true, + "message": "Floor deleted successfully." + }, + "runtimeInstanceId": "DNACP_Runtime_4720e362-ab2e-4697-85d3-02775fa0cb3c" + }, + + "get_single_building_deletion_response": + { + "delete_site": { + "executionId": "029041c5-70e0-4984-b383-c3ab754120c0", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/029041c5-70e0-4984-b383-c3ab754120c0", + "message": "The request has been accepted for execution" + } + }, + + "get_single_area_deletion_response" : + { + "delete_site": { + "executionId": "ca755654-cdf0-4cd3-b6ee-3bf9390e924b", + "executionStatusUrl": "/dna/intent/api/v1/dnacaap/management/execution-status/ca755654-cdf0-4cd3-b6ee-3bf9390e924b", + "message": "The request has been accepted for execution" + } + } + + + } + + + diff --git a/tests/unit/modules/dnac/test_site_workflow_manager.py b/tests/unit/modules/dnac/test_site_workflow_manager.py new file mode 100644 index 0000000000..395221a557 --- /dev/null +++ b/tests/unit/modules/dnac/test_site_workflow_manager.py @@ -0,0 +1,221 @@ +# Copyright (c) 2020 Cisco and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from unittest.mock import patch +from ansible_collections.cisco.dnac.plugins.modules import site_workflow_manager +from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData + + +class TestDnacSiteWorkflow(TestDnacModule): + + module = site_workflow_manager + test_data = loadPlaybookData("site_workflow_manager") + playbook_config_site = test_data.get("playbook_config_site") + playbook_update_site = test_data.get("playbook_update_site") + playbook_config_invalid_param = test_data.get("playbook_config_invalid_param") + + def setUp(self): + super(TestDnacSiteWorkflow, self).setUp() + + self.mock_dnac_init = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__") + self.run_dnac_init = self.mock_dnac_init.start() + self.run_dnac_init.side_effect = [None] + self.mock_dnac_exec = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK._exec" + ) + self.run_dnac_exec = self.mock_dnac_exec.start() + + self.load_fixtures() + + def tearDown(self): + super(TestDnacSiteWorkflow, self).tearDown() + self.mock_dnac_exec.stop() + self.mock_dnac_init.stop() + + def load_fixtures(self, response=None, device=""): + """ + Load fixtures for user. + """ + if "create_site" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + Exception(), + self.test_data.get("create_site_area_response"), + self.test_data.get("currentExecution"), + self.test_data.get("get_site_area"), + self.test_data.get("get_site_building"), + self.test_data.get("get_site_floor") + ] + if "update_site" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_site_area"), + self.test_data.get("get_site_building"), + self.test_data.get("update_site_building"), + self.test_data.get("building_updation_execution"), + self.test_data.get("get_site_floor"), + self.test_data.get("update_site_floor_response"), + self.test_data.get("floor_updation_execution"), + self.test_data.get("get_site_updated_floor") + ] + if "delete_existing_site" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_site_area"), + self.test_data.get("get_membership"), + self.test_data.get("get_single_floor_deletion_response"), + self.test_data.get("delete_site_execution_detail"), + self.test_data.get("get_single_building_deletion_response"), + self.test_data.get("delete_site_execution_detail"), + self.test_data.get("get_single_area_deletion_response"), + self.test_data.get("delete_site_execution_detail"), + ] + + if "verify_diff" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_site_area"), + self.test_data.get("get_site_area"), + self.test_data.get("get_site_building"), + self.test_data.get("get_site_building"), + self.test_data.get("get_site_floor"), + self.test_data.get("get_site_floor"), + ] + + def test_Site_workflow_manager_create_site(self): + """ + Test case for site workflow manager when creating a site. + + This test case checks the behavior of the site workflow manager when creating a new site in the specified DNAC. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_config_site + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertEqual( + result.get('msg'), + "Site(s) '['Global/Mysore']' created successfully and some site(s) '['Global/Mysore/Mod-x'," + + " 'Global/Mysore/Mod-x/Mezzanine']' not needs any update in Cisco Catalyst\n" + " Center." + ) + + def test_Site_workflow_manager_update_site(self): + """ + Test case for site workflow manager when an update is needed. + + This test case checks the behavior of the site workflow manager when an + + update is required for the specified site in the DNAC. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + # config_verify=True, + config=self.playbook_update_site + ) + ) + result = self.execute_module(changed=True, failed=False) + self.assertEqual( + result.get("msg"), + "Site(s) '['Global/Mysore/Mod-x', 'Global/Mysore/Mod-x/Mezzanine']' updated successfully and some site(s)" + " '['Global/Mysore']' not needs any update in Cisco Catalyst\n" + + " Center." + ) + + def test_site_workflow_manager_invalid_param(self): + + """ + Test case for site workflow manager with invalid parameters in the playbook. + + This test case checks the behavior of the site workflow manager when the playbook contains invalid parameters. + """ + + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.test_data.get("playbook_config_invalid_param") + ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertFalse( + "Invalid parameters in playbook:" in result.get('msg') + ) + + def test_site_workflow_manager_delete_existing_site(self): + + """ + Test case for site workflow manager when deleting an existing site. + + This test case checks the behavior of the site workflow manager when deleting an existing site in the DNAC. + """ + + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="deleted", + config_verify=True, + config=self.playbook_config_site + ) + ) + result = self.execute_module(changed=True, failed=False) + + self.assertEqual( + result.get('msg'), + "Given site(s) '['Mezzanine', 'Mod-x', 'Global/Mysore']' deleted successfully from Cisco Catalyst Center" + + " and unable to deleted some site(s) '['Global/Mysore/Mod-x', 'Global/Mysore/Mod-x/Mezzanine']' as they\n" + + " are not found in Cisco Catalyst Center." + ) + + def test_Site_workflow_manager_verify_diff_merged_site(self): + """ + Test case for verify parameters in site workflow manager after applying merged state. + + This test case checks the behavior of the site workflow manager after applying merged state Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_config_site + ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual( + result.get('msg'), + "Site(s) '['Global/Mysore', 'Global/Mysore/Mod-x', 'Global/Mysore/Mod-x/Mezzanine']'" + + " not needs any update in Cisco Catalyst Center." + ) From 56f175ff3deb4895c354112a81ed75865d61ae76 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 8 Aug 2024 12:18:29 +0530 Subject: [PATCH 028/121] Updated user and role workflow feature --- plugins/modules/user_role_workflow_manager.py | 25 +-- .../dnac/fixtures/swim_workflow_manager.json | 151 ++++++++++++++++++ .../dnac/test_swim_workflow_manager.py | 126 +++++++++++++++ 3 files changed, 290 insertions(+), 12 deletions(-) create mode 100644 tests/unit/modules/dnac/fixtures/swim_workflow_manager.json create mode 100644 tests/unit/modules/dnac/test_swim_workflow_manager.py diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 3f265b4a90..ab8b391a21 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1004,9 +1004,9 @@ def valid_role_config_parameters(self, role_config): error_messages = [] role_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") + role_name_regex_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." self.validate_string_field(role_config.get("role_name"), role_name_regex, - "Role name: 'role_name' must only contain letters, numbers, underscores " - "and hyphens and should not contain spaces or other special characters.", error_messages) + "role_name: '{0}' {1}".format(role_config.get("role_name"), role_name_regex_msg), error_messages) if role_config.get("description"): self.validate_string_parameter("description", role_config["description"], error_messages) @@ -1057,25 +1057,26 @@ def valid_user_config_parameters(self, user_config): self.log("Validating user configuration parameters...", "INFO") error_messages = [] regex_name_validation = re.compile(r"^[A-Za-z0-9_-]+$") + regex_name_validation_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." - self.validate_string_field(user_config.get("first_name"), regex_name_validation, "first_name: 'first_name' must only contain letters, numbers, " - "underscores and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("first_name"), regex_name_validation, + "first_name: '{0}' {1}".format(user_config.get("first_name"), regex_name_validation_msg), error_messages) - self.validate_string_field(user_config.get("last_name"), regex_name_validation, "last_name: 'last_name' must only contain letters, numbers, " - "underscores and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("last_name"), regex_name_validation, + "last_name: '{0}' {1}".format(user_config.get("last_name"), regex_name_validation_msg), error_messages) email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") + email_regex_msg = "email: Invalid email format for 'email': {0}".format(user_config.get("email")) if user_config.get("email"): - self.validate_string_field(user_config.get("email"), email_regex, - "email: Invalid email format for 'email': {0}".format(user_config.get("email")), error_messages) + self.validate_string_field(user_config.get("email"), email_regex, email_regex_msg, error_messages) password_regex = re.compile(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$") + password_regex_msg = "password: 'Password' does not meet complexity requirements for password: {0}".format(user_config.get("password")) if user_config.get("password"): - self.validate_string_field(user_config.get("password"), password_regex, "password: 'Password' does not meet complexity requirements " - "for password: {0}".format(user_config.get("password")), error_messages) + self.validate_string_field(user_config.get("password"), password_regex, password_regex_msg, error_messages) - self.validate_string_field(user_config.get("username"), regex_name_validation, "username: 'Username' must only contain letters, numbers, " - "underscores and hyphens and should not contain spaces or other special characters.", error_messages) + self.validate_string_field(user_config.get("username"), regex_name_validation, + "username: '{0}' {1}".format(user_config.get("username"), regex_name_validation_msg), error_messages) if user_config.get("role_list"): param_spec = dict(type="list", elements="str") diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json new file mode 100644 index 0000000000..20a9106b67 --- /dev/null +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -0,0 +1,151 @@ +{ + "playbook_import_image_url_tag_golden_load": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + }, + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_serial_number": "FJC2327U0S2" + }, + "image_activation_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "schedule_validate": false, + "activate_lower_image_version": false, + "distribute_if_needed": true, + "device_serial_number": "FJC2327U0S2" + } + } + ], + + "playbook_import_image_from_local_tag_golden": [ + { + "import_image_details": { + "type": "local", + "local_image_details": { + "file_path": "/Users/Downloads/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + } + } + ], + + "playbook_tag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ACCESS", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "site_name": "Global/USA/San Francisco/BGL_18", + "tagging": true + } + } + ], + + "playbook_untag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "tagging": false + } + } + ], + + "playbook_Distribute_image_associate_site_role": [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "site_name": "Global/USA/San Francisco/BGL_18", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches" + } + } + ], + + "playbook_Activate_image_associate_site_role": [ + { + "image_activation_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "site_name": "Global/USA/San Francisco/BGL_18", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches", + "schedule_validate": false, + "activate_lower_image_version": true, + "distribute_if_needed": true + } + } + ], + "playbook_import_image_url_tag_golden": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", + "is_third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.01.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "tagging": true + } + } + ], + "get_software_image_details": { + "response": [], + "version": "1.0" + }, + + "import_software_image_via_url": {"response": {"taskId": "01912ce2-73b4-7bbe-a6f3-428e4809602a", "url": "/api/v1/task/01912ce2-73b4-7bbe-a6f3-428e4809602a"}, "version": "1.0"}, + "get_software_image_details_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, + "tag_as_golden_image": {"response": {"taskId": "01912ce2-f6f0-777c-8ec6-b404471af00d", "url": "/api/v1/task/01912ce2-f6f0-777c-8ec6-b404471af00d"}, "version": "1.0"}, + "import_image_url_tag_golden_response":{ + "message": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + }, + "get_software_image_details_untag_golden": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_device_family_identifiers_untag_golden": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_software_image_details_untag_golden_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_untag_golden_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, + "remove_golden_tag_for_image_untag_golden": {"response": {"taskId": "01913071-3edb-746d-a773-bd38590b2c27", "url": "/api/v1/task/01913071-3edb-746d-a773-bd38590b2c27"}, "version": "1.0"}, + "get_software_image_details_untag_golden_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_device_family_identifiers_untag_golden_1": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}], "version": "1.0"}, + "get_software_image_details_untag_golden_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_untag_golden": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, + + "untag_image_as_golden_and_load_on_device_responce":{ + "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." +} +} diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py new file mode 100644 index 0000000000..a797150c04 --- /dev/null +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -0,0 +1,126 @@ +# Copyright (c) 2024 Cisco and/or its affiliates. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Make coding more python3-ish + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.cisco.dnac.plugins.modules import swim_workflow_manager +from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData + + +class TestswimWorkflowManager(TestDnacModule): + + module = swim_workflow_manager + + test_data = loadPlaybookData("swim_workflow_manager") + + playbook_import_image_url_tag_golden_load = test_data.get("playbook_import_image_url_tag_golden_load") + playbook_import_image_from_local_tag_golden = test_data.get("playbook_import_image_from_local_tag_golden") + playbook_tag_image_as_golden_and_load_on_device = test_data.get("playbook_tag_image_as_golden_and_load_on_device") + playbook_untag_image_as_golden_and_load_on_device = test_data.get("playbook_untag_image_as_golden_and_load_on_device") + playbook_Distribute_image_associate_site_role = test_data.get("playbook_Distribute_image_associate_site_role") + playbook_Activate_image_associate_site_role = test_data.get("playbook_Activate_image_associate_site_role") + playbook_import_image_url_tag_golden = test_data.get("playbook_import_image_url_tag_golden") + + + def setUp(self): + super(TestswimWorkflowManager, self).setUp() + + self.mock_dnac_init = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__") + self.run_dnac_init = self.mock_dnac_init.start() + self.run_dnac_init.side_effect = [None] + self.mock_dnac_exec = patch( + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK._exec" + ) + self.run_dnac_exec = self.mock_dnac_exec.start() + + def tearDown(self): + super(TestswimWorkflowManager, self).tearDown() + self.mock_dnac_exec.stop() + self.mock_dnac_init.stop() + + def load_fixtures(self, response=None, device=""): + """ + Load fixtures for user. + """ + if "playbook_import_image_url_tag_golden" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details"), + self.test_data.get("import_software_image_via_url"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_device_family_identifiers"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_golden_tag_status_of_an_image"), + self.test_data.get("tag_as_golden_image"), + self.test_data.get("import_image_url_tag_golden_response") + ] + # elif "user_update_needed" in self._testMethodName: + # self.run_dnac_exec.side_effect = [ + # self.test_data.get("update_needed_get_user_response"), + # self.test_data.get("update_user_needed_get_role_response"), + # self.test_data.get("update_needed_user_response") + # ] + + def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): + """ + Test case for user role workflow manager when creating a user. + + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_import_image_url_tag_golden + ) + ) + result = self.execute_module(changed=True, failed=False) + print(result) + self.assertEqual( + result.get('response'), + "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + ) + + def test_user_role_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): + """ + Test case for user role workflow manager when creating a user. + + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_untag_image_as_golden_and_load_on_device + ) + ) + result = self.execute_module(changed=True, failed=False) + print(result) + self.assertEqual( + result.get('response'), + "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + ) From ac7e327811319dff800967c745e01d18eb20e7ae Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Thu, 8 Aug 2024 12:53:59 +0530 Subject: [PATCH 029/121] 2 test cases added --- playbooks/swim_workflow_manager.yml | 13 +--- .../dnac/fixtures/swim_workflow_manager.json | 54 +++++++++----- .../dnac/test_swim_workflow_manager.py | 73 ++++++++++++++----- 3 files changed, 96 insertions(+), 44 deletions(-) diff --git a/playbooks/swim_workflow_manager.yml b/playbooks/swim_workflow_manager.yml index 38b75b7f19..c986d85106 100644 --- a/playbooks/swim_workflow_manager.yml +++ b/playbooks/swim_workflow_manager.yml @@ -23,17 +23,12 @@ dnac_task_poll_interval: 1 state : "merged" config: - - import_image_details: - type: remote - url_details: - payload: - - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin - third_party: False - tagging_details: + - tagging_details: image_name: cat9k_iosxe.17.12.02.SPA.bin device_role: ALL - device_image_family_name: Cisco Catalyst 9300 Switch - tagging: True + device_image_family_name: Cisco Catalyst 9000 UADP 8 Port Virtual Switch + site_name: "Global/LTTS" + tagging: false diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json index 239b077f3f..6045572cc4 100644 --- a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -64,18 +64,6 @@ } ], - "playbook_untag_image_as_golden_and_load_on_device": [ - { - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": false - } - } - ], - "playbook_Distribute_image_associate_site_role": [ { "image_distribution_details": { @@ -102,6 +90,7 @@ } } ], + "playbook_import_image_url_tag_golden": [ { "import_image_details": { @@ -123,18 +112,49 @@ } } ], - "get_software_image_details": { - "response": [], - "version": "1.0" - }, + "get_software_image_details": { "response": [], "version": "1.0"}, "import_software_image_via_url": {"response": {"taskId": "01912ce2-73b4-7bbe-a6f3-428e4809602a", "url": "/api/v1/task/01912ce2-73b4-7bbe-a6f3-428e4809602a"}, "version": "1.0"}, "get_software_image_details_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, "tag_as_golden_image": {"response": {"taskId": "01912ce2-f6f0-777c-8ec6-b404471af00d", "url": "/api/v1/task/01912ce2-f6f0-777c-8ec6-b404471af00d"}, "version": "1.0"}, + "get_device_family_identifiers1": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_software_image_details_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_software_image_details_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_2": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, + "import_image_url_tag_golden_response":{ "message": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - } + }, + + "playbook_untag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name" :"Global/LTTS", + "tagging": false + } + } + ], + + "get_software_image_details_untag_golden": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_site_untag_golden": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_device_family_identifiers_untag_golden": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_software_image_details_untag_golden_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_untag_golden": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, + "remove_golden_tag_for_image_untag_golden": {"response": {"taskId": "019130ab-acc6-7c9b-9a8d-22f622440fc3", "url": "/api/v1/task/019130ab-acc6-7c9b-9a8d-22f622440fc3"}, "version": "1.0"}, + "get_software_image_details_untag_golden_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_site_untag_golden_1": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_device_family_identifiers_untag_golden_1": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}], "version": "1.0"}, + "get_software_image_details_untag_golden_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_untag_golden_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, + + + "untag_image_as_golden_and_load_on_device_responce":{ + "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." +} } \ No newline at end of file diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py index 06061faa17..122e2488a7 100644 --- a/tests/unit/modules/dnac/test_swim_workflow_manager.py +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -60,30 +60,44 @@ def load_fixtures(self, response=None, device=""): """ Load fixtures for user. """ - if "playbook_import_image_url_tag_golden" in self._testMethodName: - self.run_dnac_exec.side_effect = [ - self.test_data.get("get_software_image_details"), - self.test_data.get("import_software_image_via_url"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_device_family_identifiers"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_golden_tag_status_of_an_image"), - self.test_data.get("tag_as_golden_image"), - self.test_data.get("import_image_url_tag_golden_response") - ] - # elif "user_update_needed" in self._testMethodName: + # if "playbook_import_image_url_tag_golden" in self._testMethodName: # self.run_dnac_exec.side_effect = [ - # self.test_data.get("update_needed_get_user_response"), - # self.test_data.get("update_user_needed_get_role_response"), - # self.test_data.get("update_needed_user_response") + # self.test_data.get("get_software_image_details"), + # self.test_data.get("import_software_image_via_url"), + # self.test_data.get("get_software_image_details_1"), + # self.test_data.get("get_software_image_details_1"), + # self.test_data.get("get_device_family_identifiers"), + # self.test_data.get("get_software_image_details_1"), + # self.test_data.get("get_golden_tag_status_of_an_image"), + # self.test_data.get("tag_as_golden_image"), + # self.test_data.get("get_software_image_details_1"), + # self.test_data.get("get_device_family_identifiers1"), + # self.test_data.get("get_software_image_details_2"), + # self.test_data.get("get_software_image_details_3"), + # self.test_data.get("get_golden_tag_status_of_an_image_2"), + # self.test_data.get("import_image_url_tag_golden_response") # ] + if "playbook_untag_image_as_golden_and_load_on_device" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_untag_golden"), + self.test_data.get("get_site_untag_golden"), + self.test_data.get("get_device_family_identifiers_untag_golden"), + self.test_data.get("get_software_image_details_untag_golden_1"), + self.test_data.get("get_golden_tag_status_of_an_image_untag_golden"), + self.test_data.get("remove_golden_tag_for_image_untag_golden"), + self.test_data.get("get_software_image_details_untag_golden_2"), + self.test_data.get("get_site_untag_golden_1"), + self.test_data.get("get_device_family_identifiers_untag_golden_1"), + self.test_data.get("get_software_image_details_untag_golden_3"), + self.test_data.get("get_golden_tag_status_of_an_image_untag_golden_1"), + self.test_data.get("untag_image_as_golden_and_load_on_device_responce") + ] def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): """ - Test case for user role workflow manager when creating a user. + Test case for user swim workflow manager when Import an image from a URL, tag it as golden. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + This test case checks the behavior of the swim workflow when Import an image from a URL, tag it as golden in the specified Cisco Catalyst Center. """ set_module_args( dict( @@ -101,3 +115,26 @@ def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): result.get('response'), "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." ) + + def test_user_role_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): + """ + Test case for user role workflow manager when creating a user. + + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_untag_image_as_golden_and_load_on_device + ) + ) + result = self.execute_module(changed=True, failed=False) + print(result) + self.assertEqual( + result.get('msg'), + "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + ) From e2c990794633d2d2f9c703c59768b007fcd93e66 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 8 Aug 2024 12:57:37 +0530 Subject: [PATCH 030/121] Updated user and role workflow feature --- plugins/modules/user_role_workflow_manager.py | 20 ++- .../dnac/fixtures/swim_workflow_manager.json | 151 ------------------ .../fixtures/user_role_workflow_manager.json | 4 +- .../dnac/test_swim_workflow_manager.py | 126 --------------- .../dnac/test_user_role_workflow_manager.py | 8 +- 5 files changed, 18 insertions(+), 291 deletions(-) delete mode 100644 tests/unit/modules/dnac/fixtures/swim_workflow_manager.json delete mode 100644 tests/unit/modules/dnac/test_swim_workflow_manager.py diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index ab8b391a21..7e2ab35f7b 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1003,10 +1003,11 @@ def valid_role_config_parameters(self, role_config): self.log("Validating role configuration parameters...", "INFO") error_messages = [] + role_name = role_config.get("role_name") role_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") role_name_regex_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." - self.validate_string_field(role_config.get("role_name"), role_name_regex, - "role_name: '{0}' {1}".format(role_config.get("role_name"), role_name_regex_msg), error_messages) + self.validate_string_field(role_name, role_name_regex, + "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) if role_config.get("description"): self.validate_string_parameter("description", role_config["description"], error_messages) @@ -1059,11 +1060,13 @@ def valid_user_config_parameters(self, user_config): regex_name_validation = re.compile(r"^[A-Za-z0-9_-]+$") regex_name_validation_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." - self.validate_string_field(user_config.get("first_name"), regex_name_validation, - "first_name: '{0}' {1}".format(user_config.get("first_name"), regex_name_validation_msg), error_messages) + first_name = user_config.get("first_name") + self.validate_string_field(first_name, regex_name_validation, + "first_name: '{0}' {1}".format(first_name, regex_name_validation_msg), error_messages) - self.validate_string_field(user_config.get("last_name"), regex_name_validation, - "last_name: '{0}' {1}".format(user_config.get("last_name"), regex_name_validation_msg), error_messages) + last_name = user_config.get("last_name") + self.validate_string_field(last_name, regex_name_validation, + "last_name: '{0}' {1}".format(last_name, regex_name_validation_msg), error_messages) email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") email_regex_msg = "email: Invalid email format for 'email': {0}".format(user_config.get("email")) @@ -1075,8 +1078,9 @@ def valid_user_config_parameters(self, user_config): if user_config.get("password"): self.validate_string_field(user_config.get("password"), password_regex, password_regex_msg, error_messages) - self.validate_string_field(user_config.get("username"), regex_name_validation, - "username: '{0}' {1}".format(user_config.get("username"), regex_name_validation_msg), error_messages) + username = user_config.get("username") + self.validate_string_field(username, regex_name_validation, + "username: '{0}' {1}".format(username, regex_name_validation_msg), error_messages) if user_config.get("role_list"): param_spec = dict(type="list", elements="str") diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json deleted file mode 100644 index 20a9106b67..0000000000 --- a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "playbook_import_image_url_tag_golden_load": [ - { - "import_image_details": { - "type": "remote", - "url_details": { - "payload": [ - { - "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - ] - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - }, - "image_distribution_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_serial_number": "FJC2327U0S2" - }, - "image_activation_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "schedule_validate": false, - "activate_lower_image_version": false, - "distribute_if_needed": true, - "device_serial_number": "FJC2327U0S2" - } - } - ], - - "playbook_import_image_from_local_tag_golden": [ - { - "import_image_details": { - "type": "local", - "local_image_details": { - "file_path": "/Users/Downloads/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - } - } - ], - - "playbook_tag_image_as_golden_and_load_on_device": [ - { - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - } - } - ], - - "playbook_untag_image_as_golden_and_load_on_device": [ - { - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.02.SPA.bin", - "device_role": "ALL", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "tagging": false - } - } - ], - - "playbook_Distribute_image_associate_site_role": [ - { - "image_distribution_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "site_name": "Global/USA/San Francisco/BGL_18", - "device_role": "ALL", - "device_family_name": "Switches and Hubs", - "device_series_name": "Cisco Catalyst 9300 Series Switches" - } - } - ], - - "playbook_Activate_image_associate_site_role": [ - { - "image_activation_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "site_name": "Global/USA/San Francisco/BGL_18", - "device_role": "ALL", - "device_family_name": "Switches and Hubs", - "device_series_name": "Cisco Catalyst 9300 Series Switches", - "schedule_validate": false, - "activate_lower_image_version": true, - "distribute_if_needed": true - } - } - ], - "playbook_import_image_url_tag_golden": [ - { - "import_image_details": { - "type": "remote", - "url_details": { - "payload": [ - { - "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - ] - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ALL", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "tagging": true - } - } - ], - "get_software_image_details": { - "response": [], - "version": "1.0" - }, - - "import_software_image_via_url": {"response": {"taskId": "01912ce2-73b4-7bbe-a6f3-428e4809602a", "url": "/api/v1/task/01912ce2-73b4-7bbe-a6f3-428e4809602a"}, "version": "1.0"}, - "get_software_image_details_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - "tag_as_golden_image": {"response": {"taskId": "01912ce2-f6f0-777c-8ec6-b404471af00d", "url": "/api/v1/task/01912ce2-f6f0-777c-8ec6-b404471af00d"}, "version": "1.0"}, - "import_image_url_tag_golden_response":{ - "message": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - }, - "get_software_image_details_untag_golden": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_device_family_identifiers_untag_golden": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_software_image_details_untag_golden_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_untag_golden_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, - "remove_golden_tag_for_image_untag_golden": {"response": {"taskId": "01913071-3edb-746d-a773-bd38590b2c27", "url": "/api/v1/task/01913071-3edb-746d-a773-bd38590b2c27"}, "version": "1.0"}, - "get_software_image_details_untag_golden_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_device_family_identifiers_untag_golden_1": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}], "version": "1.0"}, - "get_software_image_details_untag_golden_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_untag_golden": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - - "untag_image_as_golden_and_load_on_device_responce":{ - "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." -} -} diff --git a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json index 9f089b87e9..0a63f3d646 100644 --- a/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/user_role_workflow_manager.json @@ -460,7 +460,7 @@ } }, "user_invalid_param_not_correct_formate_responce":{ - "message": "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., last_name: 'last_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: Ajith123, username: 'Username' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." + "message": "Invalid parameters in playbook config: first_name: 'ajith ' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., last_name: 'andrew ' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, password: 'Password' does not meet complexity requirements for password: Ajith123, username: 'ajithandrewj ' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." }, "user_invalid_param_not_type_list_response":{ "message": "Invalid parameter(s) found in playbook: Super-Admin-Role : is not a valid list" @@ -1372,7 +1372,7 @@ } }, "role_invalid_param_rolename_not_correct_formate_responce":{ - "message": "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." + "message": "Invalid parameters in playbook config: role_name: 'Test_Role_1 ' must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." }, "invalid_param_type_list_missing_response":{ "message": "Configuration is not available in the playbook for validation or user/role details are not type list" diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py deleted file mode 100644 index a797150c04..0000000000 --- a/tests/unit/modules/dnac/test_swim_workflow_manager.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (c) 2024 Cisco and/or its affiliates. - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Make coding more python3-ish - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type - -from unittest.mock import patch - -from ansible_collections.cisco.dnac.plugins.modules import swim_workflow_manager -from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData - - -class TestswimWorkflowManager(TestDnacModule): - - module = swim_workflow_manager - - test_data = loadPlaybookData("swim_workflow_manager") - - playbook_import_image_url_tag_golden_load = test_data.get("playbook_import_image_url_tag_golden_load") - playbook_import_image_from_local_tag_golden = test_data.get("playbook_import_image_from_local_tag_golden") - playbook_tag_image_as_golden_and_load_on_device = test_data.get("playbook_tag_image_as_golden_and_load_on_device") - playbook_untag_image_as_golden_and_load_on_device = test_data.get("playbook_untag_image_as_golden_and_load_on_device") - playbook_Distribute_image_associate_site_role = test_data.get("playbook_Distribute_image_associate_site_role") - playbook_Activate_image_associate_site_role = test_data.get("playbook_Activate_image_associate_site_role") - playbook_import_image_url_tag_golden = test_data.get("playbook_import_image_url_tag_golden") - - - def setUp(self): - super(TestswimWorkflowManager, self).setUp() - - self.mock_dnac_init = patch( - "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__") - self.run_dnac_init = self.mock_dnac_init.start() - self.run_dnac_init.side_effect = [None] - self.mock_dnac_exec = patch( - "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK._exec" - ) - self.run_dnac_exec = self.mock_dnac_exec.start() - - def tearDown(self): - super(TestswimWorkflowManager, self).tearDown() - self.mock_dnac_exec.stop() - self.mock_dnac_init.stop() - - def load_fixtures(self, response=None, device=""): - """ - Load fixtures for user. - """ - if "playbook_import_image_url_tag_golden" in self._testMethodName: - self.run_dnac_exec.side_effect = [ - self.test_data.get("get_software_image_details"), - self.test_data.get("import_software_image_via_url"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_device_family_identifiers"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_golden_tag_status_of_an_image"), - self.test_data.get("tag_as_golden_image"), - self.test_data.get("import_image_url_tag_golden_response") - ] - # elif "user_update_needed" in self._testMethodName: - # self.run_dnac_exec.side_effect = [ - # self.test_data.get("update_needed_get_user_response"), - # self.test_data.get("update_user_needed_get_role_response"), - # self.test_data.get("update_needed_user_response") - # ] - - def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): - """ - Test case for user role workflow manager when creating a user. - - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config=self.playbook_import_image_url_tag_golden - ) - ) - result = self.execute_module(changed=True, failed=False) - print(result) - self.assertEqual( - result.get('response'), - "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - ) - - def test_user_role_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): - """ - Test case for user role workflow manager when creating a user. - - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config=self.playbook_untag_image_as_golden_and_load_on_device - ) - ) - result = self.execute_module(changed=True, failed=False) - print(result) - self.assertEqual( - result.get('response'), - "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - ) diff --git a/tests/unit/modules/dnac/test_user_role_workflow_manager.py b/tests/unit/modules/dnac/test_user_role_workflow_manager.py index dea7f32d8b..274df365d9 100644 --- a/tests/unit/modules/dnac/test_user_role_workflow_manager.py +++ b/tests/unit/modules/dnac/test_user_role_workflow_manager.py @@ -390,12 +390,12 @@ def test_user_role_workflow_manager_user_invalid_param_not_correct_formate(self) print(result) self.assertEqual( result.get("msg"), - "Invalid parameters in playbook config: first_name: 'first_name' must only contain letters, \ + "Invalid parameters in playbook config: first_name: 'ajith ' must only contain letters, \ numbers, underscores and hyphens and should not contain spaces or other \ -special characters., last_name: 'last_name' must only contain letters, numbers, underscores \ +special characters., last_name: 'andrew ' must only contain letters, numbers, underscores \ and hyphens and should not contain spaces or other special characters., email: Invalid email format for 'email': ajith.andrewexample.com, \ password: 'Password' does not meet complexity requirements for password: \ -Ajith123, username: 'Username' must only contain letters, numbers, underscores \ +Ajith123, username: 'ajithandrewj ' must only contain letters, numbers, underscores \ and hyphens and should not contain spaces or other special characters." ) @@ -695,7 +695,7 @@ def test_user_role_workflow_manager_role_invalid_param_rolename_not_correct_form print(result) self.assertEqual( result.get("msg"), - "Invalid parameters in playbook config: Role name: 'role_name' must only contain letters, numbers, underscores \ + "Invalid parameters in playbook config: role_name: 'Test_Role_1 ' must only contain letters, numbers, underscores \ and hyphens and should not contain spaces or other special characters." ) From 5c690342c0bd3fde9aba40fd5ef89c56fb732160 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 8 Aug 2024 13:19:17 +0530 Subject: [PATCH 031/121] Updated user and role workflow feature --- plugins/modules/user_role_workflow_manager.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 7e2ab35f7b..2641d3976e 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1069,14 +1069,16 @@ def valid_user_config_parameters(self, user_config): "last_name: '{0}' {1}".format(last_name, regex_name_validation_msg), error_messages) email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") - email_regex_msg = "email: Invalid email format for 'email': {0}".format(user_config.get("email")) - if user_config.get("email"): - self.validate_string_field(user_config.get("email"), email_regex, email_regex_msg, error_messages) + email = user_config.get("email") + email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) + if email: + self.validate_string_field(email, email_regex, email_regex_msg, error_messages) password_regex = re.compile(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$") - password_regex_msg = "password: 'Password' does not meet complexity requirements for password: {0}".format(user_config.get("password")) - if user_config.get("password"): - self.validate_string_field(user_config.get("password"), password_regex, password_regex_msg, error_messages) + password = user_config.get("password") + password_regex_msg = "password: 'Password' does not meet complexity requirements for password: {0}".format(password) + if password: + self.validate_string_field(password, password_regex, password_regex_msg, error_messages) username = user_config.get("username") self.validate_string_field(username, regex_name_validation, From e9b8509bf31db59d1a10917649ab3ba0c41eef91 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 8 Aug 2024 16:45:49 +0530 Subject: [PATCH 032/121] Updated user and role workflow feature --- plugins/modules/user_role_workflow_manager.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 2641d3976e..732cd1c11d 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -45,6 +45,7 @@ username: description: The 'username' associated with the user account. type: str + required: true first_name: description: The first name of the user. type: str @@ -64,6 +65,7 @@ and a minimum length of 15 characters. - Required for creating a new user account. type: str + required: true role_list: description: - A list of role names to be assigned to the user. If no role is specified, the default role will be "OBSERVER-ROLE". @@ -74,6 +76,7 @@ - OBSERVER-ROLE grants view-only access, no configuration or control functions. type: list elements: str + required: true role_details: description: Manages the configuration details for roles. type: list @@ -1397,6 +1400,14 @@ def create_user(self, user_params): - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ + required_keys = ['username', 'password'] + missing_keys = [] + + self.log("Check if each required key is present in the user_params dictionary...", "DEBUG") + for key in required_keys: + if key not in user_params: + missing_keys.append(key) + try: self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( @@ -1409,7 +1420,7 @@ def create_user(self, user_params): return response except Exception: - error_message = "Mandatory field not present: An error occurred while creating the user" + error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) return {"error": error_message} def create_role(self, role_params): From ff307b5fe4b2a712c601cd036c475c2aa289c404 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Thu, 8 Aug 2024 19:50:55 +0530 Subject: [PATCH 033/121] untag test case compleated --- .../dnac/fixtures/swim_workflow_manager.json | 157 ++---------------- .../dnac/test_swim_workflow_manager.py | 79 +++------ 2 files changed, 34 insertions(+), 202 deletions(-) diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json index 6045572cc4..dd8111752c 100644 --- a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -1,133 +1,4 @@ { - "playbook_import_image_url_tag_golden_load": [ - { - "import_image_details": { - "type": "remote", - "url_details": { - "payload": [ - { - "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - ] - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - }, - "image_distribution_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_serial_number": "FJC2327U0S2" - }, - "image_activation_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "schedule_validate": false, - "activate_lower_image_version": false, - "distribute_if_needed": true, - "device_serial_number": "FJC2327U0S2" - } - } - ], - - "playbook_import_image_from_local_tag_golden": [ - { - "import_image_details": { - "type": "local", - "local_image_details": { - "file_path": "/Users/Downloads/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - } - } - ], - - "playbook_tag_image_as_golden_and_load_on_device": [ - { - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ACCESS", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "site_name": "Global/USA/San Francisco/BGL_18", - "tagging": true - } - } - ], - - "playbook_Distribute_image_associate_site_role": [ - { - "image_distribution_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "site_name": "Global/USA/San Francisco/BGL_18", - "device_role": "ALL", - "device_family_name": "Switches and Hubs", - "device_series_name": "Cisco Catalyst 9300 Series Switches" - } - } - ], - - "playbook_Activate_image_associate_site_role": [ - { - "image_activation_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "site_name": "Global/USA/San Francisco/BGL_18", - "device_role": "ALL", - "device_family_name": "Switches and Hubs", - "device_series_name": "Cisco Catalyst 9300 Series Switches", - "schedule_validate": false, - "activate_lower_image_version": true, - "distribute_if_needed": true - } - } - ], - - "playbook_import_image_url_tag_golden": [ - { - "import_image_details": { - "type": "remote", - "url_details": { - "payload": [ - { - "source_url": "http://172.21.236.183/stda/cat9k_iosxe.17.12.01.SPA.bin", - "is_third_party": false - } - ] - } - }, - "tagging_details": { - "image_name": "cat9k_iosxe.17.12.01.SPA.bin", - "device_role": "ALL", - "device_image_family_name": "Cisco Catalyst 9300 Switch", - "tagging": true - } - } - ], - - "get_software_image_details": { "response": [], "version": "1.0"}, - "import_software_image_via_url": {"response": {"taskId": "01912ce2-73b4-7bbe-a6f3-428e4809602a", "url": "/api/v1/task/01912ce2-73b4-7bbe-a6f3-428e4809602a"}, "version": "1.0"}, - "get_software_image_details_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - "tag_as_golden_image": {"response": {"taskId": "01912ce2-f6f0-777c-8ec6-b404471af00d", "url": "/api/v1/task/01912ce2-f6f0-777c-8ec6-b404471af00d"}, "version": "1.0"}, - "get_device_family_identifiers1": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_software_image_details_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_software_image_details_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_2": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, - - "import_image_url_tag_golden_response":{ - "message": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - }, - "playbook_untag_image_as_golden_and_load_on_device": [ { "tagging_details": { @@ -137,21 +8,21 @@ "site_name" :"Global/LTTS", "tagging": false } - } - ], + }] + , + "get_software_image_details": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_site": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}], "version": "1.0"}, + "get_software_image_details_1": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, + "remove_golden_tag_for_image": {"response": {"taskId": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2", "url": "/api/v1/task/01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0"}, + "Task_details": {"response": {"startTime": 1723122250767, "data": "Golden-Tagging", "endTime": 1723122250876, "progress": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful.", "version": 1723122250876, "serviceType": "NCSW", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0"}, + "get_software_image_details_2": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_site_1": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_device_family_identifiers_1": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_software_image_details_3": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - "get_software_image_details_untag_golden": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_site_untag_golden": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, - "get_device_family_identifiers_untag_golden": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_software_image_details_untag_golden_1": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_untag_golden": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, - "remove_golden_tag_for_image_untag_golden": {"response": {"taskId": "019130ab-acc6-7c9b-9a8d-22f622440fc3", "url": "/api/v1/task/019130ab-acc6-7c9b-9a8d-22f622440fc3"}, "version": "1.0"}, - "get_software_image_details_untag_golden_2": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_site_untag_golden_1": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, - "get_device_family_identifiers_untag_golden_1": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}], "version": "1.0"}, - "get_software_image_details_untag_golden_3": {"response": [{"imageUuid": "bb3c6249-ec3d-4d48-a376-5accaf286221", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-07 12:49:06.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "bf13fa1d-8108-405e-b6fe-d75df23c62a8", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_untag_golden_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - "untag_image_as_golden_and_load_on_device_responce":{ "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py index 122e2488a7..f4e93df2e5 100644 --- a/tests/unit/modules/dnac/test_swim_workflow_manager.py +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -30,13 +30,8 @@ class TestswimWorkflowManager(TestDnacModule): test_data = loadPlaybookData("swim_workflow_manager") - playbook_import_image_url_tag_golden_load = test_data.get("playbook_import_image_url_tag_golden_load") - playbook_import_image_from_local_tag_golden = test_data.get("playbook_import_image_from_local_tag_golden") - playbook_tag_image_as_golden_and_load_on_device = test_data.get("playbook_tag_image_as_golden_and_load_on_device") + playbook_untag_image_as_golden_and_load_on_device = test_data.get("playbook_untag_image_as_golden_and_load_on_device") - playbook_Distribute_image_associate_site_role = test_data.get("playbook_Distribute_image_associate_site_role") - playbook_Activate_image_associate_site_role = test_data.get("playbook_Activate_image_associate_site_role") - playbook_import_image_url_tag_golden = test_data.get("playbook_import_image_url_tag_golden") def setUp(self): @@ -60,63 +55,24 @@ def load_fixtures(self, response=None, device=""): """ Load fixtures for user. """ - # if "playbook_import_image_url_tag_golden" in self._testMethodName: - # self.run_dnac_exec.side_effect = [ - # self.test_data.get("get_software_image_details"), - # self.test_data.get("import_software_image_via_url"), - # self.test_data.get("get_software_image_details_1"), - # self.test_data.get("get_software_image_details_1"), - # self.test_data.get("get_device_family_identifiers"), - # self.test_data.get("get_software_image_details_1"), - # self.test_data.get("get_golden_tag_status_of_an_image"), - # self.test_data.get("tag_as_golden_image"), - # self.test_data.get("get_software_image_details_1"), - # self.test_data.get("get_device_family_identifiers1"), - # self.test_data.get("get_software_image_details_2"), - # self.test_data.get("get_software_image_details_3"), - # self.test_data.get("get_golden_tag_status_of_an_image_2"), - # self.test_data.get("import_image_url_tag_golden_response") - # ] if "playbook_untag_image_as_golden_and_load_on_device" in self._testMethodName: self.run_dnac_exec.side_effect = [ - self.test_data.get("get_software_image_details_untag_golden"), - self.test_data.get("get_site_untag_golden"), - self.test_data.get("get_device_family_identifiers_untag_golden"), - self.test_data.get("get_software_image_details_untag_golden_1"), - self.test_data.get("get_golden_tag_status_of_an_image_untag_golden"), - self.test_data.get("remove_golden_tag_for_image_untag_golden"), - self.test_data.get("get_software_image_details_untag_golden_2"), - self.test_data.get("get_site_untag_golden_1"), - self.test_data.get("get_device_family_identifiers_untag_golden_1"), - self.test_data.get("get_software_image_details_untag_golden_3"), - self.test_data.get("get_golden_tag_status_of_an_image_untag_golden_1"), + self.test_data.get("get_software_image_details"), + self.test_data.get("get_site"), + self.test_data.get("get_device_family_identifiers"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_golden_tag_status_of_an_image"), + self.test_data.get("remove_golden_tag_for_image"), + self.test_data.get("Task_details"), + self.test_data.get("get_software_image_details_2"), + self.test_data.get("get_site_1"), + self.test_data.get("get_device_family_identifiers_1"), + self.test_data.get("get_software_image_details_3"), + self.test_data.get("get_golden_tag_status_of_an_image_1"), self.test_data.get("untag_image_as_golden_and_load_on_device_responce") ] - def test_user_role_workflow_manager_playbook_import_image_url_tag_golden(self): - """ - Test case for user swim workflow manager when Import an image from a URL, tag it as golden. - - This test case checks the behavior of the swim workflow when Import an image from a URL, tag it as golden in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config=self.playbook_import_image_url_tag_golden - ) - ) - result = self.execute_module(changed=True, failed=False) - print(result) - self.assertEqual( - result.get('response'), - "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." - ) - - def test_user_role_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): + def test_swim_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): """ Test case for user role workflow manager when creating a user. @@ -132,9 +88,14 @@ def test_user_role_workflow_manager_playbook_untag_image_as_golden_and_load_on_d config=self.playbook_untag_image_as_golden_and_load_on_device ) ) + print(1) result = self.execute_module(changed=True, failed=False) + print(2) print(result) self.assertEqual( result.get('msg'), - "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site Global for family Cisco Catalyst 9300 Switch for device deviceTag ALL successful." + "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." ) + + + From 048a4bf7aa611a8525f667b35a1c3f2e3d616792 Mon Sep 17 00:00:00 2001 From: Abinash Date: Fri, 9 Aug 2024 04:19:48 +0000 Subject: [PATCH 034/121] Adding site as a paramter for device configs backup --- .../device_configs_backup_workflow_manager.py | 153 +++++++++++++++++- 1 file changed, 147 insertions(+), 6 deletions(-) diff --git a/plugins/modules/device_configs_backup_workflow_manager.py b/plugins/modules/device_configs_backup_workflow_manager.py index 9eb0a1d342..4684b8885d 100644 --- a/plugins/modules/device_configs_backup_workflow_manager.py +++ b/plugins/modules/device_configs_backup_workflow_manager.py @@ -44,6 +44,9 @@ management_ip_address: description: IP address of the device as displayed on the inventory GUI of Cisco Catalyst Center type: str + site: + description: Name of the site to which the device is assigned + type: str mac_address: description: Mac address of the device as displayed on the inventory GUI of Cisco Catalyst Center type: str @@ -110,6 +113,22 @@ series: Cisco Catalyst 9300 Series Switches collection_status: Managed file_path: /home/admin/madhan_ansible/collections/ansible_collections/cisco/dnac/playbooks/new_tmp + +- name: Take backup of a 9300 wired device associated with site + cisco.dnac.device_configs_backup_workflow_manager: + dnac_host: "{{dnac_host}}" + dnac_username: "{{dnac_username}}" + dnac_password: "{{dnac_password}}" + dnac_verify: "{{dnac_verify}}" + dnac_port: "{{dnac_port}}" + dnac_version: "{{dnac_version}}" + dnac_debug: "{{dnac_debug}}" + dnac_log: True + dnac_log_level: "{{dnac_log_level}}" + state: merged + config: + - site: Global/USA/New York/BLDNYC + """ RETURN = r""" @@ -214,6 +233,7 @@ def validate_input(self): device_configs_backup_spec = { 'hostname': {'type': 'str', 'required': False}, + 'site': {'type': 'str', 'required': False}, 'management_ip_address': {'type': 'str', 'required': False}, 'mac_address': {'type': 'str', 'required': False}, 'serial_number': {'type': 'str', 'required': False}, @@ -255,6 +275,44 @@ def validate_ipv4_address(self): self.log("Validated IP address collected for config collection is {0}".format(ip_address), "INFO") + def get_site_details(self, site_name_hierarchy): + """ + Fetches the existance status of the site + + Parameters: + - self: The instance of the class containing the 'config' attribute + to be validated. + - site_name_hierarchy: Name of the site collected from the input. + Returns: + - site_exits: A boolean value indicating the existance of the site. + Example: + Post creation of the validated input, this method checks whther the site + exists in the Cisco Catalyst Center or not + """ + + site_exists = False + try: + response = self.dnac_apply['exec']( + family="sites", + function='get_site', + params={"name": site_name_hierarchy}, + op_modifies=True + ) + except Exception: + self.log("Exception occurred as \ + site '{0}' was not found".format(self.want.get("site_name")), "CRITICAL") + self.module.fail_json(msg="Site not found", response=[]) + + if response: + self.log("Received site details\ + for '{0}': {1}".format(site_name_hierarchy, str(response)), "DEBUG") + site = response.get("response") + if len(site) == 1: + site_exists = True + self.log("Site Name: {0} exists in the Cisco Catalyst Center".format(site_name_hierarchy), "INFO") + + return site_exists + def get_have(self): """ Get the current device_configs_backup details @@ -275,7 +333,7 @@ def get_have(self): def get_device_ids_list(self): """ - Fethces the list of device ids from various paramters passed in the playbook + Fetches the list of device ids from various paramters passed in the playbook Args: self: The instance of the class containing the 'config' attribute to be validated. Returns: @@ -315,7 +373,7 @@ def get_device_ids_list(self): is not possible due to collection status not being in Managed state".format(ip_address) self.log(msg, "WARNING") - elif dev_info.get("family") != "Unified AP": + elif dev_info.get("family") == "Unified AP": msg = "Device backup of device with IP address {0} \ is not possible due to device being an Unified AP".format(ip_address) self.log(msg, "WARNING") @@ -339,6 +397,74 @@ def get_device_ids_list(self): self.log("Backup of {0} devices out of {1} devices is possible".format(valid_device_count, original_valid_device_count), "INFO") return device_ids + def get_site_devices(self): + """ + Fetches the list of device ids when site is passed along with other parameters + Args: + self: The instance of the class containing the 'config' attribute to be validated. + Returns: + device_in_site: The list of device ids based on the parameters passed by the user, + present in the passed site + Example: + Stored paramters like management ip address/ family can be used to fetch the device ids + list present at the given site + """ + + site = self.validated_config[0].get("site") + device_ids = self.get_device_ids_list() + dev_in_site = [] + for dev_id in device_ids: + dev_response = self.dnac_apply['exec']( + family="devices", + function='get_device_detail', + params={"search_by": dev_id, + "identifier": "uuid"}, + op_modifies=True + ) + self.log("Response collected from the API 'get_device_detail' {0}".format(dev_response), "DEBUG") + dev_response = dev_response.get("response") + if dev_response.get("location") == site: + dev_in_site.append(dev_id) + + if len(dev_in_site) == 0: + msg = "No devices found in the given site {0}".format(site) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + + return dev_in_site + + def handle_only_site(self): + """ + Fetches the list of device ids present at the site, when only site is passed as the parameter + Args: + self: The instance of the class containing the 'config' attribute to be validated. + Returns: + device_ids: The list of device ids present at the site passed by the user + Example: + Stored paramters like management ip address/ family can be used to fetch the device ids + list present at the given site + """ + site = self.validated_config[0].get("site") + response = self.dnac_apply['exec']( + family="devices", + function='get_device_list', + op_modifies=True + ) + self.log("Response collected from the API 'get_device_list' is {0}".format(str(response)), "DEBUG") + device_list = response.get("response") + self.log("Length of the device list fetched from the API 'get_device_list' is {0}".format(str(device_list)), "INFO") + if len(device_list): + msg = "No devices found in the inventory" + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + + device_ids = [] + for dev_info in device_list: + if dev_info.get("site") == site: + device_ids.append(dev_info) + self.log("Device IDs collected:{0}".format(device_ids), "INFO") + return device_ids + def password_generator(self): """ Creates a password that matches Cisco Catalyst Center's requirements @@ -400,10 +526,25 @@ def get_want(self): """ self.want = {} + device_params = self.validated_config[0] + site = device_params.get("site") + filtered_keys = {} + for key, value in device_params.items(): + if key not in ['file_path', 'file_password']: + filtered_keys[key] = value + + if device_params.get("site") and self.get_site_details(site): + if len(filtered_keys) > 1: + self.want["deviceId"] = self.get_site_devices() + else: + self.want["deviceId"] = self.handle_only_site() + else: + self.want["deviceId"] = self.get_device_ids_list() - self.want["deviceId"] = self.get_device_ids_list() - if self.validated_config[0].get("file_password"): - password = self.validated_config[0].get("file_password") + self.log("Device IDs passed is {0}".format(self.want["deviceId"]), "INFO") + + if device_params.get("file_password"): + password = device_params.get("file_password") if self.validate_password(password=password) is True: self.want["password"] = password @@ -418,7 +559,7 @@ def get_want(self): self.want["password"] = self.password_generator() self.msg = "Successfully collected all parameters from playbook " + \ - "for comparison" + "for exceution" self.status = "success" self.log(self.msg, "INFO") return self From c71972c844528313508c7b811a439fbefb227c9f Mon Sep 17 00:00:00 2001 From: MUTHU-RAKESH-27 <19cs127@psgitech.ac.in> Date: Fri, 9 Aug 2024 11:48:24 +0530 Subject: [PATCH 035/121] Optimized the code for the reserving the subpool --- .../network_settings_workflow_manager.py | 101 +++++++++++------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index fa842025bc..85227fe6bb 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -638,6 +638,7 @@ import copy import re +import time from ansible.module_utils.basic import AnsibleModule from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( DnacBase, @@ -660,6 +661,7 @@ def __init__(self, module): self.global_pool_obj_params = self.get_obj_params("GlobalPool") self.reserve_pool_obj_params = self.get_obj_params("ReservePool") self.network_obj_params = self.get_obj_params("Network") + self.all_reserved_pool_details = [] def validate_input(self): """ @@ -1193,6 +1195,55 @@ def get_network_params(self, site_id): self.log("Formatted playbook network details: {0}".format(network_details), "DEBUG") return network_details + def get_reserved_ip_subpool(self, site_id): + """ + Retrieve all the reserved IP subpool details from the Cisco Catalyst Center. + + Parameters: + self (object) - The current object details. + + Returns: + self (object) - The current object with updated desired Fabric Transits information. + """ + + value = 1 + start_time = time.time() + while True: + end_time = time.time() + if (end_time - start_time) >= self.max_timeout: + self.msg = ( + "Max timeout of {0} sec has reached for the API 'get_reserved_ip_subpool' status." + .format(self.max_timeout) + ) + self.status = "failed" + break + + response = self.dnac._exec( + family="network_settings", + function="get_reserve_ip_subpool", + op_modifies=True, + params={ + "site_id": site_id, + "offset": value + } + ) + if not isinstance(response, dict): + self.msg = "Error in getting reserve pool - Response is not a dictionary" + self.log(self.msg, "CRITICAL") + self.status = "exited" + return self.check_return_status() + + reserve_pool_details = response.get("response") + if not reserve_pool_details: + self.log("No subpools are reserved in the site with ID - '{0}'." + .format(site_id), "DEBUG") + return self + + self.all_reserved_pool_details.extend(reserve_pool_details) + value += 25 + + return self + def global_pool_exists(self, name): """ Check if the Global Pool with the given name exists @@ -1273,43 +1324,19 @@ def reserve_pool_exists(self, name, site_name): self.status = "failed" return reserve_pool - value = 1 - while True: - self.log(str(value)) - response = self.dnac._exec( - family="network_settings", - function="get_reserve_ip_subpool", - op_modifies=True, - params={ - "site_id": site_id, - "offset": value - } - ) - if not isinstance(response, dict): - reserve_pool.update({"success": False}) - self.msg = "Error in getting reserve pool - Response is not a dictionary" - self.log(self.msg, "CRITICAL") - self.status = "exited" - return self.check_return_status() - - all_reserve_pool_details = response.get("response") - self.log(str(all_reserve_pool_details)) - if not all_reserve_pool_details: - self.log("Reserved pool {0} does not exist in the site {1}" - .format(name, site_name), "DEBUG") - return reserve_pool - - reserve_pool_details = get_dict_result(all_reserve_pool_details, "groupName", name) - self.log(str(reserve_pool_details)) - if reserve_pool_details: - self.log("Reserve pool found with name '{0}' in the site '{1}': {2}" - .format(name, site_name, reserve_pool_details), "INFO") - reserve_pool.update({"exists": True}) - reserve_pool.update({"id": reserve_pool_details.get("id")}) - reserve_pool.update({"details": self.get_reserve_pool_params(reserve_pool_details)}) - break + self.get_reserved_ip_subpool(site_id) + if not self.all_reserved_pool_details: + self.log("Reserved pool {0} does not exist in the site {1}" + .format(name, site_name), "DEBUG") + return reserve_pool - value += 25 + reserve_pool_details = get_dict_result(self.all_reserved_pool_details, "groupName", name) + if reserve_pool_details: + self.log("Reserve pool found with name '{0}' in the site '{1}': {2}" + .format(name, site_name, reserve_pool_details), "INFO") + reserve_pool.update({"exists": True}) + reserve_pool.update({"id": reserve_pool_details.get("id")}) + reserve_pool.update({"details": self.get_reserve_pool_params(reserve_pool_details)}) self.log("Reserved pool details: {0}".format(reserve_pool.get("details")), "DEBUG") self.log("Reserved pool id: {0}".format(reserve_pool.get("id")), "DEBUG") @@ -2535,6 +2562,7 @@ def verify_diff_merged(self, config): self """ + self.all_reserved_pool_details = [] self.get_have(config) self.log("Current State (have): {0}".format(self.have), "INFO") self.log("Requested State (want): {0}".format(self.want), "INFO") @@ -2617,6 +2645,7 @@ def verify_diff_deleted(self, config): self """ + self.all_reserved_pool_details = [] self.get_have(config) self.log("Current State (have): {0}".format(self.have), "INFO") self.log("Desired State (want): {0}".format(self.want), "INFO") From 1afea704f595ab84427f99d158cc32891a52c22d Mon Sep 17 00:00:00 2001 From: Abinash Date: Fri, 9 Aug 2024 09:02:30 +0000 Subject: [PATCH 036/121] Adding site as a paramter for device configs backup --- .../device_configs_backup_workflow_manager.py | 61 +++++++++---------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/plugins/modules/device_configs_backup_workflow_manager.py b/plugins/modules/device_configs_backup_workflow_manager.py index 4684b8885d..05088d371e 100644 --- a/plugins/modules/device_configs_backup_workflow_manager.py +++ b/plugins/modules/device_configs_backup_workflow_manager.py @@ -366,26 +366,26 @@ def get_device_ids_list(self): self.module.fail_json(msg=msg) valid_devices = [] - for dev_info in device_list: - ip_address = dev_info.get("managementIpAddress") - if dev_info.get("collectionStatus") != "Managed": + for device in device_list: + ip_address = device.get("managementIpAddress") + if device.get("collectionStatus") != "Managed": msg = "Device backup of device with IP address {0} \ is not possible due to collection status not being in Managed state".format(ip_address) self.log(msg, "WARNING") - elif dev_info.get("family") == "Unified AP": + elif device.get("family") == "Unified AP": msg = "Device backup of device with IP address {0} \ is not possible due to device being an Unified AP".format(ip_address) self.log(msg, "WARNING") - elif dev_info.get("reachabilityStatus") != "Reachable": + elif device.get("reachabilityStatus") != "Reachable": msg = "Device backup of device with IP address {0} \ is not possible due to device being not reachable".format(ip_address) self.log(msg, "WARNING") else: - valid_devices.append(dev_info) + valid_devices.append(device) - if len(valid_devices) == 0: + if not valid_devices: msg = "No device IDs were collected because the devices are either Unified APs \ not in the Managed state, or not reachable." self.log(msg, "CRITICAL") @@ -397,52 +397,50 @@ def get_device_ids_list(self): self.log("Backup of {0} devices out of {1} devices is possible".format(valid_device_count, original_valid_device_count), "INFO") return device_ids - def get_site_devices(self): + def get_devices_by_site_and_params(self): """ - Fetches the list of device ids when site is passed along with other parameters + Retrieves a list of device IDs that match the given site and other parameters. Args: self: The instance of the class containing the 'config' attribute to be validated. Returns: - device_in_site: The list of device ids based on the parameters passed by the user, - present in the passed site + device_in_site: List of device IDs that match the criteria and are located at the specified site. Example: - Stored paramters like management ip address/ family can be used to fetch the device ids - list present at the given site + Uses stored parameters like management IP address/family to fetch device IDs at the given site. """ site = self.validated_config[0].get("site") device_ids = self.get_device_ids_list() - dev_in_site = [] + devices_in_site = [] + for dev_id in device_ids: - dev_response = self.dnac_apply['exec']( + device_details_response = self.dnac_apply['exec']( family="devices", function='get_device_detail', params={"search_by": dev_id, "identifier": "uuid"}, op_modifies=True ) - self.log("Response collected from the API 'get_device_detail' {0}".format(dev_response), "DEBUG") - dev_response = dev_response.get("response") - if dev_response.get("location") == site: - dev_in_site.append(dev_id) + self.log("Response collected from the API 'get_device_detail' {0}".format(device_details_response), "DEBUG") + device_details = device_details_response.get("response") + if device_details.get("location") == site: + devices_in_site.append(dev_id) - if len(dev_in_site) == 0: + if not devices_in_site: msg = "No devices found in the given site {0}".format(site) self.log(msg, "CRITICAL") self.module.fail_json(msg=msg) - return dev_in_site + return devices_in_site - def handle_only_site(self): + def get_device_ids_by_site(self): """ - Fetches the list of device ids present at the site, when only site is passed as the parameter + Retrieves the list of device IDs at the specified site., when only site is passed as the parameter Args: self: The instance of the class containing the 'config' attribute to be validated. Returns: device_ids: The list of device ids present at the site passed by the user Example: - Stored paramters like management ip address/ family can be used to fetch the device ids - list present at the given site + Uses site as parameter to fetch the device IDs at the given site. """ site = self.validated_config[0].get("site") response = self.dnac_apply['exec']( @@ -453,15 +451,16 @@ def handle_only_site(self): self.log("Response collected from the API 'get_device_list' is {0}".format(str(response)), "DEBUG") device_list = response.get("response") self.log("Length of the device list fetched from the API 'get_device_list' is {0}".format(str(device_list)), "INFO") - if len(device_list): + if len(device_list) == 0: msg = "No devices found in the inventory" self.log(msg, "CRITICAL") self.module.fail_json(msg=msg) device_ids = [] - for dev_info in device_list: - if dev_info.get("site") == site: - device_ids.append(dev_info) + for device in device_list: + if device.get("site") == site: + device_ids.append(device) + self.log("Device IDs collected:{0}".format(device_ids), "INFO") return device_ids @@ -535,9 +534,9 @@ def get_want(self): if device_params.get("site") and self.get_site_details(site): if len(filtered_keys) > 1: - self.want["deviceId"] = self.get_site_devices() + self.want["deviceId"] = self.get_devices_by_site_and_params() else: - self.want["deviceId"] = self.handle_only_site() + self.want["deviceId"] = self.get_device_ids_by_site() else: self.want["deviceId"] = self.get_device_ids_list() From 9f46b75cfe23fe6d5e7f37c956d468fa63072e1b Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Fri, 9 Aug 2024 07:03:55 -0700 Subject: [PATCH 037/121] demo commit --- .../ccc_site_management/vars/vars_site_management.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/ccc_site_management/vars/vars_site_management.yml b/tests/integration/ccc_site_management/vars/vars_site_management.yml index 82df1088dc..7ef0ecefe0 100644 --- a/tests/integration/ccc_site_management/vars/vars_site_management.yml +++ b/tests/integration/ccc_site_management/vars/vars_site_management.yml @@ -58,6 +58,7 @@ design_sites: longitude: -78.88105620286412 country: United States site_type: building + - site: building: name: BLD12 From 12a14e3a914065c82724ff093d0992476604accd Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Fri, 9 Aug 2024 07:13:09 -0700 Subject: [PATCH 038/121] demo commit --- .../ccc_site_management/vars/vars_site_management.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integration/ccc_site_management/vars/vars_site_management.yml b/tests/integration/ccc_site_management/vars/vars_site_management.yml index 7ef0ecefe0..ad6409b7e0 100644 --- a/tests/integration/ccc_site_management/vars/vars_site_management.yml +++ b/tests/integration/ccc_site_management/vars/vars_site_management.yml @@ -59,6 +59,7 @@ design_sites: country: United States site_type: building + - site: building: name: BLD12 @@ -68,6 +69,7 @@ design_sites: longitude: -78.88217248318003 country: United States site_type: building + - site: building: name: BLD23 From c0fab0a3a83b66ea9f1bb2533f4053427451fe25 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Fri, 9 Aug 2024 08:06:32 -0700 Subject: [PATCH 039/121] demo commit --- plugins/modules/site_workflow_manager.py | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 4b442ef4a0..675e33f139 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -363,6 +363,7 @@ def validate_input(self): will contain the validated configuration. If it fails, 'self.status' will be 'failed', and 'self.msg' will describe the validation issues. """ + self.log("Validating Input Parameters", "DEBUG") if not self.config: self.status = "success" From 244e5ecaa90deea79d92a6df4c361928a51cf59a Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Fri, 9 Aug 2024 08:14:05 -0700 Subject: [PATCH 040/121] test commit --- plugins/modules/site_workflow_manager.py | 1 - .../ccc_site_management/vars/vars_site_management.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 675e33f139..4b442ef4a0 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -363,7 +363,6 @@ def validate_input(self): will contain the validated configuration. If it fails, 'self.status' will be 'failed', and 'self.msg' will describe the validation issues. """ - self.log("Validating Input Parameters", "DEBUG") if not self.config: self.status = "success" diff --git a/tests/integration/ccc_site_management/vars/vars_site_management.yml b/tests/integration/ccc_site_management/vars/vars_site_management.yml index ad6409b7e0..96eb82e445 100644 --- a/tests/integration/ccc_site_management/vars/vars_site_management.yml +++ b/tests/integration/ccc_site_management/vars/vars_site_management.yml @@ -38,7 +38,6 @@ design_sites: site_type: area - - site: building: name: BLD10 @@ -59,7 +58,6 @@ design_sites: country: United States site_type: building - - site: building: name: BLD12 @@ -79,6 +77,7 @@ design_sites: longitude: -121.912974 country: United States site_type: building + - site: building: name: BLD20 From 356ce1272a06c3cffe6e149a00481b968a34156f Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Tue, 13 Aug 2024 11:31:45 +0530 Subject: [PATCH 041/121] Fix the issue of resyncing more than 400 devices in inventory. --- plugins/modules/inventory_intent.py | 109 ++++++++++++----- plugins/modules/inventory_workflow_manager.py | 110 +++++++++++++----- 2 files changed, 165 insertions(+), 54 deletions(-) diff --git a/plugins/modules/inventory_intent.py b/plugins/modules/inventory_intent.py index 4f312cb7d1..eaf11b7e4e 100644 --- a/plugins/modules/inventory_intent.py +++ b/plugins/modules/inventory_intent.py @@ -185,6 +185,17 @@ description: Make this as true needed for the resyncing of device. type: bool default: False + resync_device_count: + description: Specifies the number of devices to be resynced in the inventory. Ensure that this count does not exceed 200, + as attempting to resync more than 200 devices may cause the 'sync_devices_using_forcesync' API to enter an + infinite loop. + type: int + default: 200 + resync_max_timeout: + description: Sets the maximum timeout, in seconds, for the resync process of devices in the inventory to prevent an infinite + loop. The default value is set to 600 seconds. + type: int + default: 600 reboot_device: description: Make this as true needed for the Rebooting of Access Points. type: bool @@ -782,7 +793,9 @@ def validate_input(self): 'device_resync': {'type': 'bool'}, 'reboot_device': {'type': 'bool'}, 'credential_update': {'type': 'bool'}, - 'export_device_details_limit': {'default': 500, 'type': 'bool'}, + 'export_device_details_limit': {'default': 500, 'type': 'int'}, + 'resync_device_count': {'default': 200, 'type': 'int'}, + 'resync_max_timeout': {'default': 600, 'type': 'int'}, 'force_sync': {'type': 'bool'}, 'clean_config': {'type': 'bool'}, 'add_user_defined_field': { @@ -1335,43 +1348,85 @@ def resync_devices(self): device_ids = self.get_device_ids(input_device_ips) try: + # Resync the device in a batch of 200 devices at a time in inventory by default + start = 0 + resync_failed_for_all_device = False + resync_device_count = self.config[0].get("resync_device_count", 200) + device_resync_list, resync_failed_devices = [], [] force_sync = self.config[0].get("force_sync", False) - resync_param_dict = { - 'payload': device_ids, - 'force_sync': force_sync - } - response = self.dnac._exec( - family="devices", - function='sync_devices_using_forcesync', - op_modifies=True, - params=resync_param_dict, - ) - self.log("Received API response from 'sync_devices_using_forcesync': {0}".format(str(response)), "DEBUG") + resync_task_dict = {} + + while start < len(device_ids): + device_ids_list = device_ids[start:start + resync_device_count] + device_ips_list = input_device_ips[start:start + resync_device_count] + resync_param_dict = { + 'payload': device_ids_list, + 'force_sync': force_sync + } + self.log("Request payload for reysnc Device having the device ids: {0}".format(device_ids_list), "INFO") + response = self.dnac._exec( + family="devices", + function='sync_devices_using_forcesync', + op_modifies=True, + params=resync_param_dict, + ) + self.log("Received API response from 'sync_devices_using_forcesync': {0}".format(str(response)), "DEBUG") + + if not response or not isinstance(response, dict): + self.status = "failed" + self.msg = "Unable to resync the device(s) {0} in the inventory as response is empty.".format(device_ips_list) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + return self - if response and isinstance(response, dict): task_id = response.get('response').get('taskId') + resync_task_dict[task_id] = device_ips_list + start += resync_device_count + + for task_id, device_list in resync_task_dict.items(): + max_timeout = self.config[0].get("resync_max_timeout", 600) + start_time = time.time() + + while (True): + end_time = time.time() + + if (end_time - start_time) >= max_timeout: + self.log("""Max timeout of {0} has reached for the task id '{1}' for the device(s) '{2}' to be resynced and unexpected + task status so moving out to next task id""".format(max_timeout, task_id, device_list), "WARNING") + resync_failed_devices.extend(device_list) + break - while True: execution_details = self.get_task_details(task_id) if 'Synced' in execution_details.get("progress"): - self.status = "success" - self.result['changed'] = True - self.result['response'] = execution_details - self.msg = "Devices have been successfully resynced. Devices resynced: {0}".format(str(input_device_ips)) - self.log(self.msg, "INFO") + device_resync_list.extend(device_list) break elif execution_details.get("isError"): - self.status = "failed" - failure_reason = execution_details.get("failureReason") - if failure_reason: - self.msg = "Device resynced get failed because of {0}".format(failure_reason) - else: - self.msg = "Device resynced get failed." - self.log(self.msg, "ERROR") - self.result['response'] = self.msg + resync_failed_devices.extend(device_list) break + time.sleep(self.params.get('dnac_task_poll_interval')) + + if resync_failed_devices and device_resync_list: + self.msg = ( + "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " + "And there were some device(s) '{1}' for which resync task not executed successfully." + ).format(device_resync_list, resync_failed_devices) + elif resync_failed_devices: + resync_failed_for_all_device = True + self.msg = "Device resynced get failed for all given device(s) '{0}'.".format(resync_failed_devices) + else: + self.msg = ( + "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " + ).format(device_resync_list) + if resync_failed_for_all_device: + self.status = "failed" + self.log(self.msg, "ERROR") + else: + self.status = "success" + self.log(self.msg, "INFO") + self.result['changed'] = True + self.result["response"] = self.msg except Exception as e: self.status = "failed" error_message = "Error while resyncing device in Cisco Catalyst Center: {0}".format(str(e)) diff --git a/plugins/modules/inventory_workflow_manager.py b/plugins/modules/inventory_workflow_manager.py index ac719c6205..961dc03573 100644 --- a/plugins/modules/inventory_workflow_manager.py +++ b/plugins/modules/inventory_workflow_manager.py @@ -185,6 +185,17 @@ description: Make this as true needed for the resyncing of device. type: bool default: False + resync_device_count: + description: Specifies the number of devices to be resynced in the inventory. Ensure that this count does not exceed 200, + as attempting to resync more than 200 devices may cause the 'sync_devices_using_forcesync' API to enter an + infinite loop. + type: int + default: 200 + resync_max_timeout: + description: Sets the maximum timeout, in seconds, for the resync process of devices in the inventory to prevent an infinite + loop. The default value is set to 600 seconds. + type: int + default: 600 reboot_device: description: Make this as true needed for the Rebooting of Access Points. type: bool @@ -781,7 +792,9 @@ def validate_input(self): 'device_resync': {'type': 'bool'}, 'reboot_device': {'type': 'bool'}, 'credential_update': {'type': 'bool'}, - 'export_device_details_limit': {'default': 500, 'type': 'bool'}, + 'export_device_details_limit': {'default': 500, 'type': 'int'}, + 'resync_device_count': {'default': 200, 'type': 'int'}, + 'resync_max_timeout': {'default': 600, 'type': 'int'}, 'force_sync': {'type': 'bool'}, 'clean_config': {'type': 'bool'}, 'add_user_defined_field': { @@ -1335,42 +1348,85 @@ def resync_devices(self): device_ids = self.get_device_ids(input_device_ips) try: + # Resync the device in a batch of 200 devices at a time in inventory by default + start = 0 + resync_failed_for_all_device = False + resync_device_count = self.config[0].get("resync_device_count", 200) + device_resync_list, resync_failed_devices = [], [] force_sync = self.config[0].get("force_sync", False) - resync_param_dict = { - 'payload': device_ids, - 'force_sync': force_sync - } - response = self.dnac._exec( - family="devices", - function='sync_devices_using_forcesync', - op_modifies=True, - params=resync_param_dict, - ) - self.log("Received API response from 'sync_devices_using_forcesync': {0}".format(str(response)), "DEBUG") + resync_task_dict = {} + + while start < len(device_ids): + device_ids_list = device_ids[start:start + resync_device_count] + device_ips_list = input_device_ips[start:start + resync_device_count] + resync_param_dict = { + 'payload': device_ids_list, + 'force_sync': force_sync + } + self.log("Request payload for reysnc Device having the device ids: {0}".format(device_ids_list), "INFO") + response = self.dnac._exec( + family="devices", + function='sync_devices_using_forcesync', + op_modifies=True, + params=resync_param_dict, + ) + self.log("Received API response from 'sync_devices_using_forcesync': {0}".format(str(response)), "DEBUG") + + if not response or not isinstance(response, dict): + self.status = "failed" + self.msg = "Unable to resync the device(s) {0} in the inventory as response is empty.".format(device_ips_list) + self.log(self.msg, "ERROR") + self.result['response'] = self.msg + return self - if response and isinstance(response, dict): task_id = response.get('response').get('taskId') + resync_task_dict[task_id] = device_ips_list + start += resync_device_count + + for task_id, device_list in resync_task_dict.items(): + max_timeout = self.config[0].get("resync_max_timeout", 600) + start_time = time.time() + + while (True): + end_time = time.time() + + if (end_time - start_time) >= max_timeout: + self.log("""Max timeout of {0} has reached for the task id '{1}' for the device(s) '{2}' to be resynced and unexpected + task status so moving out to next task id""".format(max_timeout, task_id, device_list), "WARNING") + resync_failed_devices.extend(device_list) + break - while True: execution_details = self.get_task_details(task_id) if 'Synced' in execution_details.get("progress"): - self.status = "success" - self.result['changed'] = True - self.result['response'] = execution_details - self.msg = "Devices have been successfully resynced. Devices resynced: {0}".format(str(input_device_ips)) - self.log(self.msg, "INFO") + device_resync_list.extend(device_list) break elif execution_details.get("isError"): - self.status = "failed" - failure_reason = execution_details.get("failureReason") - if failure_reason: - self.msg = "Device resynced get failed because of {0}".format(failure_reason) - else: - self.msg = "Device resynced get failed." - self.log(self.msg, "ERROR") - self.result['response'] = self.msg + resync_failed_devices.extend(device_list) break + time.sleep(self.params.get('dnac_task_poll_interval')) + + if resync_failed_devices and device_resync_list: + self.msg = ( + "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " + "And there were some device(s) '{1}' for which resync task not executed successfully." + ).format(device_resync_list, resync_failed_devices) + elif resync_failed_devices: + resync_failed_for_all_device = True + self.msg = "Device resynced get failed for all given device(s) '{0}'.".format(resync_failed_devices) + else: + self.msg = ( + "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " + ).format(device_resync_list) + + if resync_failed_for_all_device: + self.status = "failed" + self.log(self.msg, "ERROR") + else: + self.status = "success" + self.log(self.msg, "INFO") + self.result['changed'] = True + self.result["response"] = self.msg except Exception as e: self.status = "failed" From 9fd78e6c230ca614ce7ee4acef8c53383a844a3c Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Tue, 13 Aug 2024 12:48:39 +0530 Subject: [PATCH 042/121] Fix the issue of swim tagging/untagging for Global site --- plugins/modules/swim_intent.py | 2 +- plugins/modules/swim_workflow_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/swim_intent.py b/plugins/modules/swim_intent.py index 549efa9823..6243afb254 100644 --- a/plugins/modules/swim_intent.py +++ b/plugins/modules/swim_intent.py @@ -928,7 +928,7 @@ def get_have(self): # check if given site exists, store siteid # if not then use global site site_name = tagging_details.get("site_name") - if site_name: + if site_name and site_name != "Global": site_exists = False (site_exists, site_id) = self.site_exists(site_name) if site_exists: diff --git a/plugins/modules/swim_workflow_manager.py b/plugins/modules/swim_workflow_manager.py index df1e816aa3..5f6c53cb78 100644 --- a/plugins/modules/swim_workflow_manager.py +++ b/plugins/modules/swim_workflow_manager.py @@ -914,7 +914,7 @@ def get_have(self): # check if given site exists, store siteid # if not then use global site site_name = tagging_details.get("site_name") - if site_name: + if site_name and site_name != "Global": site_exists = False (site_exists, site_id) = self.site_exists(site_name) if site_exists: From 7566e528ea7ca38e75d544ebabb0855fc5297254 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 13 Aug 2024 13:04:56 +0530 Subject: [PATCH 043/121] swim UT 70% compleated --- .../dnac/fixtures/swim_workflow_manager.json | 839 +++++++++++++++++- .../dnac/test_swim_workflow_manager.py | 584 +++++++++++- 2 files changed, 1379 insertions(+), 44 deletions(-) diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json index dd8111752c..424981ad6f 100644 --- a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -1,31 +1,814 @@ { - "playbook_untag_image_as_golden_and_load_on_device": [ - { + "playbook_config_invalid_param_import_image_url_tag_golden_load": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + }, "tagging_details": { - "image_name": "cat9k_iosxe.17.12.02.SPA.bin", - "device_role": "ALL", - "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", - "site_name" :"Global/LTTS", - "tagging": false - } - }] - , - "get_software_image_details": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_site": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, - "get_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}], "version": "1.0"}, - "get_software_image_details_1": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, - "remove_golden_tag_for_image": {"response": {"taskId": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2", "url": "/api/v1/task/01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0"}, - "Task_details": {"response": {"startTime": 1723122250767, "data": "Golden-Tagging", "endTime": 1723122250876, "progress": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful.", "version": 1723122250876, "serviceType": "NCSW", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0"}, - "get_software_image_details_2": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_site_1": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, - "get_device_family_identifiers_1": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, - "get_software_image_details_3": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, - "get_golden_tag_status_of_an_image_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, - - - "untag_image_as_golden_and_load_on_device_responce":{ - "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." -} + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name": "Global/LTTS/FLOOR1", + "tagging": true + }, + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_serial_number": "FOC2225U12L" + }, + "image_activation_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "schedule_validate": false, + "activate_lower_image_version": false, + "distribute_if_needed": true, + "device_serial_number": "FOC2225U12L" + } + } + ], + "get_software_image_details": { + "response": [], + "version": "1.0" + }, + "import_software_image_via_url": { + "response": { + "taskId": "019131f4-d6e5-7fb4-87cf-f2b201107fbe", + "url": "/api/v1/task/019131f4-d6e5-7fb4-87cf-f2b201107fbe" + }, + "version": "1.0" + }, + "task_details": { + "response": { + "startTime": 1723120015077, + "data": "import", + "endTime": 1723120047094, + "progress": "completed successfully. Success = 1, Failure = 0, Running = 0, Pending = 0, Total = 1", + "version": 1723120047094, + "lastUpdate": 1723120015086, + "serviceType": "Swim Service", + "isError": false, + "instanceTenantId": "6663114d388b29001399e46a", + "id": "019131f4-d6e5-7fb4-87cf-f2b201107fbe" + }, + "version": "1.0" + }, + "get_software_image_1_details": { + "response": [ + { + "imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", + "name": "cat9k_iosxe.17.12.02.SPA.bin", + "family": "CAT9K", + "version": "17.12.02.0.2739", + "displayVersion": "17.12.02", + "md5Checksum": "2405eeb2627eeee594078b6019a2d936", + "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", + "createdTime": "2024-08-08 12:27:18.0", + "imageType": "SYSTEM_SW", + "fileSize": "1316755714 bytes", + "imageName": "cat9k_iosxe.17.12.02.SPA.bin", + "applicationType": "", + "feature": "", + "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", + "isTaggedGolden": false, + "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "extendedAttributes": { + "Description": "Cisco IOS Software, IOS-XE Software", + "BOOTROM": "UNKNOWN", + "MINFLASHSIZE": "UNKNOWN", + "MEDIA": "5", + "FULL_VERSION": "17.12.02.0.2739", + "DEFAULT_BOOTROM": "UNKNOWN", + "DEFAULT_RAM": "UNKNOWN", + "GAIA_FEATURE": "UNKNOWN", + "COMPRESSION_CODE": "-1", + "DEFAULT_MINFLASHSIZE": "UNKNOWN", + "RAM": "UNKNOWN" + }, + "vendor": "CISCO", + "imageIntegrityStatus": "UNKNOWN", + "importSourceType": "REMOTEURL" + } + ], + "version": "1.0" + }, + "get_site": { + "response": [ + { + "parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", + "name": "FLOOR1", + "additionalInfo": [ + { + "nameSpace": "Location", + "attributes": { + "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", + "type": "floor" + } + }, + { + "nameSpace": "mapsSummary", + "attributes": { + "rfModel": "162162", + "floorIndex": "1" + } + }, + { + "nameSpace": "mapGeometry", + "attributes": { + "offsetX": "0.0", + "offsetY": "0.0", + "length": "100.0", + "width": "100.0", + "height": "10.0" + } + } + ], + "instanceTenantId": "6663114d388b29001399e46a", + "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", + "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", + "siteNameHierarchy": "Global/LTTS/FLOOR1" + } + ] + }, + "get_device_family_identifiers": { + "response": [ + { + "deviceFamily": "Cisco Catalyst 8000V Edge Software", + "deviceFamilyIdentifier": "286327102" + }, + { + "deviceFamily": "Cisco Catalyst 9300 Switch", + "deviceFamilyIdentifier": "286315874" + }, + { + "deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "deviceFamilyIdentifier": "999999901" + } + ], + "version": "1.0" + }, + "get_device_list": { + "response": [ + { + "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", + "type": "Cisco Catalyst 9300 Switch", + "lastUpdateTime": 1723089164674, + "serialNumber": "FOC2225U12L", + "deviceSupportLevel": "Supported", + "softwareType": "IOS-XE", + "softwareVersion": "17.16.20240710:041147", + "inventoryStatusDetail": "", + "collectionInterval": "Global Default", + "dnsResolvedManagementAddress": "204.1.2.2", + "lastManagedResyncReasons": "Periodic", + "managementState": "Managed", + "pendingSyncRequestsCount": "0", + "reasonsForDeviceResync": "Periodic", + "reasonsForPendingSyncRequests": "", + "syncRequestedByApp": "", + "upTime": "18 days, 6:08:51.84", + "roleSource": "AUTO", + "interfaceCount": "0", + "lastUpdated": "2024-08-08 03:52:44", + "bootDateTime": "2024-07-20 21:44:44", + "apManagerInterfaceIp": "", + "collectionStatus": "Managed", + "family": "Switches and Hubs", + "hostname": "sjc_border.cisco.com", + "locationName": null, + "managementIpAddress": "204.1.2.2", + "platformId": "C9300-24UX", + "reachabilityFailureReason": "", + "reachabilityStatus": "Reachable", + "series": "Cisco Catalyst 9300 Series Switches", + "snmpContact": "", + "snmpLocation": "", + "associatedWlcIp": "", + "apEthernetMacAddress": null, + "errorCode": null, + "errorDescription": null, + "lastDeviceResyncStartTime": "2024-08-08 03:52:09", + "lineCardCount": "0", + "lineCardId": "", + "managedAtleastOnce": true, + "memorySize": "NA", + "tagCount": "0", + "tunnelUdpPort": null, + "uptimeSeconds": 1608166, + "vendor": "Cisco", + "waasDeviceMode": null, + "location": null, + "role": "DISTRIBUTION", + "macAddress": "dc:f7:19:33:41:00", + "instanceTenantId": "6663114d388b29001399e46a", + "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", + "id": "4ea61bbc-5965-436f-b880-178331d4bdc6" + } + ], + "version": "1.0" + }, + "get_golden_tag_status_of_an_image": { + "response": { + "deviceRole": "ALL", + "inheritedSiteName": null, + "inheritedSiteId": null, + "taggedGolden": false + }, + "version": "1.0" + }, + "tag_as_golden_image": { + "response": { + "taskId": "019131f5-6880-77cc-9b9e-0a03dade3149", + "url": "/api/v1/task/019131f5-6880-77cc-9b9e-0a03dade3149" + }, + "version": "1.0" + }, + "task_1_details": { + "response": { + "startTime": 1723120052352, + "data": "Golden-Tagging", + "endTime": 1723120053660, + "progress": "Tagging image cat9k_iosxe.17.12.02.SPA.bin golden for site FLOOR1 for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful.", + "version": 1723120053660, + "serviceType": "NCSW", + "isError": false, + "instanceTenantId": "6663114d388b29001399e46a", + "id": "019131f5-6880-77cc-9b9e-0a03dade3149" + }, + "version": "1.0" + }, + "get_1_site": { + "response": [ + { + "name": "Global", + "additionalInfo": [], + "instanceTenantId": "6663114d388b29001399e46a", + "id": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", + "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", + "siteNameHierarchy": "Global" + } + ] + }, + "get_membership": { + "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500544, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885427, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608170, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500453, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500458, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}] + }, + "trigger_software_image_distribution": { + "response": { + "taskId": "019131f5-7abc-724f-b254-dc010c4faa45", + "url": "/api/v1/task/019131f5-7abc-724f-b254-dc010c4faa45" + }, + "version": "1.0" + }, + "task_2_details": { + "response": { + "startTime": 1723120057020, + "data": "distribute", + "endTime": 1723120062831, + "progress": "completed successfully. Success = 1, Failure = 0, Running = 0, Pending = 0, Total = 1", + "version": 1723120062831, + "lastUpdate": 1723120057026, + "serviceType": "Swim Service", + "isError": false, + "instanceTenantId": "6663114d388b29001399e46a", + "id": "019131f5-7abc-724f-b254-dc010c4faa45" + }, + "version": "1.0" + }, + "get_1_membership": { + "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500553, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885436, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 12:27:42", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "1", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "Application Requested", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608179, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500462, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500467, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}] + }, + "trigger_software_image_activation": { + "response": { + "taskId": "019131f5-9bf1-7650-bf39-076e1e500f4a", + "url": "/api/v1/task/019131f5-9bf1-7650-bf39-076e1e500f4a" + }, + "version": "1.0" + }, + "task_3_details": { + "response": {"startTime": 1723120065521, "data": "activate", "progress": "image activation", "version": 1723120073490, "lastUpdate": 1723120073490, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019131f5-9bf1-7650-bf39-076e1e500f4a"}, "version": "1.0" + }, + "task_4_details": { + "response": { + "startTime": 1723120065521, + "data": "activate", + "endTime": 1723120074081, + "progress": "Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", + "version": 1723120074081, + "errorCode": "100", + "lastUpdate": 1723120073490, + "serviceType": "Swim Service", + "isError": true, + "instanceTenantId": "6663114d388b29001399e46a", + "id": "019131f5-9bf1-7650-bf39-076e1e500f4a" + }, + "version": "1.0" + }, + + "Task_details_inprogress_1": {"response": {"startTime": 1723120015077, "data": "import", "progress": "Image Import Flow Started ", "version": 1723120015086, "lastUpdate": 1723120015086, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019131f4-d6e5-7fb4-87cf-f2b201107fbe"}, "version": "1.0"}, + + "Task_details_inprogress_2": {"response": {"startTime": 1723120057020, "data": "distribute", "progress": "Starting Distribution", "version": 1723120057026, "lastUpdate": 1723120057026, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019131f5-7abc-724f-b254-dc010c4faa45"}, "version": "1.0"}, + + "Task_details_inprogress_3": {"response": {"startTime": 1723120057020, "data": "distribute", "progress": "Starting Distribution", "version": 1723120057026, "lastUpdate": 1723120057026, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019131f5-7abc-724f-b254-dc010c4faa45"}, "version": "1.0"}, + + "Task_details_inprogress_4": {"response": {"startTime": 1723120065521, "data": "activate", "progress": "image activation", "version": 1723120073490, "lastUpdate": 1723120073490, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019131f5-9bf1-7650-bf39-076e1e500f4a"}, "version": "1.0"}, + + + "playbook_config_invalid_param_import_image_url_tag_golden_load_response": { + "message": "Activation for Image with Id '4a3cccfa-dc92-4fad-a7d3-c59876cbebe6' gets failed" + }, + + + + + + + + + "playbook_untag_image_as_golden_and_load_on_device": [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name" :"Global/LTTS", + "tagging": false + } + }] + , + "get_2_software_image_details": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_2_site": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_2_device_family_identifiers": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}], "version": "1.0"}, + "get_software_image_details_1": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_2_golden_tag_status_of_an_image": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"}, + "remove_golden_tag_for_image": {"response": {"taskId": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2", "url": "/api/v1/task/01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0"}, + + "Task_details": + {"response": {"startTime": 1723122250767, "data": "Golden-Tagging", "endTime": 1723122250876, "progress": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful.", "version": 1723122250876, "serviceType": "NCSW", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0" +}, + + "Task_details_ERROR": + {"response": {"startTime": 1723122250767, "data": "Golden-Tagging", "endTime": 1723122250876,"progress": "Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723122250876, "serviceType": "NCSW", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "01913216-f40f-7c85-a0bb-dc4f3e9f30b2"}, "version": "1.0" + }, + + + "get_software_image_details_2": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_site_1": {"response": [{"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "additionalInfo": [{"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "siteNameHierarchy": "Global/LTTS"}]}, + "get_device_family_identifiers_1": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"}, + "get_software_image_details_3": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + "get_golden_tag_status_of_an_image_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"}, + + + "untag_image_as_golden_and_load_on_device_responce":{ + "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." +}, + +"untag_image_as_golden_and_load_on_device_error_responce":{ + "message": "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." +}, + + + + "playbook_import_image_already_exist": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + } + } +], + +"get_software_image_details_already_tagged": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + + +"get_software_image_details_already_tagged_1": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"get_software_image_details_already_tagged_2": {"response": [{"imageUuid": "4a3cccfa-dc92-4fad-a7d3-c59876cbebe6", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-08 12:27:18.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "e6ff4ee1-1e8a-459d-b488-b555e5e31e72", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + + + +"import_image_already_exist_response":{ + "message": "Image 'cat9k_iosxe.17.12.02.SPA.bin' already exists in the Cisco Catalyst Center" }, + + +"playbook_site_not_exist" :[ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name": "Global/LTTS/FLOOR2", + "tagging": true + } + } +], + +"get_software_image_details_site_not_exist": {"response": [{"imageUuid": "a2c2afd0-cc7f-485d-b8c2-6e6a4b6e5bf3", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 06:06:49.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "248513e5-cd25-470a-9075-5789dc3a6207", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"get_software_image_details_site_not_exist_1": {"response": [{"imageUuid": "a2c2afd0-cc7f-485d-b8c2-6e6a4b6e5bf3", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 06:06:49.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "248513e5-cd25-470a-9075-5789dc3a6207", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"get_software_image_details_site_not_exist_2": {"response": [{"imageUuid": "a2c2afd0-cc7f-485d-b8c2-6e6a4b6e5bf3", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 06:06:49.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "248513e5-cd25-470a-9075-5789dc3a6207", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"site_not_exist_response":{ + "message": "An exception occurred: Site 'Global/LTTS/FLOOR2' does not exist in the Cisco Catalyst Center" }, + +"playbook_swim_image_invalid" :[ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name": "Global/LTTS/FLOOR2", + "tagging": true + } + } +], + + + +"get_software_image_details_invalid_swim_image": {"response": [], "version": "1.0"}, + +"import_software_image_via_url_invalid_swim_image": {"response": {"taskId": "01914592-1fcb-7e36-805b-ae8cfe333c98", "url": "/api/v1/task/01914592-1fcb-7e36-805b-ae8cfe333c98"}, "version": "1.0"}, + +"Task_details_invalid_swim_image": {"response": {"startTime": 1723449089995, "data": "import", "progress": "Image Import Flow Started ", "version": 1723449090007, "lastUpdate": 1723449090007, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "01914592-1fcb-7e36-805b-ae8cfe333c98"}, "version": "1.0"}, + +"Task_details_invalid_swim_image_end": {"response": {"startTime": 1723449089995, "data": "import", "endTime": 1723449210206, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723449210206, "errorCode": "100", "lastUpdate": 1723449090007, "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "01914592-1fcb-7e36-805b-ae8cfe333c98"}, "version": "1.0"}, + +"invalid_swim_image_response":{ + "message": "SWIM Image http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin seems to be invalid" }, + + + + +"playbook_image_distribution_failed" : [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_ip_address": "204.1.2.4" + } + } +], + +"get_software_image_details_image_distribution_failed": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"get_device_list_image_distribution_failed": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723434780935, "serialNumber": "9X8X6SWXCLM", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.4", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 0:13:28.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:53:00", "bootDateTime": "2024-07-22 03:40:00", "apManagerInterfaceIp": "", "collectionStatus": "In Progress", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.4", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:12:15", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1834437, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:71:6e", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "id": "6cae0779-a144-4208-a0f8-4eaca1542d61"}], "version": "1.0"}, + +"get_site_image_distribution_failed": {"response": [{"name": "Global", "additionalInfo": [], "instanceTenantId": "6663114d388b29001399e46a", "id": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteNameHierarchy": "Global"}]}, + +"get_membership_image_distribution_failed": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1834527, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1219410, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:48", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434768480, "lastUpdated": "2024-08-12 03:52:48", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 6:08:49.66", "uptimeSeconds": 1942150, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:00", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:12:15", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434780935, "lastUpdated": "2024-08-12 03:53:00", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:28.00", "uptimeSeconds": 1834437, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1834443, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}]}, + +"trigger_software_image_distribution_image_distribution_failed": {"response": {"taskId": "019145dd-a5f2-77fc-86ef-10d3243421ae", "url": "/api/v1/task/019145dd-a5f2-77fc-86ef-10d3243421ae"}, "version": "1.0"}, + +"task_details_running_image_distribution_failed": {"response": {"startTime": 1723454039538, "data": "distribute", "progress": "Starting Distribution", "version": 1723454039542, "lastUpdate": 1723454039542, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145dd-a5f2-77fc-86ef-10d3243421ae"}, "version": "1.0"}, + +"task_details_image_distribution_failed": {"response": {"startTime": 1723454039538, "data": "distribute", "endTime": 1723454040373, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723454040373, "errorCode": "100", "lastUpdate": 1723454039542, "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145dd-a5f2-77fc-86ef-10d3243421ae"}, "version": "1.0"}, + +"image_distribution_failed_response":{ + "message": "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed" }, + + + + +"playbook_image_distribution_partially_successfull" : [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin" + } + } +], + + +"get_software_image_details_ps": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"}, + +"get_device_list_ps": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723454921231, "serialNumber": "9X8X6SWXCLM", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.4", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 5:48:45.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:28:41", "bootDateTime": "2024-07-22 03:40:41", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.4", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835763, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:71:6e", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "id": "6cae0779-a144-4208-a0f8-4eaca1542d61"}, {"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "type": "Cisco Catalyst 9300 Switch", "lastUpdateTime": 1723455253431, "serialNumber": "FOC2225U12L", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.2", "lastManagedResyncReasons": "Application Requested", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "SWIM", "upTime": "22 days, 11:50:43.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:34:13", "bootDateTime": "2024-07-20 21:44:13", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.2", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:34:04", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1943551, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "DISTRIBUTION", "macAddress": "dc:f7:19:33:41:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "id": "4ea61bbc-5965-436f-b880-178331d4bdc6"}, {"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723434775476, "serialNumber": "99IA669OFH1", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.3", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 0:13:23.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:55", "bootDateTime": "2024-07-22 03:39:55", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "locationName": null, "managementIpAddress": "204.1.2.3", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835809, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:ff:2a", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "id": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723434747880, "serialNumber": "93K2LNK8A5Y", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.9", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "13 days, 21:22:41.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:27", "bootDateTime": "2024-07-29 06:30:27", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "vRouter1.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.9", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1220777, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:f6:22:46:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "id": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723370551120, "serialNumber": "91R03VNQU8W", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.10", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "20 days, 6:24:39.73", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-11 10:02:31", "bootDateTime": "2024-07-22 03:38:31", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "Vrouter2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.10", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835894, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:7a:80:39:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "id": "93dad04d-d795-4902-a685-37205483ee5d"}], "version": "1.0"}, + +"get_site_ps": {"response": [{"name": "Global", "additionalInfo": [], "instanceTenantId": "6663114d388b29001399e46a", "id": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteNameHierarchy": "Global"}]}, + +"get_membership_ps": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1835895, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1220778, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:13", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:34:04", "lastManagedResyncReasons": "Application Requested", "lastUpdateTime": 1723455253431, "lastUpdated": "2024-08-12 09:34:13", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 11:50:43.81", "uptimeSeconds": 1943552, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:41", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723454921231, "lastUpdated": "2024-08-12 09:28:41", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 5:48:45.00", "uptimeSeconds": 1835765, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1835810, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}]}, + +"get_device_uuids" : "Device '204.1.2.10' from site 'Global' is ready for the SWIM distribution/activation process.", +"get_device_uuids_1" : "Device '204.1.2.4' matches to the specified filter requirements and is set for SWIM distribution/activation. ", + +"get_device_list_ps_1": {"response": [{"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723370551120, "serialNumber": "91R03VNQU8W", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.10", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "20 days, 6:24:39.73", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-11 10:02:31", "bootDateTime": "2024-07-22 03:38:31", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "Vrouter2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.10", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835896, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:7a:80:39:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "id": "93dad04d-d795-4902-a685-37205483ee5d"}], "version": "1.0"}, + +"trigger_software_image_distribution_ps": {"response": {"taskId": "019145f2-84fd-7682-aaa1-b276de51e558", "url": "/api/v1/task/019145f2-84fd-7682-aaa1-b276de51e558"}, "version": "1.0"} +, +"get_device_list_ps_2": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "type": "Cisco Catalyst 9300 Switch", "lastUpdateTime": 1723455253431, "serialNumber": "FOC2225U12L", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.2", "lastManagedResyncReasons": "Application Requested", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "SWIM", "upTime": "22 days, 11:50:43.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:34:13", "bootDateTime": "2024-07-20 21:44:13", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.2", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:34:04", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1943554, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "DISTRIBUTION", "macAddress": "dc:f7:19:33:41:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "id": "4ea61bbc-5965-436f-b880-178331d4bdc6"}], "version": "1.0"} +, +"trigger_software_image_distribution_ps_1": {"response": {"taskId": "019145f2-871b-7130-b06f-69412b74bd79", "url": "/api/v1/task/019145f2-871b-7130-b06f-69412b74bd79"}, "version": "1.0"} +, +"get_device_list_ps_3": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723454921231, "serialNumber": "9X8X6SWXCLM", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.4", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 5:48:45.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:28:41", "bootDateTime": "2024-07-22 03:40:41", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.4", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835767, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:71:6e", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "id": "6cae0779-a144-4208-a0f8-4eaca1542d61"}], "version": "1.0"} +, +"trigger_software_image_distribution_ps_2": {"response": {"taskId": "019145f2-8977-721b-89eb-484232dc5772", "url": "/api/v1/task/019145f2-8977-721b-89eb-484232dc5772"}, "version": "1.0"} +, +"get_device_list_ps_4": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723434775476, "serialNumber": "99IA669OFH1", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.3", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 0:13:23.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:55", "bootDateTime": "2024-07-22 03:39:55", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "locationName": null, "managementIpAddress": "204.1.2.3", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835813, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:ff:2a", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "id": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a"}], "version": "1.0"} +, +"trigger_software_image_distribution_ps_3": {"response": {"taskId": "019145f2-8bab-7157-9db4-b129f5e5b876", "url": "/api/v1/task/019145f2-8bab-7157-9db4-b129f5e5b876"}, "version": "1.0"} +, +"get_device_list_ps_5": {"response": [{"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723434747880, "serialNumber": "93K2LNK8A5Y", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.9", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "13 days, 21:22:41.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:27", "bootDateTime": "2024-07-29 06:30:27", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "vRouter1.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.9", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1220781, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:f6:22:46:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "id": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e"}], "version": "1.0"} +, +"trigger_software_image_distribution_ps_4": {"response": {"taskId": "019145f2-8e01-7211-8039-2070fa1eee0a", "url": "/api/v1/task/019145f2-8e01-7211-8039-2070fa1eee0a"}, "version": "1.0"} +, +"task_details_ps1": {"response": {"startTime": 1723455407357, "data": "distribute", "endTime": 1723455407727, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723455407727, "errorCode": "100", "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-84fd-7682-aaa1-b276de51e558"}, "version": "1.0"} , + +"task_details_ps2": {"response": {"startTime": 1723455407899, "data": "distribute", "progress": "Starting Distribution", "version": 1723455407903, "lastUpdate": 1723455407902, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-871b-7130-b06f-69412b74bd79"}, "version": "1.0"} , + +"task_details_ps3": {"response": {"startTime": 1723455407899, "data": "distribute", "endTime": 1723455414364, "progress": "completed successfully. Success = 1, Failure = 0, Running = 0, Pending = 0, Total = 1", "version": 1723455414364, "lastUpdate": 1723455407902, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-871b-7130-b06f-69412b74bd79"}, "version": "1.0"} , + +"task_details_ps4": {"response": {"startTime": 1723455408503, "data": "distribute", "endTime": 1723455409573, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723455409573, "errorCode": "100", "lastUpdate": 1723455408506, "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-8977-721b-89eb-484232dc5772"}, "version": "1.0"} , + +"task_details_ps5": {"response": {"startTime": 1723455409068, "data": "distribute", "endTime": 1723455410369, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723455410369, "errorCode": "100", "lastUpdate": 1723455409071, "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-8bab-7157-9db4-b129f5e5b876"}, "version": "1.0"} , + +"task_details_ps6": {"response": {"startTime": 1723455409666, "data": "distribute", "endTime": 1723455410756, "progress": " Success = 0, Failure = 1, Running = 0, Pending = 0, Total = 1", "version": 1723455410756, "errorCode": "100", "lastUpdate": 1723455409669, "serviceType": "Swim Service", "isError": true, "instanceTenantId": "6663114d388b29001399e46a", "id": "019145f2-8e01-7211-8039-2070fa1eee0a"}, "version": "1.0"} , + +"get_software_image_details_ps_1": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_device_list_ps_6": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723454921231, "serialNumber": "9X8X6SWXCLM", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.4", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 5:48:45.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:28:41", "bootDateTime": "2024-07-22 03:40:41", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.4", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835775, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:71:6e", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "id": "6cae0779-a144-4208-a0f8-4eaca1542d61"}, {"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "type": "Cisco Catalyst 9300 Switch", "lastUpdateTime": 1723455253431, "serialNumber": "FOC2225U12L", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.2", "lastManagedResyncReasons": "Application Requested", "managementState": "Managed", "pendingSyncRequestsCount": "1", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "Application Requested", "syncRequestedByApp": "SWIM", "upTime": "22 days, 11:50:43.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:34:13", "bootDateTime": "2024-07-20 21:44:13", "apManagerInterfaceIp": "", "collectionStatus": "In Progress", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.2", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:36:54", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1943563, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "DISTRIBUTION", "macAddress": "dc:f7:19:33:41:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "id": "4ea61bbc-5965-436f-b880-178331d4bdc6"}, {"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723434775476, "serialNumber": "99IA669OFH1", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.3", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 0:13:23.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:55", "bootDateTime": "2024-07-22 03:39:55", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "locationName": null, "managementIpAddress": "204.1.2.3", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835821, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:ff:2a", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "id": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723434747880, "serialNumber": "93K2LNK8A5Y", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.9", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "13 days, 21:22:41.81", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 03:52:27", "bootDateTime": "2024-07-29 06:30:27", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "vRouter1.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.9", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1220789, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:f6:22:46:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "id": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723370551120, "serialNumber": "91R03VNQU8W", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.10", "lastManagedResyncReasons": "Periodic", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "20 days, 6:24:39.73", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-11 10:02:31", "bootDateTime": "2024-07-22 03:38:31", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "Vrouter2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.10", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1835906, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:7a:80:39:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "id": "93dad04d-d795-4902-a685-37205483ee5d"}], "version": "1.0"} +, +"get_software_image_details_ps_2": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, + +"image_distribution_partially_successfull_response":{ + "message": "Image with Id 'c383ee35-d20e-49f2-b51c-bfe499abbbaa' Distributed and partially successfull"}, + + + + +"playbook_swim_image_golden_already_tagged" :[ + { + "import_image_details": { + "type": "REMOTE", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "tagging": true + } + } +], +"playbook_swim_image_golden_already_untagged" :[ + { + "import_image_details": { + "type": "REMOTE", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + }, + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9300 Switch", + "tagging": false + } + } +], + + +"get_software_image_details_golden_already_tagged": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_1": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_2": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_device_family_identifiers_golden_already_tagged": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}], "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_3": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_golden_tag_status_of_an_image_golden_already_tagged": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"} +, +"get_golden_tag_status_of_an_image_golden_already_untagged": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_4": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_device_family_identifiers_golden_already_tagged_1": {"response": [{"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}], "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_5": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_golden_already_tagged_6": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_golden_tag_status_of_an_image_golden_already_tagged_2": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": true}, "version": "1.0"} +, +"get_golden_tag_status_of_an_image_golden_already_untagged_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"} +, +"image_golden_already_tagged_response":{ + "message": "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already tagged as Golden image in Cisco Catalyst Center"}, + +"image_golden_already_untagged_response":{ + "message": "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already un-tagged from Golden image in Cisco Catalyst Center"}, + + +"playbook_swim_image_cant_found" : [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.022.SPA.bin", + "device_serial_number": "FOC2225U12L" + } + } +], + +"get_software_image_details_swim_image_cant_found": {"response": [], "version": "1.0"}, + +"swim_image_cant_found_response":{ + "message": "SWIM image 'cat9k_iosxe.17.12.022.SPA.bin' could not be found"}, + + +"playbook_distribution_failed_for_all_devices" : [ + { + "image_distribution_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "site_name": "Global/LTTS/FLOOR1", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches" + } + } +], + +"get_site_failed_for_all_devices": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"get_software_image_details_failed_for_all_devices": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_device_list_failed_for_all_devices": {"response": [{"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723457440572, "serialNumber": "9X8X6SWXCLM", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.4", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 6:31:06.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 10:10:40", "bootDateTime": "2024-07-22 03:39:40", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.4", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 10:09:47", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1848654, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:71:6e", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "id": "6cae0779-a144-4208-a0f8-4eaca1542d61"}, {"description": "Cisco IOS Software [Dublin], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.12.2, RELEASE SOFTWARE (fc2) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2023 by Cisco Systems, Inc. Compiled Tue 14-Nov-23 05:56 by mcpre", "type": "Cisco Catalyst 9300 Switch", "lastUpdateTime": 1723467959890, "serialNumber": "FOC2225U12L", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.12.2", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.2", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "0:13:39.25", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 13:05:59", "bootDateTime": "2024-08-12 12:52:59", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.2", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 13:05:57", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1055, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "DISTRIBUTION", "macAddress": "dc:f7:19:33:41:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "id": "4ea61bbc-5965-436f-b880-178331d4bdc6"}, {"description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "lastUpdateTime": 1723456792348, "serialNumber": "99IA669OFH1", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.3", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "21 days, 6:20:20.00", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 09:59:52", "bootDateTime": "2024-07-22 03:39:52", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "locationName": null, "managementIpAddress": "204.1.2.3", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 09:59:07", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1848643, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "ACCESS", "macAddress": "00:50:56:b4:ff:2a", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "id": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723461015697, "serialNumber": "93K2LNK8A5Y", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.9", "lastManagedResyncReasons": "Application Requested", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "SWIM", "upTime": "14 days, 4:40:44.73", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 11:10:15", "bootDateTime": "2024-07-29 06:30:15", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "vRouter1.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.9", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 11:10:13", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1233619, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:f6:22:46:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "id": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e"}, {"description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "type": "Cisco Catalyst 8000V Edge Software", "lastUpdateTime": 1723461014047, "serialNumber": "91R03VNQU8W", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.10", "lastManagedResyncReasons": "Application Requested", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "SWIM", "upTime": "21 days, 7:32:37.44", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 11:10:14", "bootDateTime": "2024-07-22 03:38:14", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Routers", "hostname": "Vrouter2.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.10", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 11:10:11", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": true, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1848741, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "BORDER ROUTER", "macAddress": "00:1e:7a:80:39:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "id": "93dad04d-d795-4902-a685-37205483ee5d"}], "version": "1.0"} +, +"get_site_failed_for_all_devices_1": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"get_membership_failed_for_all_devices": {"site": {"response": [], "version": "1.0", "message": "Site does not have childrens."}, "device": [{"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-08-12 12:52:59", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Standalone", "description": "Cisco IOS Software [Dublin], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.12.2, RELEASE SOFTWARE (fc2) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2023 by Cisco Systems, Inc. Compiled Tue 14-Nov-23 05:56 by mcpre", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 13:05:57", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723467959890, "lastUpdated": "2024-08-12 13:05:59", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.12.2", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "0:13:39.25", "uptimeSeconds": 1056, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:40", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 10:09:47", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723457440572, "lastUpdated": "2024-08-12 10:10:40", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 6:31:06.00", "uptimeSeconds": 1848655, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:52", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:59:07", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723456792348, "lastUpdated": "2024-08-12 09:59:52", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 6:20:20.00", "uptimeSeconds": 1848643, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}]} +, +"get_device_list_failed_for_all_devices_1": {"response": [{"description": "Cisco IOS Software [Dublin], Catalyst L3 Switch Software (CAT9K_IOSXE), Version 17.12.2, RELEASE SOFTWARE (fc2) Technical Support: http://www.cisco.com/techsupport Copyright (c) 1986-2023 by Cisco Systems, Inc. Compiled Tue 14-Nov-23 05:56 by mcpre", "type": "Cisco Catalyst 9300 Switch", "lastUpdateTime": 1723467959890, "serialNumber": "FOC2225U12L", "deviceSupportLevel": "Supported", "softwareType": "IOS-XE", "softwareVersion": "17.12.2", "inventoryStatusDetail": "", "collectionInterval": "Global Default", "dnsResolvedManagementAddress": "204.1.2.2", "lastManagedResyncReasons": "Config Change Event", "managementState": "Managed", "pendingSyncRequestsCount": "0", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "syncRequestedByApp": "", "upTime": "0:13:39.25", "roleSource": "AUTO", "interfaceCount": "0", "lastUpdated": "2024-08-12 13:05:59", "bootDateTime": "2024-08-12 12:52:59", "apManagerInterfaceIp": "", "collectionStatus": "Managed", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "locationName": null, "managementIpAddress": "204.1.2.2", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "associatedWlcIp": "", "apEthernetMacAddress": null, "errorCode": null, "errorDescription": null, "lastDeviceResyncStartTime": "2024-08-12 13:05:57", "lineCardCount": "0", "lineCardId": "", "managedAtleastOnce": false, "memorySize": "NA", "tagCount": "0", "tunnelUdpPort": null, "uptimeSeconds": 1057, "vendor": "Cisco", "waasDeviceMode": null, "location": null, "role": "DISTRIBUTION", "macAddress": "dc:f7:19:33:41:00", "instanceTenantId": "6663114d388b29001399e46a", "instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "id": "4ea61bbc-5965-436f-b880-178331d4bdc6"}], "version": "1.0"} +, +"trigger_software_image_distribution_failed_for_all_devices": {"response": {"taskId": "019146b6-48cf-77b7-9060-f959cdf9c7a6", "url": "/api/v1/task/019146b6-48cf-77b7-9060-f959cdf9c7a6"}, "version": "1.0"} +, +"distribution_params": {"payload": [{"deviceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa"}]} +, +"Task_details_failed_for_all_devices": {"response": {"startTime": 1723468237008, "data": "distribute", "progress": "Starting Distribution", "version": 1723468237010, "lastUpdate": 1723468237010, "serviceType": "Swim Service", "isError": false, "instanceTenantId": "6663114d388b29001399e46a", "id": "019146b6-48cf-77b7-9060-f959cdf9c7a6"}, "version": "1.0"} +, + + + + + +"distribution_failed_for_all_devices_response":{ + "message": "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed for all devices"}, + + + + + + + +"playbook_image_details_distribution_not_provided" : [ + { + "image_distribution_details": { + "site_name": "Global/LTTS/FLOOR1", + "device_role": "ALL", + "device_family_name": "Switches and Hubs", + "device_series_name": "Cisco Catalyst 9300 Series Switches" + } + } +], + +"get_site_image_details_distribution_not_provided": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"distribution_failed_for_all_devicesresponse":{ + "message": "Image details required for distribution have not been provided"}, + + + +"playbook_device_family_not_found" : [ + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "site_name": "Global/LTTS/FLOOR1", + "tagging": true + } + } +], + +"get_software_image_details_device_family_not_found": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": true, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_site_device_family_not_found": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"get_device_family_identifiers_device_family_not_found": {"response": [{"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"} +, +"device_family_not_found_response":{ + "message": "Device Family: null not found"}, + +"playbook_import_image_details_not_provided" : [ + { + "import_image_details": { + "type": "remote" + } + } +], + +"import_image_details_not_provided_response":{ + "message": "Error: Import image details are not provided in the playbook, or the Import Image API was not\n triggered successfully. Please ensure the necessary details are provided and verify the status of the Import Image process."} +, + +"playbook_verify_merged": [ + { + "import_image_details": { + "type": "remote", + "url_details": { + "payload": [ + { + "source_url": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", + "third_party": false + } + ] + } + } + }, + { + "tagging_details": { + "image_name": "cat9k_iosxe.17.12.02.SPA.bin", + "device_role": "ALL", + "device_image_family_name": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", + "site_name": "Global/LTTS/FLOOR1", + "tagging": false + } + } +], + +"get_software_image_details_verify_merged": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_verify_merged_1": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_verify_merged_2": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_software_image_details_verify_merged_3": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_site_verify_merged": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"get_device_family_identifiers_verify_merged": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"} +, +"get_software_image_details_verify_merged_4": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_golden_tag_status_of_an_image_verify_merged": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"} +, +"get_golden_tag_status_of_an_image_verify_merged_1": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"} +, +"get_site_verify_merged_1": {"response": [{"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "siteNameHierarchy": "Global/LTTS/FLOOR1"}]} +, +"get_device_family_identifiers_verify_merged_1": {"response": [{"deviceFamily": "Cisco Catalyst 8000V Edge Software", "deviceFamilyIdentifier": "286327102"}, {"deviceFamily": "Cisco Catalyst 9300 Switch", "deviceFamilyIdentifier": "286315874"}, {"deviceFamily": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "deviceFamilyIdentifier": "999999901"}], "version": "1.0"} +, +"get_software_image_details_verify_merged_5": {"response": [{"imageUuid": "c383ee35-d20e-49f2-b51c-bfe499abbbaa", "name": "cat9k_iosxe.17.12.02.SPA.bin", "family": "CAT9K", "version": "17.12.02.0.2739", "displayVersion": "17.12.02", "md5Checksum": "2405eeb2627eeee594078b6019a2d936", "shaCheckSum": "457d3bc8240bcb18066c56051a5085d6cdaea5d0cc9df6ca615b2db84abe7672a29d1c3c6eaa784b487ff812847948bbdf3107d226feb96844fe54d0ba873c4c", "createdTime": "2024-08-12 08:11:27.0", "imageType": "SYSTEM_SW", "fileSize": "1316755714 bytes", "imageName": "cat9k_iosxe.17.12.02.SPA.bin", "applicationType": "", "feature": "", "fileServiceId": "58c92b3f-4913-4e3b-85e0-2407f0d0e323", "isTaggedGolden": false, "imageSource": "http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin", "extendedAttributes": {"Description": "Cisco IOS Software, IOS-XE Software", "BOOTROM": "UNKNOWN", "MINFLASHSIZE": "UNKNOWN", "MEDIA": "5", "FULL_VERSION": "17.12.02.0.2739", "DEFAULT_BOOTROM": "UNKNOWN", "DEFAULT_RAM": "UNKNOWN", "GAIA_FEATURE": "UNKNOWN", "COMPRESSION_CODE": "-1", "DEFAULT_MINFLASHSIZE": "UNKNOWN", "RAM": "UNKNOWN"}, "vendor": "CISCO", "imageIntegrityStatus": "UNKNOWN", "importSourceType": "REMOTEURL"}], "version": "1.0"} +, +"get_golden_tag_status_of_an_image_verify_merged_2": {"response": {"deviceRole": "ALL", "inheritedSiteName": null, "inheritedSiteId": null, "taggedGolden": false}, "version": "1.0"} +, +"verify_merged_response":{ + "message": "SWIM image 'cat9k_iosxe.17.12.02.SPA.bin' could not be found"} + + } - \ No newline at end of file + + + diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py index f4e93df2e5..b44eda8eef 100644 --- a/tests/unit/modules/dnac/test_swim_workflow_manager.py +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -1,11 +1,8 @@ # Copyright (c) 2024 Cisco and/or its affiliates. - # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at - # http://www.apache.org/licenses/LICENSE-2.0 -# # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,34 +10,43 @@ # limitations under the License. # Make coding more python3-ish - from __future__ import absolute_import, division, print_function - __metaclass__ = type from unittest.mock import patch - from ansible_collections.cisco.dnac.plugins.modules import swim_workflow_manager from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData - class TestswimWorkflowManager(TestDnacModule): module = swim_workflow_manager - test_data = loadPlaybookData("swim_workflow_manager") - + playbook_config_invalid_param_import_image_url_tag_golden_load = test_data.get("playbook_config_invalid_param_import_image_url_tag_golden_load") playbook_untag_image_as_golden_and_load_on_device = test_data.get("playbook_untag_image_as_golden_and_load_on_device") + playbook_import_image_already_exist = test_data.get("playbook_import_image_already_exist") + playbook_site_not_exist = test_data.get("playbook_site_not_exist") + playbook_swim_image_invalid = test_data.get("playbook_swim_image_invalid") + playbook_image_distribution_failed = test_data.get("playbook_image_distribution_failed") + playbook_image_distribution_partially_successfull = test_data.get("playbook_image_distribution_partially_successfull") + playbook_swim_image_golden_already_tagged = test_data.get("playbook_swim_image_golden_already_tagged") + playbook_swim_image_golden_already_untagged = test_data.get("playbook_swim_image_golden_already_untagged") + playbook_swim_image_cant_found = test_data.get("playbook_swim_image_cant_found") + playbook_distribution_failed_for_all_devices = test_data.get("playbook_distribution_failed_for_all_devices") + playbook_image_details_distribution_not_provided = test_data.get("playbook_image_details_distribution_not_provided") + playbook_device_family_not_found = test_data.get("playbook_device_family_not_found") + playbook_import_image_details_not_provided = test_data.get("playbook_import_image_details_not_provided") + playbook_verify_merged = test_data.get("playbook_verify_merged") def setUp(self): super(TestswimWorkflowManager, self).setUp() - self.mock_dnac_init = patch( - "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__") + "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK.__init__" + ) self.run_dnac_init = self.mock_dnac_init.start() self.run_dnac_init.side_effect = [None] + self.mock_dnac_exec = patch( "ansible_collections.cisco.dnac.plugins.module_utils.dnac.DNACSDK._exec" ) @@ -55,13 +61,44 @@ def load_fixtures(self, response=None, device=""): """ Load fixtures for user. """ - if "playbook_untag_image_as_golden_and_load_on_device" in self._testMethodName: + if "playbook_config_invalid_param_import_image_url_tag_golden_load" in self._testMethodName: self.run_dnac_exec.side_effect = [ self.test_data.get("get_software_image_details"), + self.test_data.get("import_software_image_via_url"), + self.test_data.get("Task_details_inprogress_1"), + self.test_data.get("task_details"), + self.test_data.get("get_software_image_1_details"), + self.test_data.get("get_software_image_1_details"), self.test_data.get("get_site"), self.test_data.get("get_device_family_identifiers"), - self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_software_image_1_details"), + self.test_data.get("get_device_list"), + self.test_data.get("get_software_image_1_details"), + self.test_data.get("get_device_list"), + self.test_data.get("get_software_image_1_details"), self.test_data.get("get_golden_tag_status_of_an_image"), + self.test_data.get("tag_as_golden_image"), + self.test_data.get("Task_details_inprogress_2"), + self.test_data.get("task_1_details"), + self.test_data.get("get_1_site"), + self.test_data.get("get_membership"), + self.test_data.get("trigger_software_image_distribution"), + self.test_data.get("Task_details_inprogress_3"), + self.test_data.get("task_2_details"), + self.test_data.get("get_1_site"), + self.test_data.get("get_1_membership"), + self.test_data.get("trigger_software_image_activation"), + self.test_data.get("Task_details_inprogress_4"), + self.test_data.get("task_4_details"), + self.test_data.get("playbook_config_invalid_param_import_image_url_tag_golden_load_response") + ] + elif "playbook_untag_image_as_golden_and_load_on_device" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_2_software_image_details"), + self.test_data.get("get_2_site"), + self.test_data.get("get_2_device_family_identifiers"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_2_golden_tag_status_of_an_image"), self.test_data.get("remove_golden_tag_for_image"), self.test_data.get("Task_details"), self.test_data.get("get_software_image_details_2"), @@ -71,11 +108,191 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_golden_tag_status_of_an_image_1"), self.test_data.get("untag_image_as_golden_and_load_on_device_responce") ] + elif "playbook_untag_image_as_golden_and_error_load_on_device" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_2_software_image_details"), + self.test_data.get("get_2_site"), + self.test_data.get("get_2_device_family_identifiers"), + self.test_data.get("get_software_image_details_1"), + self.test_data.get("get_2_golden_tag_status_of_an_image"), + self.test_data.get("remove_golden_tag_for_image"), + self.test_data.get("Task_details_error"), + self.test_data.get("untag_image_as_golden_and_load_on_device_error_responce") + ] + + elif "playbook_import_image_already_exist" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_already_tagged"), + self.test_data.get("get_software_image_details_already_tagged_1"), + self.test_data.get("get_image_details"), + self.test_data.get("get_image_details"), + self.test_data.get("get_software_image_details_already_tagged_2"), + self.test_data.get("import_image_already_exist_response"), + ] + + elif "playbook_site_not_exist" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_site_not_exist"), + self.test_data.get("get_software_image_details_site_not_exist_1"), + self.test_data.get("get_software_image_details_site_not_exist_2"), + Exception(), + self.test_data.get("site_not_exist_response"), + ] + elif "playbook_swim_image_invalid" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_invalid_swim_image"), + self.test_data.get("import_software_image_via_url_invalid_swim_image"), + self.test_data.get("Task_details_invalid_swim_image"), + self.test_data.get("Task_details_invalid_swim_image_end"), + self.test_data.get("invalid_swim_image_response"), + ] + elif "playbook_image_distribution_failed" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_image_distribution_failed"), + self.test_data.get("get_device_list_image_distribution_failed"), + self.test_data.get("get_site_image_distribution_failed"), + self.test_data.get("get_membership_image_distribution_failed"), + self.test_data.get("trigger_software_image_distribution_image_distribution_failed"), + self.test_data.get("task_details_running_image_distribution_failed"), + self.test_data.get("task_details_image_distribution_failed"), + self.test_data.get("image_distribution_failed_response"), + ] + elif "playbook_image_distribution_partially_successfull" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_ps"), + self.test_data.get("get_device_list_ps"), + self.test_data.get("get_site_ps"), + self.test_data.get("get_membership_ps"), + self.test_data.get("get_device_uuids"), + self.test_data.get("get_device_uuids_1"), + self.test_data.get("get_device_list_ps_1"), + self.test_data.get("trigger_software_image_distribution_ps"), + self.test_data.get("get_device_list_ps_2"), + self.test_data.get("trigger_software_image_distribution_ps_1"), + self.test_data.get("get_device_list_ps_3"), + self.test_data.get("trigger_software_image_distribution_ps_2"), + self.test_data.get("get_device_list_ps_4"), + self.test_data.get("trigger_software_image_distribution_ps_3"), + self.test_data.get("get_device_list_ps_5"), + self.test_data.get("trigger_software_image_distribution_ps_4"), + self.test_data.get("task_details_ps1"), + self.test_data.get("task_details_ps2"), + self.test_data.get("task_details_ps3"), + self.test_data.get("task_details_ps4"), + self.test_data.get("task_details_ps5"), + self.test_data.get("task_details_ps6"), + self.test_data.get("get_software_image_details_ps_1"), + self.test_data.get("get_device_list_ps_6"), + self.test_data.get("get_software_image_details_ps_2"), + self.test_data.get("image_distribution_partially_successfull"), + ] + elif "playbook_swim_image_golden_already_tagged" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_golden_already_tagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_2"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_3"), + self.test_data.get("get_golden_tag_status_of_an_image_golden_already_tagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_4"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_5"), + self.test_data.get("get_software_image_details_golden_already_tagged_6"), + self.test_data.get("get_golden_tag_status_of_an_image_golden_already_tagged_2"), + self.test_data.get("image_golden_already_tagged_response"), + ] + elif "playbook_swim_image_golden_already_untagged" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_golden_already_tagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_2"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_3"), + self.test_data.get("get_golden_tag_status_of_an_image_golden_already_untagged"), + self.test_data.get("get_software_image_details_golden_already_tagged_4"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_5"), + self.test_data.get("get_software_image_details_golden_already_tagged_6"), + self.test_data.get("get_golden_tag_status_of_an_image_golden_already_untagged_1"), + self.test_data.get("image_golden_already_untagged_response"), + ] + elif "playbook_swim_image_cant_found" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_swim_image_cant_found"), + self.test_data.get("swim_image_cant_found_response"), + ] + elif "playbook_distribution_failed_for_all_devices" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_site_failed_for_all_devices"), + self.test_data.get("get_software_image_details_failed_for_all_devices"), + self.test_data.get("get_device_list_failed_for_all_devices"), + self.test_data.get("get_site_failed_for_all_devices_1"), + self.test_data.get("get_membership_failed_for_all_devices"), + self.test_data.get("get_device_list_failed_for_all_devices_1"), + self.test_data.get("distribution_params"), + # self.test_data.get("trigger_software_image_distribution_failed_for_all_devices"), + # self.test_data.get("Task_details_failed_for_all_devices"), + self.test_data.get("distribution_failed_for_all_devices_response"), + ] + elif "playbook_image_details_distribution_not_provided" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_site_image_details_distribution_not_provided"), + self.test_data.get("distribution_failed_for_all_devicesresponse"), + ] + elif "playbook_device_family_not_found" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_device_family_not_found"), + self.test_data.get("get_site_device_family_not_found"), + self.test_data.get("get_device_family_identifiers_device_family_not_found"), + self.test_data.get("device_family_not_found_response"), + ] + elif "playbook_import_image_details_not_provided" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("import_image_details_not_provided_response"),] + + elif "playbook_verify_merged" in self._testMethodName: + self.run_dnac_exec.side_effect = [ + self.test_data.get("get_software_image_details_verify_merged"), + self.test_data.get("get_software_image_details_verify_merged_1"), + self.test_data.get("get_software_image_details_verify_merged_2"), + self.test_data.get("get_software_image_details_verify_merged_3"), + self.test_data.get("get_site_verify_merged"), + self.test_data.get("get_device_family_identifiers_verify_merged"), + self.test_data.get("get_software_image_details_verify_merged_4"), + self.test_data.get("get_golden_tag_status_of_an_image_verify_merged"), + self.test_data.get("get_golden_tag_status_of_an_image_verify_merged_1"), + self.test_data.get("get_site_verify_merged_1"), + self.test_data.get("get_device_family_identifiers_verify_merged_1"), + self.test_data.get("get_software_image_details_verify_merged_5"), + self.test_data.get("get_golden_tag_status_of_an_image_verify_merged_2"), + self.test_data.get("verify_merged_response"), + ] + + def test_swim_workflow_manager_playbook_config_invalid_param_import_image_url_tag_golden_load(self): + """ + Test case for swim workflow manager when giving invalid param. + This test case checks the behavior of the swim workflow when giving invalid param in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_config_invalid_param_import_image_url_tag_golden_load + ) + ) + result = self.execute_module(changed=False, failed=True) + print(result) + self.assertEqual( + result.get("msg"), + "Activation for Image with Id '4a3cccfa-dc92-4fad-a7d3-c59876cbebe6' gets failed" + ) def test_swim_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): """ Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. """ set_module_args( @@ -88,14 +305,349 @@ def test_swim_workflow_manager_playbook_untag_image_as_golden_and_load_on_device config=self.playbook_untag_image_as_golden_and_load_on_device ) ) - print(1) result = self.execute_module(changed=True, failed=False) - print(2) print(result) self.assertEqual( result.get('msg'), "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." ) + # def test_swim_workflow_manager_playbook_untag_image_as_golden_and_error_load_on_device(self): ############## + # """ + # Test case for user role workflow manager when creating a user. + # This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + # """ + # set_module_args( + # dict( + # dnac_host="1.1.1.1", + # dnac_username="dummy", + # dnac_password="dummy", + # dnac_log=True, + # state="merged", + # config=self.playbook_untag_image_as_golden_and_load_on_device + # ) + # ) + # result = self.execute_module(changed=False, failed=True) + # print(result) + # self.assertEqual( + # result.get('msg'), + # "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." + # ) + def test_swim_workflow_manager_playbook_import_image_already_exist(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config=self.playbook_import_image_already_exist + ) + ) + result = self.execute_module(changed=False, failed=False) + print(result) + self.assertEqual( + result.get('msg'), + "Image 'cat9k_iosxe.17.12.02.SPA.bin' already exists in the Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_site_not_exist(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_site_not_exist + ) + ) + result = self.execute_module(changed=False, failed=True) + print(result) + self.assertEqual( + result.get('msg'), + "An exception occurred: Site 'Global/LTTS/FLOOR2' does not exist in the Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_invalid(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_invalid + ) + ) + result = self.execute_module(changed=False, failed=True) + print(result) + self.assertEqual( + result.get('msg'), + "SWIM Image http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin seems to be invalid" + ) + + def test_swim_workflow_manager_playbook_image_distribution_failed(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_distribution_failed + ) + ) + result = self.execute_module(changed=False, failed=True) + print(result) + self.assertEqual( + result.get('msg'), + "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed" + ) + + def test_swim_workflow_manager_playbook_image_distribution_partially_successfull(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_distribution_partially_successfull + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "Image with Id 'c383ee35-d20e-49f2-b51c-bfe499abbbaa' Distributed and partially successfull" + ) + + def test_swim_workflow_manager_playbook_swim_image_golden_already_tagged(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_golden_already_tagged + ) + ) + print(0) + result = self.execute_module(changed=False, failed=False) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already tagged as Golden image in Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_golden_already_untagged(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_golden_already_untagged + ) + ) + print(0) + result = self.execute_module(changed=False, failed=False) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already un-tagged from Golden image in Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_cant_found(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_cant_found + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "SWIM image 'cat9k_iosxe.17.12.022.SPA.bin' could not be found" + ) + def test_swim_workflow_manager_playbook_distribution_failed_for_all_devices(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_distribution_failed_for_all_devices + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed for all devices" + ) + def test_swim_workflow_manager_playbook_image_details_distribution_not_provided(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_details_distribution_not_provided + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "Image details required for distribution have not been provided" + ) + def test_swim_workflow_manager_playbook_device_family_not_found(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_device_family_not_found + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "Device Family: None not found" + ) + def test_swim_workflow_manager_playbook_import_image_details_not_provided(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_import_image_details_not_provided + ) + ) + print(0) + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "Error: Import image details are not provided in the playbook, or the Import Image API was not\n triggered successfully. Please ensure the necessary details are provided and verify the status of the Import Image process." + ) + def test_swim_workflow_manager_playbook_verify_merged(self): + """ + Test case for user role workflow manager when creating a user. + This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_verify_merged + ) + ) + print("merged") + result = self.execute_module(changed=False, failed=True) + print(1) + print(result) + print(2) + self.assertEqual( + result.get('msg'), + "SWIM image 'cat9k_iosxe.17.12.02.SPA.bin' could not be found" + ) + From 8aca2f62afb702b8e520a0ad05c252fa3934375c Mon Sep 17 00:00:00 2001 From: Abhishek Maheshwari <56202291+Abhishek-121@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:29:16 +0530 Subject: [PATCH 044/121] Delete plugins/modules/fabric_sites_zones_workflow_manager.py --- .../fabric_sites_zones_workflow_manager.py | 1749 ----------------- 1 file changed, 1749 deletions(-) delete mode 100644 plugins/modules/fabric_sites_zones_workflow_manager.py diff --git a/plugins/modules/fabric_sites_zones_workflow_manager.py b/plugins/modules/fabric_sites_zones_workflow_manager.py deleted file mode 100644 index c57adfc793..0000000000 --- a/plugins/modules/fabric_sites_zones_workflow_manager.py +++ /dev/null @@ -1,1749 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# Copyright (c) 2022, Cisco Systems -# GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) - -from __future__ import absolute_import, division, print_function - -__metaclass__ = type -__author__ = ("Abhishek Maheshwari, Madhan Sankaranarayanan") - -DOCUMENTATION = r""" ---- -module: fabric_sites_zones_workflow_manager -short_description: Configure the fabric site(s)/zone(s) and also updating the authentication profile template for - Cisco Catalyst Center Platform. -description: -- Creating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. -- Updating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. -- Creating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. -- Updating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. -- Deletes the fabric site(s) from Cisco Catalyst Center. -- Deletes the fabric zone(s) from Cisco Catalyst Center. -- Configure the authentication profile template for fabric site/zone in Cisco Catalyst Center. -version_added: '6.17.0' -extends_documentation_fragment: - - cisco.dnac.workflow_manager_params -author: Abhishek Maheshwari (@abmahesh) - Madhan Sankaranarayanan (@madhansansel) -options: - config_verify: - description: Set to True to verify the Cisco Catalyst Center config after applying the playbook config. - type: bool - default: False - state: - description: The state of Cisco Catalyst Center after module completion. - type: str - choices: [ merged, deleted ] - default: merged - config: - description: A list containing detailed configurations for creating, updating, or deleting fabric sites or zones - in a Software-Defined Access (SDA) environment. It also includes specifications for updating the authentication - profile template for these sites. Each element in the list represents a specific operation to be performed on - the SDA infrastructure, such as the addition, modification, or removal of fabric sites/zones, and modifications - to authentication profiles. - type: list - elements: dict - required: True - suboptions: - fabric_sites: - description: A dictionary that holds the detailed configuration for setting up or modifying REST Endpoints that will - receive Audit logs and Events from the Cisco Catalyst Center Platform. This dictionary is essential for specifying - the attributes and parameters required to manage the lifecycle of fabric sites and zones, as well as the associated - authentication profiles. - type: dict - suboptions: - site_name: - description: The full hierarchical name of the site within the SDA environment. This name uniquely identifies the site - for operations such as creation, update, or deletion of fabric sites or zones, as well as for updating the - authentication profile template. This parameter is mandatory for any operation related to fabric site/zone management. - type: str - required: True - site_type: - description: Specifies the type of site to be managed within the SDA environment. The acceptable values are 'fabric_site' - and 'fabric_zone'. The default value is 'fabric_site', indicating the configuration of a broader network area, whereas - 'fabric_zone' typically refers to a more specific segment within the site. - type: str - required: True - authentication_profile: - description: The authentication profile applied to the specified fabric. The profile determines the security posture and - controls applied to network access within the site. Possible values include 'Closed Authentication', 'Low Impact', - 'No Authentication', and 'Open Authentication'. This setting is critical during the creation or update of a fabric site - or when updating the authentication profile template. - type: str - is_pub_sub_enabled: - description: A boolean flag that indicates whether the pub/sub mechanism is enabled for control nodes in the fabric site. - This feature is only relevant during the creation or update of fabric sites, not fabric zones. When set to True, - pub/sub facilitates more efficient communication and control within the site. The default is True for fabric sites, - and this setting is not applicable for fabric zones. - type: bool - update_authentication_profile: - description: A dictionary containing the specific details required to update the authentication profile template associated - with the fabric site. This includes advanced settings that fine-tune the authentication process and security controls - within the site. - type: dict - suboptions: - authentication_order: - description: Specifies the primary method of authentication for the site. The available methods are 'dot1x' (IEEE 802.1X) - and 'mac' (MAC-based authentication). This setting determines the order in which authentication mechanisms are attempted. - type: str - dot1x_fallback_timeout: - description: The timeout duration, in seconds, for falling back from 802.1X authentication. This value must be within the - range of 3 to 120 seconds. It defines the period a device waits before attempting an alternative authentication method - if 802.1X fails. - type: int - wake_on_lan: - description: A boolean value indicating whether the Wake-on-LAN feature is enabled. Wake-on-LAN allows the network to - remotely wake up devices that are in a low-power state. - type: bool - number_of_hosts: - description: Specifies the number of hosts allowed per port. This can either be 'Single' for one device per port or - 'Unlimited' for multiple devices. This setting helps in controlling the network access and maintaining security. - type: str - enable_bpu_guard: - description: A boolean setting that enables or disables BPDU Guard. BPDU Guard provides a security mechanism by disabling - a port when a BPDU (Bridge Protocol Data Unit) is received, protecting against potential network loops. This setting - defaults to true and is only applicable when the authentication profile is set to "Closed Authentication". - type: bool - - -requirements: -- dnacentersdk >= 2.7.1 -- python >= 3.9 - -notes: - - To ensure the module operates correctly for scaled sets, which involve creating or updating fabric sites/zones and handling - the updation of authentication profile template, please provide valid input in the playbook. If any failure is encountered, - the module willhalt execution without proceeding to further operations. - - When deleting fabric sites, make sure to provide the input to remove the fabric zones associated with them in the - playbook. Fabric sites cannot be deleted until all underlying fabric zones have been removed. - - SDK Method used are - ccc_fabric_sites.FabricSitesZones.get_site - ccc_fabric_sites.FabricSitesZones.get_fabric_sites - ccc_fabric_sites.FabricSitesZones.get_fabric_zones - ccc_fabric_sites.FabricSitesZones.add_fabric_site - ccc_fabric_sites.FabricSitesZones.update_fabric_site - ccc_fabric_sites.FabricSitesZones.add_fabric_zone - ccc_fabric_sites.FabricSitesZones.update_fabric_zone - ccc_fabric_sites.FabricSitesZones.get_authentication_profiles - ccc_fabric_sites.FabricSitesZones.update_authentication_profile - ccc_fabric_sites.FabricSitesZones.delete_fabric_site_by_id - ccc_fabric_sites.FabricSitesZones.delete_fabric_zone_by_id - -""" - -EXAMPLES = r""" -- name: Create fabric site for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: merged - config: - - fabric sites: - site_name: "Global/Test_SDA/Bld1" - authentication_profile: "Closed Authentication" - is_pub_sub_enabled: False - -- name: Update fabric site for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: merged - config: - - fabric sites: - site_name: "Global/Test_SDA/Bld1" - authentication_profile: "Open Authentication" - -- name: Create fabric zone for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: merged - config: - - fabric sites: - site_name: "Global/Test_SDA/Bld1/Floor1" - site_type: "fabric_zone" - authentication_profile: "Closed Authentication" - -- name: Update fabric zone for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: merged - config: - - fabric sites: - site_name: "Global/Test_SDA/Bld1/Floor1" - site_type: "fabric_zone" - authentication_profile: "Open Authentication" - -- name: Update/customise authentication profile template for fabric site/zone. - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: merged - config: - - fabric_sites: - site_name: "Global/Test_SDA/Bld1" - site_type: "fabric_zone" - authentication_profile: "Open Authentication" - is_pub_sub_enabled: False - update_authentication_profile: - authentication_order: "dot1x" - dot1x_fallback_timeout: 28 - wake_on_lan: False - number_of_hosts: "Single" - -- name: Deleting/removing fabric site from sda from Cisco Catalyst Center - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: deleted - config: - - fabric_sites: - site_name: "Global/Test_SDA/Bld1" - -- name: Deleting/removing fabric zone from sda from Cisco Catalyst Center - cisco.dnac.events_and_notifications_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: "{{dnac_log_level}}" - dnac_log: False - state: deleted - config: - - fabric_sites: - site_name: "Global/Test_SDA/Bld1/Floor1" - site_type: "fabric_zone" - -""" - -RETURN = r""" - -dnac_response: - description: A dictionary or list with the response returned by the Cisco Catalyst Center Python SDK - returned: always - type: dict - sample: > - { - "response": { - "taskId": "string", - "url": "string" - }, - "version": "string" - } -""" - -from ansible.module_utils.basic import AnsibleModule -from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( - DnacBase, - validate_list_of_dicts, -) -import time - - -class FabricSitesZones(DnacBase): - """Class containing member attributes for fabric sites and zones workflow manager module""" - - def __init__(self, module): - super().__init__(module) - self.supported_states = ["merged", "deleted"] - self.create_site, self.update_site, self.no_update_site = [], [], [] - self.create_zone, self.update_zone, self.no_update_zone = [], [], [] - self.update_auth_profile, self.no_update_profile = [], [] - self.delete_site, self.delete_zone, self.absent_site, self.absent_zone = [], [], [], [] - - def validate_input(self): - """ - Validate the fields provided in the playbook. - Checks the configuration provided in the playbook against a predefined specification - to ensure it adheres to the expected structure and data types. - Parameters: - self: The instance of the class containing the 'config' attribute to be validated. - Returns: - The method returns an instance of the class with updated attributes: - - self.msg: A message describing the validation result. - - self.status: The status of the validation (either 'success' or 'failed'). - - self.validated_config: If successful, a validated version of the 'config' parameter. - Example: - To use this method, create an instance of the class and call 'validate_input' on it. - If the validation succeeds, 'self.status' will be 'success' and 'self.validated_config' - will contain the validated configuration. If it fails, 'self.status' will be 'failed', and - 'self.msg' will describe the validation issues. - """ - - temp_spec = { - 'fabric_sites': { - 'type': 'list', - 'elements': 'dict', - 'site_name': {'type': 'str'}, - 'site_type': {'type': 'str', 'default': 'fabric_site'}, - 'authentication_profile': {'type': 'str'}, - 'is_pub_sub_enabled': {'type': 'bool', 'default': False}, - 'update_authentication_profile': { - 'elements': 'dict', - 'site_name_hierarchy': {'type': 'str'}, - 'authentication_profile': {'type': 'str'}, - 'authentication_order': {'type': 'str'}, - 'dot1x_fallback_timeout': {'type': 'int'}, - 'wake_on_lan': {'type': 'bool'}, - 'number_of_hosts': {'type': 'str'}, - 'enable_bpu_guard': {'type': 'bool'} - } - }, - } - - # Validate device params - valid_temp, invalid_params = validate_list_of_dicts( - self.config, temp_spec - ) - - if invalid_params: - self.msg = "The playbook contains invalid parameters: {0}".format(invalid_params) - self.log(self.msg, "ERROR") - self.status = "failed" - return self - - self.validated_config = valid_temp - self.msg = "Successfully validated playbook configuration parameters using 'validate_input': {0}".format(str(valid_temp)) - self.log(self.msg, "INFO") - self.status = "success" - - return self - - def get_site_id(self, site_name): - """ - Retrieves the site IDs for a given site name from the Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site_name (str): The complete name of site for which the site ID need to be retrieved. - Returns: - str: A site ID corresponding to the provided site name. - Description: - This function invokes an API to fetch the details of each site from the Cisco Catalyst Center. If the - site is found, its site ID is extracted and added to the list of site IDs. - The function logs messages for successful API responses, missing site, and any errors - encountered during the process. The final site ID is returned. - """ - - try: - response = self.dnac._exec( - family="sites", - function='get_site', - op_modifies=True, - params={"name": site_name}, - ) - self.log("Received API response from 'get_site': {0}".format(str(response)), "DEBUG") - response = response.get('response') - - if not response: - self.status = "failed" - self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) - self.log(self.msg, "ERROR") - self.check_return_status() - site_id = response[0].get("id") - - if not site_id: - self.status = "failed" - self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) - self.log(self.msg, "ERROR") - self.check_return_status() - - except Exception as e: - self.status = "failed" - self.msg = """Error while getting the details of Site with given name '{0}' present in - Cisco Catalyst Center: {1}""".format(site_name, str(e)) - self.log(self.msg, "ERROR") - self.check_return_status() - - return site_id - - def get_fabric_site_detail(self, site_name, site_id): - """ - Retrieves the detailed information of a fabric site from the Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site_name (str): The complete name of the site for which the details need to be retrieved. - site_id (str): The unique identifier of the site in the Cisco Catalyst Center. - Returns: - dict or None: A dictionary containing the details of the fabric site if found. - Returns None if the site is not a fabric site or if an error occurs. - Description: - This function invokes an API to fetch the details of a specified fabric site from the Cisco Catalyst Center - based on the provided site ID. It logs the API response, checks if the response contains data, - and extracts the first site's details. If the site is not found or is not a fabric site, it returns None. - In case of any exceptions during the API call, it logs an error message, sets the status to "failed", - and invokes the check_return_status method to handle the failure. - """ - - try: - response = self.dnac._exec( - family="sda", - function='get_fabric_sites', - op_modifies=True, - params={"site_id": site_id}, - ) - response = response.get("response") - self.log("Received API response from 'get_fabric_sites' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") - - if not response: - self.log("Given site '{0}' is not a fabric site in Cisco Catalyst Center.".format(site_name), "INFO") - return None - site_detail = response[0] - return site_detail - except Exception as e: - self.status = "failed" - self.msg = """Error while getting the details of Site with given name '{0}' present in - Cisco Catalyst Center: {1}""".format(site_name, str(e)) - self.log(self.msg, "ERROR") - self.check_return_status() - - return None - - def get_fabric_zone_detail(self, site_name, site_id): - """ - Retrieves the detailed information of a fabric zone from the Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site_name (str): The complete name of the site for which the fabric zone details need to be retrieved. - site_id (str): The unique identifier of the site in the Cisco Catalyst Center. - Returns: - dict or None: A dictionary containing the details of the fabric zone if found, - or None if the site is not a fabric zone or an error occurs. - Description: - This function sends an API request to the Cisco Catalyst Center to fetch the details of a fabric zone - specified by the given site ID. It logs the API response and checks if the response contains data. - If the site is not recognized as a fabric zone, the function returns None. - In the event of an exception, an error message is logged, the status is set to "failed", - and the check_return_status method is called to handle the failure. - """ - - try: - response = self.dnac._exec( - family="sda", - function='get_fabric_zones', - op_modifies=True, - params={"site_id": site_id}, - ) - response = response.get("response") - self.log("Received API response from 'get_fabric_zones' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") - - if not response: - self.log("Given site '{0}' is not a fabric zone in Cisco Catalyst Center.".format(site_name), "INFO") - return None - - site_detail = response[0] - return site_detail - - except Exception as e: - self.status = "failed" - self.msg = """Error while getting the details of fabric zone '{0}' present in - Cisco Catalyst Center: {1}""".format(site_name, str(e)) - self.log(self.msg, "ERROR") - self.check_return_status() - - return None - - def get_have(self, config): - """ - Retrieves the current state of fabric sites and zones from the Cisco Catalyst Center based on the given configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): A configuration dictionary containing details about the fabric sites and zones. - The key "fabric_sites" should contain a list of dictionaries. - Returns: - self (object): The instance of the class with the updated `have` attribute containing the current state - of fabric sites and zones. - Description: - This function processes the provided configuration to determine the current state of fabric sites - and zones in the Cisco Catalyst Center. It iterates over the "fabric_sites" list in the configuration, - extracting the site name and type. For each site, it retrieves the corresponding site or zone ID - and details using the `get_site_id`, `get_fabric_site_detail`, and `get_fabric_zone_detail` methods. - The `have` attribute of the instance is updated with this dictionary, representing the current state - of the system. The function logs the final state and returns the instance for further use. - """ - - have = {} - - if config.get("fabric_sites"): - fabric_sites = config.get("fabric_sites") - fabric_sites_ids, fabric_zone_ids = [], [] - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - - if site_type == "fabric_site": - site_detail = self.get_fabric_site_detail(site_name, site_id) - if site_detail: - fabric_id = site_detail.get("siteId") - fabric_sites_ids.append(fabric_id) - else: - zone_detail = self.get_fabric_zone_detail(site_name, site_id) - if zone_detail: - fabric_id = zone_detail.get("siteId") - fabric_zone_ids.append(fabric_id) - - if fabric_sites_ids: - self.log("Fabric site with site ids present in Cisco Catalyst Center are : {0}".format(fabric_sites_ids), "DEBUG") - have["fabric_sites_ids"] = fabric_sites_ids - else: - have["fabric_sites_ids"] = [] - - if fabric_zone_ids: - self.log("Fabric zone with site ids present in Cisco Catalyst Center are : {0}".format(fabric_zone_ids), "DEBUG") - have["fabric_zone_ids"] = fabric_zone_ids - else: - have["fabric_zone_ids"] = [] - - self.have = have - self.log("Current State (have): {0}".format(str(have)), "INFO") - - return self - - def get_want(self, config): - """ - Collects and validates the desired state configuration for fabric sites and zones from the given playbook configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): A dictionary containing the configuration for the desired state of fabric sites and zones. - It should include a key "fabric_sites" with a list of dictionaries. - Returns: - self (object): The instance of the class with the updated `want` attribute containing the validated desired state - of fabric sites and zones and updating authentication profile template. - Description: - This function processes the provided playbook configuration to determine the desired state of fabric sites - and zones in the Cisco Catalyst Center. - The validated site information is stored in the `want` dictionary under the key "fabric_sites". - The `want` attribute of the instance is updated with this dictionary, representing the desired state - of the system. The function returns the instance for further processing or method chaining. - """ - - want = {} - - if config.get("fabric_sites"): - site_detail = config.get("fabric_sites") - fabric_site_info = [] - - for site in site_detail: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - - if not site_name: - self.status = "failed" - self.msg = ( - "Required parameter 'site_name' must be given in the playbook in order to perform any fabric site/zone " - "operation including creation/updation/deletion in Cisco Catalyst Center." - ) - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - return self - - if site_type not in ["fabric_site", "fabric_zone"]: - self.status = "failed" - self.msg = ( - "Invalid fabric site_type '{0}' given in the playbook. Please select one of the type 'fabric_site/fabric_zone' " - "for the creation/updation of fabric site/zone in Cisco Catalyst Center." - ).format(site_type) - self.log(self.msg, "ERROR") - return self - fabric_site_info.append(site) - - want["fabric_sites"] = fabric_site_info - - self.want = want - self.msg = "Successfully collected all parameters from the playbook for creating/updating the fabric sites/zones." - self.status = "success" - self.log("Desired State (want): {0}".format(str(self.want)), "INFO") - - return self - - def create_fabric_site(self, site): - """ - Creates a fabric site in the Cisco Catalyst Center using the provided site configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site (dict): A dictionary containing the details of the fabric site to be created. - Returns: - self (object): The instance of the class with updated status and result attributes reflecting the outcome - of the fabric site creation operation. - Description: - This function creates a fabric site in the Cisco Catalyst Center based on the configuration provided - in the `site` dictionary. - The function constructs the payload for the API request, which includes the site ID, authentication profile, - and an optional flag for PubSub enablement. The payload is then sent to the `add_fabric_site` API endpoint. - After the API call, the function monitors the status of the task using the `get_task_details` method. - If the task encounters an error, the function logs the error and sets the status to "failed". If the task completes - successfully and contains the necessary data, the status is set to "success", and the site is marked as created. - """ - - try: - fabric_site_payload = [] - site_name = site.get("site_name") - auth_profile = site.get("authentication_profile") - if not auth_profile: - self.status = "failed" - self.msg = ( - "Required parameter 'authentication_profile'is missing needed for creation of fabric sites in Cisco Catalyst Center. " - "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" - ", 'No Authentication', 'Open Authentication'] in the playbook." - ) - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - return self - - site_payload = { - "siteId": self.get_site_id(site_name), - "authenticationProfileName": site.get("authentication_profile"), - "isPubSubEnabled": site.get("is_pub_sub_enabled", False) - } - fabric_site_payload.append(site_payload) - self.log("Requested payload for creating fabric site '{0}' is: {1}".format(site_name, str(site_payload)), "INFO") - - response = self.dnac._exec( - family="sda", - function='add_fabric_site', - op_modifies=True, - params={'payload': fabric_site_payload} - ) - self.log("Received API response from 'add_fabric_site' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the creation of fabric site as the 'add_fabric_site' response is empty." - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to create the Fabric site '{0}' because of {1}.".format(site_name, failure_reason) - else: - self.msg = "Unable to create the Fabric site '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - self.create_site.append(site_name) - self.log("Fabric site '{0}' created successfully in the Cisco Catalyst Center".format(site_name), "INFO") - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "An exception occured while creating the fabric site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def fabric_site_needs_update(self, site, site_in_ccc): - """ - Determines if a fabric site in Cisco Catalyst Center needs to be updated based on the provided configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site (dict): A dictionary containing the desired configuration of the fabric site. - site_in_ccc (dict): A dictionary containing the current configuration of the fabric site as - present in the Cisco Catalyst Center. - Returns: - bool: True if the fabric site requires an update, False otherwise. - Description: - This function compares the desired configuration (`site`) of a fabric site with its current - configuration (`site_in_ccc`) in the Cisco Catalyst Center. - The function returns True, indicating that the fabric site needs to be updated. Otherwise, it returns False, - indicating no update is needed. - """ - - if site.get("authentication_profile") and site.get("authentication_profile") != site_in_ccc.get("authenticationProfileName"): - return True - - if site.get("is_pub_sub_enabled") is not None and site.get("is_pub_sub_enabled") != site_in_ccc.get("isPubSubEnabled"): - return True - - def update_fabric_site(self, site, site_in_ccc): - """ - Updates a fabric site in the Cisco Catalyst Center based on the provided configuration and current state. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - site (dict): A dictionary containing the desired configuration for the fabric site. - site_in_ccc (dict): A dictionary containing the current configuration of the fabric site - in the Cisco Catalyst Center. - Returns: - self (object): The instance of the class with updated status and result attributes reflecting the outcome - of the fabric site update operation. - Description: - This method updates a fabric site in the Cisco Catalyst Center. The constructed payload includes the site ID, - authentication profile name, and PubSub enablement status and payload is sent to the `update_fabric_site` - API endpoint. - After initiating the update, the method tracks the status of the update task using `get_task_details`. - It checks for task errors or successful completion, updating the status and logging messages accordingly. - If the task fails, an appropriate error message is logged, and the status is set to "failed". - """ - - try: - update_site_params = [] - site_name = site.get("site_name") - - if site.get("is_pub_sub_enabled") is None: - pub_sub_enable = site_in_ccc.get("isPubSubEnabled") - else: - pub_sub_enable = site.get("is_pub_sub_enabled") - - site_payload = { - "id": site_in_ccc.get("id"), - "siteId": site_in_ccc.get("siteId"), - "authenticationProfileName": site.get("authentication_profile") or site_in_ccc.get("authenticationProfileName"), - "isPubSubEnabled": pub_sub_enable - } - update_site_params.append(site_payload) - self.log("Requested payload for updating fabric site '{0}' is: {1}".format(site_name, str(site_payload)), "INFO") - - response = self.dnac._exec( - family="sda", - function='update_fabric_site', - op_modifies=True, - params={'payload': update_site_params} - ) - self.log("Received API response from 'update_fabric_site' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the updation of fabric site as the 'update_fabric_site' response is empty." - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to update the Fabric site '{0}' because of {1}.".format(site_name, failure_reason) - else: - self.msg = "Unable to update the Fabric site '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - self.update_site.append(site_name) - self.log("Fabric site '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "An exception occured while updating the fabric site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def create_fabric_zone(self, zone): - """ - Creates a fabric zone in the Cisco Catalyst Center based on the provided configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - zone (dict): A dictionary containing the desired configuration for the fabric zone. - Returns: - self (object): The instance of the class with updated status and result attributes reflecting the outcome - of the fabric zone creation operation. - Description: - This method creates a fabric zone in the Cisco Catalyst Center and payload is sent to the `add_fabric_zone` - API endpoint. The method logs the requested payload and the API response. - After initiating the creation, the method monitors the task's status using `get_task_details`. - It checks for task errors or successful completion. If the task fails, an appropriate error message - is logged, and the status is set to "failed". If the task succeeds, the status is set to "success", - and the site name is added to the list of successfully created zones. - The function returns the class instance (`self`) with the updated attributes. - """ - - try: - fabric_zone_payload = [] - site_name = zone.get("site_name") - - zone_payload = { - "siteId": self.get_site_id(site_name), - "authenticationProfileName": zone.get("authentication_profile"), - } - fabric_zone_payload.append(zone_payload) - self.log("Requested payload for creating fabric zone '{0}' is: {1}".format(site_name, zone_payload), "INFO") - - response = self.dnac._exec( - family="sda", - function='add_fabric_zone', - op_modifies=True, - params={'payload': fabric_zone_payload} - ) - self.log("Received API response from 'add_fabric_zone' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the creation of fabric zone as the 'add_fabric_zone' response is empty." - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to create the Fabric zone '{0}' because of {1}.".format(site_name, failure_reason) - else: - self.msg = "Unable to create the Fabric zone '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - self.create_zone.append(site_name) - self.log("Fabric zone '{0}' created successfully in the Cisco Catalyst Center.".format(site_name), "INFO") - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "An exception occured while creating the fabric zone '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def update_fabric_zone(self, zone, zone_in_ccc): - """ - Updates an existing fabric zone in the Cisco Catalyst Center with the provided configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - zone (dict): A dictionary containing the desired updates for the fabric zone. - zone_in_ccc (dict): A dictionary containing the current configuration of the fabric zone - in the Cisco Catalyst Center. - Returns: - self (object): The instance of the class with updated status and result attributes reflecting the outcome - of the fabric zone update operation. - Description: - This method updates the configuration of a fabric zone in the Cisco Catalyst Center. - The constructed payload is sent to the `update_fabric_zone` API endpoint. The method logs the - requested payload and the API response. - After initiating the update, the method monitors the task's status using `get_task_details`. It checks - for task errors or successful completion. - The function returns the class instance (`self`) with the updated attributes. - """ - - try: - update_zone_params = [] - site_name = zone.get("site_name") - - zone_payload = { - "id": zone_in_ccc.get("id"), - "siteId": zone_in_ccc.get("siteId"), - "authenticationProfileName": zone.get("authentication_profile") or zone_in_ccc.get("authenticationProfileName") - } - update_zone_params.append(zone_payload) - self.log("Requested payload for updating fabric zone '{0}' is: {1}".format(site_name, zone_payload), "INFO") - - response = self.dnac._exec( - family="sda", - function='update_fabric_zone', - op_modifies=True, - params={'payload': update_zone_params} - ) - self.log("Received API response from 'update_fabric_zone' for the site {0}: {1}".format(site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the updation of fabric zone as the 'update_fabric_zone' response is empty." - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to update the Fabric zone '{0}' because of {1}.".format(site_name, failure_reason) - else: - self.msg = "Unable to update the Fabric zone '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - self.log("Fabric zone '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") - self.update_zone.append(site_name) - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "An exception occured while updating the fabric zone '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def validate_auth_profile_parameters(self, auth_profile_dict): - """ - Validates the parameters provided for updating the authentication profile template. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - auth_profile_dict (dict): A dictionary containing the parameters for the authentication profile. - Returns: - self (objetc): The instance of the class with updated status and result attributes if invalid parameters are found. - Description: - This method checks the validity of the provided parameters for the authentication profile template. It validates - the "authentication_order" to ensure it is either "dot1x" or "mac". For "dot1x_fallback_timeout", it ensures the - value is an integer within the range of 3 to 120. The "number_of_hosts" must be either "Single" or "Unlimited". - If any invalid parameters are found, they are added to the `invalid_auth_profile_list`. Corresponding error messages - are logged, and the status is set to "failed". The method also logs warnings for any exceptions encountered during - the validation process. - """ - - invalid_auth_profile_list = [] - auth_order = auth_profile_dict.get("authentication_order") - fall_timeout = auth_profile_dict.get("dot1x_fallback_timeout") - number_of_hosts = auth_profile_dict.get("number_of_hosts") - - if auth_order and auth_order not in ["dot1x", "mac"]: - invalid_auth_profile_list.append("authentication_order") - msg = ( - "Invalid authentication_order '{0}'given in the playbook for the updation of authentication profile template. " - "Please provide one of the following authentication_order ['dot1x', 'mac'] in the playbook." - ).format(auth_order) - self.log(msg, "ERROR") - - if fall_timeout: - try: - timeout = int(fall_timeout) - if timeout not in range(3, 121): - invalid_auth_profile_list.append("dot1x_fallback_timeout") - msg = ( - "Invalid 'dot1x_fallback_timeout' '{0}' given in the playbook. Please select the timeout within the range " - " of numbers (3, 120) and provide it in the playbook." - ).format(timeout) - self.log(msg, "ERROR") - except Exception as e: - invalid_auth_profile_list.append("dot1x_fallback_timeout") - msg = ( - "Invalid 'dot1x_fallback_timeout' '{0}' string given in the playbook, unable to convert it into the integer. " - "Please select the timeout within the range of numbers (3, 120) and provide it in the playbook." - ).format(fall_timeout) - self.log(msg, "WARNING") - - if number_of_hosts and number_of_hosts.title() not in ["Single", "Unlimited"]: - invalid_auth_profile_list.append("number_of_hosts") - msg = ( - "Invalid number_of_hosts '{0}'given in the playbook for the updation of authentication profile template. " - "Please provide one of the following number_of_hosts ['Single', 'Unlimited'] in the playbook." - ).format(auth_order) - self.log(msg, "ERROR") - - if invalid_auth_profile_list: - self.status = "failed" - self.msg = ( - "Following invalid input parameter '{0}' given in the playbook due to this unable to " - "perform the action for updating the authentication profile template based on user input." - ).format(invalid_auth_profile_list) - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - - return self - - def get_authentication_profile(self, fabric_id, auth_profile, site_name): - """ - Retrieves the details of an authentication profile for a given fabric and site from the Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - fabric_id (str): The ID of the fabric to which the authentication profile belongs. - auth_profile (str): The name of the authentication profile to retrieve. - site_name (str): The name of the site associated with the authentication profile. - Returns: - dict or None: A dictionary containing the details of the authentication profile if found, or None if no profile is associated - with the site or if an error occurs. - Description: - This method sends a request to the Cisco Catalyst Center to fetch the authentication profile details based on the provided - `fabric_id` and `auth_profile` name. The `site_name` is used for logging purposes to provide context in the logs. - If the response contains authentication profile details, these details are returned. If no profile is found or if an error - occurs during the request, the method logs an appropriate message and returns `None`. - """ - - try: - response = self.dnac._exec( - family="sda", - function='get_authentication_profiles', - op_modifies=True, - params={ - "fabric_id": fabric_id, - "authentication_profile_name": auth_profile - } - ) - response = response.get("response") - self.log("Received API response from 'get_authentication_profiles' for the site '{0}': {1}".format(site_name, str(response)), "DEBUG") - - if not response: - self.log("No Authentication profile asssociated to this site '{0}' in Cisco Catalyst Center.".format(site_name), "INFO") - return None - - profile_details = response[0] - return profile_details - except Exception as e: - self.status = "failed" - self.msg = ( - "Error while getting the details of authentication profiles for the site '{0}' present in " - "Cisco Catalyst Center: {1}" - ).format(site_name, str(e)) - self.log(self.msg, "ERROR") - self.check_return_status() - - return None - - def auth_profile_needs_update(self, auth_profile_dict, auth_profile_in_ccc): - """ - Determines if the authentication profile requires an update by comparing it with the existing profile in Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - auth_profile_dict (dict): A dictionary containing the desired authentication profile settings to compare. - auth_profile_in_ccc (dict): A dictionary containing the current authentication profile settings from Cisco Catalyst Center. - Returns: - bool: Returns `True` if any of the settings in `auth_profile_dict` differ from those in `auth_profile_in_ccc` and an update - is needed. Returns `False` if the settings match and no update is required. - Description: - This method compares the provided authentication profile settings (`auth_profile_dict`) with the current settings retrieved from - the Cisco Catalyst Center (`auth_profile_in_ccc`). It considers the possibility of an additional setting "enable_bpu_guard" if - the current profile is "Closed Authentication". - It iterates through a mapping of profile settings and checks if any of the settings require an update. If any discrepancies are - found, the method returns `True`. If all settings match, it returns `False`. - """ - - profile_key_mapping = { - "authentication_order": "authenticationOrder", - "dot1x_fallback_timeout": "dot1xToMabFallbackTimeout", - "wake_on_lan": "wakeOnLan", - "number_of_hosts": "numberOfHosts" - } - if auth_profile_in_ccc.get("authenticationProfileName") == "Closed Authentication": - profile_key_mapping["enable_bpu_guard"] = "isBpduGuardEnabled" - - for key, ccc_key in profile_key_mapping.items(): - if auth_profile_dict.get(key) is None: - continue - if key == "dot1x_fallback_timeout": - if int(auth_profile_dict.get(key)) != int(auth_profile_in_ccc.get(ccc_key)): - return True - elif auth_profile_dict.get(key) != auth_profile_in_ccc.get(ccc_key): - return True - - return False - - def collect_authentication_params(self, auth_profile_dict, auth_profile_in_ccc): - """ - Collects and prepares the updated authentication profile parameters based on the provided dictionary and the current profile in - Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - auth_profile_dict (dict): A dictionary containing the desired authentication profile settings. - auth_profile_in_ccc (dict): A dictionary containing the current authentication profile settings from Cisco Catalyst Center. - Returns: - list: A list containing a single dictionary with the updated authentication profile parameters. - Description: - This method prepares the updated parameters for an authentication profile by combining desired settings from `auth_profile_dict` with - the current settings from `auth_profile_in_ccc`. - It creates a dictionary with the ID, fabric ID, profile name, and updated settings for authentication order, dot1x fallback timeout, - number of hosts, and Wake-on-LAN. If the profile is "Closed Authentication," it also includes the BPDU guard setting. - The method returns a list containing the updated parameters in a dictionary, which can be used for further processing or API requests. - """ - - updated_params = [] - profile_name = auth_profile_in_ccc.get("authenticationProfileName") - authentications_params_dict = { - "id": auth_profile_in_ccc.get("id"), - "fabricId": auth_profile_in_ccc.get("fabricId"), - "authenticationProfileName": profile_name, - "authenticationOrder": auth_profile_dict.get("authentication_order") or auth_profile_in_ccc.get("authenticationOrder"), - "dot1xToMabFallbackTimeout": int(auth_profile_dict.get("dot1x_fallback_timeout")) or auth_profile_in_ccc.get("dot1xToMabFallbackTimeout"), - "numberOfHosts": auth_profile_dict.get("number_of_hosts") or auth_profile_in_ccc.get("numberOfHosts"), - } - - if auth_profile_dict.get("wake_on_lan") is None: - authentications_params_dict["wakeOnLan"] = auth_profile_in_ccc.get("wakeOnLan") - else: - authentications_params_dict["wakeOnLan"] = auth_profile_dict.get("wake_on_lan") - - if profile_name == "Closed Authentication": - if auth_profile_dict.get("enable_bpu_guard") is None: - auth_profile_dict["isBpduGuardEnabled"] = auth_profile_in_ccc.get("isBpduGuardEnabled", True) - else: - auth_profile_dict["isBpduGuardEnabled"] = auth_profile_dict.get("enable_bpu_guard") - - updated_params.append(authentications_params_dict) - - return updated_params - - def update_authentication_profile_template(self, profile_update_params, site_name): - """ - Updates the authentication profile template for a specified site in Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - profile_update_params (dict): A dictionary containing the parameters to update the authentication profile. - site_name (str): The name of the site where the authentication profile is being updated. - Returns: - self (object): Returns the current instance of the class with updated status and message attributes. - Description: - This method sends a request to update the authentication profile template for the specified site using the - provided parameters. It first logs the requested payload and sends it to the API for processing. - It then monitors the task status by polling until the update is complete. If the update is successful, - it logs a success message and appends the site name to the list of updated profiles. If an error occurs or - the task fails, it logs an error message and updates the status to "failed". - """ - - try: - self.log("Requested payload for updating authentication profile for site {0}: {1}".format(site_name, profile_update_params), "INFO") - response = self.dnac._exec( - family="sda", - function='update_authentication_profile', - op_modifies=True, - params={'payload': profile_update_params} - ) - self.log("Received API response from 'update_authentication_profile'for site {0}: {1}".format(site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the updation of authentication profile for site '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to update the authentication profile for site '{0}' because of {1}.".format(site_name, failure_reason) - else: - self.msg = "Unable to update the authentication profile for site '{0}'.".format(site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - self.update_auth_profile.append(site_name) - self.log("Authentication profile for the site '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "An exception occured while updating the authentication profile for site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def delete_fabric_site_zone(self, fabric_id, site_name, site_type): - """ - Deletes a fabric site or fabric zone from Cisco Catalyst Center. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - fabric_id (str): The ID of the fabric site or fabric zone to be deleted. - site_name (str): The name of the fabric site or fabric zone to be deleted. - site_type (str): The type of the entity to be deleted. Should be either "fabric_site" or "fabric_zone". - Returns: - self (object): Returns the current instance of the class with updated status and message attributes. - Description: - This method sends a request to delete a fabric site or fabric zone based on the provided `fabric_id` and `site_type`. - It determines the appropriate API function to call based on the `site_type`, either "delete_fabric_site_by_id" or - "delete_fabric_zone_by_id". It returns the class instance for further processing or chaining. - """ - - try: - if site_type == "fabric_site": - api_name = "delete_fabric_site_by_id" - type_name = "fabric site" - else: - api_name = "delete_fabric_zone_by_id" - type_name = "fabric zone" - - response = self.dnac._exec( - family="sda", - function=api_name, - op_modifies=True, - params={"id": fabric_id}, - ) - self.log("Received API response from '{0}' for the site {1}: {2}".format(api_name, site_name, str(response)), "DEBUG") - response = response.get("response") - - if not response: - self.status = "failed" - self.msg = "Unable to fetch the task Id for the deletion of {0}: '{1}'.".format(type_name, site_name) - self.log(self.msg, "ERROR") - return self - - task_id = response.get("taskId") - - while True: - task_details = self.get_task_details(task_id) - - if task_details.get("isError"): - self.status = "failed" - failure_reason = task_details.get("failureReason") - if failure_reason: - self.msg = "Unable to delete {0} '{1}' because of {2}.".format(type_name, site_name, failure_reason) - else: - self.msg = "Unable to delete {0} '{1}'.".format(type_name, site_name) - self.log(self.msg, "ERROR") - self.result['response'] = self.msg - break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): - self.status = "success" - if site_type == "fabric_site": - self.delete_site.append(site_name) - else: - self.delete_zone.append(site_name) - self.log("{0} '{1}' deleted successfully from the Cisco Catalyst Center".format(type_name.title(), site_name), "INFO") - break - time.sleep(1) - except Exception as e: - self.status = "failed" - self.msg = "Exception occurred while deleting {0} '{1}' due to: {2}".format(type_name, site_name, str(e)) - self.log(self.msg, "ERROR") - - return self - - def update_site_zones_profile_messages(self): - """ - Updates and logs messages based on the status of fabric sites, fabric zones, and authentication profile templates. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - Returns: - self (object): Returns the current instance of the class with updated `result` and `msg` attributes. - Description: - This method aggregates status messages related to the creation, update, or deletion of fabric sites, fabric zones, - and authentication profile templates. - It checks various instance variables (`create_site`, `update_site`, `no_update_site`, `create_zone`, `update_zone`, - `no_update_zone`, `update_auth_profile`, `no_update_profile`, `delete_site`, `absent_site`, `delete_zone`, `absent_zone`) - to determine the status and generates corresponding messages. - The method also updates the `result["response"]` attribute with the concatenated status messages. - """ - - self.result["changed"] = False - result_msg_list = [] - - if self.create_site: - create_site_msg = "Fabric site(s) '{0}' created successfully in Cisco Catalyst Center.".format(self.create_site) - result_msg_list.append(create_site_msg) - - if self.update_site: - update_site_msg = "Fabric site(s) '{0}' updated successfully in Cisco Catalyst Center.".format(self.update_site) - result_msg_list.append(update_site_msg) - - if self.no_update_site: - no_update_site_msg = "Fabric site(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_site) - result_msg_list.append(no_update_site_msg) - - if self.create_zone: - create_zone_msg = "Fabric zone(s) '{0}' created successfully in Cisco Catalyst Center.".format(self.create_zone) - result_msg_list.append(create_zone_msg) - - if self.update_zone: - update_zone_msg = "Fabric zone(s) '{0}' updated successfully in Cisco Catalyst Center.".format(self.update_zone) - result_msg_list.append(update_zone_msg) - - if self.no_update_zone: - no_update_zone_msg = "Fabric zone(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_zone) - result_msg_list.append(no_update_zone_msg) - - if self.update_auth_profile: - update_auth_msg = """Authentication profile template for site(s) '{0}' updated successfully in Cisco Catalyst - Center.""".format(self.update_auth_profile) - result_msg_list.append(update_auth_msg) - - if self.no_update_profile: - no_update_auth_msg = "Authentication profile template for site(s) '{0}' need no update in Cisco Catalyst Center.".format(self.no_update_profile) - result_msg_list.append(no_update_auth_msg) - - if self.delete_site: - delete_site_msg = "Fabric site(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format(self.delete_site) - result_msg_list.append(delete_site_msg) - - if self.absent_site: - absent_site_msg = "Unable to delete fabric site(s) '{0}' as they are not present in Cisco Catalyst Center.".format(self.absent_site) - result_msg_list.append(absent_site_msg) - - if self.delete_zone: - delete_zone_msg = "Fabric zone(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format(self.delete_zone) - result_msg_list.append(delete_zone_msg) - - if self.absent_zone: - absent_zone_msg = "Unable to delete fabric zone(s) '{0}' as they are not present in Cisco Catalyst Center.".format(self.absent_zone) - result_msg_list.append(absent_zone_msg) - - if self.create_site or self.update_site or self.create_zone or self.update_zone or self.delete_site or self.update_auth_profile: - self.result["changed"] = True - - self.msg = " ".join(result_msg_list) - self.log(self.msg, "INFO") - self.result["response"] = self.msg - - return self - - def get_diff_merged(self, config): - """ - Creates, updates, or deletes fabric sites and zones based on the provided configuration, and manages - authentication profile updates. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): A dictionary containing the configuration for fabric sites and zones and updating - authentication profile template. - Returns: - self (object): Returns the current instance of the class with updated attributes based on the operations performed. - Description: - This method processes the provided configuration to manage fabric sites and zones in Cisco Catalyst Center. - 1. Fabric Sites - - If 'fabric_sites' is present in the configuration, it iterates over the list of sites. - - Checks if the site needs to be created or updated based on its type ("fabric_site" or "fabric_zone"). - - Creates or updates the site as necessary. If the site does not need any updates, it logs this information. - 2. Authentication Profile - - If an `update_authentication_profile` parameter is provided, it validates and updates the authentication - profile template associated with the site. - - Ensures that the authentication profile is valid and performs updates if needed. - - If no update is necessary or if the profile is not present, it logs the appropriate messages. - """ - - # Create/Update Fabric sites/zones in Cisco Catalyst Center - if config.get('fabric_sites'): - fabric_sites = self.want.get('fabric_sites') - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - auth_profile = site.get("authentication_profile") - - if auth_profile and auth_profile not in ["Closed Authentication", "Low Impact", "No Authentication", "Open Authentication"]: - self.status = "failed" - self.msg = ( - "Invalid authentication_profile '{0}'given in the playbook for the creation of fabric site. " - "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" - ", 'No Authentication', 'Open Authentication'] in the playbook." - ).format(auth_profile) - - if site_type == "fabric_site": - # Check whether site is already fabric or not. - if site_id not in self.have.get("fabric_sites_ids"): - # Create the fabric site in Cisco Catalyst Center - self.create_fabric_site(site).check_return_status() - else: - # Check whether fabric site needs any update or not - site_in_ccc = self.get_fabric_site_detail(site_name, site_id) - require_update = self.fabric_site_needs_update(site, site_in_ccc) - if require_update: - self.update_fabric_site(site, site_in_ccc).check_return_status() - else: - self.status = "success" - self.no_update_site.append(site_name) - self.log("Fabric site '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") - else: - # Check whether site zone is already fabric or not. - if site_id not in self.have.get("fabric_zone_ids"): - # Create the fabric site in Cisco Catalyst Center - self.create_fabric_zone(site).check_return_status() - else: - # Check whether fabric site needs any update or not - zone_in_ccc = self.get_fabric_zone_detail(site_name, site_id) - if auth_profile and auth_profile != zone_in_ccc.get("authenticationProfileName"): - self.update_fabric_zone(site, zone_in_ccc).check_return_status() - else: - self.status = "success" - self.no_update_zone.append(site_name) - self.log("Fabric zone '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") - - # Updating/customising the default parameters for authentication profile template - if site.get("update_authentication_profile"): - if not auth_profile: - self.status = "failed" - self.msg = ( - "Required parameter 'authentication_profile' is missing needed for updation of Authentication Profile template. " - "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" - ", 'Open Authentication'] in the playbook." - ) - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - return self - - if auth_profile == "No Authentication": - self.status = "success" - msg = ( - "Unable to update 'authentication_profile' for the site '{0}' as for the profile template 'No Authentication' updating " - "authentication_profile is not supported. Please provide one of the following authentication_profile ['Closed Authentication'" - ", 'Low Impact', 'Open Authentication'] in the playbook." - ).format(site_name) - self.log(msg, "INFO") - self.no_update_profile.append(site_name) - return self - - # With the given site id collect the fabric site/zone id - if site_type == "fabric_site": - site_detail = self.get_fabric_site_detail(site_name, site_id) - fabric_id = site_detail.get("id") - else: - zone_detail = self.get_fabric_zone_detail(site_name, site_id) - fabric_id = zone_detail.get("id") - - # Validate the playbook input parameter for updating the authentication profile - auth_profile_dict = site.get("update_authentication_profile") - self.validate_auth_profile_parameters(auth_profile_dict).check_return_status() - validate_msg = ( - "All the given parameter(s) '{0}' in the playbook for the updation of authentication " - " profile in SDA fabric site/zone are validated successfully." - ).format(auth_profile_dict) - self.log(validate_msg, "INFO") - auth_profile_in_ccc = self.get_authentication_profile(fabric_id, auth_profile, site_name) - - if not auth_profile_in_ccc: - self.status = "success" - msg = ( - "There is no authentication template profile associated to the site '{0}' " - "in the Cisco Catalyst Center so unable to update the profile parameters." - ).format(site_name) - self.log(self.msg, "INFO") - self.no_update_profile.append(site_name) - return self - - profile_needs_update = self.auth_profile_needs_update(auth_profile_dict, auth_profile_in_ccc) - if not profile_needs_update: - self.status = "success" - msg = ( - "Authentication profile for the site '{0}' does not need any update in the " - "Cisco Catalyst Center." - ).format(site_name) - self.log(msg, "INFO") - self.no_update_profile.append(site_name) - return self - - # Collect the authentication profile parameters for the update operation - profile_update_params = self.collect_authentication_params(auth_profile_dict, auth_profile_in_ccc) - self.update_authentication_profile_template(profile_update_params, site_name).check_return_status() - - return self - - def get_diff_deleted(self, config): - """ - Deletes fabric sites and zones from the Cisco Catalyst Center based on the provided configuration. - Args: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): A dictionary containing the configuration for fabric sites and zones. It may include: - - 'fabric_sites' - List of dictionaries, where each dictionary represents a fabric site or zone. - - 'site_name' - The name of the site or zone to be deleted. - - 'site_type'- Type of the site or zone, either "fabric_site" or "fabric_zone". Defaults to "fabric_site". - Returns: - self (object): Returns the current instance of the class with updated attributes based on the deletion operations performed. - Description: - This method processes the provided configuration to manage the deletion of fabric sites and zones in Cisco Catalyst Center. - - For Fabric Sites - - Verifies if the site exists in Cisco Catalyst Center. - - Deletes the site if it exists; otherwise, logs a message indicating the site is not present. - - For Fabric Zones - - Verifies if the zone exists in Cisco Catalyst Center. - - Deletes the zone if it exists; otherwise, logs a message indicating the zone is not present. - """ - - # Delete Fabric sites/zones from the Cisco Catalyst Center - if not config.get('fabric_sites'): - self.status = "success" - self.msg = "Unable to delete any fabric site/zone or authentication profile template as input is not given in the playbook." - - fabric_sites = self.want.get('fabric_sites') - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - if not site_name: - self.status = "failed" - self.msg = "Unable to delete fabric site/zone as required parameter 'site_name' is not given in the playbook." - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - return self - - site_id = self.get_site_id(site_name) - - if site_type == "fabric_site": - # Check whether fabric site is present in Cisco Catalyst Center. - if site_id in self.have.get("fabric_sites_ids"): - site_detail = self.get_fabric_site_detail(site_name, site_id) - fabric_id = site_detail.get("id") - # Delete the fabric site from the Cisco Catalyst Center - self.delete_fabric_site_zone(fabric_id, site_name, site_type).check_return_status() - else: - self.status = "success" - self.absent_site.append(site_name) - self.log("Unable to delete fabric site '{0}' as it is not present in the Cisco Catalyst Center.".format(site_name), "INFO") - else: - # Check whether fabric zone is present in Cisco Catalyst Center. - if site_id in self.have.get("fabric_zone_ids"): - site_detail = self.get_fabric_zone_detail(site_name, site_id, ) - fabric_id = site_detail.get("id") - # Delete the fabric zone from the Cisco Catalyst Center - self.delete_fabric_site_zone(fabric_id, site_name, site_type).check_return_status() - else: - self.status = "success" - self.absent_zone.append(site_name) - self.log("Unable to delete fabric zone '{0}' as it is not present in the Cisco Catalyst Center.".format(site_name), "INFO") - - return self - - def verify_diff_merged(self, config): - """ - Verify the addition/update status of fabric site/zones in Cisco Catalyst Center. - Parameters: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): The configuration details to be verified. - Returns: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - Description: - This method verifies whether the specified configurations have been successfully added/updated - in Cisco Catalyst Center as desired. - """ - - self.get_have(config) - self.log("Current State (have): {0}".format(str(self.have)), "INFO") - self.log("Desired State (want): {0}".format(str(self.want)), "INFO") - - if config.get('fabric_sites'): - fabric_sites = self.want.get('fabric_sites') - verify_site_list, verify_auth_list = [], [] - site_name_list, auth_name_list = [], [] - auth_flag = False - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - - if site_type == "fabric_site": - if site_id not in self.have.get("fabric_sites_ids"): - verify_site_list.append(site_name) - else: - site_name_list.append(site_name) - else: - if site_id not in self.have.get("fabric_zone_ids"): - verify_site_list.append(site_name) - else: - site_name_list.append(site_name) - - # Verifying updating/customising the default parameters for authentication profile template - if site.get("update_authentication_profile"): - auth_flag = True - # With the given site id collect the fabric site/zone id - if site_type == "fabric_site": - site_detail = self.get_fabric_site_detail(site_name, site_id) - fabric_id = site_detail.get("id") - auth_name_list.append(site_name) - else: - zone_detail = self.get_fabric_zone_detail(site_name, site_id) - fabric_id = zone_detail.get("id") - auth_name_list.append(site_name) - - if not fabric_id: - verify_auth_list.append(site_name) - - if not verify_site_list: - self.status = "success" - msg = ( - "Requested fabric site(s)/zone(s) '{0}' have been successfully added/updated to the Cisco Catalyst Center " - "and their addition/updation has been verified." - ).format(site_name_list) - self.log(msg, "INFO") - else: - msg = ( - "Playbook's input does not match with Cisco Catalyst Center, indicating that the fabric site(s) '{0}' " - " addition/updation task may not have executed successfully." - ).format(verify_site_list) - self.log(msg, "INFO") - - if not auth_flag: - return self - - if not verify_auth_list: - self.status = "success" - msg = ( - "Authentication template profile for the site(s) '{0}' have been successfully updated to the Cisco Catalyst Center " - "and their updation has been verified." - ).format(auth_name_list) - self.log(msg, "INFO") - else: - msg = ( - "Playbook's input does not match with Cisco Catalyst Center, indicating that the Authentication template " - "profile for the site(s) '{0}' updation task may not have executed successfully." - ).format(verify_auth_list) - self.log(msg, "INFO") - - return self - - def verify_diff_deleted(self, config): - """ - Verify the deletion status of fabric sites/zones fromt the Cisco Catalyst Center. - Parameters: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - config (dict): The configuration details to be verified. - Returns: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - Description: - This method checks the deletion status of a configuration in Cisco Catalyst Center. - It validates whether the specified fabric site/zone deleted from Cisco Catalyst Center. - """ - - self.get_have(config) - self.log("Current State (have): {0}".format(str(self.have)), "INFO") - self.log("Desired State (want): {0}".format(str(self.want)), "INFO") - - fabric_sites = self.want.get('fabric_sites') - verify_site_list, site_name_list = [], [] - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - - if site_type == "fabric_site": - # Check whether fabric site is present in Cisco Catalyst Center. - if site_id in self.have.get("fabric_sites_ids"): - verify_site_list.append(site_name) - else: - site_name_list.append(site_name) - else: - # Check whether fabric zone is present in Cisco Catalyst Center. - if site_id in self.have.get("fabric_zone_ids"): - verify_site_list.append(site_name) - else: - site_name_list.append(site_name) - - if not verify_site_list: - self.status = "success" - msg = ( - "Requested fabric site(s)/zones(s) '{0}' have been successfully deleted from the Cisco Catalyst " - "Center and their deletion has been verified." - ).format(site_name_list) - self.log(msg, "INFO") - else: - msg = ( - "Playbook's input does not match with Cisco Catalyst Center, indicating that fabric site(s)/zones(s)" - " '{0}' deletion task may not have executed successfully." - ).format(verify_site_list) - - return self - - -def main(): - """ main entry point for module execution - """ - - element_spec = {'dnac_host': {'required': True, 'type': 'str'}, - 'dnac_port': {'type': 'str', 'default': '443'}, - 'dnac_username': {'type': 'str', 'default': 'admin', 'aliases': ['user']}, - 'dnac_password': {'type': 'str', 'no_log': True}, - 'dnac_verify': {'type': 'bool', 'default': 'True'}, - 'dnac_version': {'type': 'str', 'default': '2.2.3.3'}, - 'dnac_debug': {'type': 'bool', 'default': False}, - 'dnac_log_level': {'type': 'str', 'default': 'WARNING'}, - "dnac_log_file_path": {"type": 'str', "default": 'dnac.log'}, - "dnac_log_append": {"type": 'bool', "default": True}, - 'dnac_log': {'type': 'bool', 'default': False}, - 'validate_response_schema': {'type': 'bool', 'default': True}, - 'config_verify': {'type': 'bool', "default": False}, - 'dnac_api_task_timeout': {'type': 'int', "default": 1200}, - 'dnac_task_poll_interval': {'type': 'int', "default": 2}, - 'config': {'required': True, 'type': 'list', 'elements': 'dict'}, - 'state': {'default': 'merged', 'choices': ['merged', 'deleted']} - } - - module = AnsibleModule(argument_spec=element_spec, - supports_check_mode=False) - - ccc_fabric_sites = FabricSitesZones(module) - state = ccc_fabric_sites.params.get("state") - - if state not in ccc_fabric_sites.supported_states: - ccc_fabric_sites.status = "invalid" - ccc_fabric_sites.msg = "State {0} is invalid".format(state) - ccc_fabric_sites.check_return_status() - - ccc_fabric_sites.validate_input().check_return_status() - config_verify = ccc_fabric_sites.params.get("config_verify") - - for config in ccc_fabric_sites.validated_config: - ccc_fabric_sites.reset_values() - ccc_fabric_sites.get_want(config).check_return_status() - ccc_fabric_sites.get_have(config).check_return_status() - ccc_fabric_sites.get_diff_state_apply[state](config).check_return_status() - if config_verify: - ccc_fabric_sites.verify_diff_state_apply[state](config).check_return_status() - - # Invoke the API to check the status and log the output of each site/zone and authentication profile update on console. - ccc_fabric_sites.update_site_zones_profile_messages().check_return_status() - - module.exit_json(**ccc_fabric_sites.result) - - -if __name__ == '__main__': - main() From 1f4ed6c17cb211a6f3c08f3043e1dd11b10c1059 Mon Sep 17 00:00:00 2001 From: Abhishek Maheshwari <56202291+Abhishek-121@users.noreply.github.com> Date: Tue, 13 Aug 2024 14:29:44 +0530 Subject: [PATCH 045/121] Delete playbooks/fabric_sites_zones_workflow_manager.yml --- .../fabric_sites_zones_workflow_manager.yml | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 playbooks/fabric_sites_zones_workflow_manager.yml diff --git a/playbooks/fabric_sites_zones_workflow_manager.yml b/playbooks/fabric_sites_zones_workflow_manager.yml deleted file mode 100644 index 9f49706fb9..0000000000 --- a/playbooks/fabric_sites_zones_workflow_manager.yml +++ /dev/null @@ -1,37 +0,0 @@ ---- -- name: Configure fabric site/zones and authentication profile template in Cisco Catalyst Center - hosts: localhost - connection: local - gather_facts: no - vars_files: - - "input_fabric_sites_zones.yml" - - "credentials.yml" - tasks: - - name: Configure fabric sites/zones and authentication profile template in Cisco Catalyst Center. - cisco.dnac.fabric_sites_zones_workflow_manager: - dnac_host: "{{dnac_host}}" - dnac_username: "{{dnac_username}}" - dnac_password: "{{dnac_password}}" - dnac_verify: "{{dnac_verify}}" - dnac_port: "{{dnac_port}}" - dnac_version: "{{dnac_version}}" - dnac_debug: "{{dnac_debug}}" - dnac_log_level: DEBUG - dnac_log: true - config_verify: True - state: merged - config: - - fabric_sites: - - site_name: "{{item.fabric_sites.site_name}}" - site_type: "{{item.fabric_sites.site_type}}" - authentication_profile: "{{item.fabric_sites.authentication_profile}}" - is_pub_sub_enabled: "{{item.fabric_sites.is_pub_sub_enabled}}" - update_authentication_profile: - authentication_order: "{{item.fabric_sites.update_authentication_profile.authentication_order}}" - dot1x_fallback_timeout: "{{item.fabric_sites.update_authentication_profile.dot1x_fallback_timeout}}" - wake_on_lan: "{{item.fabric_sites.update_authentication_profile.wake_on_lan}}" - number_of_hosts: "{{item.fabric_sites.update_authentication_profile.number_of_hosts}}" - - with_items: "{{ fabric_sites}}" - tags: - - fabric_site_zones_testing From 5355a6908b3aca0013fbe58e67ec20f6392e4c4d Mon Sep 17 00:00:00 2001 From: MUTHU-RAKESH-27 <19cs127@psgitech.ac.in> Date: Tue, 13 Aug 2024 15:45:42 +0530 Subject: [PATCH 046/121] Addressed the review comments --- .../network_settings_workflow_manager.py | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 1b346d00be..41784aed2c 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -661,7 +661,7 @@ def __init__(self, module): self.global_pool_obj_params = self.get_obj_params("GlobalPool") self.reserve_pool_obj_params = self.get_obj_params("ReservePool") self.network_obj_params = self.get_obj_params("Network") - self.all_reserved_pool_details = [] + self.all_reserved_pool_details = {} def validate_input(self): """ @@ -1200,6 +1200,7 @@ def get_reserved_ip_subpool(self, site_id): Retrieve all the reserved IP subpool details from the Cisco Catalyst Center. Parameters: + site_id (str) - The Site ID for which reserved pool details are requested. self (object) - The current object details. Returns: @@ -1207,17 +1208,9 @@ def get_reserved_ip_subpool(self, site_id): """ value = 1 + self.all_reserved_pool_details.update({site_id: []}) start_time = time.time() while True: - end_time = time.time() - if (end_time - start_time) >= self.max_timeout: - self.msg = ( - "Max timeout of {0} sec has reached for the API 'get_reserved_ip_subpool' status." - .format(self.max_timeout) - ) - self.status = "failed" - break - response = self.dnac._exec( family="network_settings", function="get_reserve_ip_subpool", @@ -1239,8 +1232,16 @@ def get_reserved_ip_subpool(self, site_id): .format(site_id), "DEBUG") return self - self.all_reserved_pool_details.extend(reserve_pool_details) + self.all_reserved_pool_details.get(site_id).extend(reserve_pool_details) value += 25 + end_time = time.time() + if (end_time - start_time) >= self.max_timeout: + self.msg = ( + "Max timeout of {0} sec has reached for the API 'get_reserved_ip_subpool' status." + .format(self.max_timeout) + ) + self.status = "failed" + break return self @@ -1324,13 +1325,15 @@ def reserve_pool_exists(self, name, site_name): self.status = "failed" return reserve_pool - self.get_reserved_ip_subpool(site_id) - if not self.all_reserved_pool_details: + if not self.all_reserved_pool_details.get(site_id): + self.get_reserved_ip_subpool(site_id) + + if not self.all_reserved_pool_details.get(site_id): self.log("Reserved pool {0} does not exist in the site {1}" .format(name, site_name), "DEBUG") return reserve_pool - reserve_pool_details = get_dict_result(self.all_reserved_pool_details, "groupName", name) + reserve_pool_details = get_dict_result(self.all_reserved_pool_details.get(site_id), "groupName", name) if reserve_pool_details: self.log("Reserve pool found with name '{0}' in the site '{1}': {2}" .format(name, site_name, reserve_pool_details), "INFO") @@ -2562,7 +2565,7 @@ def verify_diff_merged(self, config): self """ - self.all_reserved_pool_details = [] + self.all_reserved_pool_details = {} self.get_have(config) self.log("Current State (have): {0}".format(self.have), "INFO") self.log("Requested State (want): {0}".format(self.want), "INFO") @@ -2645,7 +2648,7 @@ def verify_diff_deleted(self, config): self """ - self.all_reserved_pool_details = [] + self.all_reserved_pool_details = {} self.get_have(config) self.log("Current State (have): {0}".format(self.have), "INFO") self.log("Desired State (want): {0}".format(self.want), "INFO") From 6bbc0c46b28c0f988fb6c676e8ad1e2851aa51a2 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 13 Aug 2024 16:05:30 +0530 Subject: [PATCH 047/121] SWIM UT compleated --- .../dnac/test_swim_workflow_manager.py | 585 +++++++----------- 1 file changed, 240 insertions(+), 345 deletions(-) diff --git a/tests/unit/modules/dnac/test_swim_workflow_manager.py b/tests/unit/modules/dnac/test_swim_workflow_manager.py index b44eda8eef..8dc1a62d1e 100644 --- a/tests/unit/modules/dnac/test_swim_workflow_manager.py +++ b/tests/unit/modules/dnac/test_swim_workflow_manager.py @@ -17,6 +17,7 @@ from ansible_collections.cisco.dnac.plugins.modules import swim_workflow_manager from .dnac_module import TestDnacModule, set_module_args, loadPlaybookData + class TestswimWorkflowManager(TestDnacModule): module = swim_workflow_manager @@ -37,7 +38,6 @@ class TestswimWorkflowManager(TestDnacModule): playbook_device_family_not_found = test_data.get("playbook_device_family_not_found") playbook_import_image_details_not_provided = test_data.get("playbook_import_image_details_not_provided") playbook_verify_merged = test_data.get("playbook_verify_merged") - def setUp(self): super(TestswimWorkflowManager, self).setUp() @@ -108,17 +108,6 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_golden_tag_status_of_an_image_1"), self.test_data.get("untag_image_as_golden_and_load_on_device_responce") ] - elif "playbook_untag_image_as_golden_and_error_load_on_device" in self._testMethodName: - self.run_dnac_exec.side_effect = [ - self.test_data.get("get_2_software_image_details"), - self.test_data.get("get_2_site"), - self.test_data.get("get_2_device_family_identifiers"), - self.test_data.get("get_software_image_details_1"), - self.test_data.get("get_2_golden_tag_status_of_an_image"), - self.test_data.get("remove_golden_tag_for_image"), - self.test_data.get("Task_details_error"), - self.test_data.get("untag_image_as_golden_and_load_on_device_error_responce") - ] elif "playbook_import_image_already_exist" in self._testMethodName: self.run_dnac_exec.side_effect = [ @@ -165,10 +154,10 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_membership_ps"), self.test_data.get("get_device_uuids"), self.test_data.get("get_device_uuids_1"), - self.test_data.get("get_device_list_ps_1"), + self.test_data.get("get_device_list_ps_1"), self.test_data.get("trigger_software_image_distribution_ps"), - self.test_data.get("get_device_list_ps_2"), - self.test_data.get("trigger_software_image_distribution_ps_1"), + self.test_data.get("get_device_list_ps_2"), + self.test_data.get("trigger_software_image_distribution_ps_1"), self.test_data.get("get_device_list_ps_3"), self.test_data.get("trigger_software_image_distribution_ps_2"), self.test_data.get("get_device_list_ps_4"), @@ -194,10 +183,10 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_device_family_identifiers_golden_already_tagged"), self.test_data.get("get_software_image_details_golden_already_tagged_3"), self.test_data.get("get_golden_tag_status_of_an_image_golden_already_tagged"), - self.test_data.get("get_software_image_details_golden_already_tagged_4"), - self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), - self.test_data.get("get_software_image_details_golden_already_tagged_5"), - self.test_data.get("get_software_image_details_golden_already_tagged_6"), + self.test_data.get("get_software_image_details_golden_already_tagged_4"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_5"), + self.test_data.get("get_software_image_details_golden_already_tagged_6"), self.test_data.get("get_golden_tag_status_of_an_image_golden_already_tagged_2"), self.test_data.get("image_golden_already_tagged_response"), ] @@ -209,10 +198,10 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_device_family_identifiers_golden_already_tagged"), self.test_data.get("get_software_image_details_golden_already_tagged_3"), self.test_data.get("get_golden_tag_status_of_an_image_golden_already_untagged"), - self.test_data.get("get_software_image_details_golden_already_tagged_4"), - self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), - self.test_data.get("get_software_image_details_golden_already_tagged_5"), - self.test_data.get("get_software_image_details_golden_already_tagged_6"), + self.test_data.get("get_software_image_details_golden_already_tagged_4"), + self.test_data.get("get_device_family_identifiers_golden_already_tagged_1"), + self.test_data.get("get_software_image_details_golden_already_tagged_5"), + self.test_data.get("get_software_image_details_golden_already_tagged_6"), self.test_data.get("get_golden_tag_status_of_an_image_golden_already_untagged_1"), self.test_data.get("image_golden_already_untagged_response"), ] @@ -230,8 +219,8 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("get_membership_failed_for_all_devices"), self.test_data.get("get_device_list_failed_for_all_devices_1"), self.test_data.get("distribution_params"), - # self.test_data.get("trigger_software_image_distribution_failed_for_all_devices"), - # self.test_data.get("Task_details_failed_for_all_devices"), + self.test_data.get("trigger_software_image_distribution_failed_for_all_devices"), + self.test_data.get("Task_details_failed_for_all_devices"), self.test_data.get("distribution_failed_for_all_devices_response"), ] elif "playbook_image_details_distribution_not_provided" in self._testMethodName: @@ -249,28 +238,10 @@ def load_fixtures(self, response=None, device=""): elif "playbook_import_image_details_not_provided" in self._testMethodName: self.run_dnac_exec.side_effect = [ self.test_data.get("import_image_details_not_provided_response"),] - - elif "playbook_verify_merged" in self._testMethodName: - self.run_dnac_exec.side_effect = [ - self.test_data.get("get_software_image_details_verify_merged"), - self.test_data.get("get_software_image_details_verify_merged_1"), - self.test_data.get("get_software_image_details_verify_merged_2"), - self.test_data.get("get_software_image_details_verify_merged_3"), - self.test_data.get("get_site_verify_merged"), - self.test_data.get("get_device_family_identifiers_verify_merged"), - self.test_data.get("get_software_image_details_verify_merged_4"), - self.test_data.get("get_golden_tag_status_of_an_image_verify_merged"), - self.test_data.get("get_golden_tag_status_of_an_image_verify_merged_1"), - self.test_data.get("get_site_verify_merged_1"), - self.test_data.get("get_device_family_identifiers_verify_merged_1"), - self.test_data.get("get_software_image_details_verify_merged_5"), - self.test_data.get("get_golden_tag_status_of_an_image_verify_merged_2"), - self.test_data.get("verify_merged_response"), - ] def test_swim_workflow_manager_playbook_config_invalid_param_import_image_url_tag_golden_load(self): """ - Test case for swim workflow manager when giving invalid param. + Test case for swim workflow manager when giving invalid param import image url tag golden load This test case checks the behavior of the swim workflow when giving invalid param in the specified Cisco Catalyst Center. """ set_module_args( @@ -284,7 +255,6 @@ def test_swim_workflow_manager_playbook_config_invalid_param_import_image_url_ta ) ) result = self.execute_module(changed=False, failed=True) - print(result) self.assertEqual( result.get("msg"), "Activation for Image with Id '4a3cccfa-dc92-4fad-a7d3-c59876cbebe6' gets failed" @@ -292,8 +262,8 @@ def test_swim_workflow_manager_playbook_config_invalid_param_import_image_url_ta def test_swim_workflow_manager_playbook_untag_image_as_golden_and_load_on_device(self): """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + Test case for swim workflow manager when giving untag image as golden and load on device + This test case checks the behavior of the swim workflow when giving untag image as golden and load on device """ set_module_args( dict( @@ -309,34 +279,14 @@ def test_swim_workflow_manager_playbook_untag_image_as_golden_and_load_on_device print(result) self.assertEqual( result.get('msg'), - "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." + "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco \ +Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." ) - # def test_swim_workflow_manager_playbook_untag_image_as_golden_and_error_load_on_device(self): ############## - # """ - # Test case for user role workflow manager when creating a user. - # This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - # """ - # set_module_args( - # dict( - # dnac_host="1.1.1.1", - # dnac_username="dummy", - # dnac_password="dummy", - # dnac_log=True, - # state="merged", - # config=self.playbook_untag_image_as_golden_and_load_on_device - # ) - # ) - # result = self.execute_module(changed=False, failed=True) - # print(result) - # self.assertEqual( - # result.get('msg'), - # "Untagging of image cat9k_iosxe.17.12.02.SPA.bin for site LTTS for family Cisco Catalyst 9000 UADP 8 Port Virtual Switch for device deviceTag ALL successful." - # ) def test_swim_workflow_manager_playbook_import_image_already_exist(self): """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. + Test case for swim workflow manager when giving import image already exist + This test case checks the behavior of the swim workflow when giving import image already exist """ set_module_args( dict( @@ -349,305 +299,250 @@ def test_swim_workflow_manager_playbook_import_image_already_exist(self): ) ) result = self.execute_module(changed=False, failed=False) - print(result) self.assertEqual( result.get('msg'), "Image 'cat9k_iosxe.17.12.02.SPA.bin' already exists in the Cisco Catalyst Center" ) def test_swim_workflow_manager_playbook_site_not_exist(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_site_not_exist - ) - ) - result = self.execute_module(changed=False, failed=True) - print(result) - self.assertEqual( - result.get('msg'), - "An exception occurred: Site 'Global/LTTS/FLOOR2' does not exist in the Cisco Catalyst Center" + """ + Test case for swim workflow manager when giving site not exist + This test case checks the behavior of the swim workflow when giving site not exist + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_site_not_exist ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "An exception occurred: Site 'Global/LTTS/FLOOR2' does not exist in the Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_invalid(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_swim_image_invalid - ) - ) - result = self.execute_module(changed=False, failed=True) - print(result) - self.assertEqual( - result.get('msg'), - "SWIM Image http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin seems to be invalid" + """ + Test case for swim workflow manager when giving swim image invalid + This test case checks the behavior of the swim workflow when giving swim image invalid + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_invalid ) - + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "SWIM Image http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin seems to be invalid" + ) + def test_swim_workflow_manager_playbook_image_distribution_failed(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_image_distribution_failed - ) - ) - result = self.execute_module(changed=False, failed=True) - print(result) - self.assertEqual( - result.get('msg'), - "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed" + """ + Test case for swim workflow manager when giving image distribution failed + This test case checks the behavior of the swim workflow when giving image distribution failed + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_distribution_failed ) - + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed" + ) + def test_swim_workflow_manager_playbook_image_distribution_partially_successfull(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_image_distribution_partially_successfull - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "Image with Id 'c383ee35-d20e-49f2-b51c-bfe499abbbaa' Distributed and partially successfull" + """ + Test case for swim workflow manager when giving image distribution partially successfull + This test case checks the behavior of the swim workflow when giving image distribution partially successfull + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_distribution_partially_successfull ) - + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Image with Id 'c383ee35-d20e-49f2-b51c-bfe499abbbaa' Distributed and partially successfull" + ) + def test_swim_workflow_manager_playbook_swim_image_golden_already_tagged(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_swim_image_golden_already_tagged - ) - ) - print(0) - result = self.execute_module(changed=False, failed=False) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already tagged as Golden image in Cisco Catalyst Center" + """ + Test case for swim workflow manager when givingswim image golden already tagged + This test case checks the behavior of the swim workflow when giving swim image golden already tagged + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_golden_already_tagged ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual( + result.get('msg'), + "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already tagged as Golden image in Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_golden_already_untagged(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_swim_image_golden_already_untagged - ) - ) - print(0) - result = self.execute_module(changed=False, failed=False) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already un-tagged from Golden image in Cisco Catalyst Center" + """ + Test case for swim workflow manager when giving swim image golden already untagged + This test case checks the behavior of the swim workflow when giving swim image golden already untagged + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_golden_already_untagged ) + ) + result = self.execute_module(changed=False, failed=False) + self.assertEqual( + result.get('msg'), + "SWIM Image 'cat9k_iosxe.17.12.02.SPA.bin' already un-tagged from Golden image in Cisco Catalyst Center" + ) + def test_swim_workflow_manager_playbook_swim_image_cant_found(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_swim_image_cant_found - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "SWIM image 'cat9k_iosxe.17.12.022.SPA.bin' could not be found" + """ + Test case for swim workflow manager when giving swim image cant found + This test case checks the behavior of the swim workflow when giving swim image cant found + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_swim_image_cant_found ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "SWIM image 'cat9k_iosxe.17.12.022.SPA.bin' could not be found" + ) def test_swim_workflow_manager_playbook_distribution_failed_for_all_devices(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_distribution_failed_for_all_devices - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed for all devices" + """ + Test case for swim workflow manager when giving distribution failed for all devices + This test case checks the behavior of the swim workflow when giving distribution failed for all devices + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_distribution_failed_for_all_devices ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Image with Id c383ee35-d20e-49f2-b51c-bfe499abbbaa Distribution Failed for all devices" + ) + def test_swim_workflow_manager_playbook_image_details_distribution_not_provided(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_image_details_distribution_not_provided - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "Image details required for distribution have not been provided" + """ + Test case for swim workflow manager when giving image details distribution not provided + This test case checks the behavior of the swim workflow when giving image details distribution not provided + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_image_details_distribution_not_provided ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Image details required for distribution have not been provided" + ) + def test_swim_workflow_manager_playbook_device_family_not_found(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_device_family_not_found - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "Device Family: None not found" + """ + Test case for swim workflow manager when giving device family not found + This test case checks the behavior of the swim workflow when giving device family not found + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_device_family_not_found ) + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Device Family: None not found" + ) + def test_swim_workflow_manager_playbook_import_image_details_not_provided(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_import_image_details_not_provided - ) - ) - print(0) - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "Error: Import image details are not provided in the playbook, or the Import Image API was not\n triggered successfully. Please ensure the necessary details are provided and verify the status of the Import Image process." - ) - def test_swim_workflow_manager_playbook_verify_merged(self): - """ - Test case for user role workflow manager when creating a user. - This test case checks the behavior of the user workflow when creating a new user in the specified Cisco Catalyst Center. - """ - set_module_args( - dict( - dnac_host="1.1.1.1", - dnac_username="dummy", - dnac_password="dummy", - dnac_log=True, - state="merged", - config_verify=True, - config=self.playbook_verify_merged - ) - ) - print("merged") - result = self.execute_module(changed=False, failed=True) - print(1) - print(result) - print(2) - self.assertEqual( - result.get('msg'), - "SWIM image 'cat9k_iosxe.17.12.02.SPA.bin' could not be found" + """ + Test case for swim workflow manager when giving import image details not provided + This test case checks the behavior of the swim workflow when giving import image details not provided + """ + set_module_args( + dict( + dnac_host="1.1.1.1", + dnac_username="dummy", + dnac_password="dummy", + dnac_log=True, + state="merged", + config_verify=True, + config=self.playbook_import_image_details_not_provided ) - + ) + result = self.execute_module(changed=False, failed=True) + self.assertEqual( + result.get('msg'), + "Error: Import image details are not provided in the playbook, or the Import Image API was not\n triggered successfully.\ + Please ensure the necessary details are provided and verify the status of the Import Image process." + ) From 2d7f3fd4f4285b6fc52cddc1df2e67ece7900154 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 13 Aug 2024 16:09:19 +0530 Subject: [PATCH 048/121] SWIM UT compleated --- playbooks/swim_workflow_manager.yml | 77 +++++++++++------------------ 1 file changed, 30 insertions(+), 47 deletions(-) diff --git a/playbooks/swim_workflow_manager.yml b/playbooks/swim_workflow_manager.yml index c986d85106..7b382c61c6 100644 --- a/playbooks/swim_workflow_manager.yml +++ b/playbooks/swim_workflow_manager.yml @@ -21,51 +21,34 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 - state : "merged" config: - - tagging_details: - image_name: cat9k_iosxe.17.12.02.SPA.bin - device_role: ALL - device_image_family_name: Cisco Catalyst 9000 UADP 8 Port Virtual Switch - site_name: "Global/LTTS" - tagging: false - - - - - - - - - - - # - import_image_details: - # type: "{{ item.type }}" - # url_details: - # payload: - # - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin - # third_party: False - # tagging_details: - # image_name: cat9k_iosxe.17.12.02.SPA.bin - # device_role: ALL - # device_image_family_name: Cisco Catalyst 9300 Switch - # site_name: "{{item.site_name}}" - # tagging: True - # # image_distribution_details: - # # image_name: cat9k_iosxe.17.12.02.SPA.bin - # # site_name: "{{item.site_name}}" - # # device_role: "{{ item.device_role }}" - # # device_family_name: "{{ item.device_family_name }}" - # # device_series_name: "Catalyst 9300 Series" - # image_activation_details: - # image_name: cat9k_iosxe.17.12.02.SPA.bin - # site_name: "{{item.site_name}}" - # device_role: "{{ item.device_role }}" - # device_family_name: "{{ item.device_family_name }}" - # device_series_name: "Catalyst 9300 Series" - # scehdule_validate: False - # distribute_if_needed: True - - # with_items: "{{ image_details }}" - # tags: - # - swim + - import_image_details: + type: "{{ item.type }}" + url_details: + payload: + - source_url: http://172.21.236.183/swim/V1712_2_CCO/cat9k_iosxe.17.12.02.SPA.bin + third_party: False + tagging_details: + image_name: cat9k_iosxe.17.12.02.SPA.bin + device_role: ALL + device_image_family_name: Cisco Catalyst 9300 Switch + site_name: "{{item.site_name}}" + tagging: True + # image_distribution_details: + # image_name: cat9k_iosxe.17.12.02.SPA.bin + # site_name: "{{item.site_name}}" + # device_role: "{{ item.device_role }}" + # device_family_name: "{{ item.device_family_name }}" + # device_series_name: "Catalyst 9300 Series" + image_activation_details: + image_name: cat9k_iosxe.17.12.02.SPA.bin + site_name: "{{item.site_name}}" + device_role: "{{ item.device_role }}" + device_family_name: "{{ item.device_family_name }}" + device_series_name: "Catalyst 9300 Series" + scehdule_validate: False + distribute_if_needed: True + + with_items: "{{ image_details }}" + tags: + - swim \ No newline at end of file From 7f6ebf4ad32a74c966a45b9617ff4fa2c9647a95 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 13 Aug 2024 16:11:17 +0530 Subject: [PATCH 049/121] SWIM playbook --- playbooks/swim_workflow_manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/swim_workflow_manager.yml b/playbooks/swim_workflow_manager.yml index 7b382c61c6..c6898fb51c 100644 --- a/playbooks/swim_workflow_manager.yml +++ b/playbooks/swim_workflow_manager.yml @@ -51,4 +51,4 @@ with_items: "{{ image_details }}" tags: - - swim \ No newline at end of file + - swim From 3d3c239bc58ed232f3296bdb43e3c22fc8737953 Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Tue, 13 Aug 2024 16:11:51 +0530 Subject: [PATCH 050/121] SWIM playbook --- playbooks/swim_workflow_manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/playbooks/swim_workflow_manager.yml b/playbooks/swim_workflow_manager.yml index c6898fb51c..c4f027c469 100644 --- a/playbooks/swim_workflow_manager.yml +++ b/playbooks/swim_workflow_manager.yml @@ -51,4 +51,4 @@ with_items: "{{ image_details }}" tags: - - swim + - swim From 35970925c7ac8864bb3066cbc1d60b229ebc7917 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 13 Aug 2024 21:45:20 +0530 Subject: [PATCH 051/121] changes after sanity check --- tests/unit/modules/dnac/fixtures/site_workflow_manager.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/modules/dnac/fixtures/site_workflow_manager.json b/tests/unit/modules/dnac/fixtures/site_workflow_manager.json index 4bfbbc7e18..d64f6fd8eb 100644 --- a/tests/unit/modules/dnac/fixtures/site_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/site_workflow_manager.json @@ -418,19 +418,19 @@ "response": [], "version": "1.0", "siteId": "45513959-fc87-4d15-af59-fba6ec5353b3", - "message": "Site doesn’t not have device member with given device family or serial number as input" + "message": "Site does not have device member with given device family or serial number as input" }, { "response": [], "version": "1.0", "siteId": "484b0a4f-81c0-41c1-82eb-1ef0ffe99b60", - "message": "Site doesn’t not have device member with given device family or serial number as input" + "message": "Site does not have device member with given device family or serial number as input" }, { "response": [], "version": "1.0", "siteId": "b8773b63-3652-4d39-a4ef-3c6a5aa657b0", - "message": "Site doesn’t not have device member with given device family or serial number as input" + "message": "Site does not have device member with given device family or serial number as input" } ] }, From 613e964e9bd62015dd9beea01cf30af508c82b7c Mon Sep 17 00:00:00 2001 From: Syed-khadeerahmed Date: Wed, 14 Aug 2024 12:57:09 +0530 Subject: [PATCH 052/121] sanity done for JSON --- .../unit/modules/dnac/fixtures/swim_workflow_manager.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json index 424981ad6f..b9050cb123 100644 --- a/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json +++ b/tests/unit/modules/dnac/fixtures/swim_workflow_manager.json @@ -255,7 +255,7 @@ ] }, "get_membership": { - "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500544, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885427, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608170, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500453, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500458, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}] + "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500544, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885427, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608170, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500453, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500458, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesntnot have device member with given device family or serial number as input"}] }, "trigger_software_image_distribution": { "response": { @@ -280,7 +280,7 @@ "version": "1.0" }, "get_1_membership": { - "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500553, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885436, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 12:27:42", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "1", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "Application Requested", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608179, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500462, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500467, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}] + "site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:30", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723111350784, "lastUpdated": "2024-08-08 10:02:30", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "17 days, 6:24:44.22", "uptimeSeconds": 1500553, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089147815, "lastUpdated": "2024-08-08 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "9 days, 21:22:47.82", "uptimeSeconds": 885436, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:44", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 12:27:42", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089164674, "lastUpdated": "2024-08-08 03:52:44", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "1", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "Application Requested", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "18 days, 6:08:51.84", "uptimeSeconds": 1608179, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:02", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089182511, "lastUpdated": "2024-08-08 03:53:02", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:34.00", "uptimeSeconds": 1500462, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:56", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-08 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723089176947, "lastUpdated": "2024-08-08 03:52:56", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "17 days, 0:13:29.00", "uptimeSeconds": 1500467, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesntnot have device member with given device family or serial number as input"}] }, "trigger_software_image_activation": { "response": { @@ -488,7 +488,7 @@ "get_site_image_distribution_failed": {"response": [{"name": "Global", "additionalInfo": [], "instanceTenantId": "6663114d388b29001399e46a", "id": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteNameHierarchy": "Global"}]}, -"get_membership_image_distribution_failed": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1834527, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1219410, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:48", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434768480, "lastUpdated": "2024-08-12 03:52:48", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 6:08:49.66", "uptimeSeconds": 1942150, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:00", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:12:15", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434780935, "lastUpdated": "2024-08-12 03:53:00", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:28.00", "uptimeSeconds": 1834437, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1834443, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}]}, +"get_membership_image_distribution_failed": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1834527, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1219410, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:48", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434768480, "lastUpdated": "2024-08-12 03:52:48", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 6:08:49.66", "uptimeSeconds": 1942150, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:00", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "In Progress", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:12:15", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434780935, "lastUpdated": "2024-08-12 03:53:00", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:28.00", "uptimeSeconds": 1834437, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1834443, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesntnot have device member with given device family or serial number as input"}]}, "trigger_software_image_distribution_image_distribution_failed": {"response": {"taskId": "019145dd-a5f2-77fc-86ef-10d3243421ae", "url": "/api/v1/task/019145dd-a5f2-77fc-86ef-10d3243421ae"}, "version": "1.0"}, @@ -517,7 +517,7 @@ "get_site_ps": {"response": [{"name": "Global", "additionalInfo": [], "instanceTenantId": "6663114d388b29001399e46a", "id": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "siteNameHierarchy": "Global"}]}, -"get_membership_ps": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1835895, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1220778, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:13", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:34:04", "lastManagedResyncReasons": "Application Requested", "lastUpdateTime": 1723455253431, "lastUpdated": "2024-08-12 09:34:13", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 11:50:43.81", "uptimeSeconds": 1943552, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:41", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723454921231, "lastUpdated": "2024-08-12 09:28:41", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 5:48:45.00", "uptimeSeconds": 1835765, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1835810, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesn’t not have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesn’t not have device member with given device family or serial number as input"}]}, +"get_membership_ps": {"site": {"response": [{"parentId": "847b187b-6827-4a07-8d46-16d69f6e0389", "name": "Chennai", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "2", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2", "groupNameHierarchy": "Global/INDIA/Chennai", "instanceTenantId": "6663114d388b29001399e46a", "id": "235c9a87-67ba-4952-bf77-7c6fa6902ee2"}, {"parentId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "name": "FLOOR1", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}, {"nameSpace": "Location", "attributes": {"addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "floor"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "3"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6/d5f8d954-0bc9-49a9-a24c-b3f05a01d31e", "groupNameHierarchy": "Global/LTTS/FLOOR1", "instanceTenantId": "6663114d388b29001399e46a", "id": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"parentId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "name": "Ground Floor", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "0", "hasChild": "FALSE", "group.count.direct": "0", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "Location", "attributes": {"address": "Chennai, Tamil Nadu, India", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "floor"}}, {"nameSpace": "mapGeometry", "attributes": {"offsetX": "0.0", "offsetY": "0.0", "length": "100.0", "width": "100.0", "height": "10.0"}}, {"nameSpace": "mapsSummary", "attributes": {"rfModel": "162162", "floorIndex": "1"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8/09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3/Ground Floor", "instanceTenantId": "6663114d388b29001399e46a", "id": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "INDIA", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "Location", "attributes": {"addressInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "type": "area"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "3", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389", "groupNameHierarchy": "Global/INDIA", "instanceTenantId": "6663114d388b29001399e46a", "id": "847b187b-6827-4a07-8d46-16d69f6e0389"}, {"parentId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "name": "LnT TC3", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "0", "member.count.direct": "0"}}, {"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "anchorWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "tertiaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389", "primaryWlcInheritedFrom": "847b187b-6827-4a07-8d46-16d69f6e0389"}}, {"nameSpace": "Location", "attributes": {"country": "India", "address": "Chennai, Tamil Nadu, India", "latitude": "13.084289", "addressInheritedFrom": "0985a6ec-ae46-4033-be6e-db9379c036c8", "type": "building", "longitude": "80.27044"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/847b187b-6827-4a07-8d46-16d69f6e0389/235c9a87-67ba-4952-bf77-7c6fa6902ee2/0985a6ec-ae46-4033-be6e-db9379c036c8", "groupNameHierarchy": "Global/INDIA/Chennai/LnT TC3", "instanceTenantId": "6663114d388b29001399e46a", "id": "0985a6ec-ae46-4033-be6e-db9379c036c8"}, {"parentId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b", "name": "LTTS", "groupTypeList": ["SITE"], "additionalInfo": [{"nameSpace": "com.wireless.managingwlc", "attributes": {"secondaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "anchorWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "tertiaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "primaryWlcInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}}, {"nameSpace": "Location", "attributes": {"country": "India", "latitude": "37.23", "addressInheritedFrom": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "type": "building", "longitude": "180"}}, {"nameSpace": "System Settings", "attributes": {"group.count.total": "1", "hasChild": "TRUE", "group.count.direct": "1", "group.hierarchy.groupType": "SITE", "member.count.total": "3", "member.count.direct": "0"}}], "groupHierarchy": "0ceaa46f-e64b-448b-a01c-1c100c9b134b/f41654c0-d03b-40ad-a3ae-4318ee2821d6", "groupNameHierarchy": "Global/LTTS", "instanceTenantId": "6663114d388b29001399e46a", "id": "f41654c0-d03b-40ad-a3ae-4318ee2821d6"}], "version": "1.0"}, "device": [{"response": [{"instanceUuid": "93dad04d-d795-4902-a685-37205483ee5d", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:38:31", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.10", "family": "Routers", "hostname": "Vrouter2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-11 10:02:12", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723370551120, "lastUpdated": "2024-08-11 10:02:31", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:7a:80:39:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.10", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 10", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "91R03VNQU8W", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "20 days, 6:24:39.73", "uptimeSeconds": 1835895, "vendor": "Cisco"}, {"instanceUuid": "c5c9215b-5c4a-4a9d-bfe5-9f18739a3b3e", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-29 06:30:27", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Virtual XE Software (X86_64_LINUX_IOSD-UNIVERSALK9-M), Experimental Version 17.15.20240713:012228 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Com", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.9", "family": "Routers", "hostname": "vRouter1.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434747880, "lastUpdated": "2024-08-12 03:52:27", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:1e:f6:22:46:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.9", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 9", "pendingSyncRequestsCount": "0", "platformId": "C8000V", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "BORDER ROUTER", "roleSource": "AUTO", "serialNumber": "93K2LNK8A5Y", "series": "Cloud Edge", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:012228", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 8000V Edge Software", "upTime": "13 days, 21:22:41.81", "uptimeSeconds": 1220778, "vendor": "Cisco"}], "version": "1.0", "siteId": "0ceaa46f-e64b-448b-a01c-1c100c9b134b"}, {"response": [], "version": "1.0", "siteId": "235c9a87-67ba-4952-bf77-7c6fa6902ee2", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [{"instanceUuid": "4ea61bbc-5965-436f-b880-178331d4bdc6", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-20 21:44:13", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.16.20240710:041147 [BLD_POLARIS_DEV_LATEST_20240710_033600:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 09-Ju", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.2", "family": "Switches and Hubs", "hostname": "sjc_border.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:34:04", "lastManagedResyncReasons": "Application Requested", "lastUpdateTime": 1723455253431, "lastUpdated": "2024-08-12 09:34:13", "lineCardCount": "0", "lineCardId": "", "macAddress": "dc:f7:19:33:41:00", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.2", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 2", "pendingSyncRequestsCount": "0", "platformId": "C9300-24UX", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Application Requested", "reasonsForPendingSyncRequests": "", "role": "DISTRIBUTION", "roleSource": "AUTO", "serialNumber": "FOC2225U12L", "series": "Cisco Catalyst 9300 Series Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.16.20240710:041147", "syncRequestedByApp": "SWIM", "tagCount": "0", "type": "Cisco Catalyst 9300 Switch", "upTime": "22 days, 11:50:43.81", "uptimeSeconds": 1943552, "vendor": "Cisco"}, {"instanceUuid": "6cae0779-a144-4208-a0f8-4eaca1542d61", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:40:41", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Mandatory", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240713:010153 [BLD_V1715_THROTTLE_LATEST_20240713_002204:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Fri 12", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.4", "family": "Switches and Hubs", "hostname": "RMA_SJC_EDGE2.cisco.com", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 09:27:26", "lastManagedResyncReasons": "Config Change Event", "lastUpdateTime": 1723454921231, "lastUpdated": "2024-08-12 09:28:41", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:71:6e", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.4", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 4", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Config Change Event", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "9X8X6SWXCLM", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240713:010153", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 5:48:45.00", "uptimeSeconds": 1835765, "vendor": "Cisco"}, {"instanceUuid": "ec0b4a9e-fc6e-4ffe-86bc-955b3940578a", "instanceTenantId": "6663114d388b29001399e46a", "deployPending": "NONE", "instanceVersion": 0, "apManagerInterfaceIp": "", "associatedWlcIp": "", "bootDateTime": "2024-07-22 03:39:55", "collectionInterval": "Global Default", "collectionIntervalValue": "24:00:00", "collectionStatus": "Managed", "collectionTier": "Not Applicable", "description": "Cisco IOS Software [IOSXE], Catalyst L3 Switch Software (CAT9K_IOSXE), Experimental Version 17.15.20240612:011957 [BLD_V1715_THROTTLE_LATEST_20240612_002610:/nobackup/mcpre/s2c-build-ws 101] Copyright (c) 1986-2024 by Cisco Systems, Inc. Compiled Tue 11", "deviceSupportLevel": "Supported", "dnsResolvedManagementAddress": "204.1.2.3", "family": "Switches and Hubs", "hostname": "SJ_Edge1", "interfaceCount": "0", "inventoryStatusDetail": "", "lastDeviceResyncStartTime": "2024-08-12 03:52:09", "lastManagedResyncReasons": "Periodic", "lastUpdateTime": 1723434775476, "lastUpdated": "2024-08-12 03:52:55", "lineCardCount": "0", "lineCardId": "", "macAddress": "00:50:56:b4:ff:2a", "managedAtleastOnce": false, "managementIpAddress": "204.1.2.3", "managementState": "Managed", "memorySize": "NA", "paddedMgmtIpAddress": "204. 1. 2. 3", "pendingSyncRequestsCount": "0", "platformId": "C9KV-UADP-8P", "reachabilityFailureReason": "", "reachabilityStatus": "Reachable", "reasonsForDeviceResync": "Periodic", "reasonsForPendingSyncRequests": "", "role": "ACCESS", "roleSource": "AUTO", "serialNumber": "99IA669OFH1", "series": "Cisco Catalyst 9000 Series Virtual Switches", "snmpContact": "", "snmpLocation": "", "softwareType": "IOS-XE", "softwareVersion": "17.15.20240612:011957", "syncRequestedByApp": "", "tagCount": "0", "type": "Cisco Catalyst 9000 UADP 8 Port Virtual Switch", "upTime": "21 days, 0:13:23.00", "uptimeSeconds": 1835810, "vendor": "Cisco"}], "version": "1.0", "siteId": "d5f8d954-0bc9-49a9-a24c-b3f05a01d31e"}, {"response": [], "version": "1.0", "siteId": "09f1dc00-6d7f-4d0c-b8b5-cdd6cd6dc7d7", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "847b187b-6827-4a07-8d46-16d69f6e0389", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "0985a6ec-ae46-4033-be6e-db9379c036c8", "message": "Site doesntnot have device member with given device family or serial number as input"}, {"response": [], "version": "1.0", "siteId": "f41654c0-d03b-40ad-a3ae-4318ee2821d6", "message": "Site doesntnot have device member with given device family or serial number as input"}]}, "get_device_uuids" : "Device '204.1.2.10' from site 'Global' is ready for the SWIM distribution/activation process.", "get_device_uuids_1" : "Device '204.1.2.4' matches to the specified filter requirements and is set for SWIM distribution/activation. ", From 9e0aef357ca9959501121e11c8e6a9850e4bff74 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Wed, 14 Aug 2024 13:03:28 +0530 Subject: [PATCH 053/121] changes done --- tests/unit/modules/dnac/test_site_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/modules/dnac/test_site_workflow_manager.py b/tests/unit/modules/dnac/test_site_workflow_manager.py index 395221a557..8b50f41902 100644 --- a/tests/unit/modules/dnac/test_site_workflow_manager.py +++ b/tests/unit/modules/dnac/test_site_workflow_manager.py @@ -1,4 +1,4 @@ -# Copyright (c) 2020 Cisco and/or its affiliates. +# Copyright (c) 2024 Cisco and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. From 5030b0adc17c399a8f832ba828e664fc7ce10720 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 14:23:04 +0530 Subject: [PATCH 054/121] updated the code with version based routing and bug fixed --- playbooks/user_role_workflow_manager.yml | 35 +-- plugins/modules/user_role_workflow_manager.py | 293 ++++++++++++++---- .../dnac/test_user_role_workflow_manager.py | 2 +- 3 files changed, 236 insertions(+), 94 deletions(-) diff --git a/playbooks/user_role_workflow_manager.yml b/playbooks/user_role_workflow_manager.yml index 2002034a42..b08a32448d 100644 --- a/playbooks/user_role_workflow_manager.yml +++ b/playbooks/user_role_workflow_manager.yml @@ -20,7 +20,7 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 - state: merged + state: deleted config: user_details: - first_name: "ajith" @@ -29,11 +29,6 @@ email: "ajith.andrew@example.com" password: "Ajith@123" role_list: ["Super-Admin-role"] - - first_name: "syed" - last_name: "khadeer" - username: "syedkhadeer" - email: "syedkhadeer@example.com" - password: "Syedkhadeer@123" role_details: - role_name: "Test_Role_1" description: "Default role creation" @@ -42,31 +37,3 @@ troubleshooting_tools: "write" network_analytics: - overall: "deny" - - role_name: "Test_Role_2" - description: "Default role creation" - network_design: - - overall: "deny" - image_repository: "read" - network_provision: - - overall: "read" - eox: "write" - image_update: "read" - inventory_management: - - overall: "write" - port_management: "read" - network_services: - - overall: "deny" - bonjour: "Write" - platform: - - overall: "deny" - bundles: "read" - security: - - overall: "deny" - ip_based_access_control: "read" - security_advisories: "write" - system: - - overall: "deny" - machine_reasoning: "write" - utilities: - - overall: "deny" - audit_log: "read" diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 732cd1c11d..327e377d71 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -43,9 +43,10 @@ elements: dict suboptions: username: - description: The 'username' associated with the user account. + description: + - The 'username' associated with the user account. + - Required for user create, update and delete operations. type: str - required: true first_name: description: The first name of the user. type: str @@ -62,10 +63,9 @@ description: - The password for the user account, which must adhere to specified complexity requirements. - Must contain at least one special character, one capital letter, one lowercase letter, - and a minimum length of 15 characters. + and a minimum length of 8 characters. - Required for creating a new user account. type: str - required: true role_list: description: - A list of role names to be assigned to the user. If no role is specified, the default role will be "OBSERVER-ROLE". @@ -76,7 +76,6 @@ - OBSERVER-ROLE grants view-only access, no configuration or control functions. type: list elements: str - required: true role_details: description: Manages the configuration details for roles. type: list @@ -540,7 +539,7 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 - state: merged + state: deleted config: user_details: username: "ajithandrewj" @@ -695,7 +694,7 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 - state: merged + state: deleted config: role_details: - rolename: "role_name" @@ -864,6 +863,51 @@ def __init__(self, module): self.supported_states = ["merged", "deleted"] self.payload = module.params self.keymap = {} + self.versions = { + '2.3.5.3 - 2.3.7.6 functions': { + 'user_and_roles': { + 'get_permissions': 'get_permissions_api', + 'get_roles': 'get_roles_api', + 'get_users': 'get_users_api', + 'add_user': 'add_user_api', + 'update_user': 'update_user_api', + 'get_external_authentication_servers': 'get_external_authentication_servers_api' + } + }, + '2.3.7.6': { + 'user_and_roles': { + 'add_role': 'add_role_api', + 'update_role': 'update_role_api', + 'delete_role': 'delete_role_api', + 'delete_user': 'delete_user_api', + 'get_external_authentication_setting': 'get_external_authentication_setting_api', + 'manage_external_authentication_setting': 'manage_external_authentication_setting_api', + 'add_and_update_a_a_a_attribute': 'add_and_update_a_a_a_attribute_api', + 'delete_a_a_a_attribute': 'delete_a_a_a_attribute_api', + 'get_a_a_a_attribute': 'get_a_a_a_attribute_api' + } + } + } + self.versions_functions_params = { + '2.3.5.3 - 2.3.7.6 function params': { + 'user_and_roles': { + 'get_users': {"invoke_source": "external"}, + 'add_user': {'email': None, 'firstName': None, 'lastName': None, 'password': "required", 'roleList': "required", 'username': "required"}, + 'update_user': {'email': None, 'firstName': None, 'lastName': None, 'userId': "required", 'roleList': None, 'username': None}, + 'get_external_authentication_servers': {"invoke_source": "external"} + } + }, + '2.3.7.6': { + 'user_and_roles': { + 'add_role': {'description': None, 'resourceTypes': None, 'role': "required"}, + 'update_role': {'description': None, 'resourceTypes': None, 'roleId': "required"}, + 'delete_role': {'role_id': "required"}, + 'delete_user': {'user_id': "required"}, + 'manage_external_authentication_setting': {'enable': None}, + 'add_and_update_a_a_a_attribute': {'attributeName': None} + } + } + } def validate_input_yml(self, user_role_details): """ @@ -1102,6 +1146,71 @@ def valid_user_config_parameters(self, user_config): self.status = "success" return self + def version_route(self, version, family, function_key, provided_params=None): + """ + Retrieves function parameters and validates the provided provided_params for the specified version, + family, and function key. + + Parameters: + - version (str): The API or system version. + - family (str): The function family within the version. + - function_key (str): The specific function to check. + - provided_params (dict): The input parameters to validate. + + Returns: + A tuple with: + - version_dict (dict or None): The function definitions for the version, family, and key, or None if not found. + - payload (dict): Valid parameters from the provided_params. + - error (dict or str): An error message if validation fails, or "No Error" if successful. + + Description: + - The function finds the function definition and parameters, then validates the provided_params. + - It returns an error message for missing or extra parameters or "No Error" if all is correct. + """ + version_dict = None + function_parameters = None + missing_params = [] + extra_params = [] + payload = {} + + if function_key in self.versions.get("2.3.5.3 - 2.3.7.6 functions", {}).get(family, {}): + version_dict = self.versions["2.3.5.3 - 2.3.7.6 functions"][family][function_key] + elif function_key in self.versions.get(version, {}).get(family, {}): + version_dict = self.versions[version][family][function_key] + + if function_key in self.versions_functions_params.get("2.3.5.3 - 2.3.7.6 function params", {}).get(family, {}): + function_parameters = self.versions_functions_params['2.3.5.3 - 2.3.7.6 function params'][family][function_key] + elif function_key in self.versions_functions_params.get(version, {}).get(family, {}): + function_parameters = self.versions_functions_params[version][family][function_key] + + if not version_dict and not function_parameters: + error_message = "The specified version '{0}' does not have the '{1}' functionality".format(version, function_key) + return None, None, {"error": error_message} + + if function_parameters is not None and provided_params is not None: + for param_key, param_value in function_parameters.items(): + if param_value == "required" and param_key not in provided_params: + missing_params.append(param_key) + elif param_key in provided_params: + payload[param_key] = provided_params[param_key] + + for param_key in provided_params: + if param_key not in function_parameters: + extra_params.append("{0}: {1}".format(param_key, provided_params[param_key])) + + if missing_params: + error_message = "Missing required parameters: {0}".format(', '.join(missing_params)) + return version_dict, payload, {"error": error_message} + + if extra_params: + error_message = "In version '{0}' the parameter(s) in the playbook: '{1}' are not part of the '{2}' function parameters".format( + version, ', '.join(extra_params), version_dict) + return version_dict, payload, {"error_message": error_message} + else: + return version_dict, function_parameters, {} + + return version_dict, payload, {} + def get_want(self, config): """ Retrieve all user or role-related information from the playbook needed for creation/updation in Cisco Catalyst Center. @@ -1400,28 +1509,24 @@ def create_user(self, user_params): - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ - required_keys = ['username', 'password'] - missing_keys = [] + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "add_user" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) - self.log("Check if each required key is present in the user_params dictionary...", "DEBUG") - for key in required_keys: - if key not in user_params: - missing_keys.append(key) + if error_message: + return error_message - try: - self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") - response = self.dnac._exec( - family="user_and_roles", - function="add_user_ap_i", - op_modifies=True, - params=user_params, - ) - self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") - return response - - except Exception: - error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) - return {"error": error_message} + self.log("Create user with function_parameters: {0}".format(str(function_parameters)), "DEBUG") + response = self.dnac._exec( + family=family_name, + function=function_called, + op_modifies=True, + params=function_parameters, + ) + self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") + return response def create_role(self, role_params): """ @@ -1437,13 +1542,22 @@ def create_role(self, role_params): - Logs the provided role parameters and the received API response. - Returns the API response from the "create_role" function. """ + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "add_role" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) + + if error_message: + return error_message + try: - self.log("Create role with role_info_params: {0}".format(str(role_params)), "DEBUG") + self.log("Create role with function_parameters: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( - family="user_and_roles", - function="add_role_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params=role_params, + params=function_parameters, ) self.log("Received API response from create_role: {0}".format(str(response)), "DEBUG") return response @@ -1464,11 +1578,20 @@ def get_user(self): and "get_users_ap_i" function. - Logs the received API response and returns it. """ + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "get_users" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key) + + if error_message: + return error_message + response = self.dnac._exec( - family="user_and_roles", - function="get_users_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params={"invoke_source": "external"}, + params=function_parameters, ) self.log("Received API response from get_users_api: {0}".format(str(response)), "DEBUG") return response @@ -1485,9 +1608,18 @@ def get_role(self): and "get_roles_ap_i" function. - Logs the received API response and returns it. """ + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "get_roles" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key) + + if error_message: + return error_message + response = self.dnac._exec( - family="user_and_roles", - function="get_roles_ap_i", + family=family_name, + function=function_called, op_modifies=True, ) self.log("Received API response from get_roles_api: {0}".format(str(response)), "DEBUG") @@ -2375,12 +2507,21 @@ def update_user(self, user_params): - This method sends a request to update a user in Cisco Catalyst Center using the provided - user parameters. It logs the response and returns it. """ - self.log("Updating user with parameters: {0}".format(user_params), "DEBUG") + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "update_user" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) + + if error_message: + return error_message + + self.log("Updating user with function_parameters: {0}".format(function_parameters), "DEBUG") response = self.dnac._exec( - family="user_and_roles", - function="update_user_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params=user_params, + params=function_parameters, ) self.log("Received API response from update_user: {0}".format(str(response)), "DEBUG") return response @@ -2400,12 +2541,21 @@ def update_role(self, role_params): and the "update_role_ap_i" function. The method logs the received API response at the "DEBUG" level and finally returns the response. """ - self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "update_role" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) + + if error_message: + return error_message + + self.log("Update role with function_parameters: {0}".format(str(function_parameters)), "DEBUG") response = self.dnac._exec( - family="user_and_roles", - function="update_role_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params=role_params, + params=function_parameters, ) self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") @@ -2732,12 +2882,18 @@ def get_diff_deleted(self, config): task_response = self.delete_user(user_id_to_delete) self.log("Task response {0}".format(str(task_response)), "INFO") - responses = {"users_operation": {"response": task_response}} - self.msg = responses - self.result["response"] = self.msg - self.result["changed"] = True - self.status = "success" - self.log(self.msg, "INFO") + if task_response and "error" not in task_response: + responses = {"users_operation": {"response": task_response}} + self.msg = responses + self.result["response"] = self.msg + self.result["changed"] = True + self.status = "success" + self.log(self.msg, "INFO") + return self + + self.msg = task_response + self.log(self.msg, "ERROR") + self.status = "failed" return self self.msg = "Please provide a valid 'username' or 'email' for user deletion" @@ -2758,12 +2914,21 @@ def delete_user(self, user_params): - It logs the response and returns it. - The function uses the "user_and_roles" family and the "delete_user_ap_i" function from the Cisco Catalyst Center API. """ - self.log("delete user with user_params: {0}".format(str(user_params)), "DEBUG") + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "delete_user" + self.log("Validating the specified version to get function and its params...", "DEBUG") + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) + + if error_message: + return error_message + + self.log("delete user with function_parameters: {0}".format(str(function_parameters)), "DEBUG") response = self.dnac._exec( - family="user_and_roles", - function="delete_user_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params=user_params, + params=function_parameters, ) self.log("Received API response from delete_user: {0}".format(str(response)), "DEBUG") return response @@ -2781,13 +2946,23 @@ def delete_role(self, role_params): - It logs the response and returns it. - The function uses the "user_and_roles" family and the "delete_role_ap_i" function from the Cisco Catalyst Center API. """ + version = self.payload.get("dnac_version") + family_name = "user_and_roles" + function_key = "delete_role" + self.log("Validating the specified version to get function and its params...", "DEBUG") + self.log("rooooollllooooo {0}".format(role_params)) + function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) + + if error_message: + return error_message + try: - self.log("delete role with role_params: {0}".format(str(role_params)), "DEBUG") + self.log("delete role with function_parameters: {0}".format(str(function_parameters)), "DEBUG") response = self.dnac._exec( - family="user_and_roles", - function="delete_role_ap_i", + family=family_name, + function=function_called, op_modifies=True, - params=role_params, + params=function_parameters, ) self.log("Received API response from delete_role: {0}".format(str(response)), "DEBUG") except Exception: @@ -2965,7 +3140,7 @@ def main(): "dnac_username": {"type": "str", "default": "admin", 'aliases': ['user']}, "dnac_password": {"type": "str", "no_log": True}, "dnac_verify": {"type": "bool", "default": "True"}, - "dnac_version": {"type": "str", "default": "2.2.3.3"}, + "dnac_version": {"type": "str", "default": "2.3.5.3"}, "dnac_debug": {"type": "bool", "default": False}, "dnac_log": {"type": "bool", "default": False}, "dnac_log_level": {"type": "str", "default": "WARNING"}, diff --git a/tests/unit/modules/dnac/test_user_role_workflow_manager.py b/tests/unit/modules/dnac/test_user_role_workflow_manager.py index 274df365d9..82dcd48e33 100644 --- a/tests/unit/modules/dnac/test_user_role_workflow_manager.py +++ b/tests/unit/modules/dnac/test_user_role_workflow_manager.py @@ -85,7 +85,7 @@ def load_fixtures(self, response=None, device=""): self.test_data.get("update_user_needed_get_role_response"), self.test_data.get("update_needed_user_response") ] - if "user_update_not_needed" in self._testMethodName: + elif "user_update_not_needed" in self._testMethodName: self.run_dnac_exec.side_effect = [ self.test_data.get("update_not_needed_get_user_response"), self.test_data.get("update_user_not_needed_get_role_response"), From 4d07377ba61700b56315100437face0e8f344b84 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 14:50:50 +0530 Subject: [PATCH 055/121] user_role_workflow_manager bugs fixed --- playbooks/user_role_workflow_manager.yml | 35 ++- plugins/modules/user_role_workflow_manager.py | 278 ++++-------------- 2 files changed, 85 insertions(+), 228 deletions(-) diff --git a/playbooks/user_role_workflow_manager.yml b/playbooks/user_role_workflow_manager.yml index b08a32448d..2002034a42 100644 --- a/playbooks/user_role_workflow_manager.yml +++ b/playbooks/user_role_workflow_manager.yml @@ -20,7 +20,7 @@ config_verify: True dnac_api_task_timeout: 1000 dnac_task_poll_interval: 1 - state: deleted + state: merged config: user_details: - first_name: "ajith" @@ -29,6 +29,11 @@ email: "ajith.andrew@example.com" password: "Ajith@123" role_list: ["Super-Admin-role"] + - first_name: "syed" + last_name: "khadeer" + username: "syedkhadeer" + email: "syedkhadeer@example.com" + password: "Syedkhadeer@123" role_details: - role_name: "Test_Role_1" description: "Default role creation" @@ -37,3 +42,31 @@ troubleshooting_tools: "write" network_analytics: - overall: "deny" + - role_name: "Test_Role_2" + description: "Default role creation" + network_design: + - overall: "deny" + image_repository: "read" + network_provision: + - overall: "read" + eox: "write" + image_update: "read" + inventory_management: + - overall: "write" + port_management: "read" + network_services: + - overall: "deny" + bonjour: "Write" + platform: + - overall: "deny" + bundles: "read" + security: + - overall: "deny" + ip_based_access_control: "read" + security_advisories: "write" + system: + - overall: "deny" + machine_reasoning: "write" + utilities: + - overall: "deny" + audit_log: "read" diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 327e377d71..6db77ee63c 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -863,51 +863,6 @@ def __init__(self, module): self.supported_states = ["merged", "deleted"] self.payload = module.params self.keymap = {} - self.versions = { - '2.3.5.3 - 2.3.7.6 functions': { - 'user_and_roles': { - 'get_permissions': 'get_permissions_api', - 'get_roles': 'get_roles_api', - 'get_users': 'get_users_api', - 'add_user': 'add_user_api', - 'update_user': 'update_user_api', - 'get_external_authentication_servers': 'get_external_authentication_servers_api' - } - }, - '2.3.7.6': { - 'user_and_roles': { - 'add_role': 'add_role_api', - 'update_role': 'update_role_api', - 'delete_role': 'delete_role_api', - 'delete_user': 'delete_user_api', - 'get_external_authentication_setting': 'get_external_authentication_setting_api', - 'manage_external_authentication_setting': 'manage_external_authentication_setting_api', - 'add_and_update_a_a_a_attribute': 'add_and_update_a_a_a_attribute_api', - 'delete_a_a_a_attribute': 'delete_a_a_a_attribute_api', - 'get_a_a_a_attribute': 'get_a_a_a_attribute_api' - } - } - } - self.versions_functions_params = { - '2.3.5.3 - 2.3.7.6 function params': { - 'user_and_roles': { - 'get_users': {"invoke_source": "external"}, - 'add_user': {'email': None, 'firstName': None, 'lastName': None, 'password': "required", 'roleList': "required", 'username': "required"}, - 'update_user': {'email': None, 'firstName': None, 'lastName': None, 'userId': "required", 'roleList': None, 'username': None}, - 'get_external_authentication_servers': {"invoke_source": "external"} - } - }, - '2.3.7.6': { - 'user_and_roles': { - 'add_role': {'description': None, 'resourceTypes': None, 'role': "required"}, - 'update_role': {'description': None, 'resourceTypes': None, 'roleId': "required"}, - 'delete_role': {'role_id': "required"}, - 'delete_user': {'user_id': "required"}, - 'manage_external_authentication_setting': {'enable': None}, - 'add_and_update_a_a_a_attribute': {'attributeName': None} - } - } - } def validate_input_yml(self, user_role_details): """ @@ -1146,71 +1101,6 @@ def valid_user_config_parameters(self, user_config): self.status = "success" return self - def version_route(self, version, family, function_key, provided_params=None): - """ - Retrieves function parameters and validates the provided provided_params for the specified version, - family, and function key. - - Parameters: - - version (str): The API or system version. - - family (str): The function family within the version. - - function_key (str): The specific function to check. - - provided_params (dict): The input parameters to validate. - - Returns: - A tuple with: - - version_dict (dict or None): The function definitions for the version, family, and key, or None if not found. - - payload (dict): Valid parameters from the provided_params. - - error (dict or str): An error message if validation fails, or "No Error" if successful. - - Description: - - The function finds the function definition and parameters, then validates the provided_params. - - It returns an error message for missing or extra parameters or "No Error" if all is correct. - """ - version_dict = None - function_parameters = None - missing_params = [] - extra_params = [] - payload = {} - - if function_key in self.versions.get("2.3.5.3 - 2.3.7.6 functions", {}).get(family, {}): - version_dict = self.versions["2.3.5.3 - 2.3.7.6 functions"][family][function_key] - elif function_key in self.versions.get(version, {}).get(family, {}): - version_dict = self.versions[version][family][function_key] - - if function_key in self.versions_functions_params.get("2.3.5.3 - 2.3.7.6 function params", {}).get(family, {}): - function_parameters = self.versions_functions_params['2.3.5.3 - 2.3.7.6 function params'][family][function_key] - elif function_key in self.versions_functions_params.get(version, {}).get(family, {}): - function_parameters = self.versions_functions_params[version][family][function_key] - - if not version_dict and not function_parameters: - error_message = "The specified version '{0}' does not have the '{1}' functionality".format(version, function_key) - return None, None, {"error": error_message} - - if function_parameters is not None and provided_params is not None: - for param_key, param_value in function_parameters.items(): - if param_value == "required" and param_key not in provided_params: - missing_params.append(param_key) - elif param_key in provided_params: - payload[param_key] = provided_params[param_key] - - for param_key in provided_params: - if param_key not in function_parameters: - extra_params.append("{0}: {1}".format(param_key, provided_params[param_key])) - - if missing_params: - error_message = "Missing required parameters: {0}".format(', '.join(missing_params)) - return version_dict, payload, {"error": error_message} - - if extra_params: - error_message = "In version '{0}' the parameter(s) in the playbook: '{1}' are not part of the '{2}' function parameters".format( - version, ', '.join(extra_params), version_dict) - return version_dict, payload, {"error_message": error_message} - else: - return version_dict, function_parameters, {} - - return version_dict, payload, {} - def get_want(self, config): """ Retrieve all user or role-related information from the playbook needed for creation/updation in Cisco Catalyst Center. @@ -1509,24 +1399,28 @@ def create_user(self, user_params): - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "add_user" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) + required_keys = ['username', 'password'] + missing_keys = [] - if error_message: - return error_message + self.log("Check if each required key is present in the user_params dictionary...", "DEBUG") + for key in required_keys: + if key not in user_params: + missing_keys.append(key) - self.log("Create user with function_parameters: {0}".format(str(function_parameters)), "DEBUG") - response = self.dnac._exec( - family=family_name, - function=function_called, - op_modifies=True, - params=function_parameters, - ) - self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") - return response + try: + self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") + response = self.dnac._exec( + family="user_and_roles", + function="add_user_ap_i", + op_modifies=True, + params=user_params, + ) + self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") + return response + + except Exception: + error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) + return {"error": error_message} def create_role(self, role_params): """ @@ -1542,22 +1436,13 @@ def create_role(self, role_params): - Logs the provided role parameters and the received API response. - Returns the API response from the "create_role" function. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "add_role" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) - - if error_message: - return error_message - try: - self.log("Create role with function_parameters: {0}".format(str(role_params)), "DEBUG") + self.log("Create role with role_info_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="add_role_ap_i", op_modifies=True, - params=function_parameters, + params=role_params, ) self.log("Received API response from create_role: {0}".format(str(response)), "DEBUG") return response @@ -1578,20 +1463,11 @@ def get_user(self): and "get_users_ap_i" function. - Logs the received API response and returns it. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "get_users" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key) - - if error_message: - return error_message - response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="get_users_ap_i", op_modifies=True, - params=function_parameters, + params={"invoke_source": "external"}, ) self.log("Received API response from get_users_api: {0}".format(str(response)), "DEBUG") return response @@ -1608,18 +1484,9 @@ def get_role(self): and "get_roles_ap_i" function. - Logs the received API response and returns it. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "get_roles" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key) - - if error_message: - return error_message - response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="get_roles_ap_i", op_modifies=True, ) self.log("Received API response from get_roles_api: {0}".format(str(response)), "DEBUG") @@ -2507,21 +2374,12 @@ def update_user(self, user_params): - This method sends a request to update a user in Cisco Catalyst Center using the provided - user parameters. It logs the response and returns it. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "update_user" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) - - if error_message: - return error_message - - self.log("Updating user with function_parameters: {0}".format(function_parameters), "DEBUG") + self.log("Updating user with parameters: {0}".format(user_params), "DEBUG") response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="update_user_ap_i", op_modifies=True, - params=function_parameters, + params=user_params, ) self.log("Received API response from update_user: {0}".format(str(response)), "DEBUG") return response @@ -2541,21 +2399,12 @@ def update_role(self, role_params): and the "update_role_ap_i" function. The method logs the received API response at the "DEBUG" level and finally returns the response. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "update_role" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) - - if error_message: - return error_message - - self.log("Update role with function_parameters: {0}".format(str(function_parameters)), "DEBUG") + self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="update_role_ap_i", op_modifies=True, - params=function_parameters, + params=role_params, ) self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") @@ -2882,18 +2731,12 @@ def get_diff_deleted(self, config): task_response = self.delete_user(user_id_to_delete) self.log("Task response {0}".format(str(task_response)), "INFO") - if task_response and "error" not in task_response: - responses = {"users_operation": {"response": task_response}} - self.msg = responses - self.result["response"] = self.msg - self.result["changed"] = True - self.status = "success" - self.log(self.msg, "INFO") - return self - - self.msg = task_response - self.log(self.msg, "ERROR") - self.status = "failed" + responses = {"users_operation": {"response": task_response}} + self.msg = responses + self.result["response"] = self.msg + self.result["changed"] = True + self.status = "success" + self.log(self.msg, "INFO") return self self.msg = "Please provide a valid 'username' or 'email' for user deletion" @@ -2914,21 +2757,12 @@ def delete_user(self, user_params): - It logs the response and returns it. - The function uses the "user_and_roles" family and the "delete_user_ap_i" function from the Cisco Catalyst Center API. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "delete_user" - self.log("Validating the specified version to get function and its params...", "DEBUG") - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, user_params) - - if error_message: - return error_message - - self.log("delete user with function_parameters: {0}".format(str(function_parameters)), "DEBUG") + self.log("delete user with user_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="delete_user_ap_i", op_modifies=True, - params=function_parameters, + params=user_params, ) self.log("Received API response from delete_user: {0}".format(str(response)), "DEBUG") return response @@ -2946,23 +2780,13 @@ def delete_role(self, role_params): - It logs the response and returns it. - The function uses the "user_and_roles" family and the "delete_role_ap_i" function from the Cisco Catalyst Center API. """ - version = self.payload.get("dnac_version") - family_name = "user_and_roles" - function_key = "delete_role" - self.log("Validating the specified version to get function and its params...", "DEBUG") - self.log("rooooollllooooo {0}".format(role_params)) - function_called, function_parameters, error_message = self.version_route(version, family_name, function_key, role_params) - - if error_message: - return error_message - try: - self.log("delete role with function_parameters: {0}".format(str(function_parameters)), "DEBUG") + self.log("delete role with role_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( - family=family_name, - function=function_called, + family="user_and_roles", + function="delete_role_ap_i", op_modifies=True, - params=function_parameters, + params=role_params, ) self.log("Received API response from delete_role: {0}".format(str(response)), "DEBUG") except Exception: From 8e1d8003c532be2a02db0d7992a3ee76c9e1e361 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 15:20:20 +0530 Subject: [PATCH 056/121] updated user_role_workflow_feature --- plugins/modules/user_role_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 6db77ee63c..802c86c891 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -2964,7 +2964,7 @@ def main(): "dnac_username": {"type": "str", "default": "admin", 'aliases': ['user']}, "dnac_password": {"type": "str", "no_log": True}, "dnac_verify": {"type": "bool", "default": "True"}, - "dnac_version": {"type": "str", "default": "2.3.5.3"}, + "dnac_version": {"type": "str", "default": "2.2.3.3"}, "dnac_debug": {"type": "bool", "default": False}, "dnac_log": {"type": "bool", "default": False}, "dnac_log_level": {"type": "str", "default": "WARNING"}, From f9e0486e28e15bba1a4ce992097007a83c0091c0 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 15:23:56 +0530 Subject: [PATCH 057/121] updated user_role_workflow_feature --- plugins/modules/user_role_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 802c86c891..849300852b 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -43,7 +43,7 @@ elements: dict suboptions: username: - description: + description: - The 'username' associated with the user account. - Required for user create, update and delete operations. type: str From 9cdea29548230abbc4a68bcd098a0249c15844cf Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 18:04:47 +0530 Subject: [PATCH 058/121] RMA bug fixed --- plugins/modules/rma_workflow_manager.py | 44 ++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index de0d8c2439..4afe6db60f 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -842,11 +842,13 @@ def unmark_device_for_replacement(self): - Updates the status, msg, and result attributes based on the task result. - Handles any exceptions that occur during the process. """ + self.log("Unmarking device for replacement...") + device_id = self.return_replacement_devices() import_params = dict( payload=[{ - "faultyDeviceId": self.have.get("faulty_device_id"), - "replacementStatus": "MARKED-FOR-REPLACEMENT" + "id": device_id, + "replacementStatus": "NON-FAULTY" }], ) @@ -865,16 +867,48 @@ def unmark_device_for_replacement(self): "Error while unmarking device for replacement" ) self.status = task_result["status"] - self.msg = task_result["msg"] + self.msg = "RMA failed to replace the device: {0}".format(task_result["msg"]) if self.status == "success": self.result['changed'] = True - except Exception as e: + except Exception: self.status = "failed" - self.msg = "Exception occurred while unmarking device for replacement: {0}".format(str(e)) + self.msg = "RMA failed to replace the device: No device found for unmarking replacement" self.log(self.msg, "ERROR") return self + def return_replacement_devices(self): + """ + Retrieves the ID of the device ready for replacement in Cisco Catalyst Center. + + Parameters: + - self (object): An instance of a class used for interacting with Cisco Catalyst Center. + + Returns: + - device_id (str or None): The ID of the device that is ready for replacement, or None if no such device is found. + + Description: + This method retrieves the ID of a device that is marked as "READY-FOR-REPLACEMENT" from Cisco Catalyst Center. + It performs the following steps: + - Sends a request to Cisco Catalyst Center to get the list of devices with their replacement status. + - Iterates through the list of devices and checks their replacement status. + - If a device is found with the status "READY-FOR-REPLACEMENT", its ID is extracted. + - The method returns the ID of the first device with the "READY-FOR-REPLACEMENT" status found, or None if no such device is present. + """ + device_id = None + response = self.dnac._exec( + family="device_replacement", + function='return_replacement_devices_with_details' + ) + devices = response.get("response", []) + for device in devices: + if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": + device_id = device.get("id") + if device_id: + self.log("Device ID retrieved: {0}".format(device_id)) + + return device_id + def check_rma_task_status(self, task_id, success_message, error_prefix): """ Check the status of an RMA task in Cisco Catalyst Center. From db0fbe021e6d5d83e7da37982af362e1cfb5a4e0 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 14 Aug 2024 18:28:26 +0530 Subject: [PATCH 059/121] RMA bug fixed --- plugins/modules/rma_workflow_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 4afe6db60f..9f618a392b 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -888,7 +888,7 @@ def return_replacement_devices(self): - device_id (str or None): The ID of the device that is ready for replacement, or None if no such device is found. Description: - This method retrieves the ID of a device that is marked as "READY-FOR-REPLACEMENT" from Cisco Catalyst Center. + This method retrieves the ID of a device that is marked as "READY-FOR-REPLACEMENT" from Cisco Catalyst Center. It performs the following steps: - Sends a request to Cisco Catalyst Center to get the list of devices with their replacement status. - Iterates through the list of devices and checks their replacement status. @@ -905,7 +905,7 @@ def return_replacement_devices(self): if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": device_id = device.get("id") if device_id: - self.log("Device ID retrieved: {0}".format(device_id)) + self.log("Device ID retrieved: {0}".format(device_id)) return device_id From eca1cb8ef6f48165bb344707e1694ba3beb5c460 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Wed, 14 Aug 2024 15:05:13 +0200 Subject: [PATCH 060/121] CI after approve --- .circleci/config.yml | 129 +++++++++++++++++++++++++++-- .github/workflows/main.yml | 25 ++++++ .github/workflows/release-prep.yml | 17 ++++ .github/workflows/release.yml | 16 ++++ 4 files changed, 181 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/release-prep.yml create mode 100644 .github/workflows/release.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 286a6c0900..0ee9d1a472 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,6 +9,21 @@ parameters: run-setup: type: boolean default: true + continued: + type: boolean + default: false + GHA_Event: + type: string + default: "" + GHA_Actor: + type: string + default: "" + GHA_Action: + type: string + default: "" + GHA_Meta: + type: string + default: "" run-any: type: boolean default: false @@ -61,7 +76,7 @@ jobs: parameters: ansible_cisco_dnac_version: type: string - default: "6.9.0" + default: "6.17.0" #machine: true docker: @@ -81,7 +96,7 @@ jobs: parameters: ansible_cisco_dnac_version: type: string - default: "6.9.0" + default: "6.17.0" #machine: true docker: @@ -260,7 +275,7 @@ jobs: parameters: ansible_cisco_dnac_version: type: string - default: "6.9.0" + default: "6.17.0" #machine: true docker: @@ -337,10 +352,60 @@ jobs: command: | python ${HOME}/static/pnp_script.py #TODO + main-pr: + parameters: + ansible_cisco_dnac_version: + type: string + default: "6.17.0" + docker: + - image: maniator/gh:v2.49.2 + resource_class: sledzikowy/dnacenter-ansible-minikube + steps: + - run: + name: Clone repo to workspace + command: git clone --depth=1 -b $CIRCLE_BRANCH https://github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME.git . + - run: + name: Create release pr to main + command: | + gh repo set-default $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME + gh pr create --base main --head $CIRCLE_BRANCH --title "Release v<< parameters.ansible_cisco_dnac_version >>" --body "Release v<< parameters.ansible_cisco_dnac_version >>" + + release-job: + parameters: + ansible_cisco_dnac_version: + type: string + default: "6.17.0" + docker: + - image: python:3.8.10 + resource_class: sledzikowy/dnacenter-ansible-minikube + steps: + - run: + name: Clone repo to workspace + command: | + git clone --depth=1 https://github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME.git . + git fetch origin $CIRCLE_BRANCH:work + git checkout work + - restore_cache: + keys: + - collection-<< pipeline.git.revision >> + # - add_ssh_keys: + # fingerprints: + # - "KEY_FINGERPRINT" + - run: + echo create release + #git tag -a v<< parameters.ansible_cisco_dnac_version >> -m "Ansible DNACCollection Version v<< parameters.ansible_cisco_dnac_version >>" + #gh release create "v<< parameters.ansible_cisco_dnac_version >>" --title "DNAC Collection Version v<< parameters.ansible_cisco_dnac_version >>" --latest + #ansible-galaxy collection publish workspace/*.tar.gz --api-key=$GALAXYKEY + workflows: building: - when: << pipeline.parameters.run-setup >> + when: + and: + - or: + - equal: [ run-tests, << pipeline.parameters.GHA_Meta >> ] + - equal: [ true, << pipeline.parameters.run-setup >> ] + - not: << pipeline.parameters.continued >> jobs: - pre @@ -348,7 +413,7 @@ workflows: matrix: parameters: ansible_cisco_dnac_version: - - "6.9.0" + - "6.17.0" requires: - pre @@ -405,8 +470,60 @@ workflows: matrix: parameters: ansible_cisco_dnac_version: - - "6.9.0" + - "6.17.0" # - post_pnp_testing: # requires: # - sanity-tests + release-candidate: + when: + or: + - equal: [ run-release-prep, << pipeline.parameters.GHA_Meta >> ] + jobs: + - build: + matrix: + parameters: + ansible_cisco_dnac_version: + - "6.17.0" + - addrole: + matrix: + parameters: + run-all: + - true + - sanity-tests: + requires: + - addrole + - build + context: + - dnac-servers + matrix: + parameters: + ansible_cisco_dnac_version: + - "6.17.0" + - hold: + type: approval + requires: + - sanity-tests + + - main-pr: + context: + - gh-token + requires: + - hold + + release: + when: + or: + - equal: [ run-release, << pipeline.parameters.GHA_Meta >> ] + jobs: + - build: + matrix: + parameters: + ansible_cisco_dnac_version: + - "6.17.0" + - release-job: + requires: + - build + context: + - gh-token + - galaxy-token \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..f63d285fc4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,25 @@ +name: main +on: + pull_request_review: + types: [submitted] +jobs: + trigger-circleci: + if: github.event.review.state == 'approved' + runs-on: ubuntu-20.04 + steps: + - run: echo -n "${GITHUB_REF}" | sed -r 's/^refs\///' | sed -r 's/merge/head/' > github_ref + - name: Store ref for circleci trigger + uses: actions/upload-artifact@v4 + with: + name: github_ref-${{ github.run_id }} + path: github_ref + automerge: + runs-on: ubuntu-latest + steps: + - uses: reitermarkus/automerge@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + merge-method: rebase + do-not-merge-labels: never-merge +# pull-request: ${{ github.event.inputs.pull-request }} + dry-run: true \ No newline at end of file diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml new file mode 100644 index 0000000000..9d02483231 --- /dev/null +++ b/.github/workflows/release-prep.yml @@ -0,0 +1,17 @@ +# name: release-prep +# on: +# push: +# branches: +# - '**-rc' + +# jobs: +# trigger-circleci: +# runs-on: ubuntu-20.04 +# steps: +# - name: Trigger CircleCI +# id: trigger-circle-ci +# uses: CircleCI-Public/trigger-circleci-pipeline-action@v1.2.0 +# with: +# GHA_Meta: "run-release-prep" +# env: +# CCI_TOKEN: ${{ secrets.CCI_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000000..936ca9d29a --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,16 @@ +# name: release +# on: +# push: +# branches: +# - 'main' +# jobs: +# trigger-circleci: +# runs-on: ubuntu-20.04 +# steps: +# - name: Trigger CircleCI +# id: trigger-circle-ci +# uses: CircleCI-Public/trigger-circleci-pipeline-action@v1.2.0 +# with: +# GHA_Meta: "run-release" +# env: +# CCI_TOKEN: ${{ secrets.CCI_TOKEN }} \ No newline at end of file From 2ecb6fb933d78d59d18fa631ad5e1c9532dbf57a Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Wed, 14 Aug 2024 16:11:33 +0200 Subject: [PATCH 061/121] fixes --- .circleci/config.yml | 83 +++++++------------------ .github/workflows/integration_tests.yml | 22 +++++++ 2 files changed, 46 insertions(+), 59 deletions(-) create mode 100644 .github/workflows/integration_tests.yml diff --git a/.circleci/config.yml b/.circleci/config.yml index 0ee9d1a472..fe1703c6b5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,20 +68,18 @@ parameters: default: false run-networkcompliance: type: boolean - default: false + default: false + ansible_cisco_dnac_version: + type: string + default: "6.17.0" jobs: pre: - parameters: - ansible_cisco_dnac_version: - type: string - default: "6.17.0" - #machine: true docker: - image: python:3.8.10 - resource_class: madhansansel/dnacenter-ansible + resource_class: madhansansel/dnacenter-ansible steps: - run: @@ -92,16 +90,11 @@ jobs: echo "CIRCLE_PROJECT_REPONAME: $CIRCLE_PROJECT_REPONAME" echo "CIRCLE_PROJECT_BRANCHNAME: $CIRCLE_PROJECT_BRANCHNAME" - build: - parameters: - ansible_cisco_dnac_version: - type: string - default: "6.17.0" - + build: #machine: true docker: - image: python:3.8.10 - resource_class: madhansansel/dnacenter-ansible + resource_class: madhansansel/dnacenter-ansible steps: - run: name: Debug information @@ -135,7 +128,7 @@ jobs: # Install ansible, dnacentersdk pip install --upgrade pip pip install -r test-requirements.txt - # Build collection and store resulting tarball in directory $HOME/.cache/v<< parameters.ansible_cisco_dnac_version >>/collection-tarballs + # Build collection and store resulting tarball in directory $HOME/.cache/v<< pipeline.parameters.ansible_cisco_dnac_version >>/collection-tarballs ansible-galaxy collection build --force --output-path workspace/ - save_cache: key: collection-<< pipeline.git.revision >> @@ -147,10 +140,14 @@ jobs: - ~/.cache/pip addrole: + parameters: + run-all: + type: boolean + default: false #machine: true docker: - image: python:3.8.10 - resource_class: madhansansel/dnacenter-ansible + resource_class: madhansansel/dnacenter-ansible steps: - run: @@ -272,15 +269,10 @@ jobs: paths: - 'ccc_roles.yml' sanity-tests: - parameters: - ansible_cisco_dnac_version: - type: string - default: "6.17.0" - #machine: true docker: - image: python:3.8.10 - resource_class: madhansansel/dnacenter-ansible + resource_class: madhansansel/dnacenter-ansible parallelism: 4 steps: - run: @@ -342,7 +334,7 @@ jobs: #machine: true docker: - image: python:3.8.10 - resource_class: madhansansel/dnacenter-ansible + resource_class: madhansansel/dnacenter-ansible steps: - when: @@ -353,13 +345,9 @@ jobs: python ${HOME}/static/pnp_script.py #TODO main-pr: - parameters: - ansible_cisco_dnac_version: - type: string - default: "6.17.0" docker: - image: maniator/gh:v2.49.2 - resource_class: sledzikowy/dnacenter-ansible-minikube + resource_class: madhansansel/dnacenter-ansible steps: - run: name: Clone repo to workspace @@ -368,16 +356,12 @@ jobs: name: Create release pr to main command: | gh repo set-default $CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME - gh pr create --base main --head $CIRCLE_BRANCH --title "Release v<< parameters.ansible_cisco_dnac_version >>" --body "Release v<< parameters.ansible_cisco_dnac_version >>" + gh pr create --base main --head $CIRCLE_BRANCH --title "Release v<< pipeline.parameters.ansible_cisco_dnac_version >>" --body "Release v<< pipeline.parameters.ansible_cisco_dnac_version >>" release-job: - parameters: - ansible_cisco_dnac_version: - type: string - default: "6.17.0" docker: - image: python:3.8.10 - resource_class: sledzikowy/dnacenter-ansible-minikube + resource_class: madhansansel/dnacenter-ansible steps: - run: name: Clone repo to workspace @@ -393,8 +377,8 @@ jobs: # - "KEY_FINGERPRINT" - run: echo create release - #git tag -a v<< parameters.ansible_cisco_dnac_version >> -m "Ansible DNACCollection Version v<< parameters.ansible_cisco_dnac_version >>" - #gh release create "v<< parameters.ansible_cisco_dnac_version >>" --title "DNAC Collection Version v<< parameters.ansible_cisco_dnac_version >>" --latest + #git tag -a v<< pipeline.parameters.ansible_cisco_dnac_version >> -m "Ansible DNACCollection Version v<< pipeline.parameters.ansible_cisco_dnac_version >>" + #gh release create "v<< pipeline.parameters.ansible_cisco_dnac_version >>" --title "DNAC Collection Version v<< pipeline.parameters.ansible_cisco_dnac_version >>" --latest #ansible-galaxy collection publish workspace/*.tar.gz --api-key=$GALAXYKEY workflows: @@ -404,16 +388,12 @@ workflows: and: - or: - equal: [ run-tests, << pipeline.parameters.GHA_Meta >> ] - - equal: [ true, << pipeline.parameters.run-setup >> ] +# - equal: [ true, << pipeline.parameters.run-setup >> ] - not: << pipeline.parameters.continued >> jobs: - pre - build: - matrix: - parameters: - ansible_cisco_dnac_version: - - "6.17.0" requires: - pre @@ -424,6 +404,7 @@ workflows: config-path: .circleci/config.yml mapping: | .* run-setup false + .* continued true plugins/.* run-any true tests/integration/.* run-any true @@ -467,10 +448,6 @@ workflows: - addrole context: - dnac-servers - matrix: - parameters: - ansible_cisco_dnac_version: - - "6.17.0" # - post_pnp_testing: # requires: # - sanity-tests @@ -480,11 +457,7 @@ workflows: or: - equal: [ run-release-prep, << pipeline.parameters.GHA_Meta >> ] jobs: - - build: - matrix: - parameters: - ansible_cisco_dnac_version: - - "6.17.0" + - build - addrole: matrix: parameters: @@ -496,10 +469,6 @@ workflows: - build context: - dnac-servers - matrix: - parameters: - ansible_cisco_dnac_version: - - "6.17.0" - hold: type: approval requires: @@ -516,11 +485,7 @@ workflows: or: - equal: [ run-release, << pipeline.parameters.GHA_Meta >> ] jobs: - - build: - matrix: - parameters: - ansible_cisco_dnac_version: - - "6.17.0" + - build - release-job: requires: - build diff --git a/.github/workflows/integration_tests.yml b/.github/workflows/integration_tests.yml new file mode 100644 index 0000000000..a45890b477 --- /dev/null +++ b/.github/workflows/integration_tests.yml @@ -0,0 +1,22 @@ +name: integration-tests +on: + workflow_run: + workflows: + - main +jobs: + trigger-circleci: + runs-on: ubuntu-20.04 + if: github.event.workflow_run.conclusion == 'success' + steps: + - uses: actions/download-artifact@v4 + with: + name: github_ref-${{ github.event.workflow_run.id }} + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + - name: Trigger circleci + run: | + printf '{"branch": "%s" ,"parameters": {"GHA_Meta":"run-tests"}}' $(cat github_ref) > req_body + curl -X POST --location "https://circleci.com/api/v2/project/gh/madhansansel/dnacenter-ansible/pipeline" \ + -H "Content-Type: application/json" \ + -H "Circle-Token: ${{ secrets.CCI_TOKEN }}" \ + -d "@req_body" \ No newline at end of file From 2dfb813c8b2ec88c4ce1c3137ebadcbfa098f4e7 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Wed, 14 Aug 2024 16:13:30 +0200 Subject: [PATCH 062/121] add codeowners --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000000..c149cd20f5 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +* @madhansansel \ No newline at end of file From a8dcb3637aee762cdd79e449194a392ad259d0c1 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 15 Aug 2024 13:34:18 +0530 Subject: [PATCH 063/121] Updated rma_workflow_manager.py file --- plugins/modules/rma_workflow_manager.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 9f618a392b..39a9498bf5 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -843,7 +843,7 @@ def unmark_device_for_replacement(self): - Handles any exceptions that occur during the process. """ self.log("Unmarking device for replacement...") - device_id = self.return_replacement_devices() + device_id = self.get_ready_for_replacement_device_id() import_params = dict( payload=[{ @@ -877,25 +877,21 @@ def unmark_device_for_replacement(self): self.log(self.msg, "ERROR") return self - def return_replacement_devices(self): + def get_ready_for_replacement_device_id(self): """ - Retrieves the ID of the device ready for replacement in Cisco Catalyst Center. + Retrieves the ID of the first device marked as "READY-FOR-REPLACEMENT" in Cisco Catalyst Center. Parameters: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. Returns: - - device_id (str or None): The ID of the device that is ready for replacement, or None if no such device is found. + - device_id (str or None): The ID of the first device ready for replacement, or None if no such device is found. Description: - This method retrieves the ID of a device that is marked as "READY-FOR-REPLACEMENT" from Cisco Catalyst Center. - It performs the following steps: - - Sends a request to Cisco Catalyst Center to get the list of devices with their replacement status. - - Iterates through the list of devices and checks their replacement status. - - If a device is found with the status "READY-FOR-REPLACEMENT", its ID is extracted. - - The method returns the ID of the first device with the "READY-FOR-REPLACEMENT" status found, or None if no such device is present. + - This method fetches a list of devices with their replacement status from Cisco Catalyst Center. + - It then checks for the first device with a "READY-FOR-REPLACEMENT" status and returns its ID. + - The method exits early if such a device is found. """ - device_id = None response = self.dnac._exec( family="device_replacement", function='return_replacement_devices_with_details' @@ -904,10 +900,11 @@ def return_replacement_devices(self): for device in devices: if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": device_id = device.get("id") - if device_id: - self.log("Device ID retrieved: {0}".format(device_id)) + self.log("Found ready-for-replacement device with ID: {0}".format(device_id)) + return device_id - return device_id + self.log("No devices found with status 'READY-FOR-REPLACEMENT'.") + return None def check_rma_task_status(self, task_id, success_message, error_prefix): """ From 82f7790c7dc3040c46274d7321aa347ad6eee432 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 15 Aug 2024 20:45:25 +0530 Subject: [PATCH 064/121] user role workflow feature bugs fixed --- plugins/modules/user_role_workflow_manager.py | 78 ++++++++++++------- 1 file changed, 50 insertions(+), 28 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 849300852b..636fae99ea 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -445,10 +445,10 @@ - python >= 3.9.19 notes: - SDK Methods used - - user_and_roles.UserandRoles.get_user_ap_i - - user_and_roles.UserandRoles.add_user_ap_i - - user_and_roles.UserandRoles.update_user_ap_i - - user_and_roles.UserandRoles.delete_user_ap_i + - user_and_roles.UserandRoles.get_user_api + - user_and_roles.UserandRoles.add_user_api + - user_and_roles.UserandRoles.update_user_api + - user_and_roles.UserandRoles.delete_user_api - Paths used - get /dna/system/api/v1/user - post /dna/system/api/v1/user @@ -963,6 +963,33 @@ def validate_string_field(self, field_value, regex, error_message, error_message if field_value and not regex.match(field_value): error_messages.append(error_message) + def validate_password(self, password, error_messages): + """ + Validate password from password and append error message if it does not match the criteria. + """ + + if password: + password_is_valid = False + password_regex_msg = "Password must be 8 to 20 characters long and should contain characters from at \ +least three of the following classes: lowercase characters, uppercase characters, digits and special characters." + + self.log(password_regex_msg, "DEBUG") + password_regexs = [ + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*[\W_]).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_])(?!.*\d).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*\d)(?=.*[\W_])(?!.*[A-Z]).{8,20}$'), + re.compile(r'^(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*[a-z]).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,20}$') + ] + + for password_regex in password_regexs: + if password_regex.match(password): + password_is_valid = True + break + + if not password_is_valid: + error_messages.append(password_regex_msg) + def validate_role_parameters(self, role_key, params_list, role_config, role_param_map, error_messages): """ Helper function to validate role parameters. @@ -1073,14 +1100,9 @@ def valid_user_config_parameters(self, user_config): email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") email = user_config.get("email") email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) - if email: - self.validate_string_field(email, email_regex, email_regex_msg, error_messages) + self.validate_string_field(email, email_regex, email_regex_msg, error_messages) - password_regex = re.compile(r"^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$") - password = user_config.get("password") - password_regex_msg = "password: 'Password' does not meet complexity requirements for password: {0}".format(password) - if password: - self.validate_string_field(password, password_regex, password_regex_msg, error_messages) + self.validate_password(user_config.get("password"), error_messages) username = user_config.get("username") self.validate_string_field(username, regex_name_validation, @@ -1323,9 +1345,9 @@ def get_current_config(self, input_config): Description: - Checks the existence of a user and retrieves user details in Cisco Catalyst Center - by querying the "get_users_ap_i" function in the "user_and_roles" family. + by querying the "get_users_api" function in the "user_and_roles" family. - Checks the existence of a role and retrieves role details in Cisco Catalyst Center - by querying the "get_roles_ap_i" function in the "user_and_roles" family. + by querying the "get_roles_api" function in the "user_and_roles" family. - Logs errors if required parameters are missing in the playbook config. """ user_exists = False @@ -1395,7 +1417,7 @@ def create_user(self, user_params): - response (dict): The API response from the "create_user" function. Description: - Sends a request to create a new user in Cisco Catalyst Center using the provided user parameters. - - Uses the "user_and_roles" family and "add_user_ap_i" function for the API call. + - Uses the "user_and_roles" family and "add_user_api" function for the API call. - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ @@ -1411,7 +1433,7 @@ def create_user(self, user_params): self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="add_user_ap_i", + function="add_user_api", op_modifies=True, params=user_params, ) @@ -1432,7 +1454,7 @@ def create_role(self, role_params): - response (dict): The API response from the "create_role" function. Description: - Sends a request to create a new role in Cisco Catalyst Center using the provided role parameters. - - Utilizes the "user_and_roles" family and "add_role_ap_i" function for the API request. + - Utilizes the "user_and_roles" family and "add_role_api" function for the API request. - Logs the provided role parameters and the received API response. - Returns the API response from the "create_role" function. """ @@ -1440,7 +1462,7 @@ def create_role(self, role_params): self.log("Create role with role_info_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="add_role_ap_i", + function="add_role_api", op_modifies=True, params=role_params, ) @@ -1460,12 +1482,12 @@ def get_user(self): - response (dict): The API response from the "get_users_api" function. Description: - Sends a request to retrieve users from Cisco Catalyst Center using the "user_and_roles" family - and "get_users_ap_i" function. + and "get_users_api" function. - Logs the received API response and returns it. """ response = self.dnac._exec( family="user_and_roles", - function="get_users_ap_i", + function="get_users_api", op_modifies=True, params={"invoke_source": "external"}, ) @@ -1481,12 +1503,12 @@ def get_role(self): - response (dict): The API response from the "get_roles" function. Description: - Sends a request to retrieve roles from Cisco Catalyst Center using the "user_and_roles" family - and "get_roles_ap_i" function. + and "get_roles_api" function. - Logs the received API response and returns it. """ response = self.dnac._exec( family="user_and_roles", - function="get_roles_ap_i", + function="get_roles_api", op_modifies=True, ) self.log("Received API response from get_roles_api: {0}".format(str(response)), "DEBUG") @@ -2377,7 +2399,7 @@ def update_user(self, user_params): self.log("Updating user with parameters: {0}".format(user_params), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="update_user_ap_i", + function="update_user_api", op_modifies=True, params=user_params, ) @@ -2396,13 +2418,13 @@ def update_role(self, role_params): - This method sends a request to update a role in Cisco Catalyst Center using the provided role parameters. It first logs the role parameters at the "DEBUG" level. Then it calls the"_exec" method of the "dnac" object to perform the API request. The API request is specified with the "user_and_roles" family - and the "update_role_ap_i" function. The method logs the received API response at the "DEBUG" level and + and the "update_role_api" function. The method logs the received API response at the "DEBUG" level and finally returns the response. """ self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="update_role_ap_i", + function="update_role_api", op_modifies=True, params=role_params, ) @@ -2755,12 +2777,12 @@ def delete_user(self, user_params): Description: - This method sends a request to delete a user in Cisco Catalyst Center using the provided user parameters. - It logs the response and returns it. - - The function uses the "user_and_roles" family and the "delete_user_ap_i" function from the Cisco Catalyst Center API. + - The function uses the "user_and_roles" family and the "delete_user_api" function from the Cisco Catalyst Center API. """ self.log("delete user with user_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="delete_user_ap_i", + function="delete_user_api", op_modifies=True, params=user_params, ) @@ -2778,13 +2800,13 @@ def delete_role(self, role_params): Description: - This method sends a request to delete a role in Cisco Catalyst Center using the provided role parameters. - It logs the response and returns it. - - The function uses the "user_and_roles" family and the "delete_role_ap_i" function from the Cisco Catalyst Center API. + - The function uses the "user_and_roles" family and the "delete_role_api" function from the Cisco Catalyst Center API. """ try: self.log("delete role with role_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", - function="delete_role_ap_i", + function="delete_role_api", op_modifies=True, params=role_params, ) From 27bf429f9620a2a14be7884e515992fd131ed476 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 15 Aug 2024 21:26:33 +0530 Subject: [PATCH 065/121] bug fixed --- plugins/modules/user_role_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 636fae99ea..403cdb5978 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1097,7 +1097,7 @@ def valid_user_config_parameters(self, user_config): self.validate_string_field(last_name, regex_name_validation, "last_name: '{0}' {1}".format(last_name, regex_name_validation_msg), error_messages) - email_regex = re.compile(r"[^@]+@[^@]+\.[^@]+") + email_regex = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,2}(?:\.[a-zA-Z]{2,2})*$") email = user_config.get("email") email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) self.validate_string_field(email, email_regex, email_regex_msg, error_messages) From db5caa270c32ccb1b4ea2fce03af8146a205e877 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 16 Aug 2024 08:58:26 +0530 Subject: [PATCH 066/121] updated user_role_workflow_manager.py file --- plugins/modules/user_role_workflow_manager.py | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 403cdb5978..4091c32dbc 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -965,30 +965,38 @@ def validate_string_field(self, field_value, regex, error_message, error_message def validate_password(self, password, error_messages): """ - Validate password from password and append error message if it does not match the criteria. - """ - - if password: - password_is_valid = False - password_regex_msg = "Password must be 8 to 20 characters long and should contain characters from at \ + Validate the provided password and append an error message if it does not meet the criteria. + Args: + - password (str): The password to be validated. Must be a string. + - error_messages (list): A list where error messages are appended if the password does not meet the criteria. + Returns: + None: This function does not return a value, but it may append an error message to `error_messages` if the password is invalid. + Criteria: + - The password must be 8 to 20 characters long. + - The password must include characters from at least three of the following classes: + lowercase letters, uppercase letters, digits, and special characters. + """ + is_valid_password = False + password_criteria_message = "Password must be 8 to 20 characters long and should contain characters from at \ least three of the following classes: lowercase characters, uppercase characters, digits and special characters." - self.log(password_regex_msg, "DEBUG") - password_regexs = [ - re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*[\W_]).{8,20}$'), - re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_])(?!.*\d).{8,20}$'), - re.compile(r'^(?=.*[a-z])(?=.*\d)(?=.*[\W_])(?!.*[A-Z]).{8,20}$'), - re.compile(r'^(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*[a-z]).{8,20}$'), - re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,20}$') - ] + self.log(password_criteria_message, "DEBUG") + password_regexs = [ + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*[\W_]).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*[\W_])(?!.*\d).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*\d)(?=.*[\W_])(?!.*[A-Z]).{8,20}$'), + re.compile(r'^(?=.*[A-Z])(?=.*\d)(?=.*[\W_])(?!.*[a-z]).{8,20}$'), + re.compile(r'^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,20}$') + ] - for password_regex in password_regexs: - if password_regex.match(password): - password_is_valid = True - break + for password_regex in password_regexs: + if password_regex.match(password): + is_valid_password = True + break - if not password_is_valid: - error_messages.append(password_regex_msg) + if not is_valid_password: + self.log("Password validation failed: {0}".format(password_criteria_message), "DEBUG") + error_messages.append(password_criteria_message) def validate_role_parameters(self, role_key, params_list, role_config, role_param_map, error_messages): """ @@ -1102,7 +1110,10 @@ def valid_user_config_parameters(self, user_config): email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) self.validate_string_field(email, email_regex, email_regex_msg, error_messages) - self.validate_password(user_config.get("password"), error_messages) + password = user_config.get("password") + + if password: + self.validate_password(password, error_messages) username = user_config.get("username") self.validate_string_field(username, regex_name_validation, From 4342e4d0625dbefca630b8cb6b6f78ef02dabb40 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 16 Aug 2024 09:12:27 +0530 Subject: [PATCH 067/121] updated user_role_workflow_manager.py file --- plugins/modules/user_role_workflow_manager.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 4091c32dbc..3ee3642954 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -977,8 +977,10 @@ def validate_password(self, password, error_messages): lowercase letters, uppercase letters, digits, and special characters. """ is_valid_password = False - password_criteria_message = "Password must be 8 to 20 characters long and should contain characters from at \ -least three of the following classes: lowercase characters, uppercase characters, digits and special characters." + password_criteria_message = ( + "Password must be 8 to 20 characters long and include characters from at least three of " + "the following classes: lowercase letters, uppercase letters, digits, and special characters." + ) self.log(password_criteria_message, "DEBUG") password_regexs = [ @@ -1105,7 +1107,7 @@ def valid_user_config_parameters(self, user_config): self.validate_string_field(last_name, regex_name_validation, "last_name: '{0}' {1}".format(last_name, regex_name_validation_msg), error_messages) - email_regex = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,2}(?:\.[a-zA-Z]{2,2})*$") + email_regex = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2,2})*$") email = user_config.get("email") email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) self.validate_string_field(email, email_regex, email_regex_msg, error_messages) From 612744a0d51c6f44028760ea958d715bfd86067c Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Mon, 19 Aug 2024 10:26:39 +0530 Subject: [PATCH 068/121] CSCwm06839 --- plugins/modules/rma_workflow_manager.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 39a9498bf5..6a418d551a 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -868,8 +868,6 @@ def unmark_device_for_replacement(self): ) self.status = task_result["status"] self.msg = "RMA failed to replace the device: {0}".format(task_result["msg"]) - if self.status == "success": - self.result['changed'] = True except Exception: self.status = "failed" From 1fe1f9e9eea0621794e48ff62ed2c498fd71de0e Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Mon, 19 Aug 2024 10:29:07 +0530 Subject: [PATCH 069/121] CSCwm06839 - RMA bug fixed --- playbooks/rma_workflow_manager.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/playbooks/rma_workflow_manager.yml b/playbooks/rma_workflow_manager.yml index 8f3189b54b..99ac72618d 100644 --- a/playbooks/rma_workflow_manager.yml +++ b/playbooks/rma_workflow_manager.yml @@ -23,6 +23,6 @@ timeout_interval: 100 state: replaced config: - - faulty_device_ip_address: 204.1.2.9 - replacement_device_ip_address: 204.1.2.10 + - faulty_device_ip_address: 204.1.2.9 + replacement_device_ip_address: 204.1.2.10 register: result From ab5eb9cdbb9ea2aaa6f3d7f0f25f4242c68fb51a Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Mon, 19 Aug 2024 18:53:04 +0530 Subject: [PATCH 070/121] user_role_workflow_feature enhancement done and bugs fixed --- plugins/modules/user_role_workflow_manager.py | 76 +++++++++++-------- 1 file changed, 45 insertions(+), 31 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 3ee3642954..4c806ed129 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -321,7 +321,7 @@ overall: description: Provides the same choice for all sub-parameters. choices: ["deny", "read", "write"] - default: "read" + default: "deny" type: str apis: description: Access Cisco Catalyst Center through REST APIs to drive value. @@ -426,13 +426,13 @@ remote_device_support: description: Allow Cisco support team to remotely troubleshoot any network devices managed by Cisco DNA Center. choices: ["deny", "read", "write"] - default: "read" + default: "deny" type: str scheduler: description: Run, schedule, and monitor network tasks and activities such as deploying policies, provisioning, or upgrading the network, integrated with other back-end services. choices: ["deny", "read", "write"] - default: "read" + default: "write" type: str search: description: Search for various objects in Cisco Catalyst Center, including sites, @@ -565,7 +565,7 @@ monitoring_settings: "read" troubleshooting_tools: "deny" network_analytics: - data_access: "write" + - data_access: "write" network_design: - advanced_network_settings: "deny" image_repository: "deny" @@ -1043,8 +1043,8 @@ def valid_role_config_parameters(self, role_config): error_messages = [] role_name = role_config.get("role_name") - role_name_regex = re.compile(r"^[A-Za-z0-9_-]+$") - role_name_regex_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." + role_name_regex = re.compile(r"^[a-zA-Z0-9._-]{,25}$") + role_name_regex_msg = "Role names must be 0 to 25 characters long and should contain only letters, numbers, dots, underscore, and hyphen." self.validate_string_field(role_name, role_name_regex, "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) @@ -1096,18 +1096,18 @@ def valid_user_config_parameters(self, user_config): """ self.log("Validating user configuration parameters...", "INFO") error_messages = [] - regex_name_validation = re.compile(r"^[A-Za-z0-9_-]+$") - regex_name_validation_msg = "must only contain letters, numbers, underscores and hyphens and should not contain spaces or other special characters." + name_regex = re.compile(r"^[A-Za-z0-9@._-]{2,50}$") + name_regex_msg = "can have alphanumeric characters only and must be 2 to 50 characters long." first_name = user_config.get("first_name") - self.validate_string_field(first_name, regex_name_validation, - "first_name: '{0}' {1}".format(first_name, regex_name_validation_msg), error_messages) + self.validate_string_field(first_name, name_regex, + "first_name: First name '{0}' {1}".format(first_name, name_regex_msg), error_messages) last_name = user_config.get("last_name") - self.validate_string_field(last_name, regex_name_validation, - "last_name: '{0}' {1}".format(last_name, regex_name_validation_msg), error_messages) + self.validate_string_field(last_name, name_regex, + "last_name: Last name '{0}' {1}".format(last_name, name_regex_msg), error_messages) - email_regex = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2,2})*$") + email_regex = re.compile(r"^[A-Za-z0-9!#$%^&*'?{}|./_+=-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2,3})*$") email = user_config.get("email") email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) self.validate_string_field(email, email_regex, email_regex_msg, error_messages) @@ -1117,9 +1117,11 @@ def valid_user_config_parameters(self, user_config): if password: self.validate_password(password, error_messages) + username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") + username_regex_msg = "The username cannot contain any special characters and must be 3 to 50 characters long." username = user_config.get("username") - self.validate_string_field(username, regex_name_validation, - "username: '{0}' {1}".format(username, regex_name_validation_msg), error_messages) + self.validate_string_field(username, username_regex, + "username: '{0}' {1}".format(username, username_regex_msg), error_messages) if user_config.get("role_list"): param_spec = dict(type="list", elements="str") @@ -1239,7 +1241,7 @@ def get_diff_merged(self, config): consolidated_data, update_required_param = self.role_requires_update(self.have["current_role_config"], desired_role) if not consolidated_data: - self.msg = "Role does not need any update" + self.msg = "Role with role_name '{0}' already exist and the role does not need any update".format(self.have.get("role_name")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1274,7 +1276,7 @@ def get_diff_merged(self, config): (consolidated_data, update_required_param) = self.user_requires_update(self.have["current_user_config"], self.have["current_role_id_config"]) if not consolidated_data: - self.msg = "User does not need any update" + self.msg = "User with username '{0}' already exist and the user does not need any update".format(self.have.get("username")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1442,6 +1444,10 @@ def create_user(self, user_params): if key not in user_params: missing_keys.append(key) + if missing_keys: + error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) + return {"error": error_message} + try: self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( @@ -1454,7 +1460,8 @@ def create_user(self, user_params): return response except Exception: - error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) + error_message = "The catalyst center user '{0}' does not have the necessary permissions to create or update user through the API.".format( + self.payload.get("dnac_username")) return {"error": error_message} def create_role(self, role_params): @@ -1483,7 +1490,8 @@ def create_role(self, role_params): return response except Exception: - error_message = "An error occurred while creating the role without access-level parameters and permissions" + error_message = "The catalyst center user '{0}' does not have the necessary permissions to create role through the API.".format( + self.payload.get("dnac_username")) return {"error": error_message} def get_user(self): @@ -2434,16 +2442,21 @@ def update_role(self, role_params): and the "update_role_api" function. The method logs the received API response at the "DEBUG" level and finally returns the response. """ - self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") - response = self.dnac._exec( - family="user_and_roles", - function="update_role_api", - op_modifies=True, - params=role_params, - ) - self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") + try: + self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") + response = self.dnac._exec( + family="user_and_roles", + function="update_role_api", + op_modifies=True, + params=role_params, + ) + self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") + return response - return response + except Exception: + error_message = "The catalyst center user '{0}' does not have the necessary permissions to update role through the API.".format( + self.payload.get("dnac_username")) + return {"error": error_message} def find_denied_permissions(self, config, parent_key=""): """ @@ -2774,7 +2787,8 @@ def get_diff_deleted(self, config): self.log(self.msg, "INFO") return self - self.msg = "Please provide a valid 'username' or 'email' for user deletion" + self.msg = "Please provide a valid 'username' or 'email' for user deletion or The catalyst center user '{0}' does not have the \ +necessary permissions to delete user through the API.".format(self.payload.get("dnac_username")) self.log(self.msg, "ERROR") self.status = "failed" return self @@ -2825,8 +2839,8 @@ def delete_role(self, role_params): ) self.log("Received API response from delete_role: {0}".format(str(response)), "DEBUG") except Exception: - error_message = "An error occurred while deleting the role. Check whether user(s) are assigned to this role \ - {0}".format(str(self.have.get("role_name"))) + error_message = "The catalyst center user '{0}' does not have the necessary permissions to delete role through the API or An error occurred while \ +deleting the role. Check whether user(s) are assigned to the role '{1}'".format(self.payload.get("dnac_username"), self.have.get("role_name")) return {"error": error_message} From 1969e0ec23fec5222c3a6417aac7bdb70ce7c780 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 20 Aug 2024 09:43:24 +0530 Subject: [PATCH 071/121] updated user_role_workflow_feature code --- plugins/modules/user_role_workflow_manager.py | 88 +++++++++++-------- 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 4c806ed129..738f30e795 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1044,7 +1044,7 @@ def valid_role_config_parameters(self, role_config): role_name = role_config.get("role_name") role_name_regex = re.compile(r"^[a-zA-Z0-9._-]{,25}$") - role_name_regex_msg = "Role names must be 0 to 25 characters long and should contain only letters, numbers, dots, underscore, and hyphen." + role_name_regex_msg = "Role names must be 1 to 25 characters long and should contain only letters, numbers, periods, underscores, and hyphens." self.validate_string_field(role_name, role_name_regex, "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) @@ -1118,7 +1118,7 @@ def valid_user_config_parameters(self, user_config): self.validate_password(password, error_messages) username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") - username_regex_msg = "The username cannot contain any special characters and must be 3 to 50 characters long." + username_regex_msg = "The username must not contain any special characters and must be 3 to 50 characters long." username = user_config.get("username") self.validate_string_field(username, username_regex, "username: '{0}' {1}".format(username, username_regex_msg), error_messages) @@ -1237,11 +1237,11 @@ def get_diff_merged(self, config): desired_role = self.generate_role_payload(self.want, "update") self.log("desired role with config {0}".format(str(desired_role)), "DEBUG") - if "error" not in desired_role: + if "error_message" not in desired_role: consolidated_data, update_required_param = self.role_requires_update(self.have["current_role_config"], desired_role) if not consolidated_data: - self.msg = "Role with role_name '{0}' already exist and the role does not need any update".format(self.have.get("role_name")) + self.msg = "Role with role_name '{0}' already exists and does not require an update.".format(self.have.get("role_name")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1257,7 +1257,7 @@ def get_diff_merged(self, config): self.log("Creating role with config {0}".format(str(config)), "DEBUG") role_info_params = self.generate_role_payload(self.want, "create") - if "error" not in role_info_params: + if "error_message" not in role_info_params: filtered_data, overall_update_required = self.get_permissions(self.want, role_info_params, "create") denied_permissions = self.find_denied_permissions(self.want) denied_required, create_role_params = self.remove_denied_operations(filtered_data, denied_permissions) @@ -1276,7 +1276,7 @@ def get_diff_merged(self, config): (consolidated_data, update_required_param) = self.user_requires_update(self.have["current_user_config"], self.have["current_role_id_config"]) if not consolidated_data: - self.msg = "User with username '{0}' already exist and the user does not need any update".format(self.have.get("username")) + self.msg = "User with username '{0}' already exists and does not require an update.".format(self.have.get("username")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1291,7 +1291,7 @@ def get_diff_merged(self, config): user_info_params = self.snake_to_camel_case(update_param) task_response = self.update_user(user_info_params) else: - task_response = {"error": "The role name in the user details role_list is not present in the Cisco Catalyst Center," + task_response = {"error_message": "The role name in the user details role_list is not present in the Cisco Catalyst Center," " Please provide a valid role name"} else: # Create the user @@ -1323,10 +1323,10 @@ def get_diff_merged(self, config): user_info_params = self.snake_to_camel_case(user_details) task_response = self.create_user(user_info_params) else: - task_response = {"error": "The role name in the user details role_list is not present in the Cisco Catalyst Center," + task_response = {"error_message": "The role name in the user details role_list is not present in the Cisco Catalyst Center," " Please provide a valid role name"} - if task_response and "error" not in task_response: + if task_response and "error_message" not in task_response: self.log("Task respoonse {0}".format(str(task_response)), "INFO") responses["operation"] = {"response": task_response} self.msg = responses @@ -1336,7 +1336,7 @@ def get_diff_merged(self, config): self.log(self.msg, "INFO") return self - self.msg = task_response.get("error") + self.msg = task_response.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self @@ -1445,8 +1445,8 @@ def create_user(self, user_params): missing_keys.append(key) if missing_keys: - error_message = "Mandatory parameter(s) {0} not present in the user details".format(", ".join(missing_keys)) - return {"error": error_message} + error_message = "Mandatory parameter(s) '{0}' not present in the user details.".format(", ".join(missing_keys)) + return {"error_message": error_message} try: self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") @@ -1459,10 +1459,11 @@ def create_user(self, user_params): self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") return response - except Exception: - error_message = "The catalyst center user '{0}' does not have the necessary permissions to create or update user through the API.".format( + except Exception as e: + self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") + error_message = "The Catalyst Center user '{0}' does not have the necessary permissions to 'create or update' a user through the API.".format( self.payload.get("dnac_username")) - return {"error": error_message} + return {"error_message": error_message} def create_role(self, role_params): """ @@ -1489,10 +1490,11 @@ def create_role(self, role_params): self.log("Received API response from create_role: {0}".format(str(response)), "DEBUG") return response - except Exception: - error_message = "The catalyst center user '{0}' does not have the necessary permissions to create role through the API.".format( + except Exception as e: + self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") + error_message = "The Catalyst Center user '{0}' does not have the necessary permissions to 'create a role' through the API.".format( self.payload.get("dnac_username")) - return {"error": error_message} + return {"error_message": error_message} def get_user(self): """ @@ -1583,7 +1585,7 @@ def process_assurance_rules(self, role_config, role_operation, unique_types): if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for assurance resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -1647,7 +1649,7 @@ def process_network_analytics_rules(self, role_config, role_operation, unique_ty if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for network analytics resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -1708,7 +1710,7 @@ def process_network_design_rules(self, role_config, role_operation, unique_types if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for network design resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -1767,7 +1769,7 @@ def process_network_provision_rules(self, role_config, role_operation, unique_ty if not isinstance(role_config["network_provision"], list): error_message = "The given network_provision is not in type: list" self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} for provision in role_config["network_provision"]: for resource_name, permission in provision.items(): @@ -1783,7 +1785,7 @@ def process_network_provision_rules(self, role_config, role_operation, unique_ty if sub_permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for network provision for sub-resource {1}".format(sub_permission, sub_resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if sub_permission == "deny": self.log("Skipping sub-resource {0} because permission is 'deny'".format(sub_resource_name), "DEBUG") @@ -1819,7 +1821,7 @@ def process_network_provision_rules(self, role_config, role_operation, unique_ty if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for network provision resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -1892,7 +1894,7 @@ def process_network_services_rules(self, role_config, role_operation, unique_typ if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for network services resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -1937,7 +1939,7 @@ def process_platform_rules(self, role_config, unique_types): if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for platform resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -2008,7 +2010,7 @@ def process_security_rules(self, role_config, role_operation, unique_types): if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for security resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -2079,7 +2081,7 @@ def process_system_rules(self, role_config, role_operation, unique_types): if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for system resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -2142,7 +2144,7 @@ def process_utilities_rules(self, role_config, role_operation, unique_types): if permission not in ["read", "write", "deny"]: error_message = "Invalid permission {0} for utilities resource {1}".format(permission, resource_name) self.log(error_message, "DEBUG") - return {"error": error_message} + return {"error_message": error_message} if permission == "deny": self.log("Skipping resource {0} because permission is 'deny'".format(resource_name), "DEBUG") @@ -2443,7 +2445,7 @@ def update_role(self, role_params): finally returns the response. """ try: - self.log("Update role with role_info_params: {0}".format(str(role_params)), "DEBUG") + self.log("Updating role with role_info_params: {0}".format(str(role_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", function="update_role_api", @@ -2453,10 +2455,11 @@ def update_role(self, role_params): self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") return response - except Exception: + except Exception as e: + self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") error_message = "The catalyst center user '{0}' does not have the necessary permissions to update role through the API.".format( self.payload.get("dnac_username")) - return {"error": error_message} + return {"error_message": error_message} def find_denied_permissions(self, config, parent_key=""): """ @@ -2749,7 +2752,7 @@ def get_diff_deleted(self, config): task_response = self.delete_role(role_id_to_delete) self.log("Task response {0}".format(str(task_response)), "INFO") - if task_response and "error" not in task_response: + if task_response and "error_message" not in task_response: responses = {"role_operation": {"response": task_response}} self.msg = responses self.result["response"] = self.msg @@ -2787,8 +2790,11 @@ def get_diff_deleted(self, config): self.log(self.msg, "INFO") return self - self.msg = "Please provide a valid 'username' or 'email' for user deletion or The catalyst center user '{0}' does not have the \ -necessary permissions to delete user through the API.".format(self.payload.get("dnac_username")) + self.msg = ( + "Please provide a valid 'username' or 'email' for user deletion, or " + "The Catalyst Center user '{0}' does not have the necessary permissions " + "to delete a user through the API.".format(self.payload.get("dnac_username")) + ) self.log(self.msg, "ERROR") self.status = "failed" return self @@ -2838,11 +2844,15 @@ def delete_role(self, role_params): params=role_params, ) self.log("Received API response from delete_role: {0}".format(str(response)), "DEBUG") - except Exception: - error_message = "The catalyst center user '{0}' does not have the necessary permissions to delete role through the API or An error occurred while \ -deleting the role. Check whether user(s) are assigned to the role '{1}'".format(self.payload.get("dnac_username"), self.have.get("role_name")) + except Exception as e: + self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") + error_message = ( + "The Catalyst Center user '{0}' does not have the necessary permissions to delete the role through the API, or " + "an error occurred while deleting the role. Check whether user(s) are assigned to the role '{1}'.".format( + self.payload.get("dnac_username"), self.have.get("role_name")) + ) - return {"error": error_message} + return {"error_message": error_message} return response From e05605163789cbb60b458ecdd1488e4751816017 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 20 Aug 2024 19:32:45 +0530 Subject: [PATCH 072/121] CSCwm07436, CSCwm07948, CSCwm26614 - user_role_workflow_manager bugs fixed --- plugins/modules/user_role_workflow_manager.py | 236 +++++++++++++++--- 1 file changed, 196 insertions(+), 40 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 738f30e795..23c62b9ee0 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -575,6 +575,7 @@ virtual_network: "read" network_provision: - compliance: "deny" + eox: "read" image_update: "write" inventory_management: - device_configuration: "write" @@ -607,6 +608,7 @@ - audit_log: "read" event_viewer: "deny" network_reasoner: "write" + remote_device_support: "read" scheduler: "read" search: "write" @@ -863,6 +865,9 @@ def __init__(self, module): self.supported_states = ["merged", "deleted"] self.payload = module.params self.keymap = {} + self.created_user, self.updated_user, self.no_update_user = [], [], [] + self.created_role, self.updated_role, self.no_update_role = [], [], [] + self.deleted_user, self.deleted_role = [], [] def validate_input_yml(self, user_role_details): """ @@ -1024,6 +1029,43 @@ def validate_role_parameters(self, role_key, params_list, role_config, role_para "DEBUG") self.validate_string_parameter(param, inventory_management[param], error_messages) + def identify_invalid_params(self, params, mismatches): + """ + Identify and collect invalid parameters from a dictionary or list based on allowed parameters. + Args: + - params (dict): The dictionary of parameters to be checked. Nested dictionaries or lists are supported. + - mismatches (list): A list where invalid parameter names are appended. This list is used to collect all parameters that are not in 'allowed_params'. + Returns: + - mismatches (list): This function returns the 'mismatches' list containing the names of any parameters that are not in the 'allowed_params' set. + Criteria: + - Parameters in 'params' must be checked recursively if they are dictionaries or lists. + - Only parameters that are not in the 'allowed_params' set are appended to the 'mismatches' list. + """ + allowed_params = [ + "monitoring_and_troubleshooting", "monitoring_settings", "troubleshooting_tools", "data_access", "advanced_network_settings", + "image_repository", "network_hierarchy", "network_profiles", "network_settings", "virtual_network", "compliance", + "eox", "image_update", "inventory_management", "license", "network_telemetry", "pnp", "provision", "device_configuration", + "discovery", "network_device", "port_management", "topology", "app_hosting", "bonjour", "stealthwatch", "umbrella", + "apis", "bundles", "events", "reports", "group_based_policy", "ip_based_access_control", "security_advisories", + "machine_reasoning", "system_management", "audit_log", "event_viewer", "network_reasoner", "remote_device_support", + "scheduler", "search", 'role_name', 'description', 'assurance', 'network_analytics', 'network_design', 'network_provision', + 'network_services', 'platform', 'security', 'system', 'utilities', 'overall' + ] + self.log("Iterate through the params to find unknown parameters are present or not", "DEBUG") + + if isinstance(params, dict): + for key, value in params.items(): + if key not in allowed_params: + mismatches.append(key) + + if isinstance(value, dict) or isinstance(value, list): + self.identify_invalid_params(value, mismatches) + elif isinstance(params, list): + for item in params: + self.identify_invalid_params(item, mismatches) + + return mismatches + def valid_role_config_parameters(self, role_config): """ Additional validation for the create role configuration payload. @@ -1040,8 +1082,18 @@ def valid_role_config_parameters(self, role_config): - If it fails, "self.status" will be "failed", and "self.msg" will describe the validation issues. """ self.log("Validating role configuration parameters...", "INFO") - error_messages = [] + invalid_params = [] + self.identify_invalid_params(role_config, invalid_params) + + if invalid_params: + self.msg = "Invalid parameters in playbook config: Mismatched parameter(s) '{0}' in role '{1}'".format( + "', '".join(invalid_params), role_config.get("role_name")) + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + error_messages = [] role_name = role_config.get("role_name") role_name_regex = re.compile(r"^[a-zA-Z0-9._-]{,25}$") role_name_regex_msg = "Role names must be 1 to 25 characters long and should contain only letters, numbers, periods, underscores, and hyphens." @@ -1107,11 +1159,6 @@ def valid_user_config_parameters(self, user_config): self.validate_string_field(last_name, name_regex, "last_name: Last name '{0}' {1}".format(last_name, name_regex_msg), error_messages) - email_regex = re.compile(r"^[A-Za-z0-9!#$%^&*'?{}|./_+=-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}(?:\.[a-zA-Z]{2,3})*$") - email = user_config.get("email") - email_regex_msg = "email: Invalid email format for 'email': {0}".format(email) - self.validate_string_field(email, email_regex, email_regex_msg, error_messages) - password = user_config.get("password") if password: @@ -1242,6 +1289,7 @@ def get_diff_merged(self, config): if not consolidated_data: self.msg = "Role with role_name '{0}' already exists and does not require an update.".format(self.have.get("role_name")) + self.no_update_role.append(self.have.get("role_name")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1277,6 +1325,7 @@ def get_diff_merged(self, config): if not consolidated_data: self.msg = "User with username '{0}' already exists and does not require an update.".format(self.have.get("username")) + self.no_update_user.append(self.have.get("username")) self.log(self.msg, "INFO") responses["role_operation"] = {"response": config} self.result["response"] = self.msg @@ -1284,12 +1333,14 @@ def get_diff_merged(self, config): return self if update_required_param.get("role_list"): - user_in_have = self.have["current_user_config"] - update_param = update_required_param - update_param["username"] = user_in_have.get("username") - update_param["user_id"] = user_in_have.get("user_id") - user_info_params = self.snake_to_camel_case(update_param) - task_response = self.update_user(user_info_params) + if self.want["username"] not in self.have["current_user_config"]["username"]: + task_response = {"error_message": "Username for an existing User cannot be updated."} + else: + user_in_have = self.have["current_user_config"] + update_param = update_required_param + update_param["user_id"] = user_in_have.get("user_id") + user_info_params = self.snake_to_camel_case(update_param) + task_response = self.update_user(user_info_params) else: task_response = {"error_message": "The role name in the user details role_list is not present in the Cisco Catalyst Center," " Please provide a valid role name"} @@ -1457,12 +1508,19 @@ def create_user(self, user_params): params=user_params, ) self.log("Received API response from create_user: {0}".format(str(response)), "DEBUG") + self.created_user.append(user_params.get("username")) return response except Exception as e: self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") - error_message = "The Catalyst Center user '{0}' does not have the necessary permissions to 'create or update' a user through the API.".format( - self.payload.get("dnac_username")) + if "[403]" in str(e): + error_message = ( + "The Catalyst Center user '{0}' does not have the necessary permissions to 'create or update' a user through the API.".format( + self.payload.get("dnac_username")) + ) + else: + error_message = "Invalid email format for email '{0}' under username '{1}'".format(user_params.get("email"), user_params.get("username")) + return {"error_message": error_message} def create_role(self, role_params): @@ -1488,6 +1546,7 @@ def create_role(self, role_params): params=role_params, ) self.log("Received API response from create_role: {0}".format(str(response)), "DEBUG") + self.created_role.append(role_params.get("role")) return response except Exception as e: @@ -1555,6 +1614,7 @@ def process_assurance_rules(self, role_config, role_operation, unique_types): - role_operation (str): The operation type (e.g., "update"). - unique_types (dict): A dictionary to store the unique resource types and their operations. """ + self.log("role_paramsssssssss: {0}".format(role_config)) entry_types = [ "Assurance.Monitoring and Troubleshooting", "Assurance.Monitoring Settings", @@ -1583,7 +1643,8 @@ def process_assurance_rules(self, role_config, role_operation, unique_types): permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for assurance resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for assurance resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1647,7 +1708,8 @@ def process_network_analytics_rules(self, role_config, role_operation, unique_ty permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for network analytics resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for network analytics resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1708,7 +1770,8 @@ def process_network_design_rules(self, role_config, role_operation, unique_types permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for network design resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for network design resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1783,7 +1846,8 @@ def process_network_provision_rules(self, role_config, role_operation, unique_ty sub_permission = sub_permission.lower() if sub_permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for network provision for sub-resource {1}".format(sub_permission, sub_resource_name) + error_message = "Invalid permission '{0}' for network provision for sub-resource '{1}' under the role '{2}'".format( + sub_permission, sub_resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1819,7 +1883,8 @@ def process_network_provision_rules(self, role_config, role_operation, unique_ty permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for network provision resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for network provision resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1892,7 +1957,8 @@ def process_network_services_rules(self, role_config, role_operation, unique_typ permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for network services resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for network services resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -1937,7 +2003,8 @@ def process_platform_rules(self, role_config, unique_types): permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for platform resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for platform resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -2008,7 +2075,8 @@ def process_security_rules(self, role_config, role_operation, unique_types): permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for security resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for security resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -2079,7 +2147,8 @@ def process_system_rules(self, role_config, role_operation, unique_types): permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for system resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for system resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -2142,7 +2211,8 @@ def process_utilities_rules(self, role_config, role_operation, unique_types): permission = permission.lower() if permission not in ["read", "write", "deny"]: - error_message = "Invalid permission {0} for utilities resource {1}".format(permission, resource_name) + error_message = "Invalid permission '{0}' for utilities resource '{1}' under the role '{2}'".format( + permission, resource_name, self.have.get("role_name")) self.log(error_message, "DEBUG") return {"error_message": error_message} @@ -2370,6 +2440,19 @@ def user_requires_update(self, current_user, current_role): else: update_user_params["last_name"] = current_last_name + # Compare and update username + desired_username = self.want.get("username") + current_username = current_user.get("username") + if desired_username is not None: + if current_username != desired_username: + self.log("Username for an existing User cannot be updated from {0} to {1}.".format(current_username, desired_username), "DEBUG") + update_user_params["username"] = desired_username + update_needed = True + elif "username" not in update_user_params: + update_user_params["username"] = current_username + else: + update_user_params["username"] = current_username + # Compare and update email desired_email = self.want.get("email") current_email = current_user.get("email") @@ -2419,15 +2502,22 @@ def update_user(self, user_params): - This method sends a request to update a user in Cisco Catalyst Center using the provided - user parameters. It logs the response and returns it. """ - self.log("Updating user with parameters: {0}".format(user_params), "DEBUG") - response = self.dnac._exec( - family="user_and_roles", - function="update_user_api", - op_modifies=True, - params=user_params, - ) - self.log("Received API response from update_user: {0}".format(str(response)), "DEBUG") - return response + try: + self.log("Updating user with parameters: {0}".format(user_params), "DEBUG") + response = self.dnac._exec( + family="user_and_roles", + function="update_user_api", + op_modifies=True, + params=user_params, + ) + self.log("Received API response from update_user: {0}".format(str(response)), "DEBUG") + self.updated_user.append(user_params.get("username")) + return response + + except Exception as e: + self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") + error_message = "Invalid email format for email '{0}' under username '{1}'".format(user_params.get("email"), user_params.get("username")) + return {"error_message": error_message} def update_role(self, role_params): """ @@ -2453,6 +2543,7 @@ def update_role(self, role_params): params=role_params, ) self.log("Received API response from update_role: {0}".format(str(response)), "DEBUG") + self.updated_role.append(self.have.get("role_name")) return response except Exception as e: @@ -2820,6 +2911,7 @@ def delete_user(self, user_params): params=user_params, ) self.log("Received API response from delete_user: {0}".format(str(response)), "DEBUG") + self.deleted_user.append(self.have.get("username")) return response def delete_role(self, role_params): @@ -2844,18 +2936,22 @@ def delete_role(self, role_params): params=role_params, ) self.log("Received API response from delete_role: {0}".format(str(response)), "DEBUG") + self.deleted_role.append(self.have.get("role_name")) + return response + except Exception as e: self.log("Unexpected error occurred: {0}".format(str(e)), "ERROR") - error_message = ( - "The Catalyst Center user '{0}' does not have the necessary permissions to delete the role through the API, or " - "an error occurred while deleting the role. Check whether user(s) are assigned to the role '{1}'.".format( - self.payload.get("dnac_username"), self.have.get("role_name")) - ) + if "[403]" in str(e): + error_message = ( + "The Catalyst Center user '{0}' does not have the necessary permissions to delete the role through the API.".format( + self.payload.get("dnac_username")) + ) + else: + error_message = "An error occurred while deleting the role. Check whether user(s) are assigned to the role '{0}'.".format( + self.have.get("role_name")) return {"error_message": error_message} - return response - def verify_diff_merged(self, config): """ Verify the merged status (Creation/Updation) of user or role details in Cisco Catalyst Center. @@ -2975,6 +3071,64 @@ def verify_diff_deleted(self, config): return self + def update_user_role_profile_messages(self): + """ + Updates and logs messages based on the status of users and roles. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Returns: + self (object): Returns the current instance of the class with updated `result` and `msg` attributes. + Description: + This method aggregates status messages related to the creation, update, or deletion of users and roles. + It checks various instance variables (`create_user`, `update_user`, `no_update_user`, `delete_user`, + `create_role`, `update_role`, `no_update_role`, `delete_role`) to determine the status and generates + corresponding messages. The method also updates the `result["response"]` attribute with the concatenated status messages. + """ + + self.result["changed"] = False + result_msg_list = [] + + if self.created_user: + create_user_msg = "User(s) '{0}' created successfully in Cisco Catalyst Center.".format("', '".join(self.created_user)) + result_msg_list.append(create_user_msg) + + if self.updated_user: + update_user_msg = "User(s) '{0}' updated successfully in Cisco Catalyst Center.".format("', '".join(self.updated_user)) + result_msg_list.append(update_user_msg) + + if self.no_update_user: + no_update_user_msg = "User(s) '{0}' need no update in Cisco Catalyst Center.".format("', '".join(self.no_update_user)) + result_msg_list.append(no_update_user_msg) + + if self.deleted_user: + delete_user_msg = "User(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format("', '".join(self.deleted_user)) + result_msg_list.append(delete_user_msg) + + if self.created_role: + create_role_msg = "Role(s) '{0}' created successfully in Cisco Catalyst Center.".format("', '".join(self.created_role)) + result_msg_list.append(create_role_msg) + + if self.updated_role: + update_role_msg = "Role(s) '{0}' updated successfully in Cisco Catalyst Center.".format("', '".join(self.updated_role)) + result_msg_list.append(update_role_msg) + + if self.no_update_role: + no_update_role_msg = "Role(s) '{0}' need no update in Cisco Catalyst Center.".format("', '".join(self.no_update_role)) + result_msg_list.append(no_update_role_msg) + + if self.deleted_role: + delete_role_msg = "Role(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format("', '".join(self.deleted_role)) + result_msg_list.append(delete_role_msg) + + if self.created_user or self.updated_user or self.deleted_user or self.created_role or self.updated_role or self.deleted_role: + self.result["changed"] = True + + self.msg = " ".join(result_msg_list) + self.log(self.msg, "INFO") + self.result["response"] = self.msg + + return self + def snake_to_camel_case(self, data): """ Convert keys from snake_case to camelCase in a given dictionary or list of dictionaries recursively. @@ -3071,6 +3225,8 @@ def main(): if config_verify: ccc_user_role.verify_diff_state_apply[state](config).check_return_status() + ccc_user_role.update_user_role_profile_messages().check_return_status() + module.exit_json(**ccc_user_role.result) From 32e768ec8f9011d30b97326a7bee1ca40b64f5fe Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 20 Aug 2024 19:40:58 +0530 Subject: [PATCH 073/121] user_role_workflow_manager bugs fixed - CSCwm07436, CSCwm07948, CSCwm26614 --- plugins/modules/user_role_workflow_manager.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 23c62b9ee0..eabe6387a3 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1034,7 +1034,8 @@ def identify_invalid_params(self, params, mismatches): Identify and collect invalid parameters from a dictionary or list based on allowed parameters. Args: - params (dict): The dictionary of parameters to be checked. Nested dictionaries or lists are supported. - - mismatches (list): A list where invalid parameter names are appended. This list is used to collect all parameters that are not in 'allowed_params'. + - mismatches (list): A list where invalid parameter names are appended. This list is used to collect all + parameters that are not in 'allowed_params'. Returns: - mismatches (list): This function returns the 'mismatches' list containing the names of any parameters that are not in the 'allowed_params' set. Criteria: @@ -1045,7 +1046,7 @@ def identify_invalid_params(self, params, mismatches): "monitoring_and_troubleshooting", "monitoring_settings", "troubleshooting_tools", "data_access", "advanced_network_settings", "image_repository", "network_hierarchy", "network_profiles", "network_settings", "virtual_network", "compliance", "eox", "image_update", "inventory_management", "license", "network_telemetry", "pnp", "provision", "device_configuration", - "discovery", "network_device", "port_management", "topology", "app_hosting", "bonjour", "stealthwatch", "umbrella", + "discovery", "network_device", "port_management", "topology", "app_hosting", "bonjour", "stealthwatch", "umbrella", "apis", "bundles", "events", "reports", "group_based_policy", "ip_based_access_control", "security_advisories", "machine_reasoning", "system_management", "audit_log", "event_viewer", "network_reasoner", "remote_device_support", "scheduler", "search", 'role_name', 'description', 'assurance', 'network_analytics', 'network_design', 'network_provision', From def95d977e98bfc59d7acda58b2246c9c5cc9302 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 20 Aug 2024 19:48:46 +0530 Subject: [PATCH 074/121] rma_workflow_manager bug fixed - CSCwm26351 --- plugins/modules/rma_workflow_manager.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 6a418d551a..d328bb3ccb 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -445,8 +445,14 @@ def get_have(self): config = self.want["config"] identifier_keys = [ ("faulty_device_serial_number", "replacement_device_serial_number"), + ("faulty_device_serial_number", "replacement_device_name"), + ("faulty_device_serial_number", "replacement_device_ip_address"), + ("faulty_device_name", "replacement_device_serial_number"), ("faulty_device_name", "replacement_device_name"), - ("faulty_device_ip_address", "replacement_device_ip_address") + ("faulty_device_name", "replacement_device_ip_address"), + ("faulty_device_ip_address","replacement_device_ip_address"), + ("faulty_device_ip_address","replacement_device_name"), + ("faulty_device_ip_address","replacement_device_serial_number") ] valid_identifier_found = False From 1c1469027781527c356b69e145715997ac72a889 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 20 Aug 2024 19:52:44 +0530 Subject: [PATCH 075/121] rma_workflow_manager bug fixed - CSCwm26351 --- plugins/modules/rma_workflow_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index d328bb3ccb..b64e198feb 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -450,9 +450,9 @@ def get_have(self): ("faulty_device_name", "replacement_device_serial_number"), ("faulty_device_name", "replacement_device_name"), ("faulty_device_name", "replacement_device_ip_address"), - ("faulty_device_ip_address","replacement_device_ip_address"), - ("faulty_device_ip_address","replacement_device_name"), - ("faulty_device_ip_address","replacement_device_serial_number") + ("faulty_device_ip_address", "replacement_device_ip_address"), + ("faulty_device_ip_address", "replacement_device_name"), + ("faulty_device_ip_address", "replacement_device_serial_number") ] valid_identifier_found = False From 60aaff7301cd07aab3f4c908f6ab208de0faab6d Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 21 Aug 2024 09:47:32 +0530 Subject: [PATCH 076/121] user_role_workflow_manager bug fixed --- plugins/modules/user_role_workflow_manager.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index eabe6387a3..245d11a94a 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1101,8 +1101,12 @@ def valid_role_config_parameters(self, role_config): self.validate_string_field(role_name, role_name_regex, "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) - if role_config.get("description"): - self.validate_string_parameter("description", role_config["description"], error_messages) + description = role_config["description"] + if description: + if len(description) > 1000: + error_messages.append("Role description exceeds the maximum length of 1000 characters.") + else: + self.validate_string_parameter("description", description, error_messages) role_param_map = { "assurance": ["overall", "monitoring_and_troubleshooting", "monitoring_settings", "troubleshooting_tools"], From 86389fa077784ca4a78dad5385e1c7ee6c5f9444 Mon Sep 17 00:00:00 2001 From: md-rafeek Date: Wed, 21 Aug 2024 22:42:28 +0530 Subject: [PATCH 077/121] Bug raised for the 9 issue was fixed. --- .../modules/accesspoint_workflow_manager.py | 580 +++++++++--------- 1 file changed, 303 insertions(+), 277 deletions(-) diff --git a/plugins/modules/accesspoint_workflow_manager.py b/plugins/modules/accesspoint_workflow_manager.py index 84ab02ee89..1e1030f4fe 100644 --- a/plugins/modules/accesspoint_workflow_manager.py +++ b/plugins/modules/accesspoint_workflow_manager.py @@ -103,14 +103,20 @@ required: False ap_mode: description: | - Mode of operation for the Access Point (AP). Possible values include "local/flexconnect", - "monitor", "sniffer", or "Bridge/Flex+Bridge".(eg. "Local") + Mode of operation for the Access Point (AP). Possible values include "Local", + "Monitor", "Sniffer", or "Bridge".(eg. "Local") type: str required: False location: description: Location name of the AP. Provide this data if a change is required.(eg. "Bangalore") type: str required: False + is_assigned_site_as_location: + description: | + To configure the access point location as the site assigned to the access point, + accepts "Enabled" or "Disabled".(eg. "Enabled") incase Enabled no need to add location. + type: str + required: False failover_priority: description: Priority order for failover in AP configuration. Accepts "Low", "Medium", "High", or "Critical". type: str @@ -172,269 +178,264 @@ description: IP address of the primary wireless LAN controller (eg. '10.0.0.2') type: str required: False - radio_settings: - description: Configuration options for radio interfaces. + 2.4ghz_radio: + description: Configuration options for the 2.4GHz radio interface. type: dict required: False suboptions: - 2.4ghz_radio: - description: Configuration options for the 2.4GHz radio interface. - type: dict + admin_status: + description: Administrative status for the 2.4GHz radio interface.(eg. 'Enabled') + type: str required: False - suboptions: - admin_status: - description: Administrative status for the 2.4GHz radio interface.(eg. 'Enabled') - type: str - required: False - antenna_name: - description: Name or type of antenna used for the 2.4GHz radio interface.(eg. "other") - type: str - required: False - antenna_gain: - description: Antenna gain value in decibels (dB) for the 2.4GHz radio interface.(eg. 4) - type: int - required: False - radio_role_assignment: - description: Role assignment mode for the 2.4GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. "Auto") - type: str - required: False - cable_loss: - description: Cable loss in dB for the 2.4GHz radio interface.(eg. 75) - type: int - required: False - antenna_cable_name: - description: Name or type of antenna cable used for the 2.4GHz radio interface.(eg. "other") - type: str - required: False - channel_assignment_mode: - description: Mode of channel assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") - type: str - required: False - channel_number: - description: Custom channel number configured for the 2.4GHz radio interface.(eg. 6) - type: int - required: False - power_assignment_mode: - description: Mode of power assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") - type: str - required: False - power_level: - description: Custom power level configured for the 2.4GHz radio interface. (eg. 3) - type: int - required: False - 5ghz_radio: - description: Configuration options for the 5GHz radio interface. - type: dict + antenna_name: + description: Name or type of antenna used for the 2.4GHz radio interface.(eg. "other") + type: str required: False - suboptions: - admin_status: - description: Administrative status for the 5GHz radio interface.(eg. "Enabled") - type: str - required: False - antenna_name: - description: Name or type of antenna used for the 5GHz radio interface.(eg. other) - type: str - required: False - antenna_gain: - description: Antenna gain value in decibels (dB) for the 5GHz radio interface.(eg. 5) - type: int - required: False - radio_role_assignment: - description: Role assignment mode for the 5GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. Auto) - type: str - required: False - cable_loss: - description: Cable loss in dB for the 5GHz radio interface. (eg. 80) - type: int - required: False - antenna_cable_name: - description: Name or type of antenna cable used for the 5GHz radio interface.(eg. other) - type: str - required: False - channel_assignment_mode: - description: Mode of channel assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") - type: str - required: False - channel_number: - description: Custom channel number configured for the 5GHz radio interface. (eg. 36) - type: int - required: False - power_assignment_mode: - description: Mode of power assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") - type: str - required: False - power_level: - description: Custom power level configured for the 5GHz radio interface. (eg. 3) - type: int - required: False - 6ghz_radio: - description: Configuration options for the 6GHz radio interface. - type: dict + antenna_gain: + description: Antenna gain value in decibels (dB) for the 2.4GHz radio interface.(eg. 4) + type: int required: False - suboptions: - admin_status: - description: Administrative status for the 6GHz radio interface.(eg. "Enabled") - type: str - required: False - antenna_name: - description: Name or type of antenna used for the 6GHz radio interface. (eg. other) - type: str - required: False - antenna_gain: - description: Antenna gain value in decibels (dB) for the 6GHz radio interface.(eg. 4) - type: int - required: False - radio_role_assignment: - description: Role assignment mode for the 6GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor". - type: str - required: False - cable_loss: - description: Cable loss in dB for the 6GHz radio interface. (eg. 75) - type: int - required: False - antenna_cable_name: - description: Name or type of antenna cable used for the 6GHz radio interface.(eg. "other") - type: str - required: False - channel_assignment_mode: - description: Mode of channel assignment for the 6GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") - type: str - required: False - channel_number: - description: Custom channel number configured for the 6GHz radio interface.(eg. 6) - type: int - required: False - power_assignment_mode: - description: Mode of power assignment for the 6GHz radio interface. Accepts "Global" or "Custom". (eg. "Custom") - type: str - required: False - power_level: - description: Custom power level configured for the 6GHz radio interface.(eg. 3) - type: int - required: False - xor_radio: - description: Configuration options for the xor radio interface. - type: dict + radio_role_assignment: + description: Role assignment mode for the 2.4GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. "Auto") + type: str required: False - suboptions: - admin_status: - description: Administrative status for the xor radio interface. (eg. Enabled) - type: str - required: False - antenna_name: - description: Name or type of antenna used for the xor radio interface.(eg. other) - type: str - required: False - antenna_gain: - description: Antenna gain value in decibels (dB) for the xor radio interface.(eg. 4) - type: int - required: False - radio_role_assignment: - description: | - Role assignment mode for the xor radio interface. Accepts "Auto", "Client-serving", or "Monitor". - If radio_role_assignment is "client-serving", then only power-level and channel-level can be changed. - (eg. Auto) - type: str - required: False - cable_loss: - description: Cable loss in dB for the xor radio interface. (e.g 75) - type: int - required: False - antenna_cable_name: - description: Name or type of antenna cable used for the xor radio interface.(eg. other) - type: str - required: False - channel_assignment_mode: - description: | - Mode of channel assignment for the xor radio interface. Accepts "Global" or "Custom". - For xor Custom, it accepts values like 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, - 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. (eg. "Custom") - type: str - required: False - channel_number: - description: Custom channel number configured for the xor radio interface.(eg. 6) - type: int - required: False - channel_width: - description: | - Width of the channel configured for the xor radio interface. Accepts values - "20 MHz", "40 MHz", "80 MHz", or "160 MHz". (eg. 20 MHz) - type: str - required: False - power_assignment_mode: - description: | - Mode of power assignment for the xor radio interface. Accepts "Global" or "Custom". - In Custom, it accepts values 1 to 5. - type: str - required: False - power_level: - description: Custom power level configured for the xor radio interface. (eg. 3) - type: int - required: False - tri_radio: - description: Configuration options for the tri radio interface. - type: dict + cable_loss: + description: Cable loss in dB for the 2.4GHz radio interface.(eg. 75) + type: int + required: False + antenna_cable_name: + description: Name or type of antenna cable used for the 2.4GHz radio interface.(eg. "other") + type: str + required: False + channel_assignment_mode: + description: Mode of channel assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + type: str + required: False + channel_number: + description: Custom channel number configured for the 2.4GHz radio interface.(eg. 6) + type: int + required: False + power_assignment_mode: + description: Mode of power assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + type: str + required: False + power_level: + description: Custom power level configured for the 2.4GHz radio interface. (eg. 3) + type: int + required: False + 5ghz_radio: + description: Configuration options for the 5GHz radio interface. + type: dict + required: False + suboptions: + admin_status: + description: Administrative status for the 5GHz radio interface.(eg. "Enabled") + type: str + required: False + antenna_name: + description: Name or type of antenna used for the 5GHz radio interface.(eg. other) + type: str + required: False + antenna_gain: + description: Antenna gain value in decibels (dB) for the 5GHz radio interface.(eg. 5) + type: int + required: False + radio_role_assignment: + description: Role assignment mode for the 5GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. Auto) + type: str + required: False + cable_loss: + description: Cable loss in dB for the 5GHz radio interface. (eg. 80) + type: int + required: False + antenna_cable_name: + description: Name or type of antenna cable used for the 5GHz radio interface.(eg. other) + type: str + required: False + channel_assignment_mode: + description: Mode of channel assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + type: str + required: False + channel_number: + description: Custom channel number configured for the 5GHz radio interface. (eg. 36) + type: int + required: False + power_assignment_mode: + description: Mode of power assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + type: str + required: False + power_level: + description: Custom power level configured for the 5GHz radio interface. (eg. 3) + type: int + required: False + 6ghz_radio: + description: Configuration options for the 6GHz radio interface. + type: dict + required: False + suboptions: + admin_status: + description: Administrative status for the 6GHz radio interface.(eg. "Enabled") + type: str + required: False + antenna_name: + description: Name or type of antenna used for the 6GHz radio interface. (eg. other) + type: str + required: False + antenna_gain: + description: Antenna gain value in decibels (dB) for the 6GHz radio interface.(eg. 4) + type: int + required: False + radio_role_assignment: + description: Role assignment mode for the 6GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor". + type: str + required: False + cable_loss: + description: Cable loss in dB for the 6GHz radio interface. (eg. 75) + type: int + required: False + antenna_cable_name: + description: Name or type of antenna cable used for the 6GHz radio interface.(eg. "other") + type: str + required: False + channel_assignment_mode: + description: Mode of channel assignment for the 6GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + type: str + required: False + channel_number: + description: Custom channel number configured for the 6GHz radio interface.(eg. 6) + type: int + required: False + power_assignment_mode: + description: Mode of power assignment for the 6GHz radio interface. Accepts "Global" or "Custom". (eg. "Custom") + type: str + required: False + power_level: + description: Custom power level configured for the 6GHz radio interface.(eg. 3) + type: int + required: False + xor_radio: + description: Configuration options for the xor radio interface. + type: dict + required: False + suboptions: + admin_status: + description: Administrative status for the xor radio interface. (eg. Enabled) + type: str + required: False + antenna_name: + description: Name or type of antenna used for the xor radio interface.(eg. other) + type: str + required: False + antenna_gain: + description: Antenna gain value in decibels (dB) for the xor radio interface.(eg. 4) + type: int + required: False + radio_role_assignment: + description: | + Role assignment mode for the xor radio interface. Accepts "Auto", "Client-serving", or "Monitor". + If radio_role_assignment is "client-serving", then only power-level and channel-level can be changed. + (eg. Auto) + type: str + required: False + cable_loss: + description: Cable loss in dB for the xor radio interface. (e.g 75) + type: int + required: False + antenna_cable_name: + description: Name or type of antenna cable used for the xor radio interface.(eg. other) + type: str + required: False + channel_assignment_mode: + description: | + Mode of channel assignment for the xor radio interface. Accepts "Global" or "Custom". + For xor Custom, it accepts values like 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, + 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. (eg. "Custom") + type: str + required: False + channel_number: + description: Custom channel number configured for the xor radio interface.(eg. 6) + type: int + required: False + channel_width: + description: | + Width of the channel configured for the xor radio interface. Accepts values + "20 MHz", "40 MHz", "80 MHz", or "160 MHz". (eg. 20 MHz) + type: str + required: False + power_assignment_mode: + description: | + Mode of power assignment for the xor radio interface. Accepts "Global" or "Custom". + In Custom, it accepts values 1 to 5. + type: str + required: False + power_level: + description: Custom power level configured for the xor radio interface. (eg. 3) + type: int + required: False + tri_radio: + description: Configuration options for the tri radio interface. + type: dict + required: False + suboptions: + admin_status: + description: Administrative status for the tri radio interface. (eg. Enabled) + type: str + required: False + antenna_name: + description: Name or type of antenna used for the tri radio interface.(eg. other) + type: str + required: False + antenna_gain: + description: Antenna gain value in decibels (dB) for the tri radio interface. (eg. 4) + type: int + required: False + radio_role_assignment: + description: | + Role assignment mode for the tri radio interface. Accepts "Auto", "Client-serving", or "Monitor". + If radio_role_assignment is "client-serving", then only power-level and channel-level can be changed. + type: str + required: False + cable_loss: + description: Cable loss in dB for the tri radio interface. (eg. 75) + type: int + required: False + antenna_cable_name: + description: Name or type of antenna cable used for the tri radio interface. (eg. "other") + type: str + required: False + channel_assignment_mode: + description: | + Mode of channel assignment for the tri radio interface. Accepts "Global" or "Custom". + For tri Custom, it accepts values like 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, + 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. (eg. Custom) + type: str + required: False + channel_number: + description: Custom channel number configured for the tri radio interface. (eg. 6) + type: int + required: False + channel_width: + description: | + Width of the channel configured for the tri radio interface. Accepts values + "20 MHz", "40 MHz", "80 MHz", or "160 MHz". eg. 20 MHz + type: str + required: False + power_assignment_mode: + description: | + Mode of power assignment for the tri radio interface. Accepts "Global" or "Custom". + In Custom, it accepts values 1 to 5. + type: str + required: False + power_level: + description: Custom power level configured for the tri radio interface.(eg. 3) + type: int + required: False + dual_radio_mode: + description: | + Mode of operation configured for the tri radio interface. Specifies how the + access point (AP) manages its dual radio functionality. eg . Auto + type: str required: False - suboptions: - admin_status: - description: Administrative status for the tri radio interface. (eg. Enabled) - type: str - required: False - antenna_name: - description: Name or type of antenna used for the tri radio interface.(eg. other) - type: str - required: False - antenna_gain: - description: Antenna gain value in decibels (dB) for the tri radio interface. (eg. 4) - type: int - required: False - radio_role_assignment: - description: | - Role assignment mode for the tri radio interface. Accepts "Auto", "Client-serving", or "Monitor". - If radio_role_assignment is "client-serving", then only power-level and channel-level can be changed. - type: str - required: False - cable_loss: - description: Cable loss in dB for the tri radio interface. (eg. 75) - type: int - required: False - antenna_cable_name: - description: Name or type of antenna cable used for the tri radio interface. (eg. "other") - type: str - required: False - channel_assignment_mode: - description: | - Mode of channel assignment for the tri radio interface. Accepts "Global" or "Custom". - For tri Custom, it accepts values like 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, - 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. (eg. Custom) - type: str - required: False - channel_number: - description: Custom channel number configured for the tri radio interface. (eg. 6) - type: int - required: False - channel_width: - description: | - Width of the channel configured for the tri radio interface. Accepts values - "20 MHz", "40 MHz", "80 MHz", or "160 MHz". eg. 20 MHz - type: str - required: False - power_assignment_mode: - description: | - Mode of power assignment for the tri radio interface. Accepts "Global" or "Custom". - In Custom, it accepts values 1 to 5. - type: str - required: False - power_level: - description: Custom power level configured for the tri radio interface.(eg. 3) - type: int - required: False - dual_radio_mode: - description: | - Mode of operation configured for the tri radio interface. Specifies how the - access point (AP) manages its dual radio functionality. eg . Auto - type: str - required: False ap_selected_fields: description: When enable the verify flag "config_verify" to see only the filter field of the AP details in the output. (eg. "id,hostname,family,type,mac_address,management_ip_address,ap_ethernet_mac_address") @@ -693,7 +694,7 @@ led_status: "Enabled" led_brightness_level: 5 ap_mode: "Local" - location: "LTTS/Cisco/Chennai" + is_assigned_site_as_location: "Enabled" failover_priority: "Low" 2.4ghz_radio: admin_status: "Enabled" @@ -810,13 +811,14 @@ def __init__(self, module): self.allowed_series = { "6ghz_radio": ["9136I", "9162I", "9163E", "9164I", "IW9167IH", "9178I", "9176I", "9176D1"], - "xor_radio": ["2800", "3800", "4800", "9120", "9166"], - "tri_radio": ["9124AXE", "9130AXI", "9130AXE"] + "xor_radio": ["280", "380", "480", "9120", "9166", "IW9167EH", "IW9165E", "IW9165DH"], + "tri_radio": ["9124AXE", "9130AXI", "9130AXE", "9178I"] } self.allowed_channel_no = { "2.4ghz_radio": list(range(1, 12)), "5ghz_radio": (36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, - 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173) + 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173), + "xor_radio": list(range(1, 12)) } def validate_input_yml(self): @@ -864,6 +866,7 @@ def validate_input_yml(self): "led_brightness_level": {"required": False, "type": "int"}, "ap_mode": {"required": False, "type": "str"}, "location": {"required": False, "type": "str"}, + "is_assigned_site_as_location": {"required": False, "type": "str"}, "failover_priority": {"required": False, "type": "str"}, "primary_controller_name": {"required": False, "type": "str"}, "primary_ip_address": {"required": False, "type": "dict"}, @@ -1045,7 +1048,6 @@ def get_diff_merged(self, ap_config): self.msg = "AP {0} provisioned successfully.".format(self.have['hostname']) self.log(self.msg, "INFO") responses["accesspoints_updates"].update({ - "provision_response": provision_details, "provision_message": self.msg }) else: @@ -1065,7 +1067,6 @@ def get_diff_merged(self, ap_config): self.log(self.msg, "INFO") del self.payload["access_point_details"] responses["accesspoints_updates"].update({ - "ap_config_response": self.payload["access_point_config"], "ap_config_message": self.msg }) self.result['ap_update_msg'] = self.msg @@ -1095,7 +1096,6 @@ def get_diff_merged(self, ap_config): self.log("Task Details: {0} .".format(self.pprint( task_details_response)), "ERROR") responses["accesspoints_updates"] = { - "ap_update_config_task_response": task_response, "ap_update_config_task_details": task_details_response, "ap_config_update_status": self.msg} self.module.fail_json(msg=self.msg, response=responses) @@ -1107,8 +1107,7 @@ def get_diff_merged(self, ap_config): .format(self.have["current_ap_config"].get("ap_name")) self.log(self.msg, "INFO") responses["accesspoints_updates"] = { - "ap_update_config_task_response": task_response, - "ap_update_config_task_details": task_details_response, + "ap_update_config_task_details": task_details_response["id"], "ap_config_update_status": self.msg } self.result['ap_update_msg'] = self.msg @@ -1159,13 +1158,14 @@ def verify_diff_merged(self, config): unmatch_count = 0 require_update = self.config_diff(self.have["current_ap_config"]) + self.log(self.pprint(require_update), "INFO") if require_update: radio_list = require_update.get("radioConfigurations") if len(radio_list) > 0: for each_radio in radio_list: radio_key_list = list(each_radio.keys()) for each_key in radio_key_list: - if each_key not in ("antenna_name", "radioType", "unmatch", "cable_loss"): + if each_key not in ("antenna_name", "radioType", "unmatch", "cable_loss", "radioRoleAssignment"): unmatch_count += 1 other_keys = list(require_update.keys()) @@ -1176,8 +1176,7 @@ def verify_diff_merged(self, config): self.log("Unmatch count for the radio configuration : {0}".format(str(unmatch_count)), "INFO") self.log(str(require_update), "INFO") responses = {} - responses["accesspoints_verify"] = {"want": self.want, - "have": self.have} + responses["accesspoints_verify"] = {} if unmatch_count < 1: msg = "The update for AP Config '{0}' has been successfully verified.".format(ap_name) @@ -1241,10 +1240,11 @@ def validate_radio_series(self, ap_config): self.log('Validating radio type: {0}'.format(radio_type), "INFO") if ap_series is not None: for series in self.allowed_series[radio_type]: - pattern = r'\b{}\b'.format(re.escape(series)) + pattern = r'\b{}\w+'.format(re.escape(series)) compiled_pattern = re.compile(pattern) is_valid = compiled_pattern.search(self.payload["access_point_details"]["series"]) if is_valid: + invalid_series = [] break if not is_valid: @@ -1324,6 +1324,11 @@ def validate_ap_config_parameters(self, ap_config): param_spec = dict(type="str", length_max=32) validate_str(ap_name, param_spec, "ap_name", errormsg) + admin_status = ap_config.get("admin_status") + if admin_status and admin_status not in ("Enabled", "Disabled"): + errormsg.append("admin_status: Invalid value '{0}' for admin_status in playbook. Must be either 'Enabled' or 'Disabled'." + .format(admin_status)) + led_brightness_level = ap_config.get("led_brightness_level") if led_brightness_level and led_brightness_level not in range(1, 9): errormsg.append("led_brightness_level: Invalid LED Brightness level '{0}' in playbook." @@ -1338,6 +1343,11 @@ def validate_ap_config_parameters(self, ap_config): param_spec = dict(type="str", length_max=255) validate_str(location, param_spec, "location", errormsg) + is_assigned_site_as_location = ap_config.get("is_assigned_site_as_location") + if is_assigned_site_as_location and is_assigned_site_as_location not in ("Disabled", "Enabled"): + errormsg.append("is_assigned_site_as_location: Invalid is_assigned_site_as_location '{0}' in playbook." + .format(is_assigned_site_as_location)) + ap_mode = ap_config.get("ap_mode") if ap_mode and ap_mode not in ("Local", "Monitor", "Sniffer", "Bridge"): errormsg.append("ap_mode: Invalid value '{0}' for ap_mode in playbook. Must be one of: Local, Monitor, Sniffer or Bridge." @@ -1530,6 +1540,12 @@ def check_current_radio_role_assignment(self, radio_type, radio_dtos, radio_band (radio_band == "5 GHz" and slot_id == 1): break + if radio_type == "tri_radio": + if (radio_band == "2.4 GHz" and slot_id == 0) or \ + (radio_band == "5 GHz" and slot_id == 1) or \ + (radio_band == "5 GHz" and slot_id == 2): + break + self.log('Completed checking radio role assignments. Role assignment: {0}, radio type: {1}, radio band: {2}' .format(role_assignment, radio_type, radio_band), "INFO") return role_assignment @@ -2091,6 +2107,8 @@ def config_diff(self, current_ap_config): if self.want["ap_name"] != current_ap_config.get("ap_name"): update_config["apNameNew"] = self.want["ap_name"] update_config["apName"] = current_ap_config.get("ap_name") + elif each_key == "is_assigned_site_as_location": + update_config["isAssignedSiteAsLocation"] = self.want["is_assigned_site_as_location"] elif each_key in ("primary_ip_address", "secondary_ip_address", "tertiary_ip_address"): if current_ap_config.get(each_key) != self.want.get(each_key): @@ -2111,7 +2129,7 @@ def config_diff(self, current_ap_config): elif each_key == "6ghz_radio" and each_radio["slot_id"] == 2: radio_data = self.compare_radio_config(each_radio, self.want[each_key]) - elif each_key == "xor_radio" and each_radio["slot_id"] == 3: + elif each_key == "xor_radio" and each_radio["slot_id"] == 0: radio_data = self.compare_radio_config(each_radio, self.want[each_key]) elif each_key == "tri_radio" and each_radio["slot_id"] == 4: @@ -2184,12 +2202,15 @@ def update_ap_configuration(self, ap_config): self.log("Updating access point configuration information: {0}" .format(ap_config["macAddress"]), "INFO") - ap_config["adminStatus"] = True - ap_config["configureAdminStatus"] = True ap_config["apList"] = [] temp_dict = {} + if ap_config.get("adminStatus") is not None: + ap_config["configureAdminStatus"] = True + ap_config["adminStatus"] = True \ + if ap_config["adminStatus"] == "Enabled" else False + if ap_config.get(self.keymap["ap_name"]) is not None: temp_dict[self.keymap["ap_name"]] = ap_config.get(self.keymap["ap_name"]) temp_dict["apNameNew"] = ap_config["apNameNew"] @@ -2203,8 +2224,11 @@ def update_ap_configuration(self, ap_config): if ap_config.get(self.keymap["location"]) is not None: ap_config["configureLocation"] = True - else: - ap_config["isAssignedSiteAsLocation"] = True + + if ap_config.get("isAssignedSiteAsLocation") is not None: + ap_config["configureLocation"] = True + ap_config["isAssignedSiteAsLocation"] = True \ + if ap_config["isAssignedSiteAsLocation"] == "Enabled" else False if ap_config.get(self.keymap["led_brightness_level"]) is not None: ap_config["configureLedBrightnessLevel"] = True @@ -2265,8 +2289,10 @@ def update_ap_configuration(self, ap_config): for each_radio in radio_config_list: radio_dtos = {} - radio_dtos["configureAdminStatus"] = True - radio_dtos["adminStatus"] = True + if each_radio.get(self.keymap["admin_status"]) is not None: + radio_dtos["configureAdminStatus"] = True + radio_dtos[self.keymap["admin_status"]] = True \ + if each_radio[self.keymap["admin_status"]] == "Enabled" else False if each_radio.get(self.keymap["channel_assignment_mode"]) is not None: radio_dtos[self.keymap["channel_assignment_mode"]] = 1 \ From 59d79b56b5ce6bbc4469978d365d2e5b691d913e Mon Sep 17 00:00:00 2001 From: md-rafeek Date: Wed, 21 Aug 2024 22:48:14 +0530 Subject: [PATCH 078/121] Bug raised for the 9 issue was fixed. --- plugins/modules/accesspoint_workflow_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/accesspoint_workflow_manager.py b/plugins/modules/accesspoint_workflow_manager.py index 1e1030f4fe..e91789e8d8 100644 --- a/plugins/modules/accesspoint_workflow_manager.py +++ b/plugins/modules/accesspoint_workflow_manager.py @@ -815,10 +815,10 @@ def __init__(self, module): "tri_radio": ["9124AXE", "9130AXI", "9130AXE", "9178I"] } self.allowed_channel_no = { - "2.4ghz_radio": list(range(1, 12)), + "2.4ghz_radio": list(range(1, 15)), "5ghz_radio": (36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173), - "xor_radio": list(range(1, 12)) + "xor_radio": list(range(1, 15)) } def validate_input_yml(self): From 3ac49b81d3514772055a8ebb088d636929275ab4 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 22 Aug 2024 10:23:42 +0530 Subject: [PATCH 079/121] user_role_workflow_manager bug fixed --- plugins/modules/user_role_workflow_manager.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 67909c4ea3..008a5e39ac 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1096,10 +1096,14 @@ def valid_role_config_parameters(self, role_config): error_messages = [] role_name = role_config.get("role_name") - role_name_regex = re.compile(r"^[a-zA-Z0-9._-]{,25}$") + role_name_regex = re.compile(r"^[a-zA-Z0-9._-]{1,25}$") role_name_regex_msg = "Role names must be 1 to 25 characters long and should contain only letters, numbers, periods, underscores, and hyphens." - self.validate_string_field(role_name, role_name_regex, - "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) + + if role_name: + self.log("role_nameeee: '{0}'".format(role_name)) + self.validate_string_field(role_name, role_name_regex, "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) + else: + error_messages.append(role_name_regex_msg) description = role_config["description"] if description: @@ -1619,7 +1623,6 @@ def process_assurance_rules(self, role_config, role_operation, unique_types): - role_operation (str): The operation type (e.g., "update"). - unique_types (dict): A dictionary to store the unique resource types and their operations. """ - self.log("role_paramsssssssss: {0}".format(role_config)) entry_types = [ "Assurance.Monitoring and Troubleshooting", "Assurance.Monitoring Settings", From 1845d565cd0848e9431f7658ea64bf65621e4139 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 22 Aug 2024 11:04:02 +0530 Subject: [PATCH 080/121] user_role_workflow_manager bug fixed --- plugins/modules/user_role_workflow_manager.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 008a5e39ac..481e24261a 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -896,7 +896,7 @@ def validate_input_yml(self, user_role_details): self.status = "failed" return self - if "role_name" in user_role_details[0] and user_role_details[0].get("role_name") is not None: + if "role_details" in self.payload.get("config") and "role_name" in user_role_details[0] and user_role_details[0].get("role_name") is not None: role_details = { "role_name": {"required": True, "type": "str"}, "description": {"required": False, "type": "str"}, @@ -924,7 +924,7 @@ def validate_input_yml(self, user_role_details): self.status = "success" return self - if "username" in user_role_details[0] or "email" in user_role_details[0]: + if "user_details" in self.payload.get("config") and "username" in user_role_details[0] or "email" in user_role_details[0]: if user_role_details[0].get("username") is not None or user_role_details[0].get("email") is not None: user_details = { "first_name": {"required": False, "type": "str"}, @@ -948,7 +948,11 @@ def validate_input_yml(self, user_role_details): self.status = "success" return self - self.msg = "Configuration params like 'username' or 'email' or 'role_name' is not available in the playbook" + self.msg = ( + "'Configuration params like 'username' or 'email' or 'role_name' is not available in the playbook' or " + "'user_details key is not valid for role creation, updation or deletion' or " + "'role_details key is not valid for user creation, updation or deletion'" + ) self.log(self.msg, "ERROR") self.status = "failed" return self From 611828263948a29a2891e7a0b8698422abfa55ff Mon Sep 17 00:00:00 2001 From: md-rafeek Date: Thu, 22 Aug 2024 20:23:05 +0530 Subject: [PATCH 081/121] Fixed code review comments --- .../modules/accesspoint_workflow_manager.py | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/plugins/modules/accesspoint_workflow_manager.py b/plugins/modules/accesspoint_workflow_manager.py index e91789e8d8..5b70262c7c 100644 --- a/plugins/modules/accesspoint_workflow_manager.py +++ b/plugins/modules/accesspoint_workflow_manager.py @@ -65,7 +65,7 @@ type: str required: True rf_profile: - description: Radio Frequency (RF) profile of the Access Point (e.g., 'HIGH'). + description: Radio Frequency (RF) profile of the Access Point. For example, "HIGH". type: str required: False site: @@ -78,43 +78,43 @@ required: False suboptions: name: - description: Name of the floor.(eg. 'FLOOR1') + description: Name of the floor. For example, "FLOOR1". type: str required: False parent_name: - description: Parent name of the floor in the site hierarchy.(eg. 'Global/USA/New York/BLDNYC') + description: Parent name of the floor in the site hierarchy. For example, "Global/USA/New York/BLDNYC". type: str required: False ap_name: - description: Current AP name that needs to be changed along with the new AP name.(eg. "Test2") + description: Current AP name that needs to be changed along with the new AP name. For example, "Test2". type: str required: False admin_status: - description: Status of the AP configuration. Accepts "Enabled" or "Disabled". (eg. "Enabled") + description: Status of the AP configuration. Accepts "Enabled" or "Disabled". For example, "Enabled". type: str required: False led_status: - description: State of the AP's LED. Accepts "Enabled" or "Disabled".(eg. "Enabled") + description: State of the AP's LED. Accepts "Enabled" or "Disabled". For example, "Enabled". type: str required: False led_brightness_level: - description: Brightness level of the AP's LED. Accepts values from 1 to 8.(eg. 3) + description: Brightness level of the AP's LED. Accepts values from 1 to 8. For example, 3. type: int required: False ap_mode: description: | - Mode of operation for the Access Point (AP). Possible values include "Local", - "Monitor", "Sniffer", or "Bridge".(eg. "Local") + Defines the mode of operation for the Access Point (AP). Possible values include "Local", + "Monitor", "Sniffer", or "Bridge". For example, "Local". type: str required: False location: - description: Location name of the AP. Provide this data if a change is required.(eg. "Bangalore") + description: Location name of the AP. Provide this data if a change is required. For example, "Bangalore". type: str required: False is_assigned_site_as_location: description: | - To configure the access point location as the site assigned to the access point, - accepts "Enabled" or "Disabled".(eg. "Enabled") incase Enabled no need to add location. + Configures whether the access point location is automatically set to the site assigned to the access point. + Accepts "Enabled" or "Disabled". If set to "Enabled", no additional location configuration is required. type: str required: False failover_priority: @@ -123,24 +123,24 @@ required: False clean_air_si_2.4ghz: description: | - Clean Air Spectrum Intelligence (SI) feature status for the 2.4GHz band. Indicates whether.(eg. "Enabled") + Clean Air Spectrum Intelligence (SI) feature status for the 2.4GHz band. Indicates whether. For example, "Enabled". Clean Air Spectrum Intelligence is enabled or disabled. type: str required: False clean_air_si_5ghz: description: | - Clean Air Spectrum Intelligence (SI) feature status for the 5GHz band. Indicates whether.(eg. "Enabled") + Clean Air Spectrum Intelligence (SI) feature status for the 5GHz band. Indicates whether. For example, "Enabled". Clean Air Spectrum Intelligence is enabled or disabled. type: str required: False clean_air_si_6ghz: description: | - Clean Air Spectrum Intelligence (SI) feature status for the 6GHz band. Indicates whether.(eg. "Enabled") + Clean Air Spectrum Intelligence (SI) feature status for the 6GHz band. Indicates whether. For example, "Enabled". Clean Air Spectrum Intelligence is enabled or disabled. type: str required: False primary_controller_name: - description: Name or identifier of the primary wireless LAN controller (WLC) managing the Access Point (AP).(eg. "SJ-EWLC-1") + description: Name or identifier of the primary wireless LAN controller (WLC) managing the Access Point (AP). For example, "SJ-EWLC-1". type: str required: False primary_ip_address: @@ -149,11 +149,11 @@ required: False suboptions: address: - description: IP address of the primary wireless LAN controller. (eg. '10.0.0.3') + description: IP address of the primary wireless LAN controller. For example, '10.0.0.3'. type: str required: False secondary_controller_name: - description: Name or identifier of the secondary wireless LAN controller (WLC) managing the Access Point (AP).(eg. "Inherit from site/Clear") + description: Name or identifier of the secondary wireless LAN controller (WLC) managing the Access Point (AP). For example, "Inherit from site/Clear". type: str required: False secondary_ip_address: @@ -162,11 +162,11 @@ required: False suboptions: address: - description: IP address of the primary wireless LAN controller (eg. '10.0.0.3') + description: IP address of the primary wireless LAN controller. For example, '10.0.0.3'. type: str required: False tertiary_controller_name: - description: Name or identifier of the tertiary wireless LAN controller (WLC) managing the Access Point (AP).(eg. "Clear") + description: Name or identifier of the tertiary wireless LAN controller (WLC) managing the Access Point (AP). For example, "Clear". type: str required: False tertiary_ip_address: @@ -175,7 +175,7 @@ required: False suboptions: address: - description: IP address of the primary wireless LAN controller (eg. '10.0.0.2') + description: IP address of the primary wireless LAN controller. For example, '10.0.0.2'. type: str required: False 2.4ghz_radio: @@ -184,43 +184,43 @@ required: False suboptions: admin_status: - description: Administrative status for the 2.4GHz radio interface.(eg. 'Enabled') + description: Administrative status for the 2.4GHz radio interface. For example, "Enabled". type: str required: False antenna_name: - description: Name or type of antenna used for the 2.4GHz radio interface.(eg. "other") + description: Name or type of antenna used for the 2.4GHz radio interface. For example, "other". type: str required: False antenna_gain: - description: Antenna gain value in decibels (dB) for the 2.4GHz radio interface.(eg. 4) + description: Specifies the antenna gain value in decibels (dB) for the 2.4GHz radio interface. For example, 4. type: int required: False radio_role_assignment: - description: Role assignment mode for the 2.4GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. "Auto") + description: Role assignment mode for the 2.4GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor". For example, Auto. type: str required: False cable_loss: - description: Cable loss in dB for the 2.4GHz radio interface.(eg. 75) + description: Cable loss in dB for the 2.4GHz radio interface. For example, 75. type: int required: False antenna_cable_name: - description: Name or type of antenna cable used for the 2.4GHz radio interface.(eg. "other") + description: Name or type of antenna cable used for the 2.4GHz radio interface. For example, "other". type: str required: False channel_assignment_mode: - description: Mode of channel assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + description: Mode of channel assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False channel_number: - description: Custom channel number configured for the 2.4GHz radio interface.(eg. 6) + description: Custom channel number configured for the 2.4GHz radio interface. For example, 6. type: int required: False power_assignment_mode: - description: Mode of power assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + description: Mode of power assignment for the 2.4GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False power_level: - description: Custom power level configured for the 2.4GHz radio interface. (eg. 3) + description: Custom power level configured for the 2.4GHz radio interface. For example, 3. type: int required: False 5ghz_radio: @@ -229,43 +229,43 @@ required: False suboptions: admin_status: - description: Administrative status for the 5GHz radio interface.(eg. "Enabled") + description: Administrative status for the 5GHz radio interface. For example, "Enabled". type: str required: False antenna_name: - description: Name or type of antenna used for the 5GHz radio interface.(eg. other) + description: Name or type of antenna used for the 5GHz radio interface. For example, "other". type: str required: False antenna_gain: - description: Antenna gain value in decibels (dB) for the 5GHz radio interface.(eg. 5) + description: Antenna gain value in decibels (dB) for the 5GHz radio interface. For example, 5. type: int required: False radio_role_assignment: - description: Role assignment mode for the 5GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor".(eg. Auto) + description: Role assignment mode for the 5GHz radio interface. Accepts "Auto", "Client-serving", or "Monitor". For example, "Auto". type: str required: False cable_loss: - description: Cable loss in dB for the 5GHz radio interface. (eg. 80) + description: Cable loss in dB for the 5GHz radio interface. For example, 80. type: int required: False antenna_cable_name: - description: Name or type of antenna cable used for the 5GHz radio interface.(eg. other) + description: Name or type of antenna cable used for the 5GHz radio interface. For example, "other". type: str required: False channel_assignment_mode: - description: Mode of channel assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + description: Mode of channel assignment for the 5GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False channel_number: - description: Custom channel number configured for the 5GHz radio interface. (eg. 36) + description: Custom channel number configured for the 5GHz radio interface. For example, 36. type: int required: False power_assignment_mode: - description: Mode of power assignment for the 5GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + description: Mode of power assignment for the 5GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False power_level: - description: Custom power level configured for the 5GHz radio interface. (eg. 3) + description: Custom power level configured for the 5GHz radio interface. For example, 3. type: int required: False 6ghz_radio: @@ -274,15 +274,15 @@ required: False suboptions: admin_status: - description: Administrative status for the 6GHz radio interface.(eg. "Enabled") + description: Administrative status for the 6GHz radio interface. For example, "Enabled". type: str required: False antenna_name: - description: Name or type of antenna used for the 6GHz radio interface. (eg. other) + description: Name or type of antenna used for the 6GHz radio interface. For example, "other". type: str required: False antenna_gain: - description: Antenna gain value in decibels (dB) for the 6GHz radio interface.(eg. 4) + description: Antenna gain value in decibels (dB) for the 6GHz radio interface. For example, 4. type: int required: False radio_role_assignment: @@ -290,27 +290,27 @@ type: str required: False cable_loss: - description: Cable loss in dB for the 6GHz radio interface. (eg. 75) + description: Cable loss in dB for the 6GHz radio interface. For example, 75. type: int required: False antenna_cable_name: - description: Name or type of antenna cable used for the 6GHz radio interface.(eg. "other") + description: Name or type of antenna cable used for the 6GHz radio interface. For example, "other". type: str required: False channel_assignment_mode: - description: Mode of channel assignment for the 6GHz radio interface. Accepts "Global" or "Custom".(eg. "Custom") + description: Mode of channel assignment for the 6GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False channel_number: - description: Custom channel number configured for the 6GHz radio interface.(eg. 6) + description: Custom channel number configured for the 6GHz radio interface. For example, 6. type: int required: False power_assignment_mode: - description: Mode of power assignment for the 6GHz radio interface. Accepts "Global" or "Custom". (eg. "Custom") + description: Mode of power assignment for the 6GHz radio interface. Accepts "Global" or "Custom". For example, "Custom". type: str required: False power_level: - description: Custom power level configured for the 6GHz radio interface.(eg. 3) + description: Custom power level configured for the 6GHz radio interface. For example, 3. type: int required: False xor_radio: @@ -319,47 +319,47 @@ required: False suboptions: admin_status: - description: Administrative status for the xor radio interface. (eg. Enabled) + description: Administrative status for the xor radio interface. For example, "Enabled". type: str required: False antenna_name: - description: Name or type of antenna used for the xor radio interface.(eg. other) + description: Name or type of antenna used for the xor radio interface. For example, "other". type: str required: False antenna_gain: - description: Antenna gain value in decibels (dB) for the xor radio interface.(eg. 4) + description: Antenna gain value in decibels (dB) for the xor radio interface. For example, 4. type: int required: False radio_role_assignment: description: | Role assignment mode for the xor radio interface. Accepts "Auto", "Client-serving", or "Monitor". If radio_role_assignment is "client-serving", then only power-level and channel-level can be changed. - (eg. Auto) + For example, Auto. type: str required: False cable_loss: - description: Cable loss in dB for the xor radio interface. (e.g 75) + description: Cable loss in dB for the xor radio interface. For example, 75. type: int required: False antenna_cable_name: - description: Name or type of antenna cable used for the xor radio interface.(eg. other) + description: Name or type of antenna cable used for the xor radio interface. For example, "other". type: str required: False channel_assignment_mode: description: | Mode of channel assignment for the xor radio interface. Accepts "Global" or "Custom". For xor Custom, it accepts values like 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, - 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. (eg. "Custom") + 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165, 169, 173. For example, "Custom". type: str required: False channel_number: - description: Custom channel number configured for the xor radio interface.(eg. 6) + description: Custom channel number configured for the xor radio interface. For example, 6. type: int required: False channel_width: description: | Width of the channel configured for the xor radio interface. Accepts values - "20 MHz", "40 MHz", "80 MHz", or "160 MHz". (eg. 20 MHz) + "20 MHz", "40 MHz", "80 MHz", or "160 MHz". For example, 20 MHz. type: str required: False power_assignment_mode: @@ -369,7 +369,7 @@ type: str required: False power_level: - description: Custom power level configured for the xor radio interface. (eg. 3) + description: Custom power level configured for the xor radio interface. For example, 3. type: int required: False tri_radio: @@ -378,15 +378,15 @@ required: False suboptions: admin_status: - description: Administrative status for the tri radio interface. (eg. Enabled) + description: Administrative status for the tri radio interface. For example, "Enabled". type: str required: False antenna_name: - description: Name or type of antenna used for the tri radio interface.(eg. other) + description: Name or type of antenna used for the tri radio interface. For example, "other". type: str required: False antenna_gain: - description: Antenna gain value in decibels (dB) for the tri radio interface. (eg. 4) + description: Antenna gain value in decibels (dB) for the tri radio interface. For example, 4. type: int required: False radio_role_assignment: @@ -396,11 +396,11 @@ type: str required: False cable_loss: - description: Cable loss in dB for the tri radio interface. (eg. 75) + description: Cable loss in dB for the tri radio interface. For example, 75. type: int required: False antenna_cable_name: - description: Name or type of antenna cable used for the tri radio interface. (eg. "other") + description: Name or type of antenna cable used for the tri radio interface. For example, "other". type: str required: False channel_assignment_mode: @@ -411,13 +411,13 @@ type: str required: False channel_number: - description: Custom channel number configured for the tri radio interface. (eg. 6) + description: Custom channel number configured for the tri radio interface. For example, 6. type: int required: False channel_width: description: | Width of the channel configured for the tri radio interface. Accepts values - "20 MHz", "40 MHz", "80 MHz", or "160 MHz". eg. 20 MHz + "20 MHz", "40 MHz", "80 MHz", or "160 MHz". . For example, 20 MHz. type: str required: False power_assignment_mode: @@ -427,7 +427,7 @@ type: str required: False power_level: - description: Custom power level configured for the tri radio interface.(eg. 3) + description: Custom power level configured for the tri radio interface. For example, 3. type: int required: False dual_radio_mode: @@ -1345,7 +1345,7 @@ def validate_ap_config_parameters(self, ap_config): is_assigned_site_as_location = ap_config.get("is_assigned_site_as_location") if is_assigned_site_as_location and is_assigned_site_as_location not in ("Disabled", "Enabled"): - errormsg.append("is_assigned_site_as_location: Invalid is_assigned_site_as_location '{0}' in playbook." + errormsg.append("is_assigned_site_as_location: Invalid value '{0}' for is_assigned_site_as_location in playbook. Must be either 'Disabled' or 'Enabled'." .format(is_assigned_site_as_location)) ap_mode = ap_config.get("ap_mode") From a0070930d7aeecd5cb09409acf8c718cc2334bc1 Mon Sep 17 00:00:00 2001 From: md-rafeek Date: Thu, 22 Aug 2024 22:04:29 +0530 Subject: [PATCH 082/121] Sanity issue on the lenth of line --- plugins/modules/accesspoint_workflow_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/modules/accesspoint_workflow_manager.py b/plugins/modules/accesspoint_workflow_manager.py index 5b70262c7c..dc30b4b564 100644 --- a/plugins/modules/accesspoint_workflow_manager.py +++ b/plugins/modules/accesspoint_workflow_manager.py @@ -1345,8 +1345,8 @@ def validate_ap_config_parameters(self, ap_config): is_assigned_site_as_location = ap_config.get("is_assigned_site_as_location") if is_assigned_site_as_location and is_assigned_site_as_location not in ("Disabled", "Enabled"): - errormsg.append("is_assigned_site_as_location: Invalid value '{0}' for is_assigned_site_as_location in playbook. Must be either 'Disabled' or 'Enabled'." - .format(is_assigned_site_as_location)) + errormsg.append("is_assigned_site_as_location: Invalid value '{0}' for is_assigned_site_as_location in playbook.\ + Must be either 'Disabled' or 'Enabled'.".format(is_assigned_site_as_location)) ap_mode = ap_config.get("ap_mode") if ap_mode and ap_mode not in ("Local", "Monitor", "Sniffer", "Bridge"): From 9e7c6db471dbdbb8deda30f70afa7752e31fcfda Mon Sep 17 00:00:00 2001 From: md-rafeek Date: Fri, 23 Aug 2024 07:50:18 +0530 Subject: [PATCH 083/121] AP update V1 to V2 to support clean air --- plugins/modules/accesspoint_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/accesspoint_workflow_manager.py b/plugins/modules/accesspoint_workflow_manager.py index dc30b4b564..bd78180194 100644 --- a/plugins/modules/accesspoint_workflow_manager.py +++ b/plugins/modules/accesspoint_workflow_manager.py @@ -2379,7 +2379,7 @@ def update_ap_configuration(self, ap_config): try: response = self.dnac._exec( family="wireless", - function='configure_access_points_v1', + function='configure_access_points_v2', op_modifies=True, params={"payload": ap_config} ) From b8134f8fc5f258e5aa4b768151a0974c62912ec8 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 23 Aug 2024 10:08:19 +0530 Subject: [PATCH 084/121] user_role_workflow_manager comments fixed --- plugins/modules/user_role_workflow_manager.py | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 481e24261a..b1205d1ab1 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -889,6 +889,7 @@ def validate_input_yml(self, user_role_details): - If the validation succeeds, this will allow to go next step, unless this will stop execution based on the fields. """ self.log("Validating the Playbook Yaml File..", "INFO") + config = self.payload.get("config") if user_role_details is None or not isinstance(user_role_details, list): self.msg = "Configuration is not available in the playbook for validation or user/role details are not type list" @@ -896,7 +897,7 @@ def validate_input_yml(self, user_role_details): self.status = "failed" return self - if "role_details" in self.payload.get("config") and "role_name" in user_role_details[0] and user_role_details[0].get("role_name") is not None: + if "role_details" in config and "role_name" in user_role_details[0] and user_role_details[0].get("role_name") is not None: role_details = { "role_name": {"required": True, "type": "str"}, "description": {"required": False, "type": "str"}, @@ -924,7 +925,7 @@ def validate_input_yml(self, user_role_details): self.status = "success" return self - if "user_details" in self.payload.get("config") and "username" in user_role_details[0] or "email" in user_role_details[0]: + if "user_details" in config and "username" in user_role_details[0] or "email" in user_role_details[0]: if user_role_details[0].get("username") is not None or user_role_details[0].get("email") is not None: user_details = { "first_name": {"required": False, "type": "str"}, @@ -949,9 +950,9 @@ def validate_input_yml(self, user_role_details): return self self.msg = ( - "'Configuration params like 'username' or 'email' or 'role_name' is not available in the playbook' or " - "'user_details key is not valid for role creation, updation or deletion' or " - "'role_details key is not valid for user creation, updation or deletion'" + "'Configuration parameters such as 'username', 'email', or 'role_name' are missing from the playbook' or " + "'The 'user_details' key is invalid for role creation, updation, or deletion' or " + "'The 'role_details' key is invalid for user creation, updation, or deletion'" ) self.log(self.msg, "ERROR") self.status = "failed" @@ -1037,7 +1038,7 @@ def identify_invalid_params(self, params, mismatches): """ Identify and collect invalid parameters from a dictionary or list based on allowed parameters. Args: - - params (dict): The dictionary of parameters to be checked. Nested dictionaries or lists are supported. + - params (dict | list): The dictionary or list of parameters to be checked. Nested dictionaries or lists are supported. - mismatches (list): A list where invalid parameter names are appended. This list is used to collect all parameters that are not in 'allowed_params'. Returns: @@ -1056,11 +1057,12 @@ def identify_invalid_params(self, params, mismatches): "scheduler", "search", 'role_name', 'description', 'assurance', 'network_analytics', 'network_design', 'network_provision', 'network_services', 'platform', 'security', 'system', 'utilities', 'overall' ] - self.log("Iterate through the params to find unknown parameters are present or not", "DEBUG") + self.log("Starting to iterate through params to identify unknown parameters.", "DEBUG") if isinstance(params, dict): for key, value in params.items(): if key not in allowed_params: + self.log(f"Invalid parameter detected: {key}", "ERROR") mismatches.append(key) if isinstance(value, dict) or isinstance(value, list): @@ -1069,6 +1071,9 @@ def identify_invalid_params(self, params, mismatches): for item in params: self.identify_invalid_params(item, mismatches) + if not mismatches: + self.log("No invalid parameters found.", "INFO") + return mismatches def valid_role_config_parameters(self, role_config): @@ -1104,7 +1109,6 @@ def valid_role_config_parameters(self, role_config): role_name_regex_msg = "Role names must be 1 to 25 characters long and should contain only letters, numbers, periods, underscores, and hyphens." if role_name: - self.log("role_nameeee: '{0}'".format(role_name)) self.validate_string_field(role_name, role_name_regex, "role_name: '{0}' {1}".format(role_name, role_name_regex_msg), error_messages) else: error_messages.append(role_name_regex_msg) @@ -1347,7 +1351,7 @@ def get_diff_merged(self, config): if update_required_param.get("role_list"): if self.want["username"] not in self.have["current_user_config"]["username"]: - task_response = {"error_message": "Username for an existing User cannot be updated."} + task_response = {"error_message": "Username for an existing user cannot be updated."} else: user_in_have = self.have["current_user_config"] update_param = update_required_param @@ -3132,13 +3136,15 @@ def update_user_role_profile_messages(self): delete_role_msg = "Role(s) '{0}' deleted successfully from the Cisco Catalyst Center.".format("', '".join(self.deleted_role)) result_msg_list.append(delete_role_msg) - if self.created_user or self.updated_user or self.deleted_user or self.created_role or self.updated_role or self.deleted_role: + if result_msg_list: self.result["changed"] = True + self.msg = " ".join(result_msg_list) + else: + self.msg = "No changes were made. No user or role actions were performed in Cisco Catalyst Center." - self.msg = " ".join(result_msg_list) self.log(self.msg, "INFO") self.result["response"] = self.msg - + return self def snake_to_camel_case(self, data): From 9b6a5e9ca44836cb7e4d67f9725e60297a38c93d Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 23 Aug 2024 10:13:49 +0530 Subject: [PATCH 085/121] user_role_workflow_manager comments fixed --- plugins/modules/user_role_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index b1205d1ab1..a15ff32cca 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -3144,7 +3144,7 @@ def update_user_role_profile_messages(self): self.log(self.msg, "INFO") self.result["response"] = self.msg - + return self def snake_to_camel_case(self, data): From 6f19354f7af4ab19b12e13cd3daa84515b758bdc Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 23 Aug 2024 10:41:08 +0530 Subject: [PATCH 086/121] user_role_workflow_manager comments fixed --- plugins/modules/user_role_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index a15ff32cca..8c029ac747 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -1062,7 +1062,7 @@ def identify_invalid_params(self, params, mismatches): if isinstance(params, dict): for key, value in params.items(): if key not in allowed_params: - self.log(f"Invalid parameter detected: {key}", "ERROR") + self.log("Invalid parameter detected: {0}".format(key), "ERROR") mismatches.append(key) if isinstance(value, dict) or isinstance(value, list): From 85fd5eedd4341eeab32b85dcc5e5fb268b555389 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 09:41:20 +0200 Subject: [PATCH 087/121] check vars --- .circleci/config.yml | 3 ++- plugins/module_utils/exceptions.py | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fe1703c6b5..f8889eb6bc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -326,6 +326,7 @@ jobs: export ANSIBLE_ROLES_PATH=$PWD/tests/integration cat ccc_roles.yml echo $(circleci tests glob "tests/integration/*") + env cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name no_output_timeout: 120m @@ -388,7 +389,7 @@ workflows: and: - or: - equal: [ run-tests, << pipeline.parameters.GHA_Meta >> ] -# - equal: [ true, << pipeline.parameters.run-setup >> ] + - equal: [ true, << pipeline.parameters.run-setup >> ] - not: << pipeline.parameters.continued >> jobs: - pre diff --git a/plugins/module_utils/exceptions.py b/plugins/module_utils/exceptions.py index 007f8e1f45..e56067b78e 100644 --- a/plugins/module_utils/exceptions.py +++ b/plugins/module_utils/exceptions.py @@ -11,3 +11,4 @@ class AnsibleDNACException(Exception): class InconsistentParameters(AnsibleDNACException): """Provided parameters are not consistent.""" pass + From 5f8a45a352b202ff05ee79ac64533e0075c3b580 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:12:32 +0200 Subject: [PATCH 088/121] store ansible logs on internal vm --- .circleci/config.yml | 9 +++++++-- run_tests.sh | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f8889eb6bc..622f7a6bdb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -326,8 +326,12 @@ jobs: export ANSIBLE_ROLES_PATH=$PWD/tests/integration cat ccc_roles.yml echo $(circleci tests glob "tests/integration/*") - env - cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name + cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name || echo -n $? > rc + echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh + ssh $LOGS_MACHINE -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM + scp -i ~/id_ssh -o UserKnownHostsFile=/dev/null sanity_tests_logs_$CIRCLE_NODE_INDEX $LOGS_MACHINE:/var/www/html/madhan-logs/$CIRCLE_BUILD_NUM + echo "LOGS URL: http://$LOGS_VM_IP/madhan-logs/$CIRCLE_BUILD_NUM/sanity_tests_logs_$CIRCLE_NODE_INDEX" + if [[ $(cat rc) != "0" ]]; then exit 1; fi no_output_timeout: 120m @@ -449,6 +453,7 @@ workflows: - addrole context: - dnac-servers + - logs-vm # - post_pnp_testing: # requires: # - sanity-tests diff --git a/run_tests.sh b/run_tests.sh index 644a6292f1..03c603df5b 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -21,4 +21,4 @@ do echo " - $role" >> ccc_test_roles.yml done -ansible-playbook -i hosts ccc_test_roles.yml +ansible-playbook -i hosts ccc_test_roles.yml > sanity_tests_logs_$CIRCLE_NODE_INDEX From 0d6f231372f7c316b382a540d4bd627f235e3c43 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:23:03 +0200 Subject: [PATCH 089/121] add no_proxy --- .circleci/config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 622f7a6bdb..ac63b84646 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -328,6 +328,8 @@ jobs: echo $(circleci tests glob "tests/integration/*") cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name || echo -n $? > rc echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh + export NO_PROXY="" + export no_proxy="" ssh $LOGS_MACHINE -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM scp -i ~/id_ssh -o UserKnownHostsFile=/dev/null sanity_tests_logs_$CIRCLE_NODE_INDEX $LOGS_MACHINE:/var/www/html/madhan-logs/$CIRCLE_BUILD_NUM echo "LOGS URL: http://$LOGS_VM_IP/madhan-logs/$CIRCLE_BUILD_NUM/sanity_tests_logs_$CIRCLE_NODE_INDEX" From 6c99cd11b3b387d97e944bc1df223e2264fd9bf8 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:33:32 +0200 Subject: [PATCH 090/121] disable StrictHostKeyCheck --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index ac63b84646..0da161623e 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -330,8 +330,8 @@ jobs: echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh export NO_PROXY="" export no_proxy="" - ssh $LOGS_MACHINE -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM - scp -i ~/id_ssh -o UserKnownHostsFile=/dev/null sanity_tests_logs_$CIRCLE_NODE_INDEX $LOGS_MACHINE:/var/www/html/madhan-logs/$CIRCLE_BUILD_NUM + ssh $LOGS_MACHINE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM + scp -i ~/id_ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sanity_tests_logs_$CIRCLE_NODE_INDEX $LOGS_MACHINE:/var/www/html/madhan-logs/$CIRCLE_BUILD_NUM echo "LOGS URL: http://$LOGS_VM_IP/madhan-logs/$CIRCLE_BUILD_NUM/sanity_tests_logs_$CIRCLE_NODE_INDEX" if [[ $(cat rc) != "0" ]]; then exit 1; fi From 03e98a47cadf3898d74a72e65b1b1cc20bcf5160 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:42:06 +0200 Subject: [PATCH 091/121] fix permissions --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0da161623e..23641d2753 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -328,6 +328,7 @@ jobs: echo $(circleci tests glob "tests/integration/*") cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name || echo -n $? > rc echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh + chmod 644 ~/id_ssh export NO_PROXY="" export no_proxy="" ssh $LOGS_MACHINE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM From 0b0fa9ffc2f5a889be915c4c90cde151c4167f9d Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:50:02 +0200 Subject: [PATCH 092/121] fix --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 23641d2753..d8aa3d7bba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -328,7 +328,7 @@ jobs: echo $(circleci tests glob "tests/integration/*") cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name || echo -n $? > rc echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh - chmod 644 ~/id_ssh + chmod 600 ~/id_ssh export NO_PROXY="" export no_proxy="" ssh $LOGS_MACHINE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM From 39cc30cbc2e381acea80ab6cb672853d548c3c83 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:56:55 +0200 Subject: [PATCH 093/121] init rc file --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index d8aa3d7bba..0b89f30b15 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -326,6 +326,7 @@ jobs: export ANSIBLE_ROLES_PATH=$PWD/tests/integration cat ccc_roles.yml echo $(circleci tests glob "tests/integration/*") + echo -n 0 > rc cat ccc_roles.yml | circleci tests run --command "xargs ./run_tests.sh" --split-by=timings --timings-type=name || echo -n $? > rc echo ${LOGS_SSH_KEY} | base64 -d > ~/id_ssh chmod 600 ~/id_ssh From 2c42981de435cb387d4099bd2d72c1edc6e37662 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 17:59:42 +0200 Subject: [PATCH 094/121] don't use context var for ip --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0b89f30b15..589549bb8d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -334,7 +334,7 @@ jobs: export no_proxy="" ssh $LOGS_MACHINE -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/id_ssh mkdir -p /var/www/html/madhan-logs/$CIRCLE_BUILD_NUM scp -i ~/id_ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null sanity_tests_logs_$CIRCLE_NODE_INDEX $LOGS_MACHINE:/var/www/html/madhan-logs/$CIRCLE_BUILD_NUM - echo "LOGS URL: http://$LOGS_VM_IP/madhan-logs/$CIRCLE_BUILD_NUM/sanity_tests_logs_$CIRCLE_NODE_INDEX" + echo "LOGS URL: http://10.195.243.37/madhan-logs/$CIRCLE_BUILD_NUM/sanity_tests_logs_$CIRCLE_NODE_INDEX" if [[ $(cat rc) != "0" ]]; then exit 1; fi no_output_timeout: 120m From d6b5ed188c91b78e48053915e0d874fe836aef00 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Tue, 20 Aug 2024 18:34:57 +0200 Subject: [PATCH 095/121] Don't run everytime --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 589549bb8d..222180f51c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -397,7 +397,7 @@ workflows: and: - or: - equal: [ run-tests, << pipeline.parameters.GHA_Meta >> ] - - equal: [ true, << pipeline.parameters.run-setup >> ] +# - equal: [ true, << pipeline.parameters.run-setup >> ] - not: << pipeline.parameters.continued >> jobs: - pre From e57c55df0a6a4255f6ba825c20676dcb22a041f1 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Wed, 21 Aug 2024 11:28:57 +0200 Subject: [PATCH 096/121] Fix shellcheck --- run_tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run_tests.sh b/run_tests.sh index 03c603df5b..6e4fb689c2 100755 --- a/run_tests.sh +++ b/run_tests.sh @@ -21,4 +21,4 @@ do echo " - $role" >> ccc_test_roles.yml done -ansible-playbook -i hosts ccc_test_roles.yml > sanity_tests_logs_$CIRCLE_NODE_INDEX +ansible-playbook -i hosts ccc_test_roles.yml > "sanity_tests_logs_$CIRCLE_NODE_INDEX" From 07daabc39ef823797b17df05611ebe31f5f6b7fa Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Fri, 23 Aug 2024 09:33:19 +0200 Subject: [PATCH 097/121] remove trailing spae --- plugins/module_utils/exceptions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/module_utils/exceptions.py b/plugins/module_utils/exceptions.py index e56067b78e..007f8e1f45 100644 --- a/plugins/module_utils/exceptions.py +++ b/plugins/module_utils/exceptions.py @@ -11,4 +11,3 @@ class AnsibleDNACException(Exception): class InconsistentParameters(AnsibleDNACException): """Provided parameters are not consistent.""" pass - From 1f1072052dbcb41df295988a8b44e971cdba718a Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Fri, 23 Aug 2024 10:02:46 +0200 Subject: [PATCH 098/121] Pass sanity tests when there's nothing to execute --- .circleci/config.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 222180f51c..9beb4b57d7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -275,6 +275,15 @@ jobs: resource_class: madhansansel/dnacenter-ansible parallelism: 4 steps: + - when: + condition: + not: << pipeline.parameters.run-any >> + steps: + - run: + name: Finish sanity tests as there is nothing to execute + command: | + circleci-agent step halt + - run: name: Debug information command: | @@ -449,7 +458,10 @@ workflows: tests/integration/ccc_network_compliance_management/.* run-networkcompliance true testing: - when: << pipeline.parameters.run-any >> + when: + or: + - equal: [ true, << pipeline.parameters.run-any >> ] + - equal: [ true, << pipeline.parameters.continued >> ] jobs: - addrole - sanity-tests: From 2f09ccdc279f6d4e3fcbab103dcc04a15b2f421b Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Fri, 23 Aug 2024 10:53:45 +0200 Subject: [PATCH 099/121] Disable dry-run --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f63d285fc4..e9588e8987 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -22,4 +22,4 @@ jobs: merge-method: rebase do-not-merge-labels: never-merge # pull-request: ${{ github.event.inputs.pull-request }} - dry-run: true \ No newline at end of file + dry-run: false \ No newline at end of file From bb39b325cc91e1b408538ad3a833796df08b5e8b Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Tue, 27 Aug 2024 19:23:42 +0530 Subject: [PATCH 100/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 118 +++++++++++++++++------- 1 file changed, 85 insertions(+), 33 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index b64e198feb..cd8a49a64f 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -__author__ = ("Trupti A Shetty, Mohamed Rafeek, Madhan Sankaranarayanan") +__author__ = ("Trupti A Shetty, Ajith Andrew J, Mohamed Rafeek, Madhan Sankaranarayanan") DOCUMENTATION = r""" @@ -42,6 +42,7 @@ - cisco.dnac.workflow_manager_params author: - Trupti A Shetty (@TruptiAShetty) + - Ajith Andrew J (@ajithandrewj) - A Mohamed Rafeek (@mohamedrafeek) - Madhan Sankaranarayanan (@madhansansel) @@ -311,6 +312,7 @@ def __init__(self, module): self.supported_states = ["merged", "deleted", "replaced"] self.payload = module.params self.keymap = {} + self.faulty_device, self.replacement_device = [], [] def pprint(self, jsondata): return json.dumps(jsondata, indent=4, separators=(',', ': ')) @@ -466,7 +468,7 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - faulty_device_id, faulty_device_serial_number = self.device_exists(faulty_identifier, faulty_key) + faulty_device_id, faulty_device_serial_number, faulty_device_name = self.device_exists(faulty_identifier, faulty_key) if faulty_device_id is None or faulty_device_serial_number is None: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") @@ -475,12 +477,13 @@ def get_have(self): have["faulty_device_id"] = faulty_device_id have["faulty_device_serial_number"] = faulty_device_serial_number + have["faulty_device_name"] = faulty_device_name have[faulty_key] = faulty_identifier have["faulty_device_exists"] = True self.log("Faulty device '{0}' found in Cisco Catalyst Center".format(faulty_identifier), "INFO") # Check if replacement device exists - replacement_device_id, replacement_device_serial_number = self.device_exists(replacement_identifier, replacement_key) + replacement_device_id, replacement_device_serial_number, replacement_device_name = self.device_exists(replacement_identifier, replacement_key) if replacement_device_id is None or replacement_device_serial_number is None: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") @@ -489,6 +492,7 @@ def get_have(self): have["replacement_device_id"] = replacement_device_id have["replacement_device_serial_number"] = replacement_device_serial_number + have["replacement_device_name"] = replacement_device_name have[replacement_key] = replacement_identifier have["replacement_device_exists"] = True self.log("Replacement device '{0}' found in Cisco Catalyst Center".format(replacement_identifier), "INFO") @@ -563,8 +567,9 @@ def device_exists(self, identifier, identifier_type): device = response['response'][0] device_id = device.get('id') serial_number = device.get('serialNumber') - if device_id and serial_number: - return device_id, serial_number + device_name = device.get('hostname') + if device_id and serial_number and device_name: + return device_id, serial_number, device_name self.log("Device found but ID or serial number missing", "ERROR") else: self.log("Device not found in Cisco Catalyst Center", "ERROR") @@ -626,6 +631,23 @@ def validate_device_replacement_params(self): self.status = "success" return self + def device_ready_for_replacement_check(self): + """ + + """ + response = self.dnac._exec( + family="device_replacement", + function='return_replacement_devices_with_details' + ) + devices = response.get("response", []) + + for device in devices: + if device.get("faultyDeviceSerialNumber") == self.have.get("faulty_device_serial_number"): + if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": + self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(device.get("faultyDeviceName")), "DEBUG") + return True + return False + def mark_faulty_device_for_replacement(self): """ Mark the faulty device for replacement in Cisco Catalyst Center. @@ -643,36 +665,37 @@ def mark_faulty_device_for_replacement(self): - Updates the status, msg, and result attributes based on the task result. - Handles any exceptions that occur during the process. """ - - import_params = dict( - payload=[{ - "faultyDeviceId": self.have.get("faulty_device_id"), - "replacementStatus": "MARKED-FOR-REPLACEMENT" - }], - ) - - try: - response = self.dnac._exec( - family="device_replacement", - function='mark_device_for_replacement', - params=import_params + is_ready_for_replacement = self.device_ready_for_replacement_check() + if not is_ready_for_replacement: + import_params = dict( + payload=[{ + "faultyDeviceId": self.have.get("faulty_device_id"), + "replacementStatus": "MARKED-FOR-REPLACEMENT" + }], ) - self.log("Received API response from 'mark_device_for_replacement': {0}".format(str(response)), "DEBUG") - task_id = response.get("response", {}).get("taskId") - task_result = self.check_rma_task_status( - task_id, - "Device marked for replacement successfully", - "Error while marking device for replacement" - ) - self.status = task_result["status"] - self.msg = task_result["msg"] - if self.status == "success": - self.result['changed'] = True - except Exception as e: - self.status = "failed" - self.msg = "Exception occurred while marking device for replacement: {0}".format(str(e)) - self.log(self.msg, "ERROR") + try: + response = self.dnac._exec( + family="device_replacement", + function='mark_device_for_replacement', + params=import_params + ) + self.log("Received API response from 'mark_device_for_replacement': {0}".format(str(response)), "DEBUG") + task_id = response.get("response", {}).get("taskId") + task_result = self.check_rma_task_status( + task_id, + "Device marked for replacement successfully", + "Error while marking device for replacement" + ) + self.status = task_result["status"] + self.msg = task_result["msg"] + if self.status == "success": + self.result['changed'] = True + + except Exception as e: + self.status = "failed" + self.msg = "Exception occurred while marking device for replacement: {0}".format(str(e)) + self.log(self.msg, "ERROR") return self @@ -752,6 +775,8 @@ def get_diff_replaced(self, config): self.result['msg'] = self.msg return self + self.faulty_device.append(self.have.get("faulty_device_name")) + self.replacement_device.append(self.have.get("replacement_device_name")) self.result['changed'] = True self.result['msg'] = self.msg @@ -946,6 +971,31 @@ def check_rma_task_status(self, task_id, success_message, error_prefix): time.sleep(ccc_poll_interval) timeout_interval -= ccc_poll_interval + def update_rma_profile_messages(self): + """ + + """ + self.result["changed"] = False + result_msg_list = [] + + if self.faulty_device and self.replacement_device: + device_replacement_msg = ( + "Device replacement was successfully completed for the faulty device(s) '{0}'," + " with the replacement device(s) '{1}'.".format("', '".join(self.faulty_device), "', '".join(self.replacement_device)) + ) + result_msg_list.append(device_replacement_msg) + + if result_msg_list: + self.result["changed"] = True + self.msg = " ".join(result_msg_list) + else: + self.msg = "No changes were made. No RMA device replacement were performed in Cisco Catalyst Center." + + self.log(self.msg, "INFO") + self.result["response"] = self.msg + + return self + def verify_diff_replaced(self, config): """ Verify the device replacement status in Cisco Catalyst Center. @@ -1040,6 +1090,8 @@ def main(): if config_verify: ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() + ccc_device_replacement.update_rma_profile_messages().check_return_status() + module.exit_json(**ccc_device_replacement.result) From 94daddca8b1bbbd0ed946f58b4b677acdf957b24 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 28 Aug 2024 14:41:48 +0530 Subject: [PATCH 101/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 100 +++++++++++++++++++++--- 1 file changed, 88 insertions(+), 12 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index cd8a49a64f..16d9fe2e5f 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -468,7 +468,9 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - faulty_device_id, faulty_device_serial_number, faulty_device_name = self.device_exists(faulty_identifier, faulty_key) + (faulty_device_id, faulty_device_serial_number, faulty_device_name, + faulty_device_family_name, faulty_device_series_name, faulty_device_reachability_status, faulty_device_platform_id) = self.device_exists( + faulty_identifier, faulty_key) if faulty_device_id is None or faulty_device_serial_number is None: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") @@ -478,12 +480,18 @@ def get_have(self): have["faulty_device_id"] = faulty_device_id have["faulty_device_serial_number"] = faulty_device_serial_number have["faulty_device_name"] = faulty_device_name + have["faulty_device_family_name"] = faulty_device_family_name + have["faulty_device_series_name"] = faulty_device_series_name + have["faulty_device_reachability_status"] = faulty_device_reachability_status + have["faulty_device_platform_id"] = faulty_device_platform_id have[faulty_key] = faulty_identifier have["faulty_device_exists"] = True self.log("Faulty device '{0}' found in Cisco Catalyst Center".format(faulty_identifier), "INFO") # Check if replacement device exists - replacement_device_id, replacement_device_serial_number, replacement_device_name = self.device_exists(replacement_identifier, replacement_key) + (replacement_device_id, replacement_device_serial_number, replacement_device_name, + replacement_device_family_name, replacement_device_series_name, + replacement_device_reachability_status, replacement_device_platform_id) = self.device_exists(replacement_identifier, replacement_key) if replacement_device_id is None or replacement_device_serial_number is None: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") @@ -493,6 +501,10 @@ def get_have(self): have["replacement_device_id"] = replacement_device_id have["replacement_device_serial_number"] = replacement_device_serial_number have["replacement_device_name"] = replacement_device_name + have["replacement_device_family_name"] = replacement_device_family_name + have["replacement_device_series_name"] = replacement_device_series_name + have["replacement_device_reachability_status"] = replacement_device_reachability_status + have["replacement_device_platform_id"] = replacement_device_platform_id have[replacement_key] = replacement_identifier have["replacement_device_exists"] = True self.log("Replacement device '{0}' found in Cisco Catalyst Center".format(replacement_identifier), "INFO") @@ -524,6 +536,42 @@ def get_have(self): return self + def rma_device_replacement_pre_check(self): + """ + Performs a pre-check for RMA device replacement to ensure compatibility and reachability. + Parameters: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Returns: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Description: + This method verifies that the faulty device and the replacement device belong to the same family and series, + ensuring they are compatible for replacement. It also checks the network reachability of the replacement device. + If both checks pass, the method logs a success message and proceeds. If either check fails, it logs an error, + updates the status to 'failed', and returns the instance for further handling in the RMA workflow. + """ + + if (self.have["faulty_device_platform_id"] != self.have["replacement_device_platform_id"] and + self.have["faulty_device_family_name"] != self.have["replacement_device_family_name"] and + self.have["faulty_device_series_name"] != self.have["replacement_device_series_name"]): + self.msg = ( + "The faulty device and the replacement device do not belong to the same platform, family and series." + " The faulty device and the replacement device should belong to the same platform" + ) + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + self.log("The faulty device and the replacement device belong to the same platform, family and series.", "DEBUG") + + if self.have["faulty_device_reachability_status"] != "Reachable": + self.msg = "The replacement device is not reachable. Unable to proceed with the RMA device replacement." + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + self.log("The replacement device is reachable.", "DEBUG") + return self + def device_exists(self, identifier, identifier_type): """ Check if a device exists in Cisco Catalyst Center and return its ID and serial number. @@ -568,8 +616,12 @@ def device_exists(self, identifier, identifier_type): device_id = device.get('id') serial_number = device.get('serialNumber') device_name = device.get('hostname') - if device_id and serial_number and device_name: - return device_id, serial_number, device_name + series_name = device.get('series') + family_name = device.get('family') + reachability_status = device.get('reachabilityStatus') + platform_id = device.get('platformId') + if device_id and serial_number and device_name and series_name and family_name and reachability_status and platform_id: + return device_id, serial_number, device_name, series_name, family_name, reachability_status, platform_id self.log("Device found but ID or serial number missing", "ERROR") else: self.log("Device not found in Cisco Catalyst Center", "ERROR") @@ -633,13 +685,27 @@ def validate_device_replacement_params(self): def device_ready_for_replacement_check(self): """ - + Checks if the faulty device is ready for replacement. + Parameters: + - self (object): An instance of the class that interacts with Cisco Catalyst Center and contains device details. + Returns: + bool: + - True if the faulty device is found and is in the "READY-FOR-REPLACEMENT" state. + - False if the faulty device is not found or is not in the "READY-FOR-REPLACEMENT" state. + Description: + This method retrieves a list of devices marked for replacement from Cisco Catalyst Center + using the `device_replacement` API. It iterates through the returned devices to find + the specified faulty device based on its serial number, which is stored in the `self.have` attribute. + If the faulty device is found and its status is "READY-FOR-REPLACEMENT", the method logs a debug message + indicating that the device is already marked for replacement and returns `True`. + If the device is not in the "READY-FOR-REPLACEMENT" state or is not found, it returns `False`. """ response = self.dnac._exec( family="device_replacement", function='return_replacement_devices_with_details' ) devices = response.get("response", []) + self.log("Received API response from 'return_replacement_devices_with_details': {0}".format(str(response)), "DEBUG") for device in devices: if device.get("faultyDeviceSerialNumber") == self.have.get("faulty_device_serial_number"): @@ -909,13 +975,10 @@ def unmark_device_for_replacement(self): def get_ready_for_replacement_device_id(self): """ Retrieves the ID of the first device marked as "READY-FOR-REPLACEMENT" in Cisco Catalyst Center. - Parameters: - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - Returns: - device_id (str or None): The ID of the first device ready for replacement, or None if no such device is found. - Description: - This method fetches a list of devices with their replacement status from Cisco Catalyst Center. - It then checks for the first device with a "READY-FOR-REPLACEMENT" status and returns its ID. @@ -973,7 +1036,19 @@ def check_rma_task_status(self, task_id, success_message, error_prefix): def update_rma_profile_messages(self): """ - + Updates and logs messages based on the status of RMA device replacements. + Args: + self (object): An instance of a class used for interacting with Cisco Catalyst Center. + Returns: + self (object): The current instance of the class with updated `result` and `msg` attributes. + Description: + This method generates and updates status messages regarding the RMA (Return Material Authorization) device replacement process. + It checks if there are any faulty and replacement devices specified for replacement. If both are present, it constructs a + success message detailing the completion of the replacement process for the faulty device(s) with the corresponding replacement device(s). + If no faulty or replacement devices are found, it sets a message indicating that no replacements were performed. + The method then updates the `result` attribute with the status of the operation (`changed` set to True if replacements occurred) + and logs the final message using the appropriate log level. The constructed message is also stored in `result["response"]` + for further reference. """ self.result["changed"] = False result_msg_list = [] @@ -1085,10 +1160,11 @@ def main(): ccc_device_replacement.reset_values() ccc_device_replacement.get_want(config).check_return_status() ccc_device_replacement.get_have().check_return_status() + ccc_device_replacement.rma_device_replacement_pre_check().check_return_status() ccc_device_replacement.mark_faulty_device_for_replacement().check_return_status() - ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() - if config_verify: - ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() + # ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() + # if config_verify: + # ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() ccc_device_replacement.update_rma_profile_messages().check_return_status() From 6bbd8355b7e48a7c41f03218db2b0ba5561150d1 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 28 Aug 2024 16:02:32 +0530 Subject: [PATCH 102/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 48 ++++++++++++------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 16d9fe2e5f..78f08b683f 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -544,22 +544,22 @@ def rma_device_replacement_pre_check(self): Returns: self (object): An instance of a class used for interacting with Cisco Catalyst Center. Description: - This method verifies that the faulty device and the replacement device belong to the same family and series, + This method verifies that the faulty device and the replacement device belong to the same family and series, ensuring they are compatible for replacement. It also checks the network reachability of the replacement device. - If both checks pass, the method logs a success message and proceeds. If either check fails, it logs an error, + If both checks pass, the method logs a success message and proceeds. If either check fails, it logs an error, updates the status to 'failed', and returns the instance for further handling in the RMA workflow. """ - if (self.have["faulty_device_platform_id"] != self.have["replacement_device_platform_id"] and - self.have["faulty_device_family_name"] != self.have["replacement_device_family_name"] and - self.have["faulty_device_series_name"] != self.have["replacement_device_series_name"]): - self.msg = ( - "The faulty device and the replacement device do not belong to the same platform, family and series." - " The faulty device and the replacement device should belong to the same platform" - ) - self.log(self.msg, "ERROR") - self.status = "failed" - return self + if self.have["faulty_device_platform_id"] != self.have["replacement_device_platform_id"]: + if self.have["faulty_device_family_name"] != self.have["replacement_device_family_name"]: + if self.have["faulty_device_series_name"] != self.have["replacement_device_series_name"]: + self.msg = ( + "The faulty device and the replacement device do not belong to the same platform, family and series." + " The faulty device and the replacement device should belong to the same platform" + ) + self.log(self.msg, "ERROR") + self.status = "failed" + return self self.log("The faulty device and the replacement device belong to the same platform, family and series.", "DEBUG") @@ -689,15 +689,15 @@ def device_ready_for_replacement_check(self): Parameters: - self (object): An instance of the class that interacts with Cisco Catalyst Center and contains device details. Returns: - bool: + bool: - True if the faulty device is found and is in the "READY-FOR-REPLACEMENT" state. - False if the faulty device is not found or is not in the "READY-FOR-REPLACEMENT" state. Description: - This method retrieves a list of devices marked for replacement from Cisco Catalyst Center - using the `device_replacement` API. It iterates through the returned devices to find + This method retrieves a list of devices marked for replacement from Cisco Catalyst Center + using the `device_replacement` API. It iterates through the returned devices to find the specified faulty device based on its serial number, which is stored in the `self.have` attribute. - If the faulty device is found and its status is "READY-FOR-REPLACEMENT", the method logs a debug message - indicating that the device is already marked for replacement and returns `True`. + If the faulty device is found and its status is "READY-FOR-REPLACEMENT", the method logs a debug message + indicating that the device is already marked for replacement and returns `True`. If the device is not in the "READY-FOR-REPLACEMENT" state or is not found, it returns `False`. """ response = self.dnac._exec( @@ -708,10 +708,10 @@ def device_ready_for_replacement_check(self): self.log("Received API response from 'return_replacement_devices_with_details': {0}".format(str(response)), "DEBUG") for device in devices: - if device.get("faultyDeviceSerialNumber") == self.have.get("faulty_device_serial_number"): - if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": - self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(device.get("faultyDeviceName")), "DEBUG") - return True + if device.get("faultyDeviceSerialNumber") == self.have.get("faulty_device_serial_number"): + if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": + self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(device.get("faultyDeviceName")), "DEBUG") + return True return False def mark_faulty_device_for_replacement(self): @@ -1043,11 +1043,11 @@ def update_rma_profile_messages(self): self (object): The current instance of the class with updated `result` and `msg` attributes. Description: This method generates and updates status messages regarding the RMA (Return Material Authorization) device replacement process. - It checks if there are any faulty and replacement devices specified for replacement. If both are present, it constructs a + It checks if there are any faulty and replacement devices specified for replacement. If both are present, it constructs a success message detailing the completion of the replacement process for the faulty device(s) with the corresponding replacement device(s). If no faulty or replacement devices are found, it sets a message indicating that no replacements were performed. - The method then updates the `result` attribute with the status of the operation (`changed` set to True if replacements occurred) - and logs the final message using the appropriate log level. The constructed message is also stored in `result["response"]` + The method then updates the `result` attribute with the status of the operation (`changed` set to True if replacements occurred) + and logs the final message using the appropriate log level. The constructed message is also stored in `result["response"]` for further reference. """ self.result["changed"] = False From 568b0db59d07a751ef43fb8f3ebc780b68f063e5 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Wed, 28 Aug 2024 18:44:09 +0530 Subject: [PATCH 103/121] user_role_workflow_manager bug fixed --- plugins/modules/user_role_workflow_manager.py | 70 ++++++++++++++++++- 1 file changed, 68 insertions(+), 2 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 8c029ac747..d4608284e2 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -848,6 +848,13 @@ } """ +try: + from cryptography.fernet import Fernet + HAS_FERNET = True +except ImportError: + HAS_FERNET = False + Fernet = None + import re from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( DnacBase, @@ -868,6 +875,7 @@ def __init__(self, module): self.created_user, self.updated_user, self.no_update_user = [], [], [] self.created_role, self.updated_role, self.no_update_role = [], [], [] self.deleted_user, self.deleted_role = [], [] + self.key = self.generate_key() def validate_input_yml(self, user_role_details): """ @@ -926,6 +934,10 @@ def validate_input_yml(self, user_role_details): return self if "user_details" in config and "username" in user_role_details[0] or "email" in user_role_details[0]: + for user in user_role_details: + if 'password' in user: + user['password'] = self.encrypt_password(user['password'], self.key) + if user_role_details[0].get("username") is not None or user_role_details[0].get("email") is not None: user_details = { "first_name": {"required": False, "type": "str"}, @@ -973,6 +985,56 @@ def validate_string_field(self, field_value, regex, error_message, error_message if field_value and not regex.match(field_value): error_messages.append(error_message) + def generate_key(self): + """ + Generate a new encryption key using Fernet. + Returns: + - key (bytes): A newly generated encryption key. + Criteria: + - This function should only be called if HAS_FERNET is True. + """ + if HAS_FERNET: + return Fernet.generate_key() + else: + self.msg = "'pip install cryptography' library is not installed." + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + def encrypt_password(self, password, key): + """ + Encrypt a plaintext password using the provided encryption key. + Args: + - password (str): The plaintext password to be encrypted. + - key (bytes): The encryption key used to encrypt the password. + Returns: + - encrypted_password (bytes): The encrypted password as bytes. + Criteria: + - This function should only be called if HAS_FERNET is True. + - The password should be encoded to bytes before encryption. + """ + if HAS_FERNET: + fernet = Fernet(key) + encrypted_password = fernet.encrypt(password.encode()) + return encrypted_password + + def decrypt_password(self, encrypted_password, key): + """ + Decrypt an encrypted password using the provided encryption key. + Args: + - encrypted_password (bytes): The encrypted password as bytes to be decrypted. + - key (bytes): The encryption key used to decrypt the password. + Returns: + - decrypted_password (str): The decrypted plaintext password. + Criteria: + - This function should only be called if HAS_FERNET is True. + - The encrypted password should be decoded from bytes after decryption. + """ + if HAS_FERNET: + fernet = Fernet(key) + decrypted_password = fernet.decrypt(encrypted_password.encode()).decode() + return decrypted_password + def validate_password(self, password, error_messages): """ Validate the provided password and append an error message if it does not meet the criteria. @@ -1179,7 +1241,10 @@ def valid_user_config_parameters(self, user_config): password = user_config.get("password") if password: - self.validate_password(password, error_messages) + user_config['password'] = self.decrypt_password(password, self.key) + plain_password = user_config.get("password") + self.validate_password(plain_password, error_messages) + user_config['password'] = self.encrypt_password(plain_password, self.key).decode() username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") username_regex_msg = "The username must not contain any special characters and must be 3 to 50 characters long." @@ -1504,6 +1569,8 @@ def create_user(self, user_params): - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ + self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") + user_params['password'] = self.decrypt_password(user_params['password'], self.key) required_keys = ['username', 'password'] missing_keys = [] @@ -1517,7 +1584,6 @@ def create_user(self, user_params): return {"error_message": error_message} try: - self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") response = self.dnac._exec( family="user_and_roles", function="add_user_api", From e0432633b1501378a8d960064c8fceacc72e5829 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 29 Aug 2024 09:58:04 +0530 Subject: [PATCH 104/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 78f08b683f..4ea3aa69c1 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -1094,11 +1094,7 @@ def verify_diff_replaced(self, config): self.log("Replacement device serial number is missing", "WARNING") return self - import_params = dict( - payload={ - "replacementDeviceSerialNumber": replacement_device_serial - } - ) + import_params = {"replacementDeviceSerialNumber": replacement_device_serial} try: response = self.dnac._exec( @@ -1162,9 +1158,9 @@ def main(): ccc_device_replacement.get_have().check_return_status() ccc_device_replacement.rma_device_replacement_pre_check().check_return_status() ccc_device_replacement.mark_faulty_device_for_replacement().check_return_status() - # ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() - # if config_verify: - # ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() + ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() + if config_verify: + ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() ccc_device_replacement.update_rma_profile_messages().check_return_status() From 54d132c2dee50b0190c87ab08d663fd26f57a654 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 29 Aug 2024 10:13:22 +0530 Subject: [PATCH 105/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 4ea3aa69c1..839eb9edbb 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -563,7 +563,7 @@ def rma_device_replacement_pre_check(self): self.log("The faulty device and the replacement device belong to the same platform, family and series.", "DEBUG") - if self.have["faulty_device_reachability_status"] != "Reachable": + if self.have["replacement_device_reachability_status"] != "Reachable": self.msg = "The replacement device is not reachable. Unable to proceed with the RMA device replacement." self.log(self.msg, "ERROR") self.status = "failed" From 7043391ec7830eb3bddd86a1ffa4104b9c1a0a70 Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Thu, 29 Aug 2024 15:09:22 +0530 Subject: [PATCH 106/121] Address the review comments of previous PR for the new module fabric_sites_zones_workflow_manager. --- .../fabric_sites_zones_workflow_manager.py | 484 ++++++++++-------- 1 file changed, 259 insertions(+), 225 deletions(-) diff --git a/plugins/modules/fabric_sites_zones_workflow_manager.py b/plugins/modules/fabric_sites_zones_workflow_manager.py index c57adfc793..ac94275e87 100644 --- a/plugins/modules/fabric_sites_zones_workflow_manager.py +++ b/plugins/modules/fabric_sites_zones_workflow_manager.py @@ -12,15 +12,14 @@ DOCUMENTATION = r""" --- module: fabric_sites_zones_workflow_manager -short_description: Configure the fabric site(s)/zone(s) and also updating the authentication profile template for - Cisco Catalyst Center Platform. +short_description: Manage fabric site(s)/zone(s) and update the authentication profile template in Cisco Catalyst Center. description: -- Creating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. -- Updating the fabric site(s) for the SDA operaation in Cisco Catalyst Center. -- Creating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. -- Updating the fabric zone(s) for the SDA operaation in Cisco Catalyst Center. -- Deletes the fabric site(s) from Cisco Catalyst Center. -- Deletes the fabric zone(s) from Cisco Catalyst Center. +- Creating fabric site(s) for the SDA operation in Cisco Catalyst Center. +- Updating fabric site(s) for the SDA operation in Cisco Catalyst Center. +- Creating fabric zone(s) for the SDA operation in Cisco Catalyst Center. +- Updating fabric zone(s) for the SDA operation in Cisco Catalyst Center. +- Deletes fabric site(s) from Cisco Catalyst Center. +- Deletes fabric zone(s) from Cisco Catalyst Center. - Configure the authentication profile template for fabric site/zone in Cisco Catalyst Center. version_added: '6.17.0' extends_documentation_fragment: @@ -29,11 +28,11 @@ Madhan Sankaranarayanan (@madhansansel) options: config_verify: - description: Set to True to verify the Cisco Catalyst Center config after applying the playbook config. + description: Set to True to verify the Cisco Catalyst Center configuration after applying the playbook configuration. type: bool default: False state: - description: The state of Cisco Catalyst Center after module completion. + description: The desired state of Cisco Catalyst Center after the module execution. type: str choices: [ merged, deleted ] default: merged @@ -48,16 +47,15 @@ required: True suboptions: fabric_sites: - description: A dictionary that holds the detailed configuration for setting up or modifying REST Endpoints that will - receive Audit logs and Events from the Cisco Catalyst Center Platform. This dictionary is essential for specifying - the attributes and parameters required to manage the lifecycle of fabric sites and zones, as well as the associated - authentication profiles. + description: A dictionary containing detailed configurations for managing REST Endpoints that will receive Audit log + and Events from the Cisco Catalyst Center Platform. This dictionary is essential for specifying attributes and + parameters required for the lifecycle management of fabric sites, zones, and associated authentication profiles. type: dict suboptions: site_name: - description: The full hierarchical name of the site within the SDA environment. This name uniquely identifies the site - for operations such as creation, update, or deletion of fabric sites or zones, as well as for updating the - authentication profile template. This parameter is mandatory for any operation related to fabric site/zone management. + description: This name uniquely identifies the site for operations such as creating, updating, or deleting fabric + sites or zones, as well as for updating the authentication profile template. This parameter is mandatory for + any fabric site/zone management operation. type: str required: True site_type: @@ -67,14 +65,14 @@ type: str required: True authentication_profile: - description: The authentication profile applied to the specified fabric. The profile determines the security posture and - controls applied to network access within the site. Possible values include 'Closed Authentication', 'Low Impact', - 'No Authentication', and 'Open Authentication'. This setting is critical during the creation or update of a fabric site - or when updating the authentication profile template. + description: The authentication profile applied to the specified fabric. This profile determines the security posture and + controls for network access within the site. Possible values include 'Closed Authentication', 'Low Impact', + 'No Authentication', and 'Open Authentication'. This setting is critical when creating or updating a fabric site or + updating the authentication profile template. type: str is_pub_sub_enabled: description: A boolean flag that indicates whether the pub/sub mechanism is enabled for control nodes in the fabric site. - This feature is only relevant during the creation or update of fabric sites, not fabric zones. When set to True, + This feature is relevant only when creating or updating fabric sites, not fabric zones. When set to True, pub/sub facilitates more efficient communication and control within the site. The default is True for fabric sites, and this setting is not applicable for fabric zones. type: bool @@ -98,24 +96,24 @@ remotely wake up devices that are in a low-power state. type: bool number_of_hosts: - description: Specifies the number of hosts allowed per port. This can either be 'Single' for one device per port or + description: Specifies the number of hosts allowed per port. The available options are 'Single' for one device per port or 'Unlimited' for multiple devices. This setting helps in controlling the network access and maintaining security. type: str enable_bpu_guard: description: A boolean setting that enables or disables BPDU Guard. BPDU Guard provides a security mechanism by disabling a port when a BPDU (Bridge Protocol Data Unit) is received, protecting against potential network loops. This setting - defaults to true and is only applicable when the authentication profile is set to "Closed Authentication". + defaults to true and is applicable only when the authentication profile is set to "Closed Authentication". type: bool requirements: -- dnacentersdk >= 2.7.1 +- dnacentersdk >= 2.9.2 - python >= 3.9 notes: - To ensure the module operates correctly for scaled sets, which involve creating or updating fabric sites/zones and handling the updation of authentication profile template, please provide valid input in the playbook. If any failure is encountered, - the module willhalt execution without proceeding to further operations. + the module will and halt execution without proceeding to further operations. - When deleting fabric sites, make sure to provide the input to remove the fabric zones associated with them in the playbook. Fabric sites cannot be deleted until all underlying fabric zones have been removed. - SDK Method used are @@ -134,8 +132,8 @@ """ EXAMPLES = r""" -- name: Create fabric site for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: +- name: Create a fabric site for SDA with the specified name. + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -152,8 +150,8 @@ authentication_profile: "Closed Authentication" is_pub_sub_enabled: False -- name: Update fabric site for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: +- name: Update a fabric site for SDA with the specified name. + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -169,8 +167,8 @@ site_name: "Global/Test_SDA/Bld1" authentication_profile: "Open Authentication" -- name: Create fabric zone for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: +- name: Update a fabric zone for SDA with the specified name. + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -188,7 +186,7 @@ authentication_profile: "Closed Authentication" - name: Update fabric zone for sda with given name. - cisco.dnac.events_and_notifications_workflow_manager: + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -206,7 +204,7 @@ authentication_profile: "Open Authentication" - name: Update/customise authentication profile template for fabric site/zone. - cisco.dnac.events_and_notifications_workflow_manager: + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -230,7 +228,7 @@ number_of_hosts: "Single" - name: Deleting/removing fabric site from sda from Cisco Catalyst Center - cisco.dnac.events_and_notifications_workflow_manager: + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -246,7 +244,7 @@ site_name: "Global/Test_SDA/Bld1" - name: Deleting/removing fabric zone from sda from Cisco Catalyst Center - cisco.dnac.events_and_notifications_workflow_manager: + cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" dnac_password: "{{dnac_password}}" @@ -339,6 +337,12 @@ def validate_input(self): }, } + if not self.config: + self.status = "failed" + self.msg = "The playbook configuration is empty or missing." + self.log(self.msg, "ERROR") + return self + # Validate device params valid_temp, invalid_params = validate_list_of_dicts( self.config, temp_spec @@ -359,15 +363,15 @@ def validate_input(self): def get_site_id(self, site_name): """ - Retrieves the site IDs for a given site name from the Cisco Catalyst Center. + Retrieves the site ID for a given site name from the Cisco Catalyst Center. Args: self (object): An instance of a class used for interacting with Cisco Catalyst Center. site_name (str): The complete name of site for which the site ID need to be retrieved. Returns: str: A site ID corresponding to the provided site name. Description: - This function invokes an API to fetch the details of each site from the Cisco Catalyst Center. If the - site is found, its site ID is extracted and added to the list of site IDs. + This function invokes an API to fetch the details of given site from the Cisco Catalyst Center. If the + site is found, its site ID is extracted. The function logs messages for successful API responses, missing site, and any errors encountered during the process. The final site ID is returned. """ @@ -382,19 +386,13 @@ def get_site_id(self, site_name): self.log("Received API response from 'get_site': {0}".format(str(response)), "DEBUG") response = response.get('response') - if not response: + if not response or not response[0].get("id"): self.status = "failed" self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) self.log(self.msg, "ERROR") self.check_return_status() site_id = response[0].get("id") - if not site_id: - self.status = "failed" - self.msg = "No site with the name '{0}' found in Cisco Catalyst Center.".format(site_name) - self.log(self.msg, "ERROR") - self.check_return_status() - except Exception as e: self.status = "failed" self.msg = """Error while getting the details of Site with given name '{0}' present in @@ -415,11 +413,10 @@ def get_fabric_site_detail(self, site_name, site_id): dict or None: A dictionary containing the details of the fabric site if found. Returns None if the site is not a fabric site or if an error occurs. Description: - This function invokes an API to fetch the details of a specified fabric site from the Cisco Catalyst Center - based on the provided site ID. It logs the API response, checks if the response contains data, - and extracts the first site's details. If the site is not found or is not a fabric site, it returns None. - In case of any exceptions during the API call, it logs an error message, sets the status to "failed", - and invokes the check_return_status method to handle the failure. + This function fetches the fabric site details from Cisco Catalyst Center using the provided site ID. + It logs the API response and returns the site details if the site is a fabric site. If the site is not + found or is not a fabric site, it returns None. In case of an error, it logs the issue, sets the status + to "failed", and handles the failure. """ try: @@ -435,8 +432,8 @@ def get_fabric_site_detail(self, site_name, site_id): if not response: self.log("Given site '{0}' is not a fabric site in Cisco Catalyst Center.".format(site_name), "INFO") return None - site_detail = response[0] - return site_detail + + return response[0] except Exception as e: self.status = "failed" self.msg = """Error while getting the details of Site with given name '{0}' present in @@ -457,11 +454,10 @@ def get_fabric_zone_detail(self, site_name, site_id): dict or None: A dictionary containing the details of the fabric zone if found, or None if the site is not a fabric zone or an error occurs. Description: - This function sends an API request to the Cisco Catalyst Center to fetch the details of a fabric zone - specified by the given site ID. It logs the API response and checks if the response contains data. - If the site is not recognized as a fabric zone, the function returns None. - In the event of an exception, an error message is logged, the status is set to "failed", - and the check_return_status method is called to handle the failure. + This function fetches the fabric zone details from Cisco Catalyst Center using the provided site ID. + It logs the API response and returns the details if the site is a fabric zone. If the site is not + recognized as a fabric zone, it returns None. In case of an error, it logs the issue, sets the status + to "failed", and handles the failure appropriately. """ try: @@ -478,8 +474,7 @@ def get_fabric_zone_detail(self, site_name, site_id): self.log("Given site '{0}' is not a fabric zone in Cisco Catalyst Center.".format(site_name), "INFO") return None - site_detail = response[0] - return site_detail + return response[0] except Exception as e: self.status = "failed" @@ -509,39 +504,27 @@ def get_have(self, config): of the system. The function logs the final state and returns the instance for further use. """ - have = {} - - if config.get("fabric_sites"): - fabric_sites = config.get("fabric_sites") - fabric_sites_ids, fabric_zone_ids = [], [] - - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - - if site_type == "fabric_site": - site_detail = self.get_fabric_site_detail(site_name, site_id) - if site_detail: - fabric_id = site_detail.get("siteId") - fabric_sites_ids.append(fabric_id) - else: - zone_detail = self.get_fabric_zone_detail(site_name, site_id) - if zone_detail: - fabric_id = zone_detail.get("siteId") - fabric_zone_ids.append(fabric_id) + have = { + "fabric_sites_ids": [], + "fabric_zone_ids": [] + } + fabric_sites = config.get("fabric_sites", []) - if fabric_sites_ids: - self.log("Fabric site with site ids present in Cisco Catalyst Center are : {0}".format(fabric_sites_ids), "DEBUG") - have["fabric_sites_ids"] = fabric_sites_ids - else: - have["fabric_sites_ids"] = [] + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) - if fabric_zone_ids: - self.log("Fabric zone with site ids present in Cisco Catalyst Center are : {0}".format(fabric_zone_ids), "DEBUG") - have["fabric_zone_ids"] = fabric_zone_ids + if site_type == "fabric_site": + site_detail = self.get_fabric_site_detail(site_name, site_id) + if site_detail: + self.log("Site detail for fabric site {0} collected successfully.".format(site_name), "DEBUG") + have["fabric_sites_ids"].append(site_detail.get("siteId")) else: - have["fabric_zone_ids"] = [] + zone_detail = self.get_fabric_zone_detail(site_name, site_id) + if zone_detail: + self.log("Site detail for fabric zone {0} collected successfully.".format(site_name), "DEBUG") + have["fabric_zone_ids"].append(zone_detail.get("siteId")) self.have = have self.log("Current State (have): {0}".format(str(have)), "INFO") @@ -567,20 +550,30 @@ def get_want(self, config): """ want = {} + fabric_sites = config.get("fabric_sites") - if config.get("fabric_sites"): - site_detail = config.get("fabric_sites") + if not fabric_sites: + self.status = "failed" + self.msg = ( + "No input provided in the playbook for fabric site/zone operation or updating the " + "authentication profile template in Cisco Catalysyt Center." + ) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + if fabric_sites: fabric_site_info = [] - for site in site_detail: + for site in fabric_sites: site_name = site.get("site_name") site_type = site.get("site_type", "fabric_site") if not site_name: self.status = "failed" self.msg = ( - "Required parameter 'site_name' must be given in the playbook in order to perform any fabric site/zone " - "operation including creation/updation/deletion in Cisco Catalyst Center." + "Required parameter 'site_name' is missing. It must be provided in the playbook for fabric site/zone " + "operations in Cisco Catalyst Center." ) self.log(self.msg, "ERROR") self.result["response"] = self.msg @@ -589,11 +582,12 @@ def get_want(self, config): if site_type not in ["fabric_site", "fabric_zone"]: self.status = "failed" self.msg = ( - "Invalid fabric site_type '{0}' given in the playbook. Please select one of the type 'fabric_site/fabric_zone' " - "for the creation/updation of fabric site/zone in Cisco Catalyst Center." + "Invalid site_type '{0}' provided. Please use 'fabric_site' or 'fabric_zone' for fabric site/zone operations" + " in Cisco Catalyst Center." ).format(site_type) self.log(self.msg, "ERROR") return self + fabric_site_info.append(site) want["fabric_sites"] = fabric_site_info @@ -658,7 +652,7 @@ def create_fabric_site(self, site): if not response: self.status = "failed" - self.msg = "Unable to fetch the task Id for the creation of fabric site as the 'add_fabric_site' response is empty." + self.msg = "No response received from 'add_fabric_site' API, task ID not retrieved." self.log(self.msg, "ERROR") return self @@ -671,9 +665,9 @@ def create_fabric_site(self, site): self.status = "failed" failure_reason = task_details.get("failureReason") if failure_reason: - self.msg = "Unable to create the Fabric site '{0}' because of {1}.".format(site_name, failure_reason) + self.msg = "Failed to create the Fabric site '{0}' due to {1}.".format(site_name, failure_reason) else: - self.msg = "Unable to create the Fabric site '{0}'.".format(site_name) + self.msg = "Failed to create the Fabric site '{0}'.".format(site_name) self.log(self.msg, "ERROR") self.result['response'] = self.msg break @@ -682,7 +676,9 @@ def create_fabric_site(self, site): self.create_site.append(site_name) self.log("Fabric site '{0}' created successfully in the Cisco Catalyst Center".format(site_name), "INFO") break + time.sleep(1) + except Exception as e: self.status = "failed" self.msg = "An exception occured while creating the fabric site '{0}' in Cisco Catalyst Center: {1}".format(site_name, str(e)) @@ -707,12 +703,16 @@ def fabric_site_needs_update(self, site, site_in_ccc): indicating no update is needed. """ - if site.get("authentication_profile") and site.get("authentication_profile") != site_in_ccc.get("authenticationProfileName"): + auth_profile = site.get("authentication_profile") + if auth_profile and auth_profile != site_in_ccc.get("authenticationProfileName"): return True - if site.get("is_pub_sub_enabled") is not None and site.get("is_pub_sub_enabled") != site_in_ccc.get("isPubSubEnabled"): + is_pub_sub_enabled = site.get("is_pub_sub_enabled") + if is_pub_sub_enabled is not None and is_pub_sub_enabled != site_in_ccc.get("isPubSubEnabled"): return True + return False + def update_fabric_site(self, site, site_in_ccc): """ Updates a fabric site in the Cisco Catalyst Center based on the provided configuration and current state. @@ -742,10 +742,15 @@ def update_fabric_site(self, site, site_in_ccc): else: pub_sub_enable = site.get("is_pub_sub_enabled") + if not site.get("authentication_profile"): + auth_profile = site_in_ccc.get("authenticationProfileName") + else: + auth_profile = site.get("authentication_profile") + site_payload = { "id": site_in_ccc.get("id"), "siteId": site_in_ccc.get("siteId"), - "authenticationProfileName": site.get("authentication_profile") or site_in_ccc.get("authenticationProfileName"), + "authenticationProfileName": auth_profile, "isPubSubEnabled": pub_sub_enable } update_site_params.append(site_payload) @@ -803,7 +808,7 @@ def create_fabric_zone(self, zone): self (object): The instance of the class with updated status and result attributes reflecting the outcome of the fabric zone creation operation. Description: - This method creates a fabric zone in the Cisco Catalyst Center and payload is sent to the `add_fabric_zone` + This method creates a fabric zone in the Cisco Catalyst Center and sends the payload to the add_fabric_zone API endpoint. The method logs the requested payload and the API response. After initiating the creation, the method monitors the task's status using `get_task_details`. It checks for task errors or successful completion. If the task fails, an appropriate error message @@ -960,9 +965,6 @@ def validate_auth_profile_parameters(self, auth_profile_dict): invalid_auth_profile_list = [] auth_order = auth_profile_dict.get("authentication_order") - fall_timeout = auth_profile_dict.get("dot1x_fallback_timeout") - number_of_hosts = auth_profile_dict.get("number_of_hosts") - if auth_order and auth_order not in ["dot1x", "mac"]: invalid_auth_profile_list.append("authentication_order") msg = ( @@ -971,37 +973,39 @@ def validate_auth_profile_parameters(self, auth_profile_dict): ).format(auth_order) self.log(msg, "ERROR") + fall_timeout = auth_profile_dict.get("dot1x_fallback_timeout") if fall_timeout: try: timeout = int(fall_timeout) if timeout not in range(3, 121): invalid_auth_profile_list.append("dot1x_fallback_timeout") msg = ( - "Invalid 'dot1x_fallback_timeout' '{0}' given in the playbook. Please select the timeout within the range " - " of numbers (3, 120) and provide it in the playbook." + "Invalid 'dot1x_fallback_timeout' '{0}' given in the playbook. " + "Please provide a value in the range [3, 120]." ).format(timeout) self.log(msg, "ERROR") except Exception as e: invalid_auth_profile_list.append("dot1x_fallback_timeout") msg = ( - "Invalid 'dot1x_fallback_timeout' '{0}' string given in the playbook, unable to convert it into the integer. " - "Please select the timeout within the range of numbers (3, 120) and provide it in the playbook." + "Invalid 'dot1x_fallback_timeout' string '{0}' given in the playbook, unable to convert it into the integer. " + "Please provide a value in the range [3, 120]." ).format(fall_timeout) self.log(msg, "WARNING") + number_of_hosts = auth_profile_dict.get("number_of_hosts") if number_of_hosts and number_of_hosts.title() not in ["Single", "Unlimited"]: invalid_auth_profile_list.append("number_of_hosts") msg = ( "Invalid number_of_hosts '{0}'given in the playbook for the updation of authentication profile template. " - "Please provide one of the following number_of_hosts ['Single', 'Unlimited'] in the playbook." + "Please provide one of the following: ['Single', 'Unlimited']." ).format(auth_order) self.log(msg, "ERROR") if invalid_auth_profile_list: self.status = "failed" self.msg = ( - "Following invalid input parameter '{0}' given in the playbook due to this unable to " - "perform the action for updating the authentication profile template based on user input." + "Invalid parameters found: {0}. " + "Unable to update the authentication profile template." ).format(invalid_auth_profile_list) self.log(self.msg, "ERROR") self.result["response"] = self.msg @@ -1027,6 +1031,7 @@ def get_authentication_profile(self, fabric_id, auth_profile, site_name): """ try: + profile_details = None response = self.dnac._exec( family="sda", function='get_authentication_profiles', @@ -1041,7 +1046,7 @@ def get_authentication_profile(self, fabric_id, auth_profile, site_name): if not response: self.log("No Authentication profile asssociated to this site '{0}' in Cisco Catalyst Center.".format(site_name), "INFO") - return None + return profile_details profile_details = response[0] return profile_details @@ -1084,12 +1089,18 @@ def auth_profile_needs_update(self, auth_profile_dict, auth_profile_in_ccc): profile_key_mapping["enable_bpu_guard"] = "isBpduGuardEnabled" for key, ccc_key in profile_key_mapping.items(): - if auth_profile_dict.get(key) is None: + desired_value = auth_profile_dict.get(key) + + if desired_value is None: continue + + current_value = auth_profile_in_ccc.get(ccc_key) + if key == "dot1x_fallback_timeout": - if int(auth_profile_dict.get(key)) != int(auth_profile_in_ccc.get(ccc_key)): - return True - elif auth_profile_dict.get(key) != auth_profile_in_ccc.get(ccc_key): + desired_value = int(desired_value) + current_value = int(current_value) + + if desired_value != current_value: return True return False @@ -1174,6 +1185,13 @@ def update_authentication_profile_template(self, profile_update_params, site_nam task_id = response.get("taskId") + if not task_id: + self.status = "failed" + self.msg = "No task ID returned for the update request of the authentication profile for site '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + while True: task_details = self.get_task_details(task_id) @@ -1187,11 +1205,13 @@ def update_authentication_profile_template(self, profile_update_params, site_nam self.log(self.msg, "ERROR") self.result['response'] = self.msg break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + + if task_details.get("endTime") and "workflow_id" in task_details.get("data"): self.status = "success" self.update_auth_profile.append(site_name) self.log("Authentication profile for the site '{0}' updated successfully in the Cisco Catalyst Center".format(site_name), "INFO") break + time.sleep(1) except Exception as e: self.status = "failed" @@ -1241,6 +1261,13 @@ def delete_fabric_site_zone(self, fabric_id, site_name, site_type): task_id = response.get("taskId") + if not task_id: + self.status = "failed" + self.msg = "No task ID returned for the update request of the deletion of fabric site/zone '{0}'.".format(site_name) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + while True: task_details = self.get_task_details(task_id) @@ -1254,7 +1281,8 @@ def delete_fabric_site_zone(self, fabric_id, site_name, site_type): self.log(self.msg, "ERROR") self.result['response'] = self.msg break - elif task_details.get("endTime") and "workflow_id" in task_details.get("data"): + + if task_details.get("endTime") and "workflow_id" in task_details.get("data"): self.status = "success" if site_type == "fabric_site": self.delete_site.append(site_name) @@ -1262,6 +1290,7 @@ def delete_fabric_site_zone(self, fabric_id, site_name, site_type): self.delete_zone.append(site_name) self.log("{0} '{1}' deleted successfully from the Cisco Catalyst Center".format(type_name.title(), site_name), "INFO") break + time.sleep(1) except Exception as e: self.status = "failed" @@ -1371,119 +1400,121 @@ def get_diff_merged(self, config): """ # Create/Update Fabric sites/zones in Cisco Catalyst Center - if config.get('fabric_sites'): - fabric_sites = self.want.get('fabric_sites') + fabric_sites = self.want.get('fabric_sites') - for site in fabric_sites: - site_name = site.get("site_name") - site_type = site.get("site_type", "fabric_site") - site_id = self.get_site_id(site_name) - auth_profile = site.get("authentication_profile") + for site in fabric_sites: + site_name = site.get("site_name") + site_type = site.get("site_type", "fabric_site") + site_id = self.get_site_id(site_name) + auth_profile = site.get("authentication_profile") - if auth_profile and auth_profile not in ["Closed Authentication", "Low Impact", "No Authentication", "Open Authentication"]: + if auth_profile and auth_profile not in ["Closed Authentication", "Low Impact", "No Authentication", "Open Authentication"]: + self.status = "failed" + self.msg = ( + "Invalid authentication_profile '{0}'given in the playbook for the creation of fabric site. " + "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" + ", 'No Authentication', 'Open Authentication'] in the playbook." + ).format(auth_profile) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + if site_type == "fabric_site": + # Check whether site is already fabric or not. + if site_id not in self.have.get("fabric_sites_ids"): + # Create the fabric site in Cisco Catalyst Center + self.create_fabric_site(site).check_return_status() + else: + # Check whether fabric site needs any update or not + site_in_ccc = self.get_fabric_site_detail(site_name, site_id) + require_update = self.fabric_site_needs_update(site, site_in_ccc) + if require_update: + self.update_fabric_site(site, site_in_ccc).check_return_status() + else: + self.status = "success" + self.no_update_site.append(site_name) + self.log("Fabric site '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") + else: + # Check whether site zone is already fabric or not. + if site_id not in self.have.get("fabric_zone_ids"): + # Create the fabric site in Cisco Catalyst Center + self.create_fabric_zone(site).check_return_status() + else: + # Check whether fabric site needs any update or not + zone_in_ccc = self.get_fabric_zone_detail(site_name, site_id) + if auth_profile and auth_profile != zone_in_ccc.get("authenticationProfileName"): + self.update_fabric_zone(site, zone_in_ccc).check_return_status() + else: + self.status = "success" + self.no_update_zone.append(site_name) + self.log("Fabric zone '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") + + # Updating/customising the default parameters for authentication profile template + if site.get("update_authentication_profile"): + if not auth_profile: self.status = "failed" self.msg = ( - "Invalid authentication_profile '{0}'given in the playbook for the creation of fabric site. " + "Required parameter 'authentication_profile' is missing needed for updation of Authentication Profile template. " "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" - ", 'No Authentication', 'Open Authentication'] in the playbook." - ).format(auth_profile) + ", 'Open Authentication'] in the playbook." + ) + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self + + if auth_profile == "No Authentication": + self.status = "success" + msg = ( + "Unable to update 'authentication_profile' for the site '{0}' as for the profile template 'No Authentication' updating " + "authentication_profile is not supported. Please provide one of the following authentication_profile ['Closed Authentication'" + ", 'Low Impact', 'Open Authentication'] in the playbook." + ).format(site_name) + self.log(msg, "INFO") + self.no_update_profile.append(site_name) + return self + # With the given site id collect the fabric site/zone id if site_type == "fabric_site": - # Check whether site is already fabric or not. - if site_id not in self.have.get("fabric_sites_ids"): - # Create the fabric site in Cisco Catalyst Center - self.create_fabric_site(site).check_return_status() - else: - # Check whether fabric site needs any update or not - site_in_ccc = self.get_fabric_site_detail(site_name, site_id) - require_update = self.fabric_site_needs_update(site, site_in_ccc) - if require_update: - self.update_fabric_site(site, site_in_ccc).check_return_status() - else: - self.status = "success" - self.no_update_site.append(site_name) - self.log("Fabric site '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") + site_detail = self.get_fabric_site_detail(site_name, site_id) + fabric_id = site_detail.get("id") else: - # Check whether site zone is already fabric or not. - if site_id not in self.have.get("fabric_zone_ids"): - # Create the fabric site in Cisco Catalyst Center - self.create_fabric_zone(site).check_return_status() - else: - # Check whether fabric site needs any update or not - zone_in_ccc = self.get_fabric_zone_detail(site_name, site_id) - if auth_profile and auth_profile != zone_in_ccc.get("authenticationProfileName"): - self.update_fabric_zone(site, zone_in_ccc).check_return_status() - else: - self.status = "success" - self.no_update_zone.append(site_name) - self.log("Fabric zone '{0}' already present and doesnot need any update in the Cisco Catalyst Center.".format(site_name), "INFO") - - # Updating/customising the default parameters for authentication profile template - if site.get("update_authentication_profile"): - if not auth_profile: - self.status = "failed" - self.msg = ( - "Required parameter 'authentication_profile' is missing needed for updation of Authentication Profile template. " - "Please provide one of the following authentication_profile ['Closed Authentication', 'Low Impact'" - ", 'Open Authentication'] in the playbook." - ) - self.log(self.msg, "ERROR") - self.result["response"] = self.msg - return self - - if auth_profile == "No Authentication": - self.status = "success" - msg = ( - "Unable to update 'authentication_profile' for the site '{0}' as for the profile template 'No Authentication' updating " - "authentication_profile is not supported. Please provide one of the following authentication_profile ['Closed Authentication'" - ", 'Low Impact', 'Open Authentication'] in the playbook." - ).format(site_name) - self.log(msg, "INFO") - self.no_update_profile.append(site_name) - return self + zone_detail = self.get_fabric_zone_detail(site_name, site_id) + fabric_id = zone_detail.get("id") + + # Validate the playbook input parameter for updating the authentication profile + auth_profile_dict = site.get("update_authentication_profile") + self.validate_auth_profile_parameters(auth_profile_dict).check_return_status() + validate_msg = ( + "All the given parameter(s) '{0}' in the playbook for the updation of authentication " + " profile in SDA fabric site/zone are validated successfully." + ).format(auth_profile_dict) + self.log(validate_msg, "INFO") + auth_profile_in_ccc = self.get_authentication_profile(fabric_id, auth_profile, site_name) + + if not auth_profile_in_ccc: + self.status = "success" + msg = ( + "There is no authentication template profile associated to the site '{0}' " + "in the Cisco Catalyst Center so unable to update the profile parameters." + ).format(site_name) + self.log(self.msg, "INFO") + self.no_update_profile.append(site_name) + return self - # With the given site id collect the fabric site/zone id - if site_type == "fabric_site": - site_detail = self.get_fabric_site_detail(site_name, site_id) - fabric_id = site_detail.get("id") - else: - zone_detail = self.get_fabric_zone_detail(site_name, site_id) - fabric_id = zone_detail.get("id") + profile_needs_update = self.auth_profile_needs_update(auth_profile_dict, auth_profile_in_ccc) + if not profile_needs_update: + self.status = "success" + msg = ( + "Authentication profile for the site '{0}' does not need any update in the " + "Cisco Catalyst Center." + ).format(site_name) + self.log(msg, "INFO") + self.no_update_profile.append(site_name) + return self - # Validate the playbook input parameter for updating the authentication profile - auth_profile_dict = site.get("update_authentication_profile") - self.validate_auth_profile_parameters(auth_profile_dict).check_return_status() - validate_msg = ( - "All the given parameter(s) '{0}' in the playbook for the updation of authentication " - " profile in SDA fabric site/zone are validated successfully." - ).format(auth_profile_dict) - self.log(validate_msg, "INFO") - auth_profile_in_ccc = self.get_authentication_profile(fabric_id, auth_profile, site_name) - - if not auth_profile_in_ccc: - self.status = "success" - msg = ( - "There is no authentication template profile associated to the site '{0}' " - "in the Cisco Catalyst Center so unable to update the profile parameters." - ).format(site_name) - self.log(self.msg, "INFO") - self.no_update_profile.append(site_name) - return self - - profile_needs_update = self.auth_profile_needs_update(auth_profile_dict, auth_profile_in_ccc) - if not profile_needs_update: - self.status = "success" - msg = ( - "Authentication profile for the site '{0}' does not need any update in the " - "Cisco Catalyst Center." - ).format(site_name) - self.log(msg, "INFO") - self.no_update_profile.append(site_name) - return self - - # Collect the authentication profile parameters for the update operation - profile_update_params = self.collect_authentication_params(auth_profile_dict, auth_profile_in_ccc) - self.update_authentication_profile_template(profile_update_params, site_name).check_return_status() + # Collect the authentication profile parameters for the update operation + profile_update_params = self.collect_authentication_params(auth_profile_dict, auth_profile_in_ccc) + self.update_authentication_profile_template(profile_update_params, site_name).check_return_status() return self @@ -1510,8 +1541,11 @@ def get_diff_deleted(self, config): # Delete Fabric sites/zones from the Cisco Catalyst Center if not config.get('fabric_sites'): - self.status = "success" + self.status = "failed" self.msg = "Unable to delete any fabric site/zone or authentication profile template as input is not given in the playbook." + self.log(self.msg, "ERROR") + self.result["response"] = self.msg + return self fabric_sites = self.want.get('fabric_sites') From 0fe7ffb71f2bd720f98bf533a4bab89f1b0c5be6 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 29 Aug 2024 16:07:21 +0530 Subject: [PATCH 107/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 839eb9edbb..062d1b1390 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -5,7 +5,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -__author__ = ("Trupti A Shetty, Ajith Andrew J, Mohamed Rafeek, Madhan Sankaranarayanan") +__author__ = ("Trupti A Shetty, Mohamed Rafeek, Madhan Sankaranarayanan, Ajith Andrew J") DOCUMENTATION = r""" @@ -42,9 +42,9 @@ - cisco.dnac.workflow_manager_params author: - Trupti A Shetty (@TruptiAShetty) - - Ajith Andrew J (@ajithandrewj) - A Mohamed Rafeek (@mohamedrafeek) - Madhan Sankaranarayanan (@madhansansel) + - Ajith Andrew J (@ajithandrewj) options: config_verify: @@ -705,12 +705,12 @@ def device_ready_for_replacement_check(self): function='return_replacement_devices_with_details' ) devices = response.get("response", []) - self.log("Received API response from 'return_replacement_devices_with_details': {0}".format(str(response)), "DEBUG") + self.log("Received API response from 'return_replacement_devices_with_details': {0}".format(self.pprint(response)), "DEBUG") for device in devices: if device.get("faultyDeviceSerialNumber") == self.have.get("faulty_device_serial_number"): if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": - self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(device.get("faultyDeviceName")), "DEBUG") + self.have["device_replacement_id"] = device.get("id") return True return False @@ -757,12 +757,15 @@ def mark_faulty_device_for_replacement(self): self.msg = task_result["msg"] if self.status == "success": self.result['changed'] = True + self.device_ready_for_replacement_check() + return self except Exception as e: self.status = "failed" self.msg = "Exception occurred while marking device for replacement: {0}".format(str(e)) self.log(self.msg, "ERROR") + self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(self.have.get("faulty_device_name")), "DEBUG") return self def get_diff_replaced(self, config): @@ -848,7 +851,7 @@ def get_diff_replaced(self, config): except Exception as e: self.status = "failed" - error_msg = "Exception occurred during device replacement: {0}".format(str(e)) + error_msg = "Exception occurred during device replacement " self.log(error_msg, "ERROR") # Attempt to unmark the device self.log("Attempting to unmark the device after exception", "INFO") @@ -1105,7 +1108,7 @@ def verify_diff_replaced(self, config): devices = response.get("response", []) replacement_status = None for device in devices: - if device.get("replacementDeviceSerialNumber") == replacement_device_serial: + if device.get("id") == self.have.get("device_replacement_id"): replacement_status = device self.log("Replacement status: {0}".format(self.pprint(replacement_status)), "INFO") except Exception as e: From 5909e6bc4a6df3906a4452c4b7f1a66a53f40a39 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 29 Aug 2024 16:39:10 +0530 Subject: [PATCH 108/121] RMA bugs fixed --- plugins/modules/rma_workflow_manager.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 062d1b1390..fba125199c 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -551,15 +551,13 @@ def rma_device_replacement_pre_check(self): """ if self.have["faulty_device_platform_id"] != self.have["replacement_device_platform_id"]: - if self.have["faulty_device_family_name"] != self.have["replacement_device_family_name"]: - if self.have["faulty_device_series_name"] != self.have["replacement_device_series_name"]: - self.msg = ( - "The faulty device and the replacement device do not belong to the same platform, family and series." - " The faulty device and the replacement device should belong to the same platform" - ) - self.log(self.msg, "ERROR") - self.status = "failed" - return self + self.msg = ( + "The faulty device and the replacement device do not belong to the same platform, family and series." + " These attributes must match for a valid replacement." + ) + self.log(self.msg, "ERROR") + self.status = "failed" + return self self.log("The faulty device and the replacement device belong to the same platform, family and series.", "DEBUG") @@ -569,7 +567,7 @@ def rma_device_replacement_pre_check(self): self.status = "failed" return self - self.log("The replacement device is reachable.", "DEBUG") + self.log("The replacement device '{0}' is reachable.".format(self.have.get("replacement_device_name")), "DEBUG") return self def device_exists(self, identifier, identifier_type): @@ -712,6 +710,7 @@ def device_ready_for_replacement_check(self): if device.get("replacementStatus") == "READY-FOR-REPLACEMENT": self.have["device_replacement_id"] = device.get("id") return True + return False def mark_faulty_device_for_replacement(self): @@ -765,7 +764,7 @@ def mark_faulty_device_for_replacement(self): self.msg = "Exception occurred while marking device for replacement: {0}".format(str(e)) self.log(self.msg, "ERROR") - self.log("The device '{0}' is already in the 'MARKED-FOR-REPLACEMENT' state.".format(self.have.get("faulty_device_name")), "DEBUG") + self.log("The device '{0}' is already in the 'READY-FOR-REPLACEMENT' state.".format(self.have.get("faulty_device_name")), "DEBUG") return self def get_diff_replaced(self, config): From 72b199d362a9d1655272ce9665e7e094ca617326 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Thu, 29 Aug 2024 18:43:22 +0530 Subject: [PATCH 109/121] user and roles comments fixed --- plugins/modules/user_role_workflow_manager.py | 49 ++++++++++--------- 1 file changed, 27 insertions(+), 22 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index d4608284e2..521d3d0209 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -875,7 +875,6 @@ def __init__(self, module): self.created_user, self.updated_user, self.no_update_user = [], [], [] self.created_role, self.updated_role, self.no_update_role = [], [], [] self.deleted_user, self.deleted_role = [], [] - self.key = self.generate_key() def validate_input_yml(self, user_role_details): """ @@ -898,6 +897,15 @@ def validate_input_yml(self, user_role_details): """ self.log("Validating the Playbook Yaml File..", "INFO") config = self.payload.get("config") + self.key = None + + if HAS_FERNET: + self.key = self.generate_key() + else: + self.msg = "The 'cryptography' library is not installed. Please install it using 'pip install cryptography'." + self.log(self.msg, "ERROR") + self.status = "failed" + return self if user_role_details is None or not isinstance(user_role_details, list): self.msg = "Configuration is not available in the playbook for validation or user/role details are not type list" @@ -993,13 +1001,7 @@ def generate_key(self): Criteria: - This function should only be called if HAS_FERNET is True. """ - if HAS_FERNET: - return Fernet.generate_key() - else: - self.msg = "'pip install cryptography' library is not installed." - self.log(self.msg, "ERROR") - self.status = "failed" - return self + return Fernet.generate_key() def encrypt_password(self, password, key): """ @@ -1013,10 +1015,9 @@ def encrypt_password(self, password, key): - This function should only be called if HAS_FERNET is True. - The password should be encoded to bytes before encryption. """ - if HAS_FERNET: - fernet = Fernet(key) - encrypted_password = fernet.encrypt(password.encode()) - return encrypted_password + fernet = Fernet(key) + encrypted_password = fernet.encrypt(password.encode()) + return encrypted_password def decrypt_password(self, encrypted_password, key): """ @@ -1030,10 +1031,9 @@ def decrypt_password(self, encrypted_password, key): - This function should only be called if HAS_FERNET is True. - The encrypted password should be decoded from bytes after decryption. """ - if HAS_FERNET: - fernet = Fernet(key) - decrypted_password = fernet.decrypt(encrypted_password.encode()).decode() - return decrypted_password + fernet = Fernet(key) + decrypted_password = fernet.decrypt(encrypted_password.encode()).decode() + return decrypted_password def validate_password(self, password, error_messages): """ @@ -1240,11 +1240,16 @@ def valid_user_config_parameters(self, user_config): password = user_config.get("password") - if password: - user_config['password'] = self.decrypt_password(password, self.key) - plain_password = user_config.get("password") - self.validate_password(plain_password, error_messages) - user_config['password'] = self.encrypt_password(plain_password, self.key).decode() + try: + if password: + user_config['password'] = self.decrypt_password(password, self.key) + plain_password = user_config.get("password") + self.validate_password(plain_password, error_messages) + user_config['password'] = self.encrypt_password(plain_password, self.key).decode() + self.log("Password decrypted, validated, and re-encrypted successfully.", "DEBUG") + except Exception as e: + self.msg = "Failed during password processing: {0}".format(str(e)) + self.log(self.msg, "ERROR") username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") username_regex_msg = "The username must not contain any special characters and must be 3 to 50 characters long." @@ -1569,7 +1574,7 @@ def create_user(self, user_params): - Logs the provided user parameters and the received API response. - Returns the API response from the "create_user" function. """ - self.log("Create user with user_info_params: {0}".format(str(user_params)), "DEBUG") + self.log("Create user with 'user_params' argument...", "DEBUG") user_params['password'] = self.decrypt_password(user_params['password'], self.key) required_keys = ['username', 'password'] missing_keys = [] From db188e13fc1857869b6d6c53ef94ef5b6aecbc2f Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Thu, 29 Aug 2024 23:57:06 +0530 Subject: [PATCH 110/121] fabric sites zones review comments addressed --- playbooks/fabric_sites_zones_workflow_manager.yml | 2 +- plugins/modules/fabric_sites_zones_workflow_manager.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/playbooks/fabric_sites_zones_workflow_manager.yml b/playbooks/fabric_sites_zones_workflow_manager.yml index 9f49706fb9..a230460d4d 100644 --- a/playbooks/fabric_sites_zones_workflow_manager.yml +++ b/playbooks/fabric_sites_zones_workflow_manager.yml @@ -7,7 +7,7 @@ - "input_fabric_sites_zones.yml" - "credentials.yml" tasks: - - name: Configure fabric sites/zones and authentication profile template in Cisco Catalyst Center. + - name: Configure the fabric sites/zones and authentication profile template in Cisco Catalyst Center. cisco.dnac.fabric_sites_zones_workflow_manager: dnac_host: "{{dnac_host}}" dnac_username: "{{dnac_username}}" diff --git a/plugins/modules/fabric_sites_zones_workflow_manager.py b/plugins/modules/fabric_sites_zones_workflow_manager.py index ac94275e87..db3e5477d1 100644 --- a/plugins/modules/fabric_sites_zones_workflow_manager.py +++ b/plugins/modules/fabric_sites_zones_workflow_manager.py @@ -12,7 +12,7 @@ DOCUMENTATION = r""" --- module: fabric_sites_zones_workflow_manager -short_description: Manage fabric site(s)/zone(s) and update the authentication profile template in Cisco Catalyst Center. +short_description: Manage fabric site(s)/zone(s) and update the authentication profile template in Cisco Catalyst Center. description: - Creating fabric site(s) for the SDA operation in Cisco Catalyst Center. - Updating fabric site(s) for the SDA operation in Cisco Catalyst Center. From 4be909a4ee766baae2a3a7f912e9175df4dd391c Mon Sep 17 00:00:00 2001 From: Abhishek-121 Date: Fri, 30 Aug 2024 00:09:46 +0530 Subject: [PATCH 111/121] Address the review comment of previous PR for the bug fix of resync device in inventory --- plugins/modules/inventory_intent.py | 21 +++++++++--------- plugins/modules/inventory_workflow_manager.py | 22 +++++++++---------- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/plugins/modules/inventory_intent.py b/plugins/modules/inventory_intent.py index eaf11b7e4e..25c5c7a012 100644 --- a/plugins/modules/inventory_intent.py +++ b/plugins/modules/inventory_intent.py @@ -186,14 +186,14 @@ type: bool default: False resync_device_count: - description: Specifies the number of devices to be resynced in the inventory. Ensure that this count does not exceed 200, + description: Specifies the maximum number of devices to be resynced in the inventory. Ensure this count does not exceed 200, as attempting to resync more than 200 devices may cause the 'sync_devices_using_forcesync' API to enter an infinite loop. type: int default: 200 resync_max_timeout: - description: Sets the maximum timeout, in seconds, for the resync process of devices in the inventory to prevent an infinite - loop. The default value is set to 600 seconds. + description: Sets the maximum timeout for the device resync process in the inventory, in seconds. The default is 600 seconds, + which helps prevent infinite loops. type: int default: 600 reboot_device: @@ -1352,7 +1352,7 @@ def resync_devices(self): start = 0 resync_failed_for_all_device = False resync_device_count = self.config[0].get("resync_device_count", 200) - device_resync_list, resync_failed_devices = [], [] + resync_successful_devices, resync_failed_devices = [], [] force_sync = self.config[0].get("force_sync", False) resync_task_dict = {} @@ -1388,9 +1388,8 @@ def resync_devices(self): start_time = time.time() while (True): - end_time = time.time() - if (end_time - start_time) >= max_timeout: + if (time.time() - start_time) >= max_timeout: self.log("""Max timeout of {0} has reached for the task id '{1}' for the device(s) '{2}' to be resynced and unexpected task status so moving out to next task id""".format(max_timeout, task_id, device_list), "WARNING") resync_failed_devices.extend(device_list) @@ -1399,25 +1398,25 @@ def resync_devices(self): execution_details = self.get_task_details(task_id) if 'Synced' in execution_details.get("progress"): - device_resync_list.extend(device_list) + resync_successful_devices.extend(device_list) break elif execution_details.get("isError"): resync_failed_devices.extend(device_list) break time.sleep(self.params.get('dnac_task_poll_interval')) - if resync_failed_devices and device_resync_list: + if resync_failed_devices and resync_successful_devices: self.msg = ( "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " - "And there were some device(s) '{1}' for which resync task not executed successfully." - ).format(device_resync_list, resync_failed_devices) + "Some device(s) '{1}' failed." + ).format(resync_successful_devices, resync_failed_devices) elif resync_failed_devices: resync_failed_for_all_device = True self.msg = "Device resynced get failed for all given device(s) '{0}'.".format(resync_failed_devices) else: self.msg = ( "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " - ).format(device_resync_list) + ).format(resync_successful_devices) if resync_failed_for_all_device: self.status = "failed" diff --git a/plugins/modules/inventory_workflow_manager.py b/plugins/modules/inventory_workflow_manager.py index 961dc03573..61fbef9f0f 100644 --- a/plugins/modules/inventory_workflow_manager.py +++ b/plugins/modules/inventory_workflow_manager.py @@ -186,14 +186,14 @@ type: bool default: False resync_device_count: - description: Specifies the number of devices to be resynced in the inventory. Ensure that this count does not exceed 200, + description: Specifies the maximum number of devices to be resynced in the inventory. Ensure this count does not exceed 200, as attempting to resync more than 200 devices may cause the 'sync_devices_using_forcesync' API to enter an infinite loop. type: int default: 200 resync_max_timeout: - description: Sets the maximum timeout, in seconds, for the resync process of devices in the inventory to prevent an infinite - loop. The default value is set to 600 seconds. + description: Sets the maximum timeout for the device resync process in the inventory, in seconds. The default is 600 seconds, + which helps prevent infinite loops. type: int default: 600 reboot_device: @@ -1347,12 +1347,13 @@ def resync_devices(self): return self device_ids = self.get_device_ids(input_device_ips) + try: # Resync the device in a batch of 200 devices at a time in inventory by default start = 0 resync_failed_for_all_device = False resync_device_count = self.config[0].get("resync_device_count", 200) - device_resync_list, resync_failed_devices = [], [] + resync_successful_devices, resync_failed_devices = [], [] force_sync = self.config[0].get("force_sync", False) resync_task_dict = {} @@ -1388,9 +1389,8 @@ def resync_devices(self): start_time = time.time() while (True): - end_time = time.time() - if (end_time - start_time) >= max_timeout: + if (time.time() - start_time) >= max_timeout: self.log("""Max timeout of {0} has reached for the task id '{1}' for the device(s) '{2}' to be resynced and unexpected task status so moving out to next task id""".format(max_timeout, task_id, device_list), "WARNING") resync_failed_devices.extend(device_list) @@ -1399,25 +1399,25 @@ def resync_devices(self): execution_details = self.get_task_details(task_id) if 'Synced' in execution_details.get("progress"): - device_resync_list.extend(device_list) + resync_successful_devices.extend(device_list) break elif execution_details.get("isError"): resync_failed_devices.extend(device_list) break time.sleep(self.params.get('dnac_task_poll_interval')) - if resync_failed_devices and device_resync_list: + if resync_failed_devices and resync_successful_devices: self.msg = ( "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " - "And there were some device(s) '{1}' for which resync task not executed successfully." - ).format(device_resync_list, resync_failed_devices) + "Some device(s) '{1}' failed." + ).format(resync_successful_devices, resync_failed_devices) elif resync_failed_devices: resync_failed_for_all_device = True self.msg = "Device resynced get failed for all given device(s) '{0}'.".format(resync_failed_devices) else: self.msg = ( "Device(s) '{0}' have been successfully resynced in the inventory in Cisco Catalyst Center. " - ).format(device_resync_list) + ).format(resync_successful_devices) if resync_failed_for_all_device: self.status = "failed" From db55584b64062fdeb4f139f90f583616a9646c52 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 08:02:14 +0530 Subject: [PATCH 112/121] RMA comments fixed --- plugins/modules/rma_workflow_manager.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index fba125199c..fbf94ff947 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -468,10 +468,9 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - (faulty_device_id, faulty_device_serial_number, faulty_device_name, - faulty_device_family_name, faulty_device_series_name, faulty_device_reachability_status, faulty_device_platform_id) = self.device_exists( - faulty_identifier, faulty_key) - if faulty_device_id is None or faulty_device_serial_number is None: + (faulty_device_id, faulty_device_serial_number, faulty_device_name, faulty_device_family_name, faulty_device_series_name, + faulty_device_reachability_status, faulty_device_platform_id, error_message) = self.device_exists(faulty_identifier, faulty_key) + if error_message: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") self.status = "failed" @@ -490,9 +489,9 @@ def get_have(self): # Check if replacement device exists (replacement_device_id, replacement_device_serial_number, replacement_device_name, - replacement_device_family_name, replacement_device_series_name, - replacement_device_reachability_status, replacement_device_platform_id) = self.device_exists(replacement_identifier, replacement_key) - if replacement_device_id is None or replacement_device_serial_number is None: + replacement_device_family_name, replacement_device_series_name, replacement_device_reachability_status, + replacement_device_platform_id, error_message) = self.device_exists(replacement_identifier, replacement_key) + if error_message: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") self.status = "failed" @@ -597,7 +596,7 @@ def device_exists(self, identifier, identifier_type): params["serialNumber"] = identifier else: self.log("Invalid identifier type provided", "ERROR") - return None, None + return None, None, None, None, None, None, None, {"Error"} try: response = self.dnac._exec( @@ -619,7 +618,7 @@ def device_exists(self, identifier, identifier_type): reachability_status = device.get('reachabilityStatus') platform_id = device.get('platformId') if device_id and serial_number and device_name and series_name and family_name and reachability_status and platform_id: - return device_id, serial_number, device_name, series_name, family_name, reachability_status, platform_id + return device_id, serial_number, device_name, series_name, family_name, reachability_status, platform_id, {} self.log("Device found but ID or serial number missing", "ERROR") else: self.log("Device not found in Cisco Catalyst Center", "ERROR") @@ -628,7 +627,7 @@ def device_exists(self, identifier, identifier_type): except Exception as e: self.log("Exception occurred while querying device: {0}".format(str(e)), "ERROR") - return None, None + return None, None, None, None, None, None, None, {"Error"} def validate_device_replacement_params(self): """ @@ -1160,7 +1159,7 @@ def main(): ccc_device_replacement.get_have().check_return_status() ccc_device_replacement.rma_device_replacement_pre_check().check_return_status() ccc_device_replacement.mark_faulty_device_for_replacement().check_return_status() - ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() + # ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() if config_verify: ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() From bc6ff03bc5960a5b40a78001ff5d46ad07ece89e Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 12:27:23 +0530 Subject: [PATCH 113/121] User and Roles comments fixed --- plugins/module_utils/dnac.py | 57 +++++++++ plugins/modules/user_role_workflow_manager.py | 109 +++++++----------- 2 files changed, 100 insertions(+), 66 deletions(-) diff --git a/plugins/module_utils/dnac.py b/plugins/module_utils/dnac.py index 12c3b8fdc9..bd5c430bd4 100644 --- a/plugins/module_utils/dnac.py +++ b/plugins/module_utils/dnac.py @@ -6,6 +6,11 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type +try: + from cryptography.fernet import Fernet + HAS_FERNET = True +except ImportError: + HAS_FERNET = False try: from dnacentersdk import api, exceptions except ImportError: @@ -505,6 +510,58 @@ def check_string_dictionary(self, task_details_data): pass return None + def generate_key(self): + """ + Generate a new encryption key using Fernet. + Returns: + - key (bytes): A newly generated encryption key. + Criteria: + - This function should only be called if HAS_FERNET is True. + """ + if HAS_FERNET: + return {"generate_key": Fernet.generate_key()} + else: + error_message = "The 'cryptography' library is not installed. Please install it using 'pip install cryptography'." + return {"error_message": error_message} + + def encrypt_password(self, password, key): + """ + Encrypt a plaintext password using the provided encryption key. + Args: + - password (str): The plaintext password to be encrypted. + - key (bytes): The encryption key used to encrypt the password. + Returns: + - encrypted_password (bytes): The encrypted password as bytes. + Criteria: + - This function should only be called if HAS_FERNET is True. + - The password should be encoded to bytes before encryption. + """ + try: + fernet = Fernet(key) + encrypted_password = fernet.encrypt(password.encode()) + return {"encrypt_password": encrypted_password} + except Exception as e: + return {"error_message": "Exception occurred while encrypting password: {0}".format(e)} + + def decrypt_password(self, encrypted_password, key): + """ + Decrypt an encrypted password using the provided encryption key. + Args: + - encrypted_password (bytes): The encrypted password as bytes to be decrypted. + - key (bytes): The encryption key used to decrypt the password. + Returns: + - decrypted_password (str): The decrypted plaintext password. + Criteria: + - This function should only be called if HAS_FERNET is True. + - The encrypted password should be decoded from bytes after decryption. + """ + try: + fernet = Fernet(key) + decrypted_password = fernet.decrypt(encrypted_password.encode()).decode() + return {"decrypt_password": decrypted_password} + except Exception as e: + return {"error_message": "Exception occurred while decrypting password: {0}".format(e)} + def camel_to_snake_case(self, config): """ Convert camel case keys to snake case keys in the config. diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 521d3d0209..455e4201f7 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -848,13 +848,6 @@ } """ -try: - from cryptography.fernet import Fernet - HAS_FERNET = True -except ImportError: - HAS_FERNET = False - Fernet = None - import re from ansible_collections.cisco.dnac.plugins.module_utils.dnac import ( DnacBase, @@ -897,12 +890,10 @@ def validate_input_yml(self, user_role_details): """ self.log("Validating the Playbook Yaml File..", "INFO") config = self.payload.get("config") - self.key = None + self.key = self.generate_key() - if HAS_FERNET: - self.key = self.generate_key() - else: - self.msg = "The 'cryptography' library is not installed. Please install it using 'pip install cryptography'." + if self.key and "error_message" in self.key: + self.msg = self.key.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self @@ -944,7 +935,15 @@ def validate_input_yml(self, user_role_details): if "user_details" in config and "username" in user_role_details[0] or "email" in user_role_details[0]: for user in user_role_details: if 'password' in user: - user['password'] = self.encrypt_password(user['password'], self.key) + encrypt_password_responce = self.encrypt_password(user['password'], self.key.get("generate_key")) + + if encrypt_password_responce and "error_message" in encrypt_password_responce: + self.msg = encrypt_password_responce.get("error_message") + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + user["password"] = encrypt_password_responce.get("encrypt_password") if user_role_details[0].get("username") is not None or user_role_details[0].get("email") is not None: user_details = { @@ -993,48 +992,6 @@ def validate_string_field(self, field_value, regex, error_message, error_message if field_value and not regex.match(field_value): error_messages.append(error_message) - def generate_key(self): - """ - Generate a new encryption key using Fernet. - Returns: - - key (bytes): A newly generated encryption key. - Criteria: - - This function should only be called if HAS_FERNET is True. - """ - return Fernet.generate_key() - - def encrypt_password(self, password, key): - """ - Encrypt a plaintext password using the provided encryption key. - Args: - - password (str): The plaintext password to be encrypted. - - key (bytes): The encryption key used to encrypt the password. - Returns: - - encrypted_password (bytes): The encrypted password as bytes. - Criteria: - - This function should only be called if HAS_FERNET is True. - - The password should be encoded to bytes before encryption. - """ - fernet = Fernet(key) - encrypted_password = fernet.encrypt(password.encode()) - return encrypted_password - - def decrypt_password(self, encrypted_password, key): - """ - Decrypt an encrypted password using the provided encryption key. - Args: - - encrypted_password (bytes): The encrypted password as bytes to be decrypted. - - key (bytes): The encryption key used to decrypt the password. - Returns: - - decrypted_password (str): The decrypted plaintext password. - Criteria: - - This function should only be called if HAS_FERNET is True. - - The encrypted password should be decoded from bytes after decryption. - """ - fernet = Fernet(key) - decrypted_password = fernet.decrypt(encrypted_password.encode()).decode() - return decrypted_password - def validate_password(self, password, error_messages): """ Validate the provided password and append an error message if it does not meet the criteria. @@ -1240,16 +1197,28 @@ def valid_user_config_parameters(self, user_config): password = user_config.get("password") - try: - if password: - user_config['password'] = self.decrypt_password(password, self.key) - plain_password = user_config.get("password") - self.validate_password(plain_password, error_messages) - user_config['password'] = self.encrypt_password(plain_password, self.key).decode() - self.log("Password decrypted, validated, and re-encrypted successfully.", "DEBUG") - except Exception as e: - self.msg = "Failed during password processing: {0}".format(str(e)) - self.log(self.msg, "ERROR") + if password: + decrypt_password_responce = self.decrypt_password(password, self.key.get("generate_key")) + + if decrypt_password_responce and "error_message" in decrypt_password_responce: + self.msg = decrypt_password_responce.get("error_message") + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + user_config['password'] = decrypt_password_responce.get("decrypt_password") + plain_password = user_config.get("password") + self.validate_password(plain_password, error_messages) + encrypt_password_responce = self.encrypt_password(plain_password, self.key.get("generate_key")) + + if encrypt_password_responce and "error_message" in encrypt_password_responce: + self.msg = encrypt_password_responce.get("error_message") + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + user_config['password'] = encrypt_password_responce.get("encrypt_password").decode() + self.log("Password decrypted, validated, and re-encrypted successfully.", "DEBUG") username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") username_regex_msg = "The username must not contain any special characters and must be 3 to 50 characters long." @@ -1575,7 +1544,15 @@ def create_user(self, user_params): - Returns the API response from the "create_user" function. """ self.log("Create user with 'user_params' argument...", "DEBUG") - user_params['password'] = self.decrypt_password(user_params['password'], self.key) + decrypt_password_responce = self.decrypt_password(user_params['password'], self.key.get("generate_key")) + + if decrypt_password_responce and "error_message" in decrypt_password_responce: + self.msg = decrypt_password_responce.get("error_message") + self.log(self.msg, "ERROR") + self.status = "failed" + return self + + user_params['password'] = decrypt_password_responce.get("decrypt_password") required_keys = ['username', 'password'] missing_keys = [] From 3c19104b2d3c4a2de8c16f4ae1adecb0ffa57fb9 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 13:14:53 +0530 Subject: [PATCH 114/121] RMA comments fixed --- plugins/modules/rma_workflow_manager.py | 72 +++++++++++++------------ 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index fbf94ff947..cf7910bd8c 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -468,42 +468,41 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - (faulty_device_id, faulty_device_serial_number, faulty_device_name, faulty_device_family_name, faulty_device_series_name, - faulty_device_reachability_status, faulty_device_platform_id, error_message) = self.device_exists(faulty_identifier, faulty_key) - if error_message: + faulty_device_param_dict = self.device_exists(faulty_identifier, faulty_key) + + if not faulty_device_param_dict: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["faulty_device_id"] = faulty_device_id - have["faulty_device_serial_number"] = faulty_device_serial_number - have["faulty_device_name"] = faulty_device_name - have["faulty_device_family_name"] = faulty_device_family_name - have["faulty_device_series_name"] = faulty_device_series_name - have["faulty_device_reachability_status"] = faulty_device_reachability_status - have["faulty_device_platform_id"] = faulty_device_platform_id + have["faulty_device_id"] = faulty_device_param_dict.get("device_id") + have["faulty_device_serial_number"] = faulty_device_param_dict.get("serial_number") + have["faulty_device_name"] = faulty_device_param_dict.get("device_name") + have["faulty_device_family_name"] = faulty_device_param_dict.get("family_name") + have["faulty_device_series_name"] = faulty_device_param_dict.get("series_name") + have["faulty_device_reachability_status"] = faulty_device_param_dict.get("reachability_status") + have["faulty_device_platform_id"] = faulty_device_param_dict.get("platform_id") have[faulty_key] = faulty_identifier have["faulty_device_exists"] = True self.log("Faulty device '{0}' found in Cisco Catalyst Center".format(faulty_identifier), "INFO") # Check if replacement device exists - (replacement_device_id, replacement_device_serial_number, replacement_device_name, - replacement_device_family_name, replacement_device_series_name, replacement_device_reachability_status, - replacement_device_platform_id, error_message) = self.device_exists(replacement_identifier, replacement_key) - if error_message: + replacement_device_param_dict = self.device_exists(replacement_identifier, replacement_key) + + if not replacement_device_param_dict: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["replacement_device_id"] = replacement_device_id - have["replacement_device_serial_number"] = replacement_device_serial_number - have["replacement_device_name"] = replacement_device_name - have["replacement_device_family_name"] = replacement_device_family_name - have["replacement_device_series_name"] = replacement_device_series_name - have["replacement_device_reachability_status"] = replacement_device_reachability_status - have["replacement_device_platform_id"] = replacement_device_platform_id + have["replacement_device_id"] = faulty_device_param_dict.get("device_id") + have["replacement_device_serial_number"] = faulty_device_param_dict.get("serial_number") + have["replacement_device_name"] = faulty_device_param_dict.get("device_name") + have["replacement_device_family_name"] = faulty_device_param_dict.get("family_name") + have["replacement_device_series_name"] = faulty_device_param_dict.get("series_name") + have["replacement_device_reachability_status"] = faulty_device_param_dict.get("reachability_status") + have["replacement_device_platform_id"] = faulty_device_param_dict.get("platform_id") have[replacement_key] = replacement_identifier have["replacement_device_exists"] = True self.log("Replacement device '{0}' found in Cisco Catalyst Center".format(replacement_identifier), "INFO") @@ -577,14 +576,15 @@ def device_exists(self, identifier, identifier_type): - identifier (str): The identifier of the device to check. - identifier_type (str): The type of identifier (name, ip_address, or serial_number). Returns: - - tuple: A tuple containing the device ID and serial number, or (None, None) if the device is not found or an error occurs. + - dict: A dict containing the device ID, serial number, device_name, series_name, family_name, reachability_status, platform_id if the device + is found or empty dict if device not found. Description: This method queries Cisco Catalyst Center to check if a specified device exists based on the provided identifier. It constructs the appropriate query parameters based on the identifier type (hostname, IP address, or serial number). The method then sends a request to Cisco Catalyst Center using the 'get_device_list' function. If the device is found and both ID and serial number are available, it returns these as a tuple. If the device is not found, lacks necessary information, or if an error occurs during the process, - it logs an appropriate error message and returns (None, None). + it logs an appropriate error message and returns empty dict. This method is used to verify the existence of both faulty and replacement devices in the RMA workflow. """ params = {} @@ -596,7 +596,7 @@ def device_exists(self, identifier, identifier_type): params["serialNumber"] = identifier else: self.log("Invalid identifier type provided", "ERROR") - return None, None, None, None, None, None, None, {"Error"} + return {} try: response = self.dnac._exec( @@ -606,19 +606,21 @@ def device_exists(self, identifier, identifier_type): params=params ) self.log("Received API response from 'get_device_list': {0}".format(self.pprint(response)), "DEBUG") + device_param_list = {} if response and response.get('response'): if len(response['response']) > 0: device = response['response'][0] - device_id = device.get('id') - serial_number = device.get('serialNumber') - device_name = device.get('hostname') - series_name = device.get('series') - family_name = device.get('family') - reachability_status = device.get('reachabilityStatus') - platform_id = device.get('platformId') - if device_id and serial_number and device_name and series_name and family_name and reachability_status and platform_id: - return device_id, serial_number, device_name, series_name, family_name, reachability_status, platform_id, {} + device_param_list["device_id"] = device.get('id') + device_param_list["serial_number"] = device.get('serialNumber') + device_param_list["device_name"] = device.get('hostname') + device_param_list["series_name"] = device.get('series') + device_param_list["family_name"] = device.get('family') + device_param_list["reachability_status"] = device.get('reachabilityStatus') + device_param_list["platform_id"] = device.get('platformId') + + if device_param_list: + return device_param_list self.log("Device found but ID or serial number missing", "ERROR") else: self.log("Device not found in Cisco Catalyst Center", "ERROR") @@ -627,7 +629,7 @@ def device_exists(self, identifier, identifier_type): except Exception as e: self.log("Exception occurred while querying device: {0}".format(str(e)), "ERROR") - return None, None, None, None, None, None, None, {"Error"} + return {} def validate_device_replacement_params(self): """ @@ -1159,7 +1161,7 @@ def main(): ccc_device_replacement.get_have().check_return_status() ccc_device_replacement.rma_device_replacement_pre_check().check_return_status() ccc_device_replacement.mark_faulty_device_for_replacement().check_return_status() - # ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() + ccc_device_replacement.get_diff_state_apply[state](config).check_return_status() if config_verify: ccc_device_replacement.verify_diff_state_apply[state](config).check_return_status() From 29222995439722a59a3c794bbca9d6f6b2a33a2f Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Fri, 30 Aug 2024 10:32:36 +0200 Subject: [PATCH 115/121] add permissions --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e9588e8987..ff113de2b8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,6 +15,7 @@ jobs: path: github_ref automerge: runs-on: ubuntu-latest + permissions: write-all steps: - uses: reitermarkus/automerge@v2 with: From a1607f03e3b1bb88b089a6a849a1c030ec95a193 Mon Sep 17 00:00:00 2001 From: sledzikowy Date: Fri, 30 Aug 2024 11:07:27 +0200 Subject: [PATCH 116/121] Move automerge to seperate workflow --- .github/workflows/automerge.yml | 25 +++++++++++++++++++++++++ .github/workflows/main.yml | 13 +------------ 2 files changed, 26 insertions(+), 12 deletions(-) create mode 100644 .github/workflows/automerge.yml diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000000..348396929a --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,25 @@ +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + pull_request_review: + types: [submitted] +jobs: + automerge-job: + runs-on: ubuntu-latest + steps: + - uses: reitermarkus/automerge@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + merge-method: rebase + do-not-merge-labels: never-merge +# pull-request: ${{ github.event.inputs.pull-request }} + dry-run: false \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ff113de2b8..2202794cc3 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,15 +12,4 @@ jobs: uses: actions/upload-artifact@v4 with: name: github_ref-${{ github.run_id }} - path: github_ref - automerge: - runs-on: ubuntu-latest - permissions: write-all - steps: - - uses: reitermarkus/automerge@v2 - with: - token: ${{ secrets.GITHUB_TOKEN }} - merge-method: rebase - do-not-merge-labels: never-merge -# pull-request: ${{ github.event.inputs.pull-request }} - dry-run: false \ No newline at end of file + path: github_ref \ No newline at end of file From 2f79cab06327b91d2e9226a6ba8712c596b4e16f Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 15:13:00 +0530 Subject: [PATCH 117/121] RMA comments fixed --- plugins/modules/rma_workflow_manager.py | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index cf7910bd8c..1a5e8e4feb 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -468,41 +468,41 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - faulty_device_param_dict = self.device_exists(faulty_identifier, faulty_key) + faulty_device_param = self.device_exists(faulty_identifier, faulty_key) - if not faulty_device_param_dict: + if not faulty_device_param: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["faulty_device_id"] = faulty_device_param_dict.get("device_id") - have["faulty_device_serial_number"] = faulty_device_param_dict.get("serial_number") - have["faulty_device_name"] = faulty_device_param_dict.get("device_name") - have["faulty_device_family_name"] = faulty_device_param_dict.get("family_name") - have["faulty_device_series_name"] = faulty_device_param_dict.get("series_name") - have["faulty_device_reachability_status"] = faulty_device_param_dict.get("reachability_status") - have["faulty_device_platform_id"] = faulty_device_param_dict.get("platform_id") + have["faulty_device_id"] = faulty_device_param.get("device_id") + have["faulty_device_serial_number"] = faulty_device_param.get("serial_number") + have["faulty_device_name"] = faulty_device_param.get("device_name") + have["faulty_device_family_name"] = faulty_device_param.get("family_name") + have["faulty_device_series_name"] = faulty_device_param.get("series_name") + have["faulty_device_reachability_status"] = faulty_device_param.get("reachability_status") + have["faulty_device_platform_id"] = faulty_device_param.get("platform_id") have[faulty_key] = faulty_identifier have["faulty_device_exists"] = True self.log("Faulty device '{0}' found in Cisco Catalyst Center".format(faulty_identifier), "INFO") # Check if replacement device exists - replacement_device_param_dict = self.device_exists(replacement_identifier, replacement_key) + replacement_device_param = self.device_exists(replacement_identifier, replacement_key) - if not replacement_device_param_dict: + if not replacement_device_param: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["replacement_device_id"] = faulty_device_param_dict.get("device_id") - have["replacement_device_serial_number"] = faulty_device_param_dict.get("serial_number") - have["replacement_device_name"] = faulty_device_param_dict.get("device_name") - have["replacement_device_family_name"] = faulty_device_param_dict.get("family_name") - have["replacement_device_series_name"] = faulty_device_param_dict.get("series_name") - have["replacement_device_reachability_status"] = faulty_device_param_dict.get("reachability_status") - have["replacement_device_platform_id"] = faulty_device_param_dict.get("platform_id") + have["replacement_device_id"] = replacement_device_param.get("device_id") + have["replacement_device_serial_number"] = replacement_device_param.get("serial_number") + have["replacement_device_name"] = replacement_device_param.get("device_name") + have["replacement_device_family_name"] = replacement_device_param.get("family_name") + have["replacement_device_series_name"] = replacement_device_param.get("series_name") + have["replacement_device_reachability_status"] = replacement_device_param.get("reachability_status") + have["replacement_device_platform_id"] = replacement_device_param.get("platform_id") have[replacement_key] = replacement_identifier have["replacement_device_exists"] = True self.log("Replacement device '{0}' found in Cisco Catalyst Center".format(replacement_identifier), "INFO") From ce23223089b146cd3a11c91fce90e31eab86dbd2 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 15:16:36 +0530 Subject: [PATCH 118/121] User and Roles comments fixed --- plugins/modules/user_role_workflow_manager.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/plugins/modules/user_role_workflow_manager.py b/plugins/modules/user_role_workflow_manager.py index 455e4201f7..8256da0627 100644 --- a/plugins/modules/user_role_workflow_manager.py +++ b/plugins/modules/user_role_workflow_manager.py @@ -935,15 +935,15 @@ def validate_input_yml(self, user_role_details): if "user_details" in config and "username" in user_role_details[0] or "email" in user_role_details[0]: for user in user_role_details: if 'password' in user: - encrypt_password_responce = self.encrypt_password(user['password'], self.key.get("generate_key")) + encrypt_password_response = self.encrypt_password(user['password'], self.key.get("generate_key")) - if encrypt_password_responce and "error_message" in encrypt_password_responce: - self.msg = encrypt_password_responce.get("error_message") + if encrypt_password_response and "error_message" in encrypt_password_response: + self.msg = encrypt_password_response.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self - user["password"] = encrypt_password_responce.get("encrypt_password") + user["password"] = encrypt_password_response.get("encrypt_password") if user_role_details[0].get("username") is not None or user_role_details[0].get("email") is not None: user_details = { @@ -1198,26 +1198,26 @@ def valid_user_config_parameters(self, user_config): password = user_config.get("password") if password: - decrypt_password_responce = self.decrypt_password(password, self.key.get("generate_key")) + decrypt_password_response = self.decrypt_password(password, self.key.get("generate_key")) - if decrypt_password_responce and "error_message" in decrypt_password_responce: - self.msg = decrypt_password_responce.get("error_message") + if decrypt_password_response and "error_message" in decrypt_password_response: + self.msg = decrypt_password_response.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self - user_config['password'] = decrypt_password_responce.get("decrypt_password") + user_config['password'] = decrypt_password_response.get("decrypt_password") plain_password = user_config.get("password") self.validate_password(plain_password, error_messages) - encrypt_password_responce = self.encrypt_password(plain_password, self.key.get("generate_key")) + encrypt_password_response = self.encrypt_password(plain_password, self.key.get("generate_key")) - if encrypt_password_responce and "error_message" in encrypt_password_responce: - self.msg = encrypt_password_responce.get("error_message") + if encrypt_password_response and "error_message" in encrypt_password_response: + self.msg = encrypt_password_response.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self - user_config['password'] = encrypt_password_responce.get("encrypt_password").decode() + user_config['password'] = encrypt_password_response.get("encrypt_password").decode() self.log("Password decrypted, validated, and re-encrypted successfully.", "DEBUG") username_regex = re.compile(r"^[A-Za-z0-9@._-]{3,50}$") @@ -1544,15 +1544,15 @@ def create_user(self, user_params): - Returns the API response from the "create_user" function. """ self.log("Create user with 'user_params' argument...", "DEBUG") - decrypt_password_responce = self.decrypt_password(user_params['password'], self.key.get("generate_key")) + decrypt_password_response = self.decrypt_password(user_params['password'], self.key.get("generate_key")) - if decrypt_password_responce and "error_message" in decrypt_password_responce: - self.msg = decrypt_password_responce.get("error_message") + if decrypt_password_response and "error_message" in decrypt_password_response: + self.msg = decrypt_password_response.get("error_message") self.log(self.msg, "ERROR") self.status = "failed" return self - user_params['password'] = decrypt_password_responce.get("decrypt_password") + user_params['password'] = decrypt_password_response.get("decrypt_password") required_keys = ['username', 'password'] missing_keys = [] From a5e79c1e4db3982f319127711f09971fbe32f6f8 Mon Sep 17 00:00:00 2001 From: Ajith Andrew J Date: Fri, 30 Aug 2024 16:20:46 +0530 Subject: [PATCH 119/121] RMA comments fixed --- plugins/modules/rma_workflow_manager.py | 36 ++++++++++++------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/rma_workflow_manager.py b/plugins/modules/rma_workflow_manager.py index 1a5e8e4feb..d9a141ead9 100644 --- a/plugins/modules/rma_workflow_manager.py +++ b/plugins/modules/rma_workflow_manager.py @@ -468,41 +468,41 @@ def get_have(self): valid_identifier_found = True # Check if faulty device exists - faulty_device_param = self.device_exists(faulty_identifier, faulty_key) + faulty_device_params = self.device_exists(faulty_identifier, faulty_key) - if not faulty_device_param: + if not faulty_device_params: self.msg = "Faulty device '{0}' not found in Cisco Catalyst Center".format(faulty_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["faulty_device_id"] = faulty_device_param.get("device_id") - have["faulty_device_serial_number"] = faulty_device_param.get("serial_number") - have["faulty_device_name"] = faulty_device_param.get("device_name") - have["faulty_device_family_name"] = faulty_device_param.get("family_name") - have["faulty_device_series_name"] = faulty_device_param.get("series_name") - have["faulty_device_reachability_status"] = faulty_device_param.get("reachability_status") - have["faulty_device_platform_id"] = faulty_device_param.get("platform_id") + have["faulty_device_id"] = faulty_device_params.get("device_id") + have["faulty_device_serial_number"] = faulty_device_params.get("serial_number") + have["faulty_device_name"] = faulty_device_params.get("device_name") + have["faulty_device_family_name"] = faulty_device_params.get("family_name") + have["faulty_device_series_name"] = faulty_device_params.get("series_name") + have["faulty_device_reachability_status"] = faulty_device_params.get("reachability_status") + have["faulty_device_platform_id"] = faulty_device_params.get("platform_id") have[faulty_key] = faulty_identifier have["faulty_device_exists"] = True self.log("Faulty device '{0}' found in Cisco Catalyst Center".format(faulty_identifier), "INFO") # Check if replacement device exists - replacement_device_param = self.device_exists(replacement_identifier, replacement_key) + replacement_device_params = self.device_exists(replacement_identifier, replacement_key) - if not replacement_device_param: + if not replacement_device_params: self.msg = "Replacement device '{0}' not found in Cisco Catalyst Center".format(replacement_identifier) self.log(self.msg, "ERROR") self.status = "failed" return self - have["replacement_device_id"] = replacement_device_param.get("device_id") - have["replacement_device_serial_number"] = replacement_device_param.get("serial_number") - have["replacement_device_name"] = replacement_device_param.get("device_name") - have["replacement_device_family_name"] = replacement_device_param.get("family_name") - have["replacement_device_series_name"] = replacement_device_param.get("series_name") - have["replacement_device_reachability_status"] = replacement_device_param.get("reachability_status") - have["replacement_device_platform_id"] = replacement_device_param.get("platform_id") + have["replacement_device_id"] = replacement_device_params.get("device_id") + have["replacement_device_serial_number"] = replacement_device_params.get("serial_number") + have["replacement_device_name"] = replacement_device_params.get("device_name") + have["replacement_device_family_name"] = replacement_device_params.get("family_name") + have["replacement_device_series_name"] = replacement_device_params.get("series_name") + have["replacement_device_reachability_status"] = replacement_device_params.get("reachability_status") + have["replacement_device_platform_id"] = replacement_device_params.get("platform_id") have[replacement_key] = replacement_identifier have["replacement_device_exists"] = True self.log("Replacement device '{0}' found in Cisco Catalyst Center".format(replacement_identifier), "INFO") From 525f85b674b0f015412b319d2978695b881bd8c1 Mon Sep 17 00:00:00 2001 From: Madhan Date: Fri, 30 Aug 2024 18:14:47 +0530 Subject: [PATCH 120/121] changes in workflow manager module --- changelogs/changelog.yaml | 18 ++++++++++++++++++ galaxy.yml | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 2ffb8427c7..e158aaf061 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -962,3 +962,21 @@ releases: release_summary: Fix family name. minor_changes: - Fix family name from userand_roles to user_and_roles. + 6.18.0: + release_date: "2024-08-30" + changes: + release_summary: Code changes in workflow manager modules. + minor_changes: + - Added 'fabric_sites_zones_workflow_manager.py' to manage fabric sites/zones and update the authentication profile template. + - Added 'sda_extranet_policies_workflow_manager' to provide SDA Extranet Policies for managing SDA Extranet Policy. + - Added Circle CI support for integration testing. + - Changes in inventory_workflow_manager to support maximum devices to resync, and resync timeout. + - Changes in network_settings_workflow_manager to support reserve ip subpools. + - Changes in provision_workflow_manager to support enhanced log messages. + - Changes in rma_workflow_manager module to support pre check for device replacement. + - Bug fixes in user_role_workflow_manager module. + - Changes in accesspoint_workflow_manager module. + - Changes in device_configs_backup_workflow_manager to support name of the site to which the device is assigned. + - inventory_workflow_manager.py: added attributes resync_device_count and resync_max_timeout + - accesspoint_workflow_manager.py: added attributes 'is_assigned_site_as_location', and other new attributes. + - device_configs_backup_workflow_manager.py. added attribute 'site'. diff --git a/galaxy.yml b/galaxy.yml index ee68239a10..a031d65653 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: cisco name: dnac -version: 6.17.1 +version: 6.18.0 readme: README.md authors: - Rafael Campos @@ -20,6 +20,7 @@ authors: - Rangaprabhu Deenadayalu - Ajith Andrew J - Syed Khadeer Ahmed + - A Mohamed Rafeek description: Ansible Modules for Cisco DNA Center license_file: "LICENSE" tags: From d99c19341dd4acd32b261a2a6269715bd182cc0c Mon Sep 17 00:00:00 2001 From: bvargasre Date: Mon, 2 Sep 2024 18:06:25 -0600 Subject: [PATCH 121/121] updated compatibility matrix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 911f7d3e88..6ed7c1e038 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ The following table shows the supported versions. | 2.2.3.3 | 6.4.0 | 2.4.11 | | 2.3.3.0 | 6.6.4 | 2.5.5 | | 2.3.5.3 | 6.13.3 | 2.6.11 | -| 2.3.7.6 | ^6.17.1 | ^2.7.2 | +| 2.3.7.6 | ^6.18.0 | ^2.7.2 | If your Ansible collection is older please consider updating it first.