diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index a3408e7e54..2720b6ee3b 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -849,3 +849,12 @@ releases: - Added attributes 'dnac_api_task_timeout' and 'dnac_task_poll_interval' in intent and workflow_manager modules. - inventory_workflow_manager - Added attributes 'add_user_defined_field', 'update_interface_details', 'export_device_list' and 'admin_status' - inventory_workflow_manager - Removed attributes 'provision_wireless_device', 'reprovision_wired_device' + 6.13.2: + release_date: "2024-04-03" + changes: + release_summary: Enhancements in discovery, site, swim and inventory workflow manager modules. + minor_changes: + - Added the op_modifies=True when calling SDK APIs in the workflow manager modules. + - Added a method to validate IP addresses. + - Fixed a minor issue in the site workflow manager module. + - Updating galaxy.yml ansible.utils dependencies. diff --git a/galaxy.yml b/galaxy.yml index 7faebedd5a..2e40b75e16 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: cisco name: dnac -version: 6.13.1 +version: 6.13.2 readme: README.md authors: - Rafael Campos @@ -27,7 +27,7 @@ tags: - networking - sdn dependencies: - ansible.utils: ">=2.0.0,<4.0" + ansible.utils: ">=2.0.0,<5.0" repository: https://github.com/cisco-en-programmability/dnacenter-ansible documentation: https://cisco-en-programmability.github.io/dnacenter-ansible/ homepage: https://github.com/cisco-en-programmability/dnacenter-ansible diff --git a/playbooks/PnP.yml b/playbooks/PnP.yml index 63bad68e09..31cb11f9bf 100644 --- a/playbooks/PnP.yml +++ b/playbooks/PnP.yml @@ -66,7 +66,7 @@ template_name: "Ansible_PNP_Switch" image_name: cat9k_iosxe_npe.17.03.07.SPA.bin project_name: Onboarding Configuration - template_details: + template_params: hostname: SJC-Switch-1 interface: TwoGigabitEthernet1/0/2 device_info: @@ -108,4 +108,4 @@ - device_info: - serial_number: QD2425L8M7 #Will get deleted - serial_number: FTC2320E0HA #Doesn't exist in the inventory - - serial_number: FKC2310E0HB #Doesn't exist in the inventory \ No newline at end of file + - serial_number: FKC2310E0HB #Doesn't exist in the inventory diff --git a/playbooks/PnP_Workflow_Manager_Playbook.yml b/playbooks/PnP_Workflow_Manager_Playbook.yml index 846ebf3a72..0f1ff25c1c 100644 --- a/playbooks/PnP_Workflow_Manager_Playbook.yml +++ b/playbooks/PnP_Workflow_Manager_Playbook.yml @@ -66,7 +66,7 @@ template_name: "Ansible_PNP_Switch" image_name: cat9k_iosxe_npe.17.03.07.SPA.bin project_name: Onboarding Configuration - template_details: + template_params: hostname: SJC-Switch-1 interface: TwoGigabitEthernet1/0/2 device_info: diff --git a/playbooks/device_provision_workflow.yml b/playbooks/device_provision_workflow.yml index 362556a09f..acb3249a54 100644 --- a/playbooks/device_provision_workflow.yml +++ b/playbooks/device_provision_workflow.yml @@ -16,23 +16,23 @@ dnac_port: "{{ dnac_port }}" dnac_version: "{{ dnac_version }}" dnac_debug: "{{ dnac_debug }}" - + tasks: - name: Provision a wired device to a site - cisco.dnac.workflow_manager: + cisco.dnac.provision_workflow_manager: <<: *dnac_login dnac_log: True state: merged config_verify: True config: - site_name_hierarchy: Global/USA/San Francisco/BGL_18 - management_ip_address: 204.1.1.1 + management_ip_address: 204.1.2.2 - name: Unprovision a wired device from a site - cisco.dnac.workflow_manager: + cisco.dnac.provision_workflow_manager: <<: *dnac_login dnac_log: True state: deleted config: - - management_ip_address: 204.1.1.1 + - management_ip_address: 204.1.2.2 diff --git a/playbooks/template_workflow_manager.yml b/playbooks/template_workflow_manager.yml index 25b0ec7975..f71e29480a 100644 --- a/playbooks/template_workflow_manager.yml +++ b/playbooks/template_workflow_manager.yml @@ -14,6 +14,10 @@ dnac_verify: "{{ dnac_verify }}" dnac_debug: "{{ dnac_debug }}" dnac_log: true + dnac_log_level: "{{ dnac_debug }}" + dnac_log_append: true + dnac_log_file_path: "{{ dnac_log_file_path }}" + validate_response_schema: false state: "merged" config_verify: true #ignore_errors: true #Enable this to continue execution even the task fails @@ -28,6 +32,16 @@ software_variant: "{{ item.variant }}" device_types: - product_family: "{{ item.family }}" + export: + project: + - Ansible_project + - Sample Velocity Templates + template: + - project_name: Onboarding Configuration + template_name: AP_Onboarding + import: + project: "{{ item.import_project }}" + # template: "{{ item.import_template }}" register: template_result with_items: '{{ template_details }}' tags: diff --git a/plugins/module_utils/dnac.py b/plugins/module_utils/dnac.py index a12e7eaf47..8064cc5e76 100644 --- a/plugins/module_utils/dnac.py +++ b/plugins/module_utils/dnac.py @@ -27,6 +27,7 @@ # import datetime import inspect import re +import socket class DnacBase(): @@ -485,6 +486,30 @@ def update_site_type_key(self, config): return new_config + def is_valid_ipv4(self, ip_address): + """ + Validates an IPv4 address. + + Parameters: + ip_address - String denoting the IPv4 address passed. + + Returns: + bool - Returns true if the passed IP address value is correct or it returns + false if it is incorrect + """ + + try: + socket.inet_aton(ip_address) + octets = ip_address.split('.') + if len(octets) != 4: + return False + for octet in octets: + if not 0 <= int(octet) <= 255: + return False + return True + except socket.error: + return False + def is_list_complex(x): return isinstance(x[0], dict) or isinstance(x[0], list) diff --git a/plugins/modules/device_credential_intent.py b/plugins/modules/device_credential_intent.py index 8e2f413843..99b56df6ec 100644 --- a/plugins/modules/device_credential_intent.py +++ b/plugins/modules/device_credential_intent.py @@ -881,6 +881,7 @@ def get_site_id(self, site_name): 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(response), "DEBUG") @@ -2209,6 +2210,7 @@ def create_device_credentials(self): response = self.dnac._exec( family="discovery", function='create_global_credentials_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response from 'create_global_credentials_v2': {0}" @@ -2273,6 +2275,7 @@ def update_device_credentials(self): response = self.dnac._exec( family="discovery", function='update_global_credentials_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response for 'update_global_credentials_v2': {0}" @@ -2328,6 +2331,7 @@ def assign_credentials_to_site(self): response = self.dnac._exec( family="network_settings", function='assign_device_credential_to_site_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response for 'assign_device_credential_to_site_v2': {0}" @@ -2414,6 +2418,7 @@ def delete_device_credential(self, config): response = self.dnac._exec( family="discovery", function="delete_global_credential_v2", + op_modifies=True, params={"id": _id}, ) self.log("Received API response for 'delete_global_credential_v2': {0}" diff --git a/plugins/modules/device_credential_workflow_manager.py b/plugins/modules/device_credential_workflow_manager.py index 3db97ce05a..f74aded0d2 100644 --- a/plugins/modules/device_credential_workflow_manager.py +++ b/plugins/modules/device_credential_workflow_manager.py @@ -880,6 +880,7 @@ def get_site_id(self, site_name): 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(response), "DEBUG") @@ -2208,6 +2209,7 @@ def create_device_credentials(self): response = self.dnac._exec( family="discovery", function='create_global_credentials_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response from 'create_global_credentials_v2': {0}" @@ -2272,6 +2274,7 @@ def update_device_credentials(self): response = self.dnac._exec( family="discovery", function='update_global_credentials_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response for 'update_global_credentials_v2': {0}" @@ -2327,6 +2330,7 @@ def assign_credentials_to_site(self): response = self.dnac._exec( family="network_settings", function='assign_device_credential_to_site_v2', + op_modifies=True, params=credential_params, ) self.log("Received API response for 'assign_device_credential_to_site_v2': {0}" @@ -2413,6 +2417,7 @@ def delete_device_credential(self, config): response = self.dnac._exec( family="discovery", function="delete_global_credential_v2", + op_modifies=True, params={"id": _id}, ) self.log("Received API response for 'delete_global_credential_v2': {0}" diff --git a/plugins/modules/discovery_intent.py b/plugins/modules/discovery_intent.py index 96759bb9c6..33eabf254a 100644 --- a/plugins/modules/discovery_intent.py +++ b/plugins/modules/discovery_intent.py @@ -721,6 +721,43 @@ def validate_input(self, state=None): self.status = "success" return self + def validate_ip4_address_list(self): + """ + Validates each ip adress paased in the IP_address_list passed by the user before preprocessing it + """ + + ip_address_list = self.validated_config[0].get('ip_address_list') + for ip in ip_address_list: + if '/' in ip: + ip = ip.split("/")[0] + if '-' in ip: + if len(ip.split('-')) == 2: + ip1, ip2 = ip.split('-') + if self.is_valid_ipv4(ip1) is False: + msg = "IP address {0} is not valid".format(ip1) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + if self.is_valid_ipv4(ip2) is False: + msg = "IP address {0} is not valid".format(ip2) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + ip1_parts = list(map(int, ip1.split('.'))) + ip2_parts = list(map(int, ip2.split('.'))) + for part in range(4): + if ip1_parts[part] > ip2_parts[part]: + msg = "Incorrect range passed: {0}. Please pass correct IP address range".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + else: + msg = "Provided range '{0}' is incorrect. IP address range should have only upper and lower limit values".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + if self.is_valid_ipv4(ip) is False and '-' not in ip: + msg = "IP address {0} is not valid".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + self.log("All the IP addresses passed are correct", "INFO") + def get_creds_ids_list(self): """ Retrieve the list of credentials IDs associated with class instance. @@ -934,6 +971,7 @@ def get_ccc_global_credentials_v2_info(self): family="discovery", function='get_all_global_credentials_v2', params=self.validated_config[0].get('headers'), + op_modifies=True ) response = response.get('response') self.log("The Global credentials response from 'get all global credentials v2' API is {0}".format(str(response)), "DEBUG") @@ -1288,6 +1326,7 @@ def get_task_status(self, task_id=None): family="task", function='get_task_by_id', params=params, + op_modifies=True, ) response = response.response self.log("Task status for the task id {0} is {1}".format(str(task_id), str(response)), "INFO") @@ -1336,7 +1375,8 @@ def lookup_discovery_by_range_via_name(self): response_part = self.dnac_apply['exec']( family="discovery", function='get_discoveries_by_range', - params=params + params=params, + op_modifies=True, ) response["response"].extend(response_part["response"]) else: @@ -1349,7 +1389,8 @@ def lookup_discovery_by_range_via_name(self): response = self.dnac_apply['exec']( family="discovery", function='get_discoveries_by_range', - params=params + params=params, + op_modifies=True, ) self.log("Response of the get discoveries via range API is {0}".format(str(response)), "DEBUG") @@ -1425,6 +1466,7 @@ def get_discovery_device_info(self, discovery_id=None, task_id=None): family="discovery", function='get_discovered_network_devices_by_discovery_id', params=params, + op_modifies=True, ) devices = response.response @@ -1495,6 +1537,7 @@ def delete_exist_discovery(self, params): family="discovery", function="delete_discovery_by_id", params=params, + op_modifies=True, ) self.log("Response collected from API 'delete_discovery_by_id': {0}".format(str(response)), "DEBUG") @@ -1513,6 +1556,7 @@ def get_diff_merged(self): - self: The instance of the class with updated attributes. """ + self.validate_ip4_address_list() devices_list_info = self.get_devices_list_info() ip_address_list = self.preprocess_device_discovery(devices_list_info) exist_discovery = self.get_exist_discovery() @@ -1611,7 +1655,8 @@ def verify_diff_merged(self, config): response = self.dnac_apply['exec']( family="discovery", function='get_discovery_by_id', - params=params + params=params, + op_modifies=True, ) discovery_name = config.get('discovery_name') if response: diff --git a/plugins/modules/discovery_workflow_manager.py b/plugins/modules/discovery_workflow_manager.py index 88ce124a39..557327ba33 100644 --- a/plugins/modules/discovery_workflow_manager.py +++ b/plugins/modules/discovery_workflow_manager.py @@ -721,6 +721,43 @@ def validate_input(self, state=None): self.status = "success" return self + def validate_ip4_address_list(self): + """ + Validates each ip adress paased in the IP_address_list passed by the user before preprocessing it + """ + + ip_address_list = self.validated_config[0].get('ip_address_list') + for ip in ip_address_list: + if '/' in ip: + ip = ip.split("/")[0] + if '-' in ip: + if len(ip.split('-')) == 2: + ip1, ip2 = ip.split('-') + if self.is_valid_ipv4(ip1) is False: + msg = "IP address {0} is not valid".format(ip1) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + if self.is_valid_ipv4(ip2) is False: + msg = "IP address {0} is not valid".format(ip2) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + ip1_parts = list(map(int, ip1.split('.'))) + ip2_parts = list(map(int, ip2.split('.'))) + for part in range(4): + if ip1_parts[part] > ip2_parts[part]: + msg = "Incorrect range passed: {0}. Please pass correct IP address range".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + else: + msg = "Provided range '{0}' is incorrect. IP address range should have only upper and lower limit values".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + if self.is_valid_ipv4(ip) is False and '-' not in ip: + msg = "IP address {0} is not valid".format(ip) + self.log(msg, "CRITICAL") + self.module.fail_json(msg=msg) + self.log("All the IP addresses passed are correct", "INFO") + def get_creds_ids_list(self): """ Retrieve the list of credentials IDs associated with class instance. @@ -934,6 +971,7 @@ def get_ccc_global_credentials_v2_info(self): family="discovery", function='get_all_global_credentials_v2', params=self.validated_config[0].get('headers'), + op_modifies=True ) response = response.get('response') self.log("The Global credentials response from 'get all global credentials v2' API is {0}".format(str(response)), "DEBUG") @@ -1288,6 +1326,7 @@ def get_task_status(self, task_id=None): family="task", function='get_task_by_id', params=params, + op_modifies=True, ) response = response.response self.log("Task status for the task id {0} is {1}".format(str(task_id), str(response)), "INFO") @@ -1336,7 +1375,8 @@ def lookup_discovery_by_range_via_name(self): response_part = self.dnac_apply['exec']( family="discovery", function='get_discoveries_by_range', - params=params + params=params, + op_modifies=True, ) response["response"].extend(response_part["response"]) else: @@ -1349,7 +1389,8 @@ def lookup_discovery_by_range_via_name(self): response = self.dnac_apply['exec']( family="discovery", function='get_discoveries_by_range', - params=params + params=params, + op_modifies=True, ) self.log("Response of the get discoveries via range API is {0}".format(str(response)), "DEBUG") @@ -1425,6 +1466,7 @@ def get_discovery_device_info(self, discovery_id=None, task_id=None): family="discovery", function='get_discovered_network_devices_by_discovery_id', params=params, + op_modifies=True, ) devices = response.response @@ -1495,6 +1537,7 @@ def delete_exist_discovery(self, params): family="discovery", function="delete_discovery_by_id", params=params, + op_modifies=True, ) self.log("Response collected from API 'delete_discovery_by_id': {0}".format(str(response)), "DEBUG") @@ -1513,6 +1556,7 @@ def get_diff_merged(self): - self: The instance of the class with updated attributes. """ + self.validate_ip4_address_list() devices_list_info = self.get_devices_list_info() ip_address_list = self.preprocess_device_discovery(devices_list_info) exist_discovery = self.get_exist_discovery() @@ -1611,7 +1655,8 @@ def verify_diff_merged(self, config): response = self.dnac_apply['exec']( family="discovery", function='get_discovery_by_id', - params=params + params=params, + op_modifies=True, ) discovery_name = config.get('discovery_name') if response: diff --git a/plugins/modules/inventory_intent.py b/plugins/modules/inventory_intent.py index 675c11c918..54161725b2 100644 --- a/plugins/modules/inventory_intent.py +++ b/plugins/modules/inventory_intent.py @@ -913,6 +913,7 @@ def is_udf_exist(self, field_name): response = self.dnac._exec( family="devices", function='get_all_user_defined_fields', + op_modifies=True, params={"name": field_name}, ) @@ -943,6 +944,7 @@ def create_user_defined_field(self, udf): response = self.dnac._exec( family="devices", function='create_user_defined_field', + op_modifies=True, params=udf, ) self.log("Received API response from 'create_user_defined_field': {0}".format(str(response)), "DEBUG") @@ -986,6 +988,7 @@ def add_field_to_devices(self, device_ids, udf): response = self.dnac._exec( family="devices", function='add_user_defined_field_to_device', + op_modifies=True, params=udf_param_dict, ) self.log("Received API response from 'add_user_defined_field_to_device': {0}".format(str(response)), "DEBUG") @@ -1226,6 +1229,7 @@ def get_ap_devices(self, device_ips): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response', []) @@ -1360,6 +1364,7 @@ def reboot_access_points(self): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response') @@ -1905,6 +1910,7 @@ def get_udf_id(self, field_name): response = self.dnac._exec( family="devices", function='get_all_user_defined_fields', + op_modifies=True, params={"name": field_name}, ) self.log("Received API response from 'get_all_user_defined_fields': {0}".format(str(response)), "DEBUG") @@ -2091,6 +2097,7 @@ def get_device_ids(self, device_ips): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) @@ -2127,6 +2134,7 @@ def get_device_ips_from_hostname(self, hostname_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"hostname": hostname} ) if response: @@ -2161,6 +2169,7 @@ def get_device_ips_from_serial_number(self, serial_number_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"serialNumber": serial_number} ) if response: @@ -2195,6 +2204,7 @@ def get_device_ips_from_mac_address(self, mac_address_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"macAddress": mac_address} ) if response: @@ -2233,6 +2243,7 @@ def get_interface_from_id_and_name(self, device_id, interface_name): response = self.dnac._exec( family="devices", function='get_interface_details', + op_modifies=True, params=interface_detail_params ) self.log("Received API response from 'get_interface_details': {0}".format(str(response)), "DEBUG") @@ -2270,6 +2281,7 @@ def get_interface_from_ip(self, device_ip): response = self.dnac._exec( family="devices", function='get_interface_by_ip', + op_modifies=True, params={"ip_address": device_ip} ) self.log("Received API response from 'get_interface_by_ip': {0}".format(str(response)), "DEBUG") @@ -2302,6 +2314,7 @@ def get_device_response(self, device_ip): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response')[0] @@ -2360,6 +2373,7 @@ def check_interface_details(self, device_ip, interface_name): response = self.dnac._exec( family="devices", function='get_interface_details', + op_modifies=True, params=interface_detail_params ) self.log("Received API response from 'get_interface_details': {0}".format(str(response)), "DEBUG") @@ -2737,6 +2751,7 @@ def is_device_exist_in_ccc(self, device_ip): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response') @@ -2982,6 +2997,7 @@ def get_diff_merged(self, config): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response')[0] @@ -3306,6 +3322,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="devices", function='delete_user_defined_field', + op_modifies=True, params={"id": udf_id}, ) if response and isinstance(response, dict): @@ -3356,6 +3373,7 @@ def get_diff_deleted(self, config): prov_respone = self.dnac._exec( family="sda", function='get_provisioned_wired_device', + op_modifies=True, params=provision_params, ) @@ -3363,6 +3381,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="sda", function='delete_provisioned_wired_device', + op_modifies=True, params=provision_params, ) executionid = response.get("executionId") @@ -3389,6 +3408,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="devices", function='delete_device_by_id', + op_modifies=True, params=delete_params, ) diff --git a/plugins/modules/inventory_workflow_manager.py b/plugins/modules/inventory_workflow_manager.py index 3eda0e2ccb..d42fe45441 100644 --- a/plugins/modules/inventory_workflow_manager.py +++ b/plugins/modules/inventory_workflow_manager.py @@ -912,6 +912,7 @@ def is_udf_exist(self, field_name): response = self.dnac._exec( family="devices", function='get_all_user_defined_fields', + op_modifies=True, params={"name": field_name}, ) @@ -942,6 +943,7 @@ def create_user_defined_field(self, udf): response = self.dnac._exec( family="devices", function='create_user_defined_field', + op_modifies=True, params=udf, ) self.log("Received API response from 'create_user_defined_field': {0}".format(str(response)), "DEBUG") @@ -984,6 +986,7 @@ def add_field_to_devices(self, device_ids, udf): response = self.dnac._exec( family="devices", function='add_user_defined_field_to_device', + op_modifies=True, params=udf_param_dict, ) self.log("Received API response from 'add_user_defined_field_to_device': {0}".format(str(response)), "DEBUG") @@ -1224,6 +1227,7 @@ def get_ap_devices(self, device_ips): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response', []) @@ -1358,6 +1362,7 @@ def reboot_access_points(self): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response') @@ -1897,6 +1902,7 @@ def get_udf_id(self, field_name): response = self.dnac._exec( family="devices", function='get_all_user_defined_fields', + op_modifies=True, params={"name": field_name}, ) self.log("Received API response from 'get_all_user_defined_fields': {0}".format(str(response)), "DEBUG") @@ -2084,6 +2090,7 @@ def get_device_ids(self, device_ips): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) @@ -2120,6 +2127,7 @@ def get_device_ips_from_hostname(self, hostname_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"hostname": hostname} ) if response: @@ -2154,6 +2162,7 @@ def get_device_ips_from_serial_number(self, serial_number_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"serialNumber": serial_number} ) if response: @@ -2188,6 +2197,7 @@ def get_device_ips_from_mac_address(self, mac_address_list): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"macAddress": mac_address} ) if response: @@ -2226,6 +2236,7 @@ def get_interface_from_id_and_name(self, device_id, interface_name): response = self.dnac._exec( family="devices", function='get_interface_details', + op_modifies=True, params=interface_detail_params ) self.log("Received API response from 'get_interface_details': {0}".format(str(response)), "DEBUG") @@ -2263,6 +2274,7 @@ def get_interface_from_ip(self, device_ip): response = self.dnac._exec( family="devices", function='get_interface_by_ip', + op_modifies=True, params={"ip_address": device_ip} ) self.log("Received API response from 'get_interface_by_ip': {0}".format(str(response)), "DEBUG") @@ -2295,6 +2307,7 @@ def get_device_response(self, device_ip): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response')[0] @@ -2353,6 +2366,7 @@ def check_interface_details(self, device_ip, interface_name): response = self.dnac._exec( family="devices", function='get_interface_details', + op_modifies=True, params=interface_detail_params ) self.log("Received API response from 'get_interface_details': {0}".format(str(response)), "DEBUG") @@ -2730,6 +2744,7 @@ def is_device_exist_in_ccc(self, device_ip): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response') @@ -2974,6 +2989,7 @@ def get_diff_merged(self, config): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"managementIpAddress": device_ip} ) response = response.get('response')[0] @@ -3299,6 +3315,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="devices", function='delete_user_defined_field', + op_modifies=True, params={"id": udf_id}, ) if response and isinstance(response, dict): @@ -3349,6 +3366,7 @@ def get_diff_deleted(self, config): prov_respone = self.dnac._exec( family="sda", function='get_provisioned_wired_device', + op_modifies=True, params=provision_params, ) @@ -3356,6 +3374,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="sda", function='delete_provisioned_wired_device', + op_modifies=True, params=provision_params, ) executionid = response.get("executionId") @@ -3383,6 +3402,7 @@ def get_diff_deleted(self, config): response = self.dnac._exec( family="devices", function='delete_device_by_id', + op_modifies=True, params=delete_params, ) diff --git a/plugins/modules/network_settings_intent.py b/plugins/modules/network_settings_intent.py index 49d6fa5d47..72c2e8421b 100644 --- a/plugins/modules/network_settings_intent.py +++ b/plugins/modules/network_settings_intent.py @@ -705,6 +705,7 @@ def get_site_id(self, site_name): 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(response), "DEBUG") @@ -856,6 +857,7 @@ def get_network_params(self, site_id): response = self.dnac._exec( family="network_settings", function='get_network_v2', + op_modifies=True, params={"site_id": site_id} ) self.log("Received API response from 'get_network_v2': {0}".format(response), "DEBUG") @@ -1061,6 +1063,7 @@ def reserve_pool_exists(self, name, site_name): response = self.dnac._exec( family="network_settings", function="get_reserve_ip_subpool", + op_modifies=True, params={"siteId": site_id} ) if not isinstance(response, dict): @@ -1748,6 +1751,7 @@ def update_global_pool(self, config): response = self.dnac._exec( family="network_settings", function="create_global_pool", + op_modifies=True, params=pool_params, ) self.check_execution_response_status(response).check_return_status() @@ -1793,6 +1797,7 @@ def update_global_pool(self, config): response = self.dnac._exec( family="network_settings", function="update_global_pool", + op_modifies=True, params=pool_params, ) @@ -1836,6 +1841,7 @@ def update_reserve_pool(self, config): response = self.dnac._exec( family="network_settings", function="reserve_ip_subpool", + op_modifies=True, params=reserve_params, ) self.check_execution_response_status(response).check_return_status() @@ -1868,6 +1874,7 @@ def update_reserve_pool(self, config): response = self.dnac._exec( family="network_settings", function="update_reserve_ip_subpool", + op_modifies=True, params=reserve_params, ) self.check_execution_response_status(response).check_return_status() @@ -1915,6 +1922,7 @@ def update_network(self, config): response = self.dnac._exec( family="network_settings", function='update_network_v2', + op_modifies=True, params=net_params, ) self.log("Received API response of 'update_network_v2': {0}".format(response), "DEBUG") @@ -1978,6 +1986,7 @@ def delete_reserve_pool(self, name): response = self.dnac._exec( family="network_settings", function="release_reserve_ip_subpool", + op_modifies=True, params={"id": _id}, ) self.check_execution_response_status(response).check_return_status() @@ -2014,6 +2023,7 @@ def delete_global_pool(self, name): response = self.dnac._exec( family="network_settings", function="delete_global_ip_pool", + op_modifies=True, params={"id": self.have.get("globalPool").get("id")}, ) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index bbae364635..71fb089428 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -701,6 +701,7 @@ def get_site_id(self, site_name): 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(response), "DEBUG") @@ -852,6 +853,7 @@ def get_network_params(self, site_id): response = self.dnac._exec( family="network_settings", function='get_network_v2', + op_modifies=True, params={"site_id": site_id} ) self.log("Received API response from 'get_network_v2': {0}".format(response), "DEBUG") @@ -1057,6 +1059,7 @@ def reserve_pool_exists(self, name, site_name): response = self.dnac._exec( family="network_settings", function="get_reserve_ip_subpool", + op_modifies=True, params={"siteId": site_id} ) if not isinstance(response, dict): @@ -1733,6 +1736,7 @@ def update_global_pool(self, config): response = self.dnac._exec( family="network_settings", function="create_global_pool", + op_modifies=True, params=pool_params, ) self.check_execution_response_status(response).check_return_status() @@ -1778,6 +1782,7 @@ def update_global_pool(self, config): response = self.dnac._exec( family="network_settings", function="update_global_pool", + op_modifies=True, params=pool_params, ) @@ -1821,6 +1826,7 @@ def update_reserve_pool(self, config): response = self.dnac._exec( family="network_settings", function="reserve_ip_subpool", + op_modifies=True, params=reserve_params, ) self.check_execution_response_status(response).check_return_status() @@ -1853,6 +1859,7 @@ def update_reserve_pool(self, config): response = self.dnac._exec( family="network_settings", function="update_reserve_ip_subpool", + op_modifies=True, params=reserve_params, ) self.check_execution_response_status(response).check_return_status() @@ -1900,6 +1907,7 @@ def update_network(self, config): response = self.dnac._exec( family="network_settings", function='update_network_v2', + op_modifies=True, params=net_params, ) self.log("Received API response of 'update_network_v2': {0}".format(response), "DEBUG") @@ -1963,6 +1971,7 @@ def delete_reserve_pool(self, name): response = self.dnac._exec( family="network_settings", function="release_reserve_ip_subpool", + op_modifies=True, params={"id": _id}, ) self.check_execution_response_status(response).check_return_status() @@ -1999,6 +2008,7 @@ def delete_global_pool(self, name): response = self.dnac._exec( family="network_settings", function="delete_global_ip_pool", + op_modifies=True, params={"id": self.have.get("globalPool").get("id")}, ) diff --git a/plugins/modules/pnp_intent.py b/plugins/modules/pnp_intent.py index 3c71046a91..f0494fb588 100644 --- a/plugins/modules/pnp_intent.py +++ b/plugins/modules/pnp_intent.py @@ -429,6 +429,7 @@ def get_site_details(self): family="sites", function='get_site', params={"name": self.want.get("site_name")}, + op_modifies=True, ) except Exception: self.log("Exception occurred as site \ @@ -467,6 +468,7 @@ def get_site_type(self): family="sites", function='get_site', params={"name": self.want.get("site_name")}, + op_modifies=True, ) except Exception: self.log("Exception occurred as \ @@ -632,7 +634,7 @@ def get_claim_params(self): self.pnp_cred_failure(msg=msg) claim_params["rfProfile"] = self.validated_config[0]["rf_profile"] - self.log("Paramters used for claiming are {0}".format(str(claim_params)), "INFO") + self.log("Parameters used for claiming are {0}".format(str(claim_params)), "INFO") return claim_params def get_reset_params(self): @@ -698,7 +700,8 @@ def get_have(self): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": self.want.get("serial_number")} + params={"serial_number": self.want.get("serial_number")}, + op_modifies=True, ) self.log("Device details for the device with serial \ number '{0}': {1}".format(self.want.get("serial_number"), str(device_response)), "DEBUG") @@ -721,6 +724,7 @@ def get_have(self): family="software_image_management_swim", function='get_software_image_details', params=self.want.get("image_params"), + op_modifies=True, ) image_list = image_response.get("response") self.log("Image details obtained from the API 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -730,13 +734,15 @@ def get_have(self): family="configuration_templates", function='gets_the_templates_available', params={"project_names": self.want.get("project_name")}, + op_modifies=True, ) self.log("List of templates under the project '{0}': {1}".format(self.want.get("project_name"), str(template_list)), "DEBUG") dev_details_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function="get_device_by_id", - params={"id": device_response[0].get("id")} + params={"id": device_response[0].get("id")}, + op_modifies=True, ) self.log("Device details retrieved after calling the 'get_device_by_id' API: {0}".format(str(dev_details_response)), "DEBUG") install_mode = dev_details_response.get("deviceInfo").get("mode") @@ -899,7 +905,8 @@ class instance for further use. multi_device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) self.log("Device details for serial number {0} \ obtained from the API 'get_device_list': {1}".format(device["deviceInfo"]["serialNumber"], str(multi_device_response)), "DEBUG") @@ -1034,7 +1041,8 @@ class instance for further use. dev_details_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function="get_device_by_id", - params={"id": self.have["device_id"]} + params={"id": self.have["device_id"]}, + op_modifies=True, ) self.log("Response from 'get_device_by_id' API for device details: {0}".format(str(dev_details_response)), "DEBUG") @@ -1133,7 +1141,8 @@ def get_diff_deleted(self): multi_device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) self.log("Response from 'get_device_list' API for claiming: {0}".format(str(multi_device_response)), "DEBUG") if multi_device_response and len(multi_device_response) == 1: @@ -1190,7 +1199,8 @@ def verify_diff_merged(self, config): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) if (device_response and (len(device_response) == 1)): msg = ( @@ -1230,7 +1240,8 @@ def verify_diff_deleted(self, config): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) if not (device_response and (len(device_response) == 1)): msg = ( diff --git a/plugins/modules/pnp_workflow_manager.py b/plugins/modules/pnp_workflow_manager.py index e1b334f71e..3080f4028a 100644 --- a/plugins/modules/pnp_workflow_manager.py +++ b/plugins/modules/pnp_workflow_manager.py @@ -429,6 +429,7 @@ def get_site_details(self): family="sites", function='get_site', params={"name": self.want.get("site_name")}, + op_modifies=True, ) except Exception: self.log("Exception occurred as site \ @@ -467,6 +468,7 @@ def get_site_type(self): family="sites", function='get_site', params={"name": self.want.get("site_name")}, + op_modifies=True, ) except Exception: self.log("Exception occurred as \ @@ -632,7 +634,7 @@ def get_claim_params(self): self.pnp_cred_failure(msg=msg) claim_params["rfProfile"] = self.validated_config[0]["rf_profile"] - self.log("Paramters used for claiming are {0}".format(str(claim_params)), "INFO") + self.log("Parameters used for claiming are {0}".format(str(claim_params)), "INFO") return claim_params def get_reset_params(self): @@ -698,7 +700,8 @@ def get_have(self): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": self.want.get("serial_number")} + params={"serial_number": self.want.get("serial_number")}, + op_modifies=True, ) self.log("Device details for the device with serial \ number '{0}': {1}".format(self.want.get("serial_number"), str(device_response)), "DEBUG") @@ -721,6 +724,7 @@ def get_have(self): family="software_image_management_swim", function='get_software_image_details', params=self.want.get("image_params"), + op_modifies=True, ) image_list = image_response.get("response") self.log("Image details obtained from the API 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -730,13 +734,15 @@ def get_have(self): family="configuration_templates", function='gets_the_templates_available', params={"project_names": self.want.get("project_name")}, + op_modifies=True, ) self.log("List of templates under the project '{0}': {1}".format(self.want.get("project_name"), str(template_list)), "DEBUG") dev_details_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function="get_device_by_id", - params={"id": device_response[0].get("id")} + params={"id": device_response[0].get("id")}, + op_modifies=True, ) self.log("Device details retrieved after calling the 'get_device_by_id' API: {0}".format(str(dev_details_response)), "DEBUG") install_mode = dev_details_response.get("deviceInfo").get("mode") @@ -899,7 +905,8 @@ class instance for further use. multi_device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) self.log("Device details for serial number {0} \ obtained from the API 'get_device_list': {1}".format(device["deviceInfo"]["serialNumber"], str(multi_device_response)), "DEBUG") @@ -1034,7 +1041,8 @@ class instance for further use. dev_details_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function="get_device_by_id", - params={"id": self.have["device_id"]} + params={"id": self.have["device_id"]}, + op_modifies=True, ) self.log("Response from 'get_device_by_id' API for device details: {0}".format(str(dev_details_response)), "DEBUG") @@ -1133,7 +1141,8 @@ def get_diff_deleted(self): multi_device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) self.log("Response from 'get_device_list' API for claiming: {0}".format(str(multi_device_response)), "DEBUG") if multi_device_response and len(multi_device_response) == 1: @@ -1190,7 +1199,8 @@ def verify_diff_merged(self, config): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) if (device_response and (len(device_response) == 1)): msg = ( @@ -1230,7 +1240,8 @@ def verify_diff_deleted(self, config): device_response = self.dnac_apply['exec']( family="device_onboarding_pnp", function='get_device_list', - params={"serial_number": device["deviceInfo"]["serialNumber"]} + params={"serial_number": device["deviceInfo"]["serialNumber"]}, + op_modifies=True, ) if not (device_response and (len(device_response) == 1)): msg = ( diff --git a/plugins/modules/provision_intent.py b/plugins/modules/provision_intent.py index 4a3c8a2286..a9231c1425 100644 --- a/plugins/modules/provision_intent.py +++ b/plugins/modules/provision_intent.py @@ -246,7 +246,8 @@ def get_dev_type(self): dev_response = self.dnac_apply['exec']( family="devices", function='get_network_device_by_ip', - params={"ip_address": self.validated_config[0]["management_ip_address"]} + params={"ip_address": self.validated_config[0]["management_ip_address"]}, + op_modifies=True ) dev_dict = dev_response.get("response") @@ -282,6 +283,7 @@ def get_task_status(self, task_id=None): family="task", function='get_task_by_id', params=params, + op_modifies=True ) response = response.response if response.get('isError') or re.search( @@ -321,6 +323,7 @@ def get_site_type(self, site_name=None): family="sites", function='get_site', params={"name": site_name}, + op_modifies=True ) except Exception: self.module.fail_json(msg="Site not found", response=[]) @@ -403,7 +406,8 @@ def get_wireless_params(self): response = self.dnac_apply['exec']( family="devices", function='get_network_device_by_ip', - params={"management_ip_address": self.validated_config[0]["management_ip_address"]} + params={"management_ip_address": self.validated_config[0]["management_ip_address"]}, + op_modifies=True ) wireless_params[0]["deviceName"] = response.get("response")[0].get("hostname") diff --git a/plugins/modules/provision_workflow_manager.py b/plugins/modules/provision_workflow_manager.py index 27ae581410..f31ad3ccfa 100644 --- a/plugins/modules/provision_workflow_manager.py +++ b/plugins/modules/provision_workflow_manager.py @@ -251,7 +251,8 @@ def get_dev_type(self): dev_response = self.dnac_apply['exec']( family="devices", function='get_network_device_by_ip', - params={"ip_address": self.validated_config[0]["management_ip_address"]} + params={"ip_address": self.validated_config[0]["management_ip_address"]}, + op_modifies=True ) self.log("The device response from 'get_network_device_by_ip' API is {0}".format(str(dev_response)), "DEBUG") @@ -289,6 +290,7 @@ def get_task_status(self, task_id=None): family="task", function='get_task_by_id', params=params, + op_modifies=True ) self.log("Response collected from 'get_task_by_id' API is {0}".format(str(response)), "DEBUG") response = response.response @@ -330,6 +332,7 @@ def get_site_type(self, site_name_hierarchy=None): family="sites", function='get_site', params={"name": site_name_hierarchy}, + op_modifies=True ) except Exception: self.log("Exception occurred as \ @@ -418,7 +421,8 @@ def get_wireless_params(self): response = self.dnac_apply['exec']( family="devices", function='get_network_device_by_ip', - params={"management_ip_address": self.validated_config[0]["management_ip_address"]} + params={"management_ip_address": self.validated_config[0]["management_ip_address"]}, + op_modifies=True ) self.log("Response collected from 'get_network_device_by_ip' is:{0}".format(str(response)), "DEBUG") @@ -475,17 +479,14 @@ class instance for further use. device_type = self.want.get("device_type") if device_type == "wired": - try: - status_response = self.dnac_apply['exec']( - family="sda", - function="get_provisioned_wired_device", - op_modifies=True, - params={ - "device_management_ip_address": self.validated_config[0]["management_ip_address"] - }, - ) - except Exception: - status_response = {} + status_response = self.dnac_apply['exec']( + family="sda", + function="get_provisioned_wired_device", + op_modifies=True, + params={ + "device_management_ip_address": self.validated_config[0]["management_ip_address"] + }, + ) self.log("Wired device's status Response collected from 'get_provisioned_wired_device' API is:{0}".format(str(status_response)), "DEBUG") status = status_response.get("status") self.log("The provisioned status of the wired device is {0}".format(status), "INFO") diff --git a/plugins/modules/site_intent.py b/plugins/modules/site_intent.py index 751d520bea..b9b5d8081a 100644 --- a/plugins/modules/site_intent.py +++ b/plugins/modules/site_intent.py @@ -489,6 +489,7 @@ def site_exists(self): response = self.dnac._exec( family="sites", function='get_site', + op_modifies=True, params={"name": self.want.get("site_name")}, ) @@ -807,12 +808,18 @@ def get_diff_merged(self, config): else: # Creating New Site site_params = self.want.get("site_params") - if site_params['site']['building']: - building_details = {} - for key, value in site_params['site']['building'].items(): - if value is not None: - building_details[key] = value - site_params['site']['building'] = building_details + try: + if site_params['site']['building']: + building_details = {} + for key, value in site_params['site']['building'].items(): + if value is not None: + building_details[key] = value + site_params['site']['building'] = building_details + except Exception as e: + site_type = site_params['type'] + site_name = site_params['site'][site_type]['name'] + self.log("""The site '{0}' is not categorized as a building; hence, there is no need to filter out 'None' + values from the 'site_params' dictionary.""".format(site_name), "INFO") response = self.dnac._exec( family="sites", @@ -877,6 +884,7 @@ def delete_single_site(self, site_id, site_name): response = self.dnac._exec( family="sites", function="delete_site", + op_modifies=True, params={"site_id": site_id}, ) @@ -941,6 +949,7 @@ def get_diff_deleted(self, config): mem_response = self.dnac._exec( family="sites", function="get_membership", + op_modifies=True, params={"site_id": site_id}, ) site_response = mem_response.get("site").get("response") diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 1ae28afd8c..5a6e0bd0b1 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -488,6 +488,7 @@ def site_exists(self): response = self.dnac._exec( family="sites", function='get_site', + op_modifies=True, params={"name": self.want.get("site_name")}, ) @@ -806,12 +807,18 @@ def get_diff_merged(self, config): else: # Creating New Site site_params = self.want.get("site_params") - if site_params['site']['building']: - building_details = {} - for key, value in site_params['site']['building'].items(): - if value is not None: - building_details[key] = value - site_params['site']['building'] = building_details + try: + if site_params['site']['building']: + building_details = {} + for key, value in site_params['site']['building'].items(): + if value is not None: + building_details[key] = value + site_params['site']['building'] = building_details + except Exception as e: + site_type = site_params['type'] + site_name = site_params['site'][site_type]['name'] + self.log("""The site '{0}' is not categorized as a building; hence, there is no need to filter out 'None' + values from the 'site_params' dictionary.""".format(site_name), "INFO") response = self.dnac._exec( family="sites", @@ -876,6 +883,7 @@ def delete_single_site(self, site_id, site_name): response = self.dnac._exec( family="sites", function="delete_site", + op_modifies=True, params={"site_id": site_id}, ) @@ -940,6 +948,7 @@ def get_diff_deleted(self, config): mem_response = self.dnac._exec( family="sites", function="get_membership", + op_modifies=True, params={"site_id": site_id}, ) site_response = mem_response.get("site").get("response") diff --git a/plugins/modules/swim_intent.py b/plugins/modules/swim_intent.py index 08f78ac309..78954cbe92 100644 --- a/plugins/modules/swim_intent.py +++ b/plugins/modules/swim_intent.py @@ -583,6 +583,7 @@ def site_exists(self, site_name): response = self.dnac._exec( family="sites", function='get_site', + op_modifies=True, params={"name": site_name}, ) except Exception as e: @@ -617,6 +618,7 @@ def get_image_id(self, name): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_name": name}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -651,6 +653,7 @@ def get_image_name_from_id(self, image_id): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_uuid": image_id}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -686,6 +689,7 @@ def is_image_exist(self, name): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_name": name}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -713,6 +717,7 @@ def get_device_id(self, params): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params=params, ) self.log("Received API response from 'get_device_list': {0}".format(str(response)), "DEBUG") @@ -1305,6 +1310,7 @@ def get_device_ip_from_id(self, device_id): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"id": device_id} ) self.log("Received API response from 'get_device_list': {0}".format(str(response)), "DEBUG") diff --git a/plugins/modules/swim_workflow_manager.py b/plugins/modules/swim_workflow_manager.py index a147b40550..7937eb0c1e 100644 --- a/plugins/modules/swim_workflow_manager.py +++ b/plugins/modules/swim_workflow_manager.py @@ -569,6 +569,7 @@ def site_exists(self, site_name): response = self.dnac._exec( family="sites", function='get_site', + op_modifies=True, params={"name": site_name}, ) except Exception as e: @@ -603,6 +604,7 @@ def get_image_id(self, name): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_name": name}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -637,6 +639,7 @@ def get_image_name_from_id(self, image_id): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_uuid": image_id}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -672,6 +675,7 @@ def is_image_exist(self, name): image_response = self.dnac._exec( family="software_image_management_swim", function='get_software_image_details', + op_modifies=True, params={"image_name": name}, ) self.log("Received API response from 'get_software_image_details': {0}".format(str(image_response)), "DEBUG") @@ -699,6 +703,7 @@ def get_device_id(self, params): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params=params, ) self.log("Received API response from 'get_device_list': {0}".format(str(response)), "DEBUG") @@ -1291,6 +1296,7 @@ def get_device_ip_from_id(self, device_id): response = self.dnac._exec( family="devices", function='get_device_list', + op_modifies=True, params={"id": device_id} ) self.log("Received API response from 'get_device_list': {0}".format(str(response)), "DEBUG") diff --git a/plugins/modules/template_intent.py b/plugins/modules/template_intent.py index c6e3042de0..19cdc455f0 100644 --- a/plugins/modules/template_intent.py +++ b/plugins/modules/template_intent.py @@ -1896,6 +1896,7 @@ def get_template(self, config): items = self.dnac_apply['exec']( family="configuration_templates", function="get_template_details", + op_modifies=True, params={"template_id": config.get("templateId")} ) if items: @@ -1980,6 +1981,7 @@ def get_have_template(self, config, template_available): template_list = self.dnac_apply['exec']( family="configuration_templates", function="gets_the_templates_available", + op_modifies=True, params={"projectNames": config.get("projectName")}, ) have_template["isCommitPending"] = True @@ -2371,8 +2373,8 @@ def update_configuration_templates(self, config): response = self.dnac_apply['exec']( family="configuration_templates", function="update_template", - params=template_params, op_modifies=True, + params=template_params, ) template_updated = True self.log("Updating existing template '{0}'." @@ -2442,6 +2444,7 @@ def handle_export(self, config): response = self.dnac._exec( family="configuration_templates", function='export_projects', + op_modifies=True, params={"payload": export_project}, ) validation_string = "successfully exported project" @@ -2458,6 +2461,7 @@ def handle_export(self, config): response = self.dnac._exec( family="configuration_templates", function='export_templates', + op_modifies=True, params={"payload": self.export_template}, ) validation_string = "successfully exported template" @@ -2501,6 +2505,7 @@ def handle_import(self, config): response = self.dnac._exec( family="configuration_templates", function='imports_the_projects_provided', + op_modifies=True, params=_import_project, ) validation_string = "successfully imported project" @@ -2529,6 +2534,7 @@ def handle_import(self, config): response = self.dnac._exec( family="configuration_templates", function='imports_the_templates_provided', + op_modifies=True, params=import_template, ) validation_string = "successfully imported template" @@ -2592,6 +2598,7 @@ def delete_project_or_template(self, config, is_delete_project=False): response = self.dnac_apply['exec']( family="configuration_templates", function=deletion_value, + op_modifies=True, params=params_key, ) task_id = response.get("response").get("taskId") @@ -2716,6 +2723,7 @@ def verify_diff_deleted(self, config): template_list = self.dnac_apply['exec']( family="configuration_templates", function="gets_the_templates_available", + op_modifies=True, params={"projectNames": config.get("projectName")}, ) if template_list and isinstance(template_list, list): diff --git a/plugins/modules/template_workflow_manager.py b/plugins/modules/template_workflow_manager.py index 13e81da9a4..e47199ed5a 100644 --- a/plugins/modules/template_workflow_manager.py +++ b/plugins/modules/template_workflow_manager.py @@ -1331,7 +1331,11 @@ def __init__(self, module): self.supported_states = ["merged", "deleted"] self.accepted_languages = ["JINJA", "VELOCITY"] self.export_template = [] - self.result['response'].append({}) + self.result['response'] = [ + {"configurationTemplate": {"response": {}, "msg": {}}}, + {"export": {"response": {}}}, + {"import": {"response": {}}} + ] def validate_input(self): """ @@ -1917,7 +1921,7 @@ def get_template_params(self, params): self.status = "failed" return self.check_return_status() - temp_params.update({"project_name": projectName}) + temp_params.update({"projectName": projectName}) softwareType = params.get("software_type") if not softwareType: @@ -1950,13 +1954,14 @@ def get_template(self, config): items = self.dnac_apply['exec']( family="configuration_templates", function="get_template_details", + op_modifies=True, params={"template_id": config.get("templateId")} ) if items: result = items self.log("Received API response from 'get_template_details': {0}".format(items), "DEBUG") - self.result['response'] = items + self.result['response'][0].get("configurationTemplate").update({"items": items}) return result def get_have_project(self, config): @@ -2034,6 +2039,7 @@ def get_have_template(self, config, template_available): template_list = self.dnac_apply['exec']( family="configuration_templates", function="gets_the_templates_available", + op_modifies=True, params={"projectNames": config.get("projectName")}, ) have_template["isCommitPending"] = True @@ -2419,7 +2425,7 @@ def update_configuration_templates(self, configuration_templates): if is_template_found: if not self.requires_update(): # Template does not need update - self.result.update({ + self.result['response'][0].get("configurationTemplate").update({ 'response': self.have_template.get("template"), 'msg': "Template does not need update" }) @@ -2433,8 +2439,8 @@ def update_configuration_templates(self, configuration_templates): response = self.dnac_apply['exec']( family="configuration_templates", function="update_template", - params=template_params, op_modifies=True, + params=template_params, ) template_updated = True self.log("Updating existing template '{0}'." @@ -2466,12 +2472,12 @@ def update_configuration_templates(self, configuration_templates): return self task_details = self.get_task_details(task_id) self.result['changed'] = True - self.result['msg'] = task_details.get('progress') - self.result['diff'] = configuration_templates + self.result['response'][0].get("configurationTemplate")['msg'] = task_details.get('progress') + self.result['response'][0].get("configurationTemplate")['diff'] = configuration_templates self.log("Task details for 'version_template': {0}".format(task_details), "DEBUG") - self.result['response'] = task_details if task_details else response + self.result['response'][0].get("configurationTemplate")['response'] = task_details if task_details else response - if not self.result.get('msg'): + if not self.result['response'][0].get("configurationTemplate").get('msg'): self.msg = "Error while versioning the template" self.status = "failed" return self @@ -2494,16 +2500,16 @@ def handle_export(self, export): response = self.dnac._exec( family="configuration_templates", function='export_projects', + op_modifies=True, params={ "payload": export_project, - "active_validation": False, }, ) validation_string = "successfully exported project" self.check_task_response_status(response, validation_string, True).check_return_status() - self.result['response'][0].update({"exportProject": self.msg}) + self.result['response'][1].get("export").get("response").update({"exportProject": self.msg}) export_values = export.get("template") if export_values: @@ -2513,16 +2519,16 @@ def handle_export(self, export): response = self.dnac._exec( family="configuration_templates", function='export_templates', + op_modifies=True, params={ "payload": self.export_template, - "active_validation": False, }, ) validation_string = "successfully exported template" self.check_task_response_status(response, validation_string, True).check_return_status() - self.result['response'][0].update({"exportTemplate": self.msg}) + self.result['response'][1].get("export").get("response").update({"exportTemplate": self.msg}) return self @@ -2566,14 +2572,15 @@ def handle_import(self, _import): response = self.dnac._exec( family="configuration_templates", function='imports_the_projects_provided', + op_modifies=True, params=_import_project, ) validation_string = "successfully imported project" self.check_task_response_status(response, validation_string).check_return_status() - self.result['response'][0].update({"importProject": validation_string}) + self.result['response'][2].get("import").get("response").update({"importProject": validation_string}) else: self.msg = "Projects '{0}' already available.".format(payload) - self.result['response'][0].update({ + self.result['response'][2].get("import").get("response").update({ "importProject": "Projects '{0}' already available.".format(payload) }) @@ -2610,11 +2617,12 @@ def handle_import(self, _import): response = self.dnac._exec( family="configuration_templates", function='imports_the_templates_provided', + op_modifies=True, params=import_template ) validation_string = "successfully imported template" self.check_task_response_status(response, validation_string).check_return_status() - self.result['response'][0].update({"importTemplate": validation_string}) + self.result['response'][2].get("import").get("response").update({"importTemplate": validation_string}) return self @@ -2679,19 +2687,20 @@ def delete_project_or_template(self, config, is_delete_project=False): response = self.dnac_apply['exec']( family="configuration_templates", function=deletion_value, + op_modifies=True, params=params_key, ) task_id = response.get("response").get("taskId") if task_id: task_details = self.get_task_details(task_id) self.result['changed'] = True - self.result['msg'] = task_details.get('progress') - self.result['diff'] = config.get("configuration_templates") + self.result['response'][0].get("configurationTemplate")['msg'] = task_details.get('progress') + self.result['response'][0].get("configurationTemplate")['diff'] = config.get("configuration_templates") self.log("Task details for '{0}': {1}".format(deletion_value, task_details), "DEBUG") - self.result['response'] = task_details if task_details else response - if not self.result['msg']: - self.result['msg'] = "Error while deleting {name} : " + self.result['response'][0].get("configurationTemplate")['response'] = task_details if task_details else response + if not self.result['response'][0].get("configurationTemplate")['msg']: + self.result['response'][0].get("configurationTemplate")['msg'] = "Error while deleting {name} : " self.status = "failed" return self @@ -2774,11 +2783,11 @@ def verify_diff_merged(self, config): "softwareVariant", "templateContent"] for item in template_params: if self.have_template.get("template").get(item) != self.want.get("template_params").get(item): - self.msg = " Configuration Template config is not applied to the Cisco Catalyst Center." + self.msg = "Configuration Template config is not applied to the Cisco Catalyst Center." self.status = "failed" return self self.log("Successfully validated the Template in the Catalyst Center.", "INFO") - self.result.get("response").update({"Validation": "Success"}) + self.result['response'][0].get("configurationTemplate").get("response").update({"Validation": "Success"}) self.msg = "Successfully validated the Configuration Templates." self.status = "success" @@ -2803,6 +2812,7 @@ def verify_diff_deleted(self, config): template_list = self.dnac_apply['exec']( family="configuration_templates", function="gets_the_templates_available", + op_modifies=True, params={"projectNames": config.get("projectName")}, ) if template_list and isinstance(template_list, list): @@ -2816,7 +2826,7 @@ def verify_diff_deleted(self, config): return self self.log("Successfully validated absence of template in the Catalyst Center.", "INFO") - self.result.get("response").update({"Validation": "Success"}) + self.result['response'][0].get("configurationTemplate").get("response").update({"Validation": "Success"}) self.msg = "Successfully validated the absence of Template in the Cisco Catalyst Center." self.status = "success" @@ -2841,24 +2851,25 @@ def reset_values(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': {'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}, - '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']} - } + 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': {'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}, + '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_template = Template(module)