From 919589f62b5aaee403de6057dda9b5a84b75a92d Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Thu, 14 Nov 2024 20:32:41 +0530 Subject: [PATCH 01/38] bug fix: Create a Global Pool with ip_address_space is 'ipv4' or 'IPV4' --- plugins/modules/network_settings_workflow_manager.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index b4bb355bbd..0754476e2c 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -2197,7 +2197,13 @@ def get_want_global_pool(self, global_ippool): "gateway": pool_details.get("gateway"), "type": pool_details.get("pool_type"), } - ip_address_space = pool_details.get("ip_address_space") + + ip_address_space = pool_details.get("ip_address_space", "").upper() + if ip_address_space == "IPV4": + ip_address_space = "IPv4" + elif ip_address_space == "IPV6": + ip_address_space = "IPv6" + if not ip_address_space: self.msg = "Missing required parameter 'ip_address_space' under global_pool_details." self.status = "failed" @@ -3077,6 +3083,7 @@ def update_global_pool(self, global_pool): "ippool": copy.deepcopy(create_global_pool) } } + try: response = self.dnac._exec( family="network_settings", From efca0e690d64d3e4a06cddb77b76a7b125ea846e Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Thu, 14 Nov 2024 20:36:01 +0530 Subject: [PATCH 02/38] bug fix: Create a Global Pool with ip_address_space is 'ipv4' or 'IPV4' --- plugins/modules/network_settings_workflow_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 0754476e2c..62c5fb146d 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3083,7 +3083,6 @@ def update_global_pool(self, global_pool): "ippool": copy.deepcopy(create_global_pool) } } - try: response = self.dnac._exec( family="network_settings", From 5c8a401823324c8aa1eb4ab5ce219d6ed37c35b7 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Thu, 14 Nov 2024 21:13:37 +0530 Subject: [PATCH 03/38] bug fix: Create a Global Pool with ip_address_space is 'ipv4' or 'IPV4' --- plugins/modules/network_settings_workflow_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 62c5fb146d..851afc2e10 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -2197,7 +2197,6 @@ def get_want_global_pool(self, global_ippool): "gateway": pool_details.get("gateway"), "type": pool_details.get("pool_type"), } - ip_address_space = pool_details.get("ip_address_space", "").upper() if ip_address_space == "IPV4": ip_address_space = "IPv4" From a1a10b513900c0bf85d87d03ed2472d5ccbccb46 Mon Sep 17 00:00:00 2001 From: skesali Date: Thu, 14 Nov 2024 23:55:56 +0530 Subject: [PATCH 04/38] Modifications for deletion changes --- plugins/modules/site_workflow_manager.py | 72 +++++++++++++++++++----- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 4989f99e2d..afa6b8bd77 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -460,7 +460,7 @@ def __init__(self, module): super().__init__(module) self.supported_states = ["merged", "deleted"] self.created_site_list, self.updated_site_list, self.update_not_needed_sites = [], [], [] - self.deleted_site_list, self.site_absent_list = [], [] + self.deleted_site_list, self.site_absent_list, self.old_version_deleted_list = [], [], [] self.keymap = {} self.handle_config = {} @@ -666,7 +666,6 @@ def site_exists(self, site_name_hierarchy=None): else: site_name_hierarchy = self.want.get("site_name_hierarchy") - self.log("CHECK {0}".format(site_name_hierarchy), "INFO") response = self.get_site_v1(site_name_hierarchy) if not response: @@ -1625,6 +1624,45 @@ def change_payload_data(self, config): self.msg = "Unable to process the payload data : {}".format(str(e)) self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + def is_site_exist(self, site_name): + """ + Checks if a site exists in Cisco Catalyst Center by retrieving site information based on the provided site name. + + Args: + site_name (str): The name or hierarchy of the site to be retrieved. + + Returns: + tuple (bool, str or None): A tuple containing: + 1. A boolean indicating whether the site exists (True if found, False otherwise). + 2. The site ID (str) if the site exists, or None if the site does not exist or an error occurs. + + Details: + - Calls `get_site()` to retrieve site details from Cisco Catalyst Center. + - If the site exists, its ID is extracted from the response, and the function returns (True, site ID). + - If the site does not exist, it returns (False, None) and logs an informational message. + - Logs detailed debug information about the retrieval attempt and any errors that occur. + + """ + site_exists = None + try: + response = self.get_site(site_name) + + if response is None: + self.log("No site details retrieved for site name: {0}".format(site_name), "DEBUG") + return site_exists + + self.log("Site details retrieved for site '{0}'': {1}".format(site_name, str(response)), "DEBUG") + site_exists = True + + except Exception as e: + self.log( + "An exception occurred while retrieving Site details for Site '{0}' " + "does not exist in the Cisco Catalyst Center. Error: {1}".format(site_name, e), + "INFO" + ) + + return site_exists + def get_diff_merged(self, config): """ Update/Create site information in Cisco Catalyst Center with fields @@ -1671,6 +1709,17 @@ def get_diff_merged(self, config): self.log("Added to floor: {}".format(payload_data), "DEBUG") for each_type in ("area", "building", "floor"): if self.handle_config[each_type]: + for create_config in self.handle_config[each_type]: + parent_name = create_config.get(self.keymap.get("parent_name_hierarchy")) + self.log(self.pprint(create_config)) + + site_exists = self.is_site_exist(parent_name) + if not site_exists: + self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) + self.log(self.msg, "DEBUG") + self.site_absent_list.append(str(parent_name) + " does not exist ") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + response = self.creating_bulk_site(self.handle_config[each_type]) self.log("Response from creating_bulk_site for {}: {}".format(each_type, response), "DEBUG") @@ -2042,17 +2091,13 @@ def get_diff_deleted(self, config): if self.compare_dnac_versions(self.get_ccc_version(), "2.3.5.3") <= 0: site_exists = self.have.get("site_exists") site_name_hierarchy = self.want.get("site_name_hierarchy") - + site_id = self.have.get("site_id") if not site_exists: - self.status = "success" - self.site_absent_list.append(site_name_hierarchy) - self.log( - "Unable to delete site '{0}' as it's not found in Cisco Catalyst Center".format(self.want.get("site_name_hierarchy")), "INFO") + if site_name_hierarchy not in self.deleted_site_list: + self.site_absent_list.append(site_name_hierarchy) + self.log("Unable to delete site '{0}' as it's not found in Cisco Catalyst Center".format(site_name_hierarchy), "INFO") return self - site_id = self.have.get("site_id") - site_name_hierarchy = self.want.get("site_name_hierarchy") api_response, response = self.get_device_ids_from_site(site_name_hierarchy, site_id) - self.log( "Received API response from 'get_membership': {0}".format(str(api_response)), "DEBUG") @@ -2066,9 +2111,11 @@ def get_diff_deleted(self, config): sorted_site_resp = sorted( site_response, key=lambda x: x.get("groupHierarchy"), reverse=True) + self.log(sorted_site_resp) for item in sorted_site_resp: - self.delete_single_site(item['id'], item['name']) + self.log(item) + self.delete_single_site(item['id'], item['groupNameHierarchy']) self.delete_single_site(site_id, site_name_hierarchy) self.log( @@ -2293,8 +2340,7 @@ def update_site_messages(self): if self.update_not_needed_sites: msg = """Site(s) '{0}' created successfully as well as Site(s) '{1}' updated successully and the some site(s) '{2}' needs no update in Cisco Catalyst Center""" - self.msg = msg.format(str(self.created_site_list), str( - self.updated_site_list), str(self.update_not_needed_sites)) + self.msg = msg.format(str(self.created_site_list), str(self.updated_site_list), str(self.update_not_needed_sites)) else: self.msg = """Site(s) '{0}' created successfully in Cisco Catalyst Center as well as Site(s) '{1}' updated successully in Cisco Catalyst Center""".format(str(self.created_site_list), str(self.updated_site_list)) From c287ed0732a165a341cb53a9aa200dd6a68a8579 Mon Sep 17 00:00:00 2001 From: skesali Date: Fri, 15 Nov 2024 00:02:21 +0530 Subject: [PATCH 05/38] Modifications for deletion changes --- plugins/modules/site_workflow_manager.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index afa6b8bd77..6fc508a2e2 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -460,7 +460,7 @@ def __init__(self, module): super().__init__(module) self.supported_states = ["merged", "deleted"] self.created_site_list, self.updated_site_list, self.update_not_needed_sites = [], [], [] - self.deleted_site_list, self.site_absent_list, self.old_version_deleted_list = [], [], [] + self.deleted_site_list, self.site_absent_list = [], [] self.keymap = {} self.handle_config = {} @@ -2111,10 +2111,8 @@ def get_diff_deleted(self, config): sorted_site_resp = sorted( site_response, key=lambda x: x.get("groupHierarchy"), reverse=True) - self.log(sorted_site_resp) for item in sorted_site_resp: - self.log(item) self.delete_single_site(item['id'], item['groupNameHierarchy']) self.delete_single_site(site_id, site_name_hierarchy) From 3d2efeddd1ffc18beeaeabd472faa64744f4d261 Mon Sep 17 00:00:00 2001 From: skesali Date: Fri, 15 Nov 2024 16:45:26 +0530 Subject: [PATCH 06/38] Modifications for update and deletion bugs --- plugins/modules/site_workflow_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 6fc508a2e2..3337ec08a2 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -826,7 +826,7 @@ def get_site_name_hierarchy(self, site): self.set_operation_result("failed", False, self.msg, "ERROR") return None - site_name_hierarchy = '/'.join([parent_name, name]) + site_name_hierarchy = '/'.join([str(parent_name), str(name)]) self.log("Constructed site name: {}".format(site_name_hierarchy), "INFO") return site_name_hierarchy @@ -1792,9 +1792,9 @@ def get_diff_merged(self, config): if response and isinstance(response, dict): taskid = response["response"]["taskId"] - task_details = self.get_task_details(taskid) while True: + task_details = self.get_task_details(taskid) if site_type != "floor": if task_details.get("progress") == "Group is updated successfully": task_detail_list.append(task_details) @@ -2162,8 +2162,8 @@ def get_diff_deleted(self, config): task_id = response.get("response", {}).get("taskId") if task_id: - task_details = self.get_task_details(task_id) while True: + task_details = self.get_task_details(task_id) if site_type == "area": if task_details.get("progress") == "Group is deleted successfully": self.msg = "Area '{0}' deleted successfully.".format(site_name_hierarchy) From d919260781740aeec9e9985f640ce820a5c2d957 Mon Sep 17 00:00:00 2001 From: skesali Date: Mon, 18 Nov 2024 12:45:24 +0530 Subject: [PATCH 07/38] Modifications for update and deletion bugs along with comments --- plugins/modules/site_workflow_manager.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 3337ec08a2..1675af9fd0 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -1632,18 +1632,15 @@ def is_site_exist(self, site_name): site_name (str): The name or hierarchy of the site to be retrieved. Returns: - tuple (bool, str or None): A tuple containing: - 1. A boolean indicating whether the site exists (True if found, False otherwise). - 2. The site ID (str) if the site exists, or None if the site does not exist or an error occurs. + A boolean indicating whether the site exists (True if found, False otherwise). Details: - Calls `get_site()` to retrieve site details from Cisco Catalyst Center. - - If the site exists, its ID is extracted from the response, and the function returns (True, site ID). - - If the site does not exist, it returns (False, None) and logs an informational message. + - If the site does not exist, it returns (False). - Logs detailed debug information about the retrieval attempt and any errors that occur. """ - site_exists = None + site_exists = False try: response = self.get_site(site_name) @@ -1651,7 +1648,7 @@ def is_site_exist(self, site_name): self.log("No site details retrieved for site name: {0}".format(site_name), "DEBUG") return site_exists - self.log("Site details retrieved for site '{0}'': {1}".format(site_name, str(response)), "DEBUG") + self.log("Site details retrieved for site {0}: {1}".format(site_name, str(response)), "DEBUG") site_exists = True except Exception as e: @@ -1709,9 +1706,15 @@ def get_diff_merged(self, config): self.log("Added to floor: {}".format(payload_data), "DEBUG") for each_type in ("area", "building", "floor"): if self.handle_config[each_type]: + self.log("Processing configurations for '{0}'.".format(each_type), "DEBUG") for create_config in self.handle_config[each_type]: + self.log("Handling configuration: {0}".format(self.pprint(create_config)), "DEBUG") parent_name = create_config.get(self.keymap.get("parent_name_hierarchy")) - self.log(self.pprint(create_config)) + if not parent_name: + self.msg = "No parent name found in configuration for '{0}'.".format(each_type) + self.log(self.msg, "DEBUG") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + self.log("Checking if parent site '{0}' exists in the hierarchy.".format(parent_name), "DEBUG") site_exists = self.is_site_exist(parent_name) if not site_exists: @@ -2095,7 +2098,10 @@ def get_diff_deleted(self, config): if not site_exists: if site_name_hierarchy not in self.deleted_site_list: self.site_absent_list.append(site_name_hierarchy) - self.log("Unable to delete site '{0}' as it's not found in Cisco Catalyst Center".format(site_name_hierarchy), "INFO") + self.log( + "Failed to delete site '{0}'. Reason: The site was not found in the Cisco Catalyst Center.".format(site_name_hierarchy), + "DEBUG" + ) return self api_response, response = self.get_device_ids_from_site(site_name_hierarchy, site_id) self.log( From 0fcbbecc55d436104afb3027312bdfd8fe787a52 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 18:19:41 +0530 Subject: [PATCH 08/38] sanity fix --- .../network_settings_workflow_manager.py | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 851afc2e10..9a7cde0938 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,7 +3,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 operations on global pool, reserve pool and network in Cisco Catalyst Center.""" +"""--- +Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center.""" from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -14,10 +15,10 @@ module: network_settings_workflow_manager short_description: Resource module for IP Address pools and network functions description: -- Manage operations on Global Pool, Reserve Pool, Network resources. -- API to create/update/delete global pool. -- API to reserve/update/delete an ip subpool from the global pool. -- API to update network settings for DHCP, Syslog, SNMP, NTP, Network AAA, Client and Endpoint AAA, + - Manage operations on Global Pool, Reserve Pool, Network resources. + - API to create/update/delete global pool. + - API to reserve/update/delete an ip subpool from the global pool. + - API to update network settings for DHCP, Syslog, SNMP, NTP, Network AAA, Client and Endpoint AAA, and/or DNS center server settings. version_added: '6.6.0' extends_documentation_fragment: @@ -33,11 +34,11 @@ state: description: The state of Cisco Catalyst Center after module completion. type: str - choices: [ merged, deleted ] + choices: [merged, deleted] default: merged config: description: - - List of details of global pool, reserved pool, network being managed. + - List of details of global pool, reserved pool, network being managed. type: list elements: dict required: true @@ -57,10 +58,10 @@ suboptions: name: description: - - Specifies the name assigned to the Global IP Pool. - - Required for the operations in the Global IP Pool. - - Length should be less than or equal to 100. - - Only letters, numbers and -_./ characters are allowed. + - Specifies the name assigned to the Global IP Pool. + - Required for the operations in the Global IP Pool. + - Length should be less than or equal to 100. + - Only letters, numbers and -_./ characters are allowed. type: str pool_type: description: > @@ -111,10 +112,10 @@ type: str name: description: - - Name of the reserve IP subpool. - - Required for the operations in the Reserve IP Pool. - - Length should be less than or equal to 100. - - Only letters, numbers and -_./ characters are allowed. + - Name of the reserve IP subpool. + - Required for the operations in the Reserve IP Pool. + - Length should be less than or equal to 100. + - Only letters, numbers and -_./ characters are allowed. type: str pool_type: description: Type of the reserve ip sub pool. @@ -139,13 +140,13 @@ type: bool ipv4_global_pool: description: - - IP v4 Global pool address with cidr, example 175.175.0.0/16. - - If both 'ipv6_global_pool' and 'ipv4_global_pool_name' are provided, the 'ipv4_global_pool' will be given priority. + - IP v4 Global pool address with cidr, example 175.175.0.0/16. + - If both 'ipv6_global_pool' and 'ipv4_global_pool_name' are provided, the 'ipv4_global_pool' will be given priority. type: str ipv4_global_pool_name: description: - - Specifies the name to be associated with the IPv4 Global IP Pool. - - If both 'ipv4_global_pool' and 'ipv4_global_pool_name' are provided, the 'ipv4_global_pool' will be given priority. + - Specifies the name to be associated with the IPv4 Global IP Pool. + - If both 'ipv4_global_pool' and 'ipv4_global_pool_name' are provided, the 'ipv4_global_pool' will be given priority. type: str version_added: 6.14.0 ipv4_subnet: @@ -191,14 +192,14 @@ type: str ipv6_global_pool: description: - - The ipv6_global_pool is a required when the ipv6_address_space is set to true. - - It specifies the global IPv6 address pool using CIDR notation, such as "2001:db8:85a3::/64". - - In cases where both ipv6_global_pool and ipv6_global_pool_name are specified, ipv6_global_pool will take precedence. + - The ipv6_global_pool is a required when the ipv6_address_space is set to true. + - It specifies the global IPv6 address pool using CIDR notation, such as "2001:db8:85a3::/64". + - In cases where both ipv6_global_pool and ipv6_global_pool_name are specified, ipv6_global_pool will take precedence. type: str ipv6_global_pool_name: description: - - Specifies the name assigned to the Ip v6 Global IP Pool. - - If both 'ipv6_global_pool' and 'ipv6_global_pool_name' are provided, the 'ipv6_global_pool' will be given priority. + - Specifies the name assigned to the Ip v6 Global IP Pool. + - If both 'ipv6_global_pool' and 'ipv6_global_pool_name' are provided, the 'ipv6_global_pool' will be given priority. type: str version_added: 6.14.0 ipv6_subnet: From a1cc939e1341ba648c8d692e5a44a6944253db5c Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 21:55:27 +0530 Subject: [PATCH 09/38] sanity fix --- .../network_settings_workflow_manager.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 9a7cde0938..6955510ee7 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,8 +3,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 operations on global pool, reserve pool and network in Cisco Catalyst Center.""" + +"""Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center.""" from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -22,31 +22,31 @@ and/or DNS center server settings. version_added: '6.6.0' extends_documentation_fragment: - - cisco.dnac.workflow_manager_params + - cisco.dnac.workflow_manager_params author: Muthu Rakesh (@MUTHU-RAKESH-27) Madhan Sankaranarayanan (@madhansansel) Megha Kandari (@kandarimegha) options: - config_verify: - description: Set to True to verify the Cisco Catalyst Center 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: - - List of details of global pool, reserved pool, network being managed. - type: list - elements: dict - required: true - suboptions: - global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + config_verify: + description: Set to True to verify the Cisco Catalyst Center 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: + - List of details of global pool, reserved pool, network being managed. + type: list + elements: dict + required: true + suboptions: + global_pool_details: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict @@ -101,7 +101,7 @@ exclusively when you need to update the global pool's name. type: str - reserve_pool_details: + reserve_pool_details: description: Reserved IP subpool details from the global pool. type: dict suboptions: @@ -227,7 +227,7 @@ Allows devices on IPv6 networks to self-configure their IP addresses autonomously, eliminating the need for manual setup. type: bool - network_management_details: + network_management_details: description: Set default network settings for the site type: list elements: dict From 0c5163a970013117815cb2d829028bd3ecdf073a Mon Sep 17 00:00:00 2001 From: skesali Date: Mon, 18 Nov 2024 22:06:49 +0530 Subject: [PATCH 10/38] Modifications for update and deletion bugs along with comments --- plugins/modules/site_workflow_manager.py | 30 +++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 1675af9fd0..f95b4c4baf 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -15,11 +15,11 @@ module: site_workflow_manager short_description: Resource module for Site operations description: -- Manage operation create, bulk create, update and delete of the resource Sites. -- Creates site with area/building/floor with specified hierarchy. -- Create multiple sites (area, building, or floor) with specified hierarchies in bulk. -- Updates site with area/building/floor with specified hierarchy. -- Deletes site with area/building/floor with specified hierarchy. + - Manage operation create, bulk create, update and delete of the resource Sites. + - Creates site with area/building/floor with specified hierarchy. + - Create multiple sites (area, building, or floor) with specified hierarchies in bulk. + - Updates site with area/building/floor with specified hierarchy. + - Deletes site with area/building/floor with specified hierarchy. version_added: '6.6.0' extends_documentation_fragment: - cisco.dnac.workflow_manager_params @@ -36,13 +36,13 @@ state: description: The state of Catalyst Center after module completion. type: str - choices: [ merged, deleted ] + choices: [merged, deleted] default: merged config: description: It represents a list of details for creating/managing/deleting sites, including areas, buildings, and floors. type: list elements: dict - required: True + required: true suboptions: site_type: description: Type of site to create/update/delete (eg area, building, floor). @@ -137,8 +137,8 @@ 2.3.7.6 Catalyst version onwards requirements: -- dnacentersdk == 2.4.5 -- python >= 3.9 + - dnacentersdk == 2.4.5 + - python >= 3.9 notes: - SDK Method used are sites.Sites.create_site, @@ -1897,6 +1897,18 @@ def get_diff_merged(self, config): self.log("The site '{0}' is not categorized as a building; no need to filter 'None' values.". format(name), "INFO") + site_type = site_params['type'] + parent_name = site_params.get('site').get(site_type).get('parentName') + try: + response = self.get_site_v1(parent_name) + if not response: + self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) + self.log(self.msg, "DEBUG") + self.site_absent_list.append(str(parent_name) + " does not exist ") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + except Exception as e: + self.log("No response received from 'get_site_v1' API for site: {0}".format(parent_name + str(e)), "ERROR") + response = self.dnac._exec( family="sites", function='create_site', From 1c935f64dbfcfd636f3a3ba4460cdf9dbfd52469 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 22:16:00 +0530 Subject: [PATCH 11/38] sanity fix --- .../network_settings_workflow_manager.py | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 6955510ee7..3ef3d261ef 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -28,25 +28,25 @@ Megha Kandari (@kandarimegha) options: config_verify: - description: Set to True to verify the Cisco Catalyst Center after applying the playbook config. - type: bool - default: False + description: Set to True to verify the Cisco Catalyst Center 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 + description: The state of Cisco Catalyst Center after module completion. + type: str + choices: [merged, deleted] + default: merged config: - description: + description: - List of details of global pool, reserved pool, network being managed. - type: list - elements: dict - required: true - suboptions: - global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + type: list + elements: dict + required: true + suboptions: + global_pool_details: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict @@ -101,7 +101,7 @@ exclusively when you need to update the global pool's name. type: str - reserve_pool_details: + reserve_pool_details: description: Reserved IP subpool details from the global pool. type: dict suboptions: @@ -227,7 +227,7 @@ Allows devices on IPv6 networks to self-configure their IP addresses autonomously, eliminating the need for manual setup. type: bool - network_management_details: + network_management_details: description: Set default network settings for the site type: list elements: dict From 16e1c05c2f363252392effe214bd32663aac3471 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 22:22:58 +0530 Subject: [PATCH 12/38] sanity fix --- .../modules/network_settings_workflow_manager.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 3ef3d261ef..07d0517d1a 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -38,15 +38,15 @@ default: merged config: description: - - List of details of global pool, reserved pool, network being managed. + - List of details of global pool, reserved pool, network being managed. type: list elements: dict required: true suboptions: - global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + global_pool_details: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict @@ -101,7 +101,7 @@ exclusively when you need to update the global pool's name. type: str - reserve_pool_details: + reserve_pool_details: description: Reserved IP subpool details from the global pool. type: dict suboptions: @@ -227,7 +227,7 @@ Allows devices on IPv6 networks to self-configure their IP addresses autonomously, eliminating the need for manual setup. type: bool - network_management_details: + network_management_details: description: Set default network settings for the site type: list elements: dict From 305add024689fceacd56ee7eaca3dabd0ae5ec01 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 22:28:21 +0530 Subject: [PATCH 13/38] sanity fix --- plugins/modules/network_settings_workflow_manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 07d0517d1a..5d0b8a4a7a 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -44,9 +44,9 @@ required: true suboptions: global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict From 7fd190b0009f4a7893fe3087dbcd018f93bff8e3 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 23:28:16 +0530 Subject: [PATCH 14/38] sanity fix --- .../network_settings_workflow_manager.py | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 5d0b8a4a7a..accb92eacf 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -47,59 +47,59 @@ description: Manages IPv4 and IPv6 IP pools in the global level. type: dict suboptions: - settings: - description: Global Pool's settings. - type: dict - suboptions: - ip_pool: - description: Contains a list of global IP pool configurations. - elements: dict - type: list - suboptions: - name: - description: - - Specifies the name assigned to the Global IP Pool. - - Required for the operations in the Global IP Pool. - - Length should be less than or equal to 100. - - Only letters, numbers and -_./ characters are allowed. - type: str - pool_type: - description: > - Includes both the Generic Ip Pool and Tunnel Ip Pool. - Generic - Used for general purpose within the network such as device - management or communication between the network devices. - Tunnel - Designated for the tunnel interfaces to encapsulate packets - within the network protocol. It is used in VPN connections, - GRE tunnels, or other types of overlay networks. - default: Generic - choices: [Generic, Tunnel] - type: str - ip_address_space: - description: IP address space either IPv4 or IPv6. - type: str - cidr: - description: > - Defines the IP pool's Classless Inter-Domain Routing block, - enabling systematic IP address distribution within a network. - type: str - gateway: - description: Serves as an entry or exit point for data traffic between networks. - type: str - dhcp_server_ips: - description: > - The DHCP server IPs responsible for automatically assigning IP addresses - and network configuration parameters to devices on a local network. - elements: str - type: list - dns_server_ips: - description: Responsible for translating domain names into corresponding IP addresses. - elements: str - type: list - prev_name: - description: > - The former identifier for the global pool. It should be used - exclusively when you need to update the global pool's name. - type: str + settings: + description: Global Pool's settings. + type: dict + suboptions: + ip_pool: + description: Contains a list of global IP pool configurations. + elements: dict + type: list + suboptions: + name: + description: + - Specifies the name assigned to the Global IP Pool. + - Required for the operations in the Global IP Pool. + - Length should be less than or equal to 100. + - Only letters, numbers and -_./ characters are allowed. + type: str + pool_type: + description: > + Includes both the Generic Ip Pool and Tunnel Ip Pool. + Generic - Used for general purpose within the network such as device + management or communication between the network devices. + Tunnel - Designated for the tunnel interfaces to encapsulate packets + within the network protocol. It is used in VPN connections, + GRE tunnels, or other types of overlay networks. + default: Generic + choices: [Generic, Tunnel] + type: str + ip_address_space: + description: IP address space either IPv4 or IPv6. + type: str + cidr: + description: > + Defines the IP pool's Classless Inter-Domain Routing block, + enabling systematic IP address distribution within a network. + type: str + gateway: + description: Serves as an entry or exit point for data traffic between networks. + type: str + dhcp_server_ips: + description: > + The DHCP server IPs responsible for automatically assigning IP addresses + and network configuration parameters to devices on a local network. + elements: str + type: list + dns_server_ips: + description: Responsible for translating domain names into corresponding IP addresses. + elements: str + type: list + prev_name: + description: > + The former identifier for the global pool. It should be used + exclusively when you need to update the global pool's name. + type: str reserve_pool_details: description: Reserved IP subpool details from the global pool. From ddc9103750971a734e2f6d8f97346a1b69545cd1 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 23:46:27 +0530 Subject: [PATCH 15/38] sanity check --- .../network_settings_workflow_manager.py | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index accb92eacf..07d0517d1a 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -44,62 +44,62 @@ required: true suboptions: global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: - settings: - description: Global Pool's settings. - type: dict + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict suboptions: - ip_pool: - description: Contains a list of global IP pool configurations. - elements: dict - type: list - suboptions: - name: - description: - - Specifies the name assigned to the Global IP Pool. - - Required for the operations in the Global IP Pool. - - Length should be less than or equal to 100. - - Only letters, numbers and -_./ characters are allowed. - type: str - pool_type: - description: > - Includes both the Generic Ip Pool and Tunnel Ip Pool. - Generic - Used for general purpose within the network such as device - management or communication between the network devices. - Tunnel - Designated for the tunnel interfaces to encapsulate packets - within the network protocol. It is used in VPN connections, - GRE tunnels, or other types of overlay networks. - default: Generic - choices: [Generic, Tunnel] - type: str - ip_address_space: - description: IP address space either IPv4 or IPv6. - type: str - cidr: - description: > - Defines the IP pool's Classless Inter-Domain Routing block, - enabling systematic IP address distribution within a network. - type: str - gateway: - description: Serves as an entry or exit point for data traffic between networks. - type: str - dhcp_server_ips: - description: > - The DHCP server IPs responsible for automatically assigning IP addresses - and network configuration parameters to devices on a local network. - elements: str - type: list - dns_server_ips: - description: Responsible for translating domain names into corresponding IP addresses. - elements: str - type: list - prev_name: - description: > - The former identifier for the global pool. It should be used - exclusively when you need to update the global pool's name. - type: str + settings: + description: Global Pool's settings. + type: dict + suboptions: + ip_pool: + description: Contains a list of global IP pool configurations. + elements: dict + type: list + suboptions: + name: + description: + - Specifies the name assigned to the Global IP Pool. + - Required for the operations in the Global IP Pool. + - Length should be less than or equal to 100. + - Only letters, numbers and -_./ characters are allowed. + type: str + pool_type: + description: > + Includes both the Generic Ip Pool and Tunnel Ip Pool. + Generic - Used for general purpose within the network such as device + management or communication between the network devices. + Tunnel - Designated for the tunnel interfaces to encapsulate packets + within the network protocol. It is used in VPN connections, + GRE tunnels, or other types of overlay networks. + default: Generic + choices: [Generic, Tunnel] + type: str + ip_address_space: + description: IP address space either IPv4 or IPv6. + type: str + cidr: + description: > + Defines the IP pool's Classless Inter-Domain Routing block, + enabling systematic IP address distribution within a network. + type: str + gateway: + description: Serves as an entry or exit point for data traffic between networks. + type: str + dhcp_server_ips: + description: > + The DHCP server IPs responsible for automatically assigning IP addresses + and network configuration parameters to devices on a local network. + elements: str + type: list + dns_server_ips: + description: Responsible for translating domain names into corresponding IP addresses. + elements: str + type: list + prev_name: + description: > + The former identifier for the global pool. It should be used + exclusively when you need to update the global pool's name. + type: str reserve_pool_details: description: Reserved IP subpool details from the global pool. From 63a2be8102b969ef0143ddb133d56bfb1da1c1fd Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Mon, 18 Nov 2024 23:57:53 +0530 Subject: [PATCH 16/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 07d0517d1a..62ec44ffdb 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,8 +3,10 @@ # 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 operations on global pool, reserve pool and network in Cisco Catalyst Center.""" +""" +--- +Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. +""" from __future__ import absolute_import, division, print_function __metaclass__ = type From 6911b67a929665f86706aa19b8cdc6daa491a610 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 00:07:49 +0530 Subject: [PATCH 17/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 62ec44ffdb..2c00a94009 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -4,7 +4,6 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) """ ---- Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. """ from __future__ import absolute_import, division, print_function From 9c71afa1c3afe140df9ace2ec358fb54f7b6920c Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 00:17:04 +0530 Subject: [PATCH 18/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 2c00a94009..c5b91defd3 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -4,7 +4,8 @@ # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) """ -Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. +--- +Description: Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. """ from __future__ import absolute_import, division, print_function @@ -45,9 +46,9 @@ required: true suboptions: global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict From e6259784a7844eaf6c7f20c159118545db16d975 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 00:25:32 +0530 Subject: [PATCH 19/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index c5b91defd3..5afdce1726 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,7 +3,7 @@ # Copyright (c) 2024, Cisco Systems # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -""" +r""" --- Description: Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. """ From 0e64e3b982ee5dd9ea96ce09f0fee32780e4d605 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 00:33:16 +0530 Subject: [PATCH 20/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 5afdce1726..dcbda26f27 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,9 +3,9 @@ # Copyright (c) 2024, Cisco Systems # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -r""" +DESCRIPTION = r""" --- -Description: Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. +info: Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. """ from __future__ import absolute_import, division, print_function @@ -13,7 +13,6 @@ __author__ = ['Muthu Rakesh, Madhan Sankaranarayanan, Megha Kandari'] DOCUMENTATION = r""" ---- module: network_settings_workflow_manager short_description: Resource module for IP Address pools and network functions description: From 845ee196fb781e34d9e24cbbb47c0c0abb22d889 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Mon, 18 Nov 2024 14:04:57 -0500 Subject: [PATCH 21/38] converted yaml syntax errors to warning --- .yamllint.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.yamllint.yml b/.yamllint.yml index e31652694c..51f48f7668 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -3,18 +3,27 @@ extends: default ignore: - tests - changelogs/changelog.yaml -# - playbooks + # - playbooks + # - plugins + level: warning rules: + syntax-error: + level: warning + line-length: max: 160 level: warning + indentation: level: warning + trailing-spaces: level: warning + new-line-at-end-of-file: level: warning + key-duplicates: level: warning From 5f78eeb62c1a07072e26ee1f6b45e3a3b3f91f47 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 00:36:04 +0530 Subject: [PATCH 22/38] sanity check --- plugins/modules/network_settings_workflow_manager.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index dcbda26f27..812daf1cb3 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,16 +3,14 @@ # Copyright (c) 2024, Cisco Systems # GNU General Public License v3.0+ (see LICENSE or https://www.gnu.org/licenses/gpl-3.0.txt) -DESCRIPTION = r""" ---- -info: Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center. -""" +"""Ansible module to perform operations on global pool, reserve pool and network in Cisco Catalyst Center.""" from __future__ import absolute_import, division, print_function __metaclass__ = type __author__ = ['Muthu Rakesh, Madhan Sankaranarayanan, Megha Kandari'] DOCUMENTATION = r""" +--- module: network_settings_workflow_manager short_description: Resource module for IP Address pools and network functions description: From 47aa32aac111f2ebb38ae16d59c23d2dd99b1fe9 Mon Sep 17 00:00:00 2001 From: skesali Date: Tue, 19 Nov 2024 08:28:56 +0530 Subject: [PATCH 23/38] Modifications for update and deletion bugs along with comments --- plugins/modules/site_workflow_manager.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index f95b4c4baf..91d3d94b40 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -659,7 +659,7 @@ def site_exists(self, site_name_hierarchy=None): for site in sites: if isinstance(site, dict): - self.log("No site information found for name: {0}".format(self.pprint(site)), "INFO") + self.log("No site information found for name: {0}".format(site), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True @@ -1115,7 +1115,7 @@ def get_have(self, config): for site in sites: if isinstance(site, dict): - self.log("site information found: {0}".format(self.pprint(site)), "INFO") + self.log("site information found: {0}".format(site), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True @@ -1137,8 +1137,8 @@ def get_have(self, config): self.have = self.handle_config["have"] self.log("All site information collected from bulk operation(create_config): {0}". - format(self.pprint(self.handle_config["create_site"])), "DEBUG") - self.log("All site information collected (have): {0}".format(self.pprint(self.have)), "DEBUG") + format(self.handle_config["create_site"]), "DEBUG") + self.log("All site information collected (have): {0}".format(self.have), "DEBUG") else: site_exists, current_site = self.site_exists() @@ -1200,7 +1200,7 @@ def get_want(self, config): want_list.append(want) self.want = want_list - self.log("Desired State (want): {0}".format(self.pprint(self.want)), "INFO") + self.log("Desired State (want): {0}".format(self.want), "INFO") return self except Exception as e: @@ -1488,12 +1488,12 @@ def update_building(self, site_params): parent_name = site_params.get("site", {}).get("building", {}).get("parentName") parent_id = self.get_parent_id(parent_name) site_params['site']['building']['parentId'] = parent_id - self.log("Updated site_params with parent_id: {0}".format(self.pprint(site_params)), "INFO") + self.log("Updated site_params with parent_id: {0}".format(site_params), "INFO") building_param = site_params.get('site', {}).get('building') site_id = site_params.get("site_id") building_param['id'] = site_id - self.log("Before updating the building params:{0}".format(self.pprint(building_param)), "INFO") + self.log("Before updating the building params:{0}".format(building_param), "INFO") response = self.dnac._exec( family="site_design", function='updates_a_building', @@ -1708,7 +1708,7 @@ def get_diff_merged(self, config): if self.handle_config[each_type]: self.log("Processing configurations for '{0}'.".format(each_type), "DEBUG") for create_config in self.handle_config[each_type]: - self.log("Handling configuration: {0}".format(self.pprint(create_config)), "DEBUG") + self.log("Handling configuration: {0}".format(create_config), "DEBUG") parent_name = create_config.get(self.keymap.get("parent_name_hierarchy")) if not parent_name: self.msg = "No parent name found in configuration for '{0}'.".format(each_type) @@ -2150,7 +2150,7 @@ def get_diff_deleted(self, config): self.site_absent_list.append(each_type + ": " + config.get("site_name_hierarchy")) else: final_deletion_list.append(config) - self.log("Deletion list re-arranged order: {0}.".format(self.pprint(final_deletion_list)), "INFO") + self.log("Deletion list re-arranged order: {0}.".format(final_deletion_list), "INFO") if len(final_deletion_list) > 0: for config in final_deletion_list: From d515fc655e9242fa958671ce5a8326673f9e5964 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Mon, 18 Nov 2024 23:46:39 -0500 Subject: [PATCH 24/38] updated sanity tests to ignore yamllinting on plugins folder --- .yamllint.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.yamllint.yml b/.yamllint.yml index 51f48f7668..d81e32ddd6 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -3,15 +3,11 @@ extends: default ignore: - tests - changelogs/changelog.yaml - # - playbooks - # - plugins + - plugins level: warning rules: - syntax-error: - level: warning - line-length: max: 160 level: warning From ec9cd44f0f63e4359469fd147399d31db4648123 Mon Sep 17 00:00:00 2001 From: Megha Kandari Date: Tue, 19 Nov 2024 10:58:22 +0530 Subject: [PATCH 25/38] Sanity check --- .../network_settings_workflow_manager.py | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index 812daf1cb3..6955510ee7 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -3,6 +3,7 @@ # 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 operations on global pool, reserve pool and network in Cisco Catalyst Center.""" from __future__ import absolute_import, division, print_function @@ -27,25 +28,25 @@ Megha Kandari (@kandarimegha) options: config_verify: - description: Set to True to verify the Cisco Catalyst Center after applying the playbook config. - type: bool - default: false + description: Set to True to verify the Cisco Catalyst Center 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 + description: The state of Cisco Catalyst Center after module completion. + type: str + choices: [merged, deleted] + default: merged config: - description: - - List of details of global pool, reserved pool, network being managed. - type: list - elements: dict - required: true - suboptions: - global_pool_details: - description: Manages IPv4 and IPv6 IP pools in the global level. - type: dict - suboptions: + description: + - List of details of global pool, reserved pool, network being managed. + type: list + elements: dict + required: true + suboptions: + global_pool_details: + description: Manages IPv4 and IPv6 IP pools in the global level. + type: dict + suboptions: settings: description: Global Pool's settings. type: dict @@ -100,7 +101,7 @@ exclusively when you need to update the global pool's name. type: str - reserve_pool_details: + reserve_pool_details: description: Reserved IP subpool details from the global pool. type: dict suboptions: @@ -226,7 +227,7 @@ Allows devices on IPv6 networks to self-configure their IP addresses autonomously, eliminating the need for manual setup. type: bool - network_management_details: + network_management_details: description: Set default network settings for the site type: list elements: dict From cfdb9771fc8da5b4df0abfe294dd080c2be8a358 Mon Sep 17 00:00:00 2001 From: Madhan Date: Tue, 19 Nov 2024 11:35:47 +0530 Subject: [PATCH 26/38] Changes not applied --- .github/workflows/sanity_tests_devel.yml | 5 ++ changelogs/changelog.yaml | 8 ++ plugins/modules/site_workflow_manager.py | 101 +++++------------------ 3 files changed, 32 insertions(+), 82 deletions(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index d0f629c1cd..73dd4d0bdb 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -52,6 +52,7 @@ jobs: if [ -s changed_files.txt ]; then changed_files=$(cat changed_files.txt) for file in $changed_files; do +<<<<<<< HEAD # Ensure file paths are relative to the working directory if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" @@ -61,6 +62,10 @@ jobs: fi done echo "Ansible sanity tests completed successfully." +======= + ansible-test sanity --docker -v --color yes $file + done +>>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) else echo "No changed files to test." fi diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index d579bd4b98..8cf4ffcc3f 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1063,7 +1063,11 @@ releases: - Code change in user_role_manager module - Changes in dnac.py - inventory_workflow_manager.py - added attribute hostnames, serial_numbers and mac_addresses +<<<<<<< HEAD - inventory_workflow_manager.py - Removed attribute hostname_list, serial_number_list and mac_address_list +======= + - inventory_workflow_manager.py - Removed attribute hostname_list, serial_number_list and mac_address_list +>>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) 6.23.0: release_date: "2024-11-06" changes: @@ -1078,7 +1082,11 @@ releases: - Changes in site_workflow_manager - accesspoint_workflow_manager - added attribute bulk_update_aps - sda_fabric_devices_workflow_manager.py - added attribute route_distribution_protocol +<<<<<<< HEAD + - sda_fabric_sites_zones_workflow_manager.py - added attribute site_name_hierarchy and removed attribute site_name +======= - sda_fabric_sites_zones_workflow_manager.py - added attribute site_name_hierarchy and removed attribute site_name +>>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) 6.24.0: release_date: "2024-11-12" changes: diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 91d3d94b40..38d633ebe3 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -659,13 +659,14 @@ def site_exists(self, site_name_hierarchy=None): for site in sites: if isinstance(site, dict): - self.log("No site information found for name: {0}".format(site), "INFO") + self.log("No site information found for name: {0}".format(self.pprint(site)), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True else: site_name_hierarchy = self.want.get("site_name_hierarchy") + self.log("CHECK {0}".format(site_name_hierarchy), "INFO") response = self.get_site_v1(site_name_hierarchy) if not response: @@ -826,7 +827,7 @@ def get_site_name_hierarchy(self, site): self.set_operation_result("failed", False, self.msg, "ERROR") return None - site_name_hierarchy = '/'.join([str(parent_name), str(name)]) + site_name_hierarchy = '/'.join([parent_name, name]) self.log("Constructed site name: {}".format(site_name_hierarchy), "INFO") return site_name_hierarchy @@ -1115,7 +1116,7 @@ def get_have(self, config): for site in sites: if isinstance(site, dict): - self.log("site information found: {0}".format(site), "INFO") + self.log("site information found: {0}".format(self.pprint(site)), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True @@ -1137,8 +1138,8 @@ def get_have(self, config): self.have = self.handle_config["have"] self.log("All site information collected from bulk operation(create_config): {0}". - format(self.handle_config["create_site"]), "DEBUG") - self.log("All site information collected (have): {0}".format(self.have), "DEBUG") + format(self.pprint(self.handle_config["create_site"])), "DEBUG") + self.log("All site information collected (have): {0}".format(self.pprint(self.have)), "DEBUG") else: site_exists, current_site = self.site_exists() @@ -1200,7 +1201,7 @@ def get_want(self, config): want_list.append(want) self.want = want_list - self.log("Desired State (want): {0}".format(self.want), "INFO") + self.log("Desired State (want): {0}".format(self.pprint(self.want)), "INFO") return self except Exception as e: @@ -1488,12 +1489,12 @@ def update_building(self, site_params): parent_name = site_params.get("site", {}).get("building", {}).get("parentName") parent_id = self.get_parent_id(parent_name) site_params['site']['building']['parentId'] = parent_id - self.log("Updated site_params with parent_id: {0}".format(site_params), "INFO") + self.log("Updated site_params with parent_id: {0}".format(self.pprint(site_params)), "INFO") building_param = site_params.get('site', {}).get('building') site_id = site_params.get("site_id") building_param['id'] = site_id - self.log("Before updating the building params:{0}".format(building_param), "INFO") + self.log("Before updating the building params:{0}".format(self.pprint(building_param)), "INFO") response = self.dnac._exec( family="site_design", function='updates_a_building', @@ -1624,42 +1625,6 @@ def change_payload_data(self, config): self.msg = "Unable to process the payload data : {}".format(str(e)) self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() - def is_site_exist(self, site_name): - """ - Checks if a site exists in Cisco Catalyst Center by retrieving site information based on the provided site name. - - Args: - site_name (str): The name or hierarchy of the site to be retrieved. - - Returns: - A boolean indicating whether the site exists (True if found, False otherwise). - - Details: - - Calls `get_site()` to retrieve site details from Cisco Catalyst Center. - - If the site does not exist, it returns (False). - - Logs detailed debug information about the retrieval attempt and any errors that occur. - - """ - site_exists = False - try: - response = self.get_site(site_name) - - if response is None: - self.log("No site details retrieved for site name: {0}".format(site_name), "DEBUG") - return site_exists - - self.log("Site details retrieved for site {0}: {1}".format(site_name, str(response)), "DEBUG") - site_exists = True - - except Exception as e: - self.log( - "An exception occurred while retrieving Site details for Site '{0}' " - "does not exist in the Cisco Catalyst Center. Error: {1}".format(site_name, e), - "INFO" - ) - - return site_exists - def get_diff_merged(self, config): """ Update/Create site information in Cisco Catalyst Center with fields @@ -1706,23 +1671,6 @@ def get_diff_merged(self, config): self.log("Added to floor: {}".format(payload_data), "DEBUG") for each_type in ("area", "building", "floor"): if self.handle_config[each_type]: - self.log("Processing configurations for '{0}'.".format(each_type), "DEBUG") - for create_config in self.handle_config[each_type]: - self.log("Handling configuration: {0}".format(create_config), "DEBUG") - parent_name = create_config.get(self.keymap.get("parent_name_hierarchy")) - if not parent_name: - self.msg = "No parent name found in configuration for '{0}'.".format(each_type) - self.log(self.msg, "DEBUG") - self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() - self.log("Checking if parent site '{0}' exists in the hierarchy.".format(parent_name), "DEBUG") - - site_exists = self.is_site_exist(parent_name) - if not site_exists: - self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) - self.log(self.msg, "DEBUG") - self.site_absent_list.append(str(parent_name) + " does not exist ") - self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() - response = self.creating_bulk_site(self.handle_config[each_type]) self.log("Response from creating_bulk_site for {}: {}".format(each_type, response), "DEBUG") @@ -1795,9 +1743,9 @@ def get_diff_merged(self, config): if response and isinstance(response, dict): taskid = response["response"]["taskId"] + task_details = self.get_task_details(taskid) while True: - task_details = self.get_task_details(taskid) if site_type != "floor": if task_details.get("progress") == "Group is updated successfully": task_detail_list.append(task_details) @@ -1897,18 +1845,6 @@ def get_diff_merged(self, config): self.log("The site '{0}' is not categorized as a building; no need to filter 'None' values.". format(name), "INFO") - site_type = site_params['type'] - parent_name = site_params.get('site').get(site_type).get('parentName') - try: - response = self.get_site_v1(parent_name) - if not response: - self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) - self.log(self.msg, "DEBUG") - self.site_absent_list.append(str(parent_name) + " does not exist ") - self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() - except Exception as e: - self.log("No response received from 'get_site_v1' API for site: {0}".format(parent_name + str(e)), "ERROR") - response = self.dnac._exec( family="sites", function='create_site', @@ -2106,16 +2042,17 @@ def get_diff_deleted(self, config): if self.compare_dnac_versions(self.get_ccc_version(), "2.3.5.3") <= 0: site_exists = self.have.get("site_exists") site_name_hierarchy = self.want.get("site_name_hierarchy") - site_id = self.have.get("site_id") + if not site_exists: - if site_name_hierarchy not in self.deleted_site_list: - self.site_absent_list.append(site_name_hierarchy) + self.status = "success" + self.site_absent_list.append(site_name_hierarchy) self.log( - "Failed to delete site '{0}'. Reason: The site was not found in the Cisco Catalyst Center.".format(site_name_hierarchy), - "DEBUG" - ) + "Unable to delete site '{0}' as it's not found in Cisco Catalyst Center".format(self.want.get("site_name_hierarchy")), "INFO") return self + site_id = self.have.get("site_id") + site_name_hierarchy = self.want.get("site_name_hierarchy") api_response, response = self.get_device_ids_from_site(site_name_hierarchy, site_id) + self.log( "Received API response from 'get_membership': {0}".format(str(api_response)), "DEBUG") @@ -2150,7 +2087,7 @@ def get_diff_deleted(self, config): self.site_absent_list.append(each_type + ": " + config.get("site_name_hierarchy")) else: final_deletion_list.append(config) - self.log("Deletion list re-arranged order: {0}.".format(final_deletion_list), "INFO") + self.log("Deletion list re-arranged order: {0}.".format(self.pprint(final_deletion_list)), "INFO") if len(final_deletion_list) > 0: for config in final_deletion_list: @@ -2180,8 +2117,8 @@ def get_diff_deleted(self, config): task_id = response.get("response", {}).get("taskId") if task_id: + task_details = self.get_task_details(task_id) while True: - task_details = self.get_task_details(task_id) if site_type == "area": if task_details.get("progress") == "Group is deleted successfully": self.msg = "Area '{0}' deleted successfully.".format(site_name_hierarchy) From c33ceedbd3cae39e7cab38d499df9f7bd437ba5f Mon Sep 17 00:00:00 2001 From: skesali Date: Tue, 19 Nov 2024 11:55:16 +0530 Subject: [PATCH 27/38] Changes done for bugs --- plugins/modules/site_workflow_manager.py | 122 ++++++----------------- 1 file changed, 31 insertions(+), 91 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 38d633ebe3..b09f1a01a1 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -125,9 +125,10 @@ description: | Floor number within the building site (e.g., 5). This value can only be specified during the creation of the floor and cannot be modified afterward. + This field is mandatory in 2.3.7.6 version. type: int units_of_measure: - description: The unit of measurement for floor dimensions, typically 'feet' or 'meters'. + description: The unit of measurement for floor dimensions, typically 'feet' or 'meters'. This field is introduced in 2.3.7.6 version. type: str upload_floor_image_path: description: | @@ -836,74 +837,6 @@ def get_site_name_hierarchy(self, site): self.log(error_message, "ERROR") return None - def get_bulk_site_names(self, site, bulk_operation=True): - """ - Collects and returns a list of constructed site names for areas, buildings, and floors. - - Parameters: - - self (object): An instance of a class used for interacting with Cisco Catalyst Center. - - site (dict): Configuration for a site in Cisco Catalyst Center. - - bulk_operation (bool, optional): Flag to indicate bulk operation mode. Default is True. - - Returns: - - list: A list of constructed site names (e.g., area/building/floor), or an empty list if none are found. - - Description: - This method constructs site names from areas, buildings, and floors defined in the configuration, - based on the Cisco Catalyst Center version. It logs missing hierarchy information as errors. - """ - name_list = [] - self.log("Starting bulk site names construction with arguments - site: {}, bulk_operation: {}".format(site, bulk_operation), "DEBUG") - - if not self.config or not isinstance(self.config, list) or not self.config[0].get('site'): - self.log("Configuration data for sites is missing or improperly formatted.", "ERROR") - return name_list - - try: - self.log("Processing areas for site names.", "DEBUG") - for area in self.config[0].get('site', {}).get('area', []): - area_name = area.get('name') - area_parent_name_hierarchy = area.get('parent_name_hierarchy') - if area_name and area_parent_name_hierarchy: - constructed_name = "{}/{}".format(area_parent_name_hierarchy, area_name) - name_list.append(constructed_name) - self.log("Constructed area name: {}".format(constructed_name), "DEBUG") - elif not area_parent_name_hierarchy: - self.log("Missing parent name hierarchy for area: {}".format(area_name), "ERROR") - - self.log("Processing buildings for site names.", "DEBUG") - for building in self.config[0].get('site', {}).get('building', []): - building_name = building.get('name') - building_parent_name_hierarchy = building.get('parent_name_hierarchy') - if building_name and building_parent_name_hierarchy: - constructed_name = "{}/{}".format(building_parent_name_hierarchy, building_name) - name_list.append(constructed_name) - self.log("Constructed building name: {}".format(constructed_name), "DEBUG") - elif not building_parent_name_hierarchy: - self.log("Missing parent name hierarchy for building: {}".format(building_name), "ERROR") - - self.log("Processing floors for site names.", "DEBUG") - for floor in self.config[0].get('site', {}).get('floor', []): - floor_name = floor.get('name') - floor_parent_name_hierarchy = floor.get('parent_name_hierarchy') - if floor_name and floor_parent_name_hierarchy: - constructed_name = "{}/{}".format(floor_parent_name_hierarchy, floor_name) - name_list.append(constructed_name) - self.log("Constructed floor name: {}".format(constructed_name), "DEBUG") - elif not floor_parent_name_hierarchy: - self.log("Missing parent name hierarchy for floor: {}".format(floor_name), "ERROR") - - if not name_list: - self.log("No site names constructed from areas, buildings, or floors.", "WARNING") - else: - self.log("Final constructed site names: {}".format(name_list), "DEBUG") - self.log("Bulk site names construction completed successfully.", "DEBUG") - - except Exception as e: - self.log("An error occurred while constructing site names: {}".format(str(e)), "ERROR") - - return name_list - def compare_float_values(self, ele1, ele2, precision=2): """ Compare two floating-point values with a specified precision. @@ -1287,11 +1220,16 @@ def validate_site_input_data(self, config): if not (isinstance(longitude, (float, int)) and -180 <= longitude <= 180): errormsg.append("Invalid longitude. Valid range is -180 to +180.") - if not (latitude and longitude or address): - errormsg.append("Either latitude/longitude or address is required.") - self.log("Missing required latitude/longitude or address for building.", "ERROR") - elif (latitude and not longitude) or (not latitude and longitude): - errormsg.append("Either Latitude or longitude is missing in the given playbook") + if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.6") >= 0: + if not (latitude and longitude or address): + errormsg.append("Either latitude/longitude or address is required.") + self.log("Missing required latitude/longitude or address for building.", "ERROR") + elif (latitude and not longitude) or (not latitude and longitude): + errormsg.append("Either Latitude or longitude is missing in the given playbook") + else: + if not (latitude and longitude): + errormsg.append("latitude and longitude is required.") + self.log("Missing required latitude and longitude for building.", "ERROR") country = site.get(site_type, {}).get("country") self.log("Validating 'country' field: " + country, "DEBUG") @@ -1304,14 +1242,15 @@ def validate_site_input_data(self, config): if site_type == "floor": self.log("Performing floor-specific validations.", "DEBUG") - floor_number = site.get(site_type, {}).get("floor_number") - if floor_number: - self.log("Validating 'floor_number': " + str(floor_number), "DEBUG") - if not (isinstance(floor_number, int) and -200 <= floor_number <= 200): - errormsg.append("Please enter a valid floor number (-200 to 200)") - self.log("Missing 'floor_number' in floor entry.", "ERROR") - else: - errormsg.append("Floor number should not be None or empty") + if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.6") >= 0: + floor_number = site.get(site_type, {}).get("floor_number") + if floor_number: + self.log("Validating 'floor_number': " + str(floor_number), "DEBUG") + if not (isinstance(floor_number, int) and -200 <= floor_number <= 200): + errormsg.append("Please enter a valid floor number (-200 to 200)") + self.log("Missing 'floor_number' in floor entry.", "ERROR") + else: + errormsg.append("Floor number should not be None or empty") rf_model = site.get(site_type, {}).get("rf_model") self.log("Validating 'rf_model': " + rf_model, "DEBUG") @@ -1353,14 +1292,15 @@ def validate_site_input_data(self, config): else: errormsg.append("height should not be None or empty") - units_of_measure = site.get(site_type, {}).get("units_of_measure") - if units_of_measure: - if units_of_measure not in ("feet", "meters"): - errormsg.append( - "units_of_measure: Invalid value '{0}' for units_of_measure in playbook. Must be one of 'feet' or 'meters'.".format( - units_of_measure)) - else: - errormsg.append("units_of_measure should not be None or empty") + if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.6") >= 0: + units_of_measure = site.get(site_type, {}).get("units_of_measure") + if units_of_measure: + if units_of_measure not in ("feet", "meters"): + errormsg.append( + "units_of_measure: Invalid value '{0}' for units_of_measure in playbook. Must be one of 'feet' or 'meters'.".format( + units_of_measure)) + else: + errormsg.append("units_of_measure should not be None or empty") upload_floor_image_path = site.get(site_type, {}).get("upload_floor_image_path") if upload_floor_image_path: @@ -1377,7 +1317,7 @@ def validate_site_input_data(self, config): ) if len(errormsg) > 0: - self.msg = "Invalid parameters in playbook config: '{0}' ".format(", ".join(errormsg)) + self.msg = "Missing or invalid parameters in playbook config: '{0}' ".format(", ".join(errormsg)) self.log(self.msg, "ERROR") self.status = "failed" return self From 9f7419ce20857c956affae2956780505240a21d4 Mon Sep 17 00:00:00 2001 From: skesali Date: Tue, 19 Nov 2024 13:12:52 +0530 Subject: [PATCH 28/38] Modifications for params bugs --- plugins/modules/site_workflow_manager.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index b09f1a01a1..7d7c75dc70 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -1148,7 +1148,7 @@ def get_want(self, config): self.log("Desired State (want): {0}".format(self.want), "INFO") return self - def validate_site_input_data(self, config): + def validate_site_input_data(self, config, state): """ Validates site-related data from the playbook configuration to ensure it meets the required standards for site creation or modification in Cisco Catalyst Center. @@ -1194,6 +1194,9 @@ def validate_site_input_data(self, config): self.log("Missing 'parent_name' field in entry.", "ERROR") errormsg.append("parent_name should not be None or empty") + if state == "deleted": + continue + if site_type: if site_type not in ("area", "building", "floor"): errormsg.append("site_type: Invalid value '{0}' for site_type in playbook. Must be one of: area, building, or Floor.".format(site_type)) @@ -1232,8 +1235,8 @@ def validate_site_input_data(self, config): self.log("Missing required latitude and longitude for building.", "ERROR") country = site.get(site_type, {}).get("country") - self.log("Validating 'country' field: " + country, "DEBUG") if country: + self.log("Validating 'country' field: " + country, "DEBUG") param_spec = dict(type="str", length_max=100) validate_str(country, param_spec, "country", errormsg) else: @@ -1253,8 +1256,8 @@ def validate_site_input_data(self, config): errormsg.append("Floor number should not be None or empty") rf_model = site.get(site_type, {}).get("rf_model") - self.log("Validating 'rf_model': " + rf_model, "DEBUG") if rf_model: + self.log("Validating 'rf_model': " + rf_model, "DEBUG") rf_model_list = [ "Free Space", "Outdoor Open Space", @@ -2438,7 +2441,7 @@ def main(): ccc_site.validate_input().check_return_status() config_verify = ccc_site.params.get("config_verify") - ccc_site.validate_site_input_data(ccc_site.validated_config).check_return_status() + ccc_site.validate_site_input_data(ccc_site.validated_config, state).check_return_status() if ccc_site.compare_dnac_versions(ccc_site.get_ccc_version(), "2.3.7.6") >= 0: ccc_site.reset_values() From 0dc1e068f78ad7011cdad02dede790f4be7cf9dd Mon Sep 17 00:00:00 2001 From: skesali Date: Tue, 19 Nov 2024 18:13:20 +0530 Subject: [PATCH 29/38] Modifications for rf_model bug along with comments --- plugins/modules/site_workflow_manager.py | 53 ++++++++++++++---------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index 7d7c75dc70..b16de9b382 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -125,10 +125,12 @@ description: | Floor number within the building site (e.g., 5). This value can only be specified during the creation of the floor and cannot be modified afterward. - This field is mandatory in 2.3.7.6 version. + It is required from version 2.3.7.6 onwards. type: int units_of_measure: - description: The unit of measurement for floor dimensions, typically 'feet' or 'meters'. This field is introduced in 2.3.7.6 version. + description: | + Specifies the unit of measurement for floor dimensions, typically 'feet' or 'meters'. + This field is introduced from version 2.3.7.6 onwards. type: str upload_floor_image_path: description: | @@ -1207,7 +1209,7 @@ def validate_site_input_data(self, config, state): self.log("Performing building-specific validations.", "DEBUG") address = site.get(site_type, {}).get("address") if address: - self.log("Validating 'address' field: " + address, "DEBUG") + self.log("Validating 'address' field: " + str(address), "DEBUG") param_spec = dict(type="str", length_max=255) validate_str(address, param_spec, "address", errormsg) @@ -1231,12 +1233,12 @@ def validate_site_input_data(self, config, state): errormsg.append("Either Latitude or longitude is missing in the given playbook") else: if not (latitude and longitude): - errormsg.append("latitude and longitude is required.") + errormsg.append("Latitude and longitude are required.") self.log("Missing required latitude and longitude for building.", "ERROR") country = site.get(site_type, {}).get("country") if country: - self.log("Validating 'country' field: " + country, "DEBUG") + self.log("Validating 'country' field: " + str(country), "DEBUG") param_spec = dict(type="str", length_max=100) validate_str(country, param_spec, "country", errormsg) else: @@ -1245,19 +1247,26 @@ def validate_site_input_data(self, config, state): if site_type == "floor": self.log("Performing floor-specific validations.", "DEBUG") + floor_number = site.get(site_type, {}).get("floor_number") if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.6") >= 0: - floor_number = site.get(site_type, {}).get("floor_number") if floor_number: self.log("Validating 'floor_number': " + str(floor_number), "DEBUG") if not (isinstance(floor_number, int) and -200 <= floor_number <= 200): errormsg.append("Please enter a valid floor number (-200 to 200)") - self.log("Missing 'floor_number' in floor entry.", "ERROR") + self.log("'floor_number' is out of the valid range (-200 to 200).", "ERROR") else: - errormsg.append("Floor number should not be None or empty") + errormsg.append("'floor_number' should not be None or empty.") + self.log("Missing 'floor_number' in floor entry.", "ERROR") + else: + if floor_number: + self.log("Validating 'floor_number': " + str(floor_number), "DEBUG") + if not (isinstance(floor_number, int) and -200 <= floor_number <= 200): + errormsg.append("Please enter a valid floor number (-200 to 200)") + self.log("'floor_number' is out of the valid range (-200 to 200).", "ERROR") rf_model = site.get(site_type, {}).get("rf_model") if rf_model: - self.log("Validating 'rf_model': " + rf_model, "DEBUG") + self.log("Validating 'rf_model': " + str(rf_model), "DEBUG") rf_model_list = [ "Free Space", "Outdoor Open Space", @@ -1268,7 +1277,7 @@ def validate_site_input_data(self, config, state): if rf_model not in rf_model_list: errormsg.append("rf_model: Invalid value '{0}' for rf_model in playbook. Must be one of: '{1}'". format(site_type, str(rf_model))) - self.log("Invalid 'rf_model': " + rf_model, "ERROR") + self.log("Invalid 'rf_model': " + str(rf_model), "ERROR") else: errormsg.append("RF should not be None or empty") @@ -1302,8 +1311,10 @@ def validate_site_input_data(self, config, state): errormsg.append( "units_of_measure: Invalid value '{0}' for units_of_measure in playbook. Must be one of 'feet' or 'meters'.".format( units_of_measure)) + self.log("Invalid 'units_of_measure': {0}. Expected 'feet' or 'meters'.".format(units_of_measure), "ERROR") else: errormsg.append("units_of_measure should not be None or empty") + self.log("Missing 'units_of_measure' in floor entry.", "ERROR") upload_floor_image_path = site.get(site_type, {}).get("upload_floor_image_path") if upload_floor_image_path: @@ -1546,18 +1557,18 @@ def change_payload_data(self, config): site_type = config.get('type') if site_type in ['area', 'building', 'floor'] and site_data: - self.log("Site type identified as: " + site_type, "DEBUG") + self.log("Site type identified as: " + str(site_type), "DEBUG") specific_data = site_data.get(site_type, {}) for key, value in specific_data.items(): if value is not None: - self.log("Mapping key: " + key + " to value: " + str(value), "DEBUG") + self.log("Mapping key: " + str(key) + " to value: " + str(value), "DEBUG") mapped_key = self.keymap.get(key, key) payload_data[mapped_key] = value payload_data["type"] = site_type self.log("Payload data created successfully.", "DEBUG") else: - self.log("Skipping key: " + key + " as value is None.", "DEBUG") + self.log("Skipping key: " + str(key) + " as value is None.", "DEBUG") else: self.log("Invalid site type or missing site data in the configuration.", "ERROR") else: @@ -1757,7 +1768,7 @@ def get_diff_merged(self, config): if execution_details.get("status") == "SUCCESS": self.result['changed'] = True site_updated = True - self.updated_site_list.append(site_type + ": " + site_name_hierarchy) + self.updated_site_list.append(str(site_type) + ": " + str(site_name_hierarchy)) self.log("Site - {0} Updated Successfully".format(site_name_hierarchy), "INFO") break elif execution_details.get("bapiError"): @@ -1804,7 +1815,7 @@ def get_diff_merged(self, config): self.result['changed'] = True break elif execution_details.get("bapiError"): - self.msg = "Unable to Create: " + execution_details.get("bapiError") + self.msg = "Unable to Create: " + str(execution_details.get("bapiError")) self.set_operation_result("failed", False, self.msg, "ERROR", execution_details).check_return_status() break @@ -1812,7 +1823,7 @@ def get_diff_merged(self, config): site_exists, current_site = self.site_exists() if site_exists: site_name_hierarchy = self.want.get("site_name_hierarchy") - self.created_site_list.append(site_type + ": " + site_name_hierarchy) + self.created_site_list.append(str(site_type) + ": " + str(site_name_hierarchy)) self.log("Site '{0}' created successfully".format(site_name_hierarchy), "INFO") return self @@ -2027,7 +2038,7 @@ def get_diff_deleted(self, config): if not site_exists: self.log("Unable to delete site {0} as it's not found in Cisco Catalyst Center". format(config.get("site_name_hierarchy")), "INFO") - self.site_absent_list.append(each_type + ": " + config.get("site_name_hierarchy")) + self.site_absent_list.append(str(each_type) + ": " + str(config.get("site_name_hierarchy"))) else: final_deletion_list.append(config) self.log("Deletion list re-arranged order: {0}.".format(self.pprint(final_deletion_list)), "INFO") @@ -2068,7 +2079,7 @@ def get_diff_deleted(self, config): self.log(self.msg, "INFO") self.result['changed'] = True self.result['response'] = task_details - self.deleted_site_list.append(site_type + ": " + site_name_hierarchy) + self.deleted_site_list.append(str(site_type) + ": " + str(site_name_hierarchy)) break elif task_details.get("failureReason"): self.msg = "Error response for 'deletes_an_area' task: {0}".format(task_details.get('failureReason')) @@ -2082,7 +2093,7 @@ def get_diff_deleted(self, config): self.log(self.msg, "INFO") self.result['changed'] = True self.result['response'] = task_details - self.deleted_site_list.append(site_type + ": " + site_name_hierarchy) + self.deleted_site_list.append(str(site_type) + ": " + str(site_name_hierarchy)) break elif task_details.get("failureReason"): self.msg = "Error response for 'deletes_building' task: {0}".format(task_details.get('failureReason')) @@ -2093,7 +2104,7 @@ def get_diff_deleted(self, config): else: if task_details.get("progress") == "NCMP00150: Service domain is deleted successfully": self.log("Area site '{0}' deleted successfully.".format(site_name_hierarchy), "INFO") - self.deleted_site_list.append(site_type + ": " + site_name_hierarchy) + self.deleted_site_list.append(str(site_type) + ": " + str(site_name_hierarchy)) break elif task_details.get("failureReason"): self.msg = "Error response for 'deletes_an_floor' task: {0}".format(task_details.get('failureReason')) @@ -2348,7 +2359,7 @@ def upload_floor_image(self, config): 'image': (os.path.basename(file_path), file_content, content_type) } - site_hierarchy = config.get(self.keymap["parent_name_hierarchy"], "parent_name_hierarchy") + "/" + config.get('name') + site_hierarchy = config.get(self.keymap["parent_name_hierarchy"], "parent_name_hierarchy") + "/" + str(config.get('name')) site_exists, current_site = self.site_exists(site_hierarchy) site_id = current_site.get("id") if not site_id: From 5b517ffc5fb22a97f42b29a117fef0a3831544f1 Mon Sep 17 00:00:00 2001 From: Madhan Date: Tue, 19 Nov 2024 18:55:48 +0530 Subject: [PATCH 30/38] Site workflow module (#33) --- plugins/modules/site_workflow_manager.py | 101 ++++++++++++++++++----- 1 file changed, 82 insertions(+), 19 deletions(-) diff --git a/plugins/modules/site_workflow_manager.py b/plugins/modules/site_workflow_manager.py index b16de9b382..a63f153c05 100644 --- a/plugins/modules/site_workflow_manager.py +++ b/plugins/modules/site_workflow_manager.py @@ -662,14 +662,13 @@ def site_exists(self, site_name_hierarchy=None): for site in sites: if isinstance(site, dict): - self.log("No site information found for name: {0}".format(self.pprint(site)), "INFO") + self.log("No site information found for name: {0}".format(site), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True else: site_name_hierarchy = self.want.get("site_name_hierarchy") - self.log("CHECK {0}".format(site_name_hierarchy), "INFO") response = self.get_site_v1(site_name_hierarchy) if not response: @@ -830,7 +829,7 @@ def get_site_name_hierarchy(self, site): self.set_operation_result("failed", False, self.msg, "ERROR") return None - site_name_hierarchy = '/'.join([parent_name, name]) + site_name_hierarchy = '/'.join([str(parent_name), str(name)]) self.log("Constructed site name: {}".format(site_name_hierarchy), "INFO") return site_name_hierarchy @@ -1051,7 +1050,7 @@ def get_have(self, config): for site in sites: if isinstance(site, dict): - self.log("site information found: {0}".format(self.pprint(site)), "INFO") + self.log("site information found: {0}".format(site), "INFO") current_site = dict(site.items()) current_site['parentName'] = site.get('nameHierarchy', '').rsplit('/', 1)[0] if site.get('nameHierarchy') else None site_exists = True @@ -1073,8 +1072,8 @@ def get_have(self, config): self.have = self.handle_config["have"] self.log("All site information collected from bulk operation(create_config): {0}". - format(self.pprint(self.handle_config["create_site"])), "DEBUG") - self.log("All site information collected (have): {0}".format(self.pprint(self.have)), "DEBUG") + format(self.handle_config["create_site"]), "DEBUG") + self.log("All site information collected (have): {0}".format(self.have), "DEBUG") else: site_exists, current_site = self.site_exists() @@ -1136,7 +1135,7 @@ def get_want(self, config): want_list.append(want) self.want = want_list - self.log("Desired State (want): {0}".format(self.pprint(self.want)), "INFO") + self.log("Desired State (want): {0}".format(self.want), "INFO") return self except Exception as e: @@ -1443,12 +1442,12 @@ def update_building(self, site_params): parent_name = site_params.get("site", {}).get("building", {}).get("parentName") parent_id = self.get_parent_id(parent_name) site_params['site']['building']['parentId'] = parent_id - self.log("Updated site_params with parent_id: {0}".format(self.pprint(site_params)), "INFO") + self.log("Updated site_params with parent_id: {0}".format(site_params), "INFO") building_param = site_params.get('site', {}).get('building') site_id = site_params.get("site_id") building_param['id'] = site_id - self.log("Before updating the building params:{0}".format(self.pprint(building_param)), "INFO") + self.log("Before updating the building params:{0}".format(building_param), "INFO") response = self.dnac._exec( family="site_design", function='updates_a_building', @@ -1579,6 +1578,42 @@ def change_payload_data(self, config): self.msg = "Unable to process the payload data : {}".format(str(e)) self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + def is_site_exist(self, site_name): + """ + Checks if a site exists in Cisco Catalyst Center by retrieving site information based on the provided site name. + + Args: + site_name (str): The name or hierarchy of the site to be retrieved. + + Returns: + A boolean indicating whether the site exists (True if found, False otherwise). + + Details: + - Calls `get_site()` to retrieve site details from Cisco Catalyst Center. + - If the site does not exist, it returns (False). + - Logs detailed debug information about the retrieval attempt and any errors that occur. + + """ + site_exists = False + try: + response = self.get_site(site_name) + + if response is None: + self.log("No site details retrieved for site name: {0}".format(site_name), "DEBUG") + return site_exists + + self.log("Site details retrieved for site {0}: {1}".format(site_name, str(response)), "DEBUG") + site_exists = True + + except Exception as e: + self.log( + "An exception occurred while retrieving Site details for Site '{0}' " + "does not exist in the Cisco Catalyst Center. Error: {1}".format(site_name, e), + "INFO" + ) + + return site_exists + def get_diff_merged(self, config): """ Update/Create site information in Cisco Catalyst Center with fields @@ -1625,6 +1660,23 @@ def get_diff_merged(self, config): self.log("Added to floor: {}".format(payload_data), "DEBUG") for each_type in ("area", "building", "floor"): if self.handle_config[each_type]: + self.log("Processing configurations for '{0}'.".format(each_type), "DEBUG") + for create_config in self.handle_config[each_type]: + self.log("Handling configuration: {0}".format(create_config), "DEBUG") + parent_name = create_config.get(self.keymap.get("parent_name_hierarchy")) + if not parent_name: + self.msg = "No parent name found in configuration for '{0}'.".format(each_type) + self.log(self.msg, "DEBUG") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + self.log("Checking if parent site '{0}' exists in the hierarchy.".format(parent_name), "DEBUG") + + site_exists = self.is_site_exist(parent_name) + if not site_exists: + self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) + self.log(self.msg, "DEBUG") + self.site_absent_list.append(str(parent_name) + " does not exist ") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + response = self.creating_bulk_site(self.handle_config[each_type]) self.log("Response from creating_bulk_site for {}: {}".format(each_type, response), "DEBUG") @@ -1697,9 +1749,9 @@ def get_diff_merged(self, config): if response and isinstance(response, dict): taskid = response["response"]["taskId"] - task_details = self.get_task_details(taskid) while True: + task_details = self.get_task_details(taskid) if site_type != "floor": if task_details.get("progress") == "Group is updated successfully": task_detail_list.append(task_details) @@ -1799,6 +1851,18 @@ def get_diff_merged(self, config): self.log("The site '{0}' is not categorized as a building; no need to filter 'None' values.". format(name), "INFO") + site_type = site_params['type'] + parent_name = site_params.get('site').get(site_type).get('parentName') + try: + response = self.get_site_v1(parent_name) + if not response: + self.msg = "Parent name '{0}' does not exist in the Cisco Catalyst Center.".format(parent_name) + self.log(self.msg, "DEBUG") + self.site_absent_list.append(str(parent_name) + " does not exist ") + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + except Exception as e: + self.log("No response received from 'get_site_v1' API for site: {0}".format(parent_name + str(e)), "ERROR") + response = self.dnac._exec( family="sites", function='create_site', @@ -1996,17 +2060,16 @@ def get_diff_deleted(self, config): if self.compare_dnac_versions(self.get_ccc_version(), "2.3.5.3") <= 0: site_exists = self.have.get("site_exists") site_name_hierarchy = self.want.get("site_name_hierarchy") - + site_id = self.have.get("site_id") if not site_exists: - self.status = "success" - self.site_absent_list.append(site_name_hierarchy) + if site_name_hierarchy not in self.deleted_site_list: + self.site_absent_list.append(site_name_hierarchy) self.log( - "Unable to delete site '{0}' as it's not found in Cisco Catalyst Center".format(self.want.get("site_name_hierarchy")), "INFO") + "Failed to delete site '{0}'. Reason: The site was not found in the Cisco Catalyst Center.".format(site_name_hierarchy), + "DEBUG" + ) return self - site_id = self.have.get("site_id") - site_name_hierarchy = self.want.get("site_name_hierarchy") api_response, response = self.get_device_ids_from_site(site_name_hierarchy, site_id) - self.log( "Received API response from 'get_membership': {0}".format(str(api_response)), "DEBUG") @@ -2041,7 +2104,7 @@ def get_diff_deleted(self, config): self.site_absent_list.append(str(each_type) + ": " + str(config.get("site_name_hierarchy"))) else: final_deletion_list.append(config) - self.log("Deletion list re-arranged order: {0}.".format(self.pprint(final_deletion_list)), "INFO") + self.log("Deletion list re-arranged order: {0}.".format(final_deletion_list), "INFO") if len(final_deletion_list) > 0: for config in final_deletion_list: @@ -2071,8 +2134,8 @@ def get_diff_deleted(self, config): task_id = response.get("response", {}).get("taskId") if task_id: - task_details = self.get_task_details(task_id) while True: + task_details = self.get_task_details(task_id) if site_type == "area": if task_details.get("progress") == "Group is deleted successfully": self.msg = "Area '{0}' deleted successfully.".format(site_name_hierarchy) From 2c63226d088c2bd345f8ce34d4e5696f2a6b010c Mon Sep 17 00:00:00 2001 From: rukapse <89453872+rukapse@users.noreply.github.com> Date: Tue, 19 Nov 2024 14:18:56 -0500 Subject: [PATCH 31/38] sanity_test_devel --- .github/workflows/sanity_tests_devel.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 73dd4d0bdb..e91f34f353 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -52,7 +52,6 @@ jobs: if [ -s changed_files.txt ]; then changed_files=$(cat changed_files.txt) for file in $changed_files; do -<<<<<<< HEAD # Ensure file paths are relative to the working directory if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" @@ -62,10 +61,6 @@ jobs: fi done echo "Ansible sanity tests completed successfully." -======= - ansible-test sanity --docker -v --color yes $file - done ->>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) else echo "No changed files to test." fi @@ -85,7 +80,6 @@ jobs: lintable_files="$lintable_files $file" fi done - if [ -n "$lintable_files" ]; then yamllint -c .yamllint.yml $lintable_files else From 58b0228bde15c1ea7fded229540dd77679b31c38 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Tue, 19 Nov 2024 20:38:21 -0500 Subject: [PATCH 32/38] Changes not applied --- .github/workflows/sanity_tests_devel.yml | 5 ++++- playbooks/sda_extranet_policies_workflow_manager.yml | 1 - plugins/modules/sda_extranet_policies_workflow_manager.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index e91f34f353..9e27760f65 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -76,10 +76,13 @@ jobs: lintable_files="" for file in $changed_files; do # Check if the file belongs to the plugins/modules or playbooks directory - if [[ $file == plugins/modules/* || $file == playbooks/* ]]; then + if [[ $file == playbooks/* ]]; then lintable_files="$lintable_files $file" fi done + + echo "Lintable Files: $lintable_files" + if [ -n "$lintable_files" ]; then yamllint -c .yamllint.yml $lintable_files else diff --git a/playbooks/sda_extranet_policies_workflow_manager.yml b/playbooks/sda_extranet_policies_workflow_manager.yml index 2583333e1e..28ee9991d5 100644 --- a/playbooks/sda_extranet_policies_workflow_manager.yml +++ b/playbooks/sda_extranet_policies_workflow_manager.yml @@ -62,7 +62,6 @@ 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 diff --git a/plugins/modules/sda_extranet_policies_workflow_manager.py b/plugins/modules/sda_extranet_policies_workflow_manager.py index e6a12a21da..1aa87eb32a 100644 --- a/plugins/modules/sda_extranet_policies_workflow_manager.py +++ b/plugins/modules/sda_extranet_policies_workflow_manager.py @@ -302,7 +302,7 @@ def get_fabric_ids_list(self, site_details): 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 + # Initialize an empty list to store the fabric IDs fabric_ids_list = [] # Iterate over each site's information in the site details From 6af6ad75636cfda5f75425945c8a8390223a5b1d Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Tue, 19 Nov 2024 20:43:52 -0500 Subject: [PATCH 33/38] testing the sanity test cases --- .github/workflows/sanity_tests_devel.yml | 3 +++ playbooks/sda_extranet_policies_workflow_manager.yml | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 9e27760f65..3e9df43ed7 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -56,6 +56,7 @@ jobs: if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" ansible-test sanity --docker -v --color yes $file + echo "Ansible sanity tests completed successfully" else echo "File not found: $file" fi @@ -84,7 +85,9 @@ jobs: echo "Lintable Files: $lintable_files" if [ -n "$lintable_files" ]; then + echo "Running yamllint test for files: $lintable_files" yamllint -c .yamllint.yml $lintable_files + echo "Yamllint tests completed successfully." else echo "No relevant files to lint." fi diff --git a/playbooks/sda_extranet_policies_workflow_manager.yml b/playbooks/sda_extranet_policies_workflow_manager.yml index 28ee9991d5..0f38ee7e0a 100644 --- a/playbooks/sda_extranet_policies_workflow_manager.yml +++ b/playbooks/sda_extranet_policies_workflow_manager.yml @@ -51,7 +51,6 @@ 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 @@ -62,6 +61,7 @@ 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 From 48d0e30db59542c032a2dfa640a932ad6829b13a Mon Sep 17 00:00:00 2001 From: Madhan Date: Wed, 20 Nov 2024 07:18:30 +0530 Subject: [PATCH 34/38] Changes not applied --- .github/workflows/sanity_tests_devel.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 3e9df43ed7..5ce79b1c60 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -81,7 +81,11 @@ jobs: lintable_files="$lintable_files $file" fi done +<<<<<<< HEAD +======= + +>>>>>>> b5f6b52c3 (Test Changes to Sanity Tests (#34)) echo "Lintable Files: $lintable_files" if [ -n "$lintable_files" ]; then From 4ed023b884170111da995398a986efbe2cd5fb75 Mon Sep 17 00:00:00 2001 From: Rugvedi Kapse Date: Tue, 19 Nov 2024 21:05:48 -0500 Subject: [PATCH 35/38] testing the sanity test cases --- .github/workflows/sanity_tests_devel.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 5ce79b1c60..6c2b5f6fd8 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -56,7 +56,6 @@ jobs: if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" ansible-test sanity --docker -v --color yes $file - echo "Ansible sanity tests completed successfully" else echo "File not found: $file" fi From c8df31869deec50171f4f6f99a737b8505917d0c Mon Sep 17 00:00:00 2001 From: Madhan Date: Wed, 20 Nov 2024 09:33:10 +0530 Subject: [PATCH 36/38] Changes not applied --- .github/workflows/sanity_tests_devel.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 6c2b5f6fd8..3e9df43ed7 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -56,6 +56,7 @@ jobs: if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" ansible-test sanity --docker -v --color yes $file + echo "Ansible sanity tests completed successfully" else echo "File not found: $file" fi @@ -80,11 +81,7 @@ jobs: lintable_files="$lintable_files $file" fi done -<<<<<<< HEAD -======= - ->>>>>>> b5f6b52c3 (Test Changes to Sanity Tests (#34)) echo "Lintable Files: $lintable_files" if [ -n "$lintable_files" ]; then From b6e6c2fd6b745e6b9f781d8358df385037aaf043 Mon Sep 17 00:00:00 2001 From: Madhan Date: Wed, 20 Nov 2024 09:33:10 +0530 Subject: [PATCH 37/38] testing the sanity test cases (#35) --- .github/workflows/sanity_tests_devel.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/sanity_tests_devel.yml b/.github/workflows/sanity_tests_devel.yml index 3e9df43ed7..528db59c03 100644 --- a/.github/workflows/sanity_tests_devel.yml +++ b/.github/workflows/sanity_tests_devel.yml @@ -56,7 +56,6 @@ jobs: if [ -f "$file" ]; then echo "Running ansible sanity test for file: $file" ansible-test sanity --docker -v --color yes $file - echo "Ansible sanity tests completed successfully" else echo "File not found: $file" fi From 089bc04c36c5a7191f52ae5cc65756729da79253 Mon Sep 17 00:00:00 2001 From: Madhan Date: Wed, 20 Nov 2024 09:36:41 +0530 Subject: [PATCH 38/38] application of the changes made in pull request #207 --- changelogs/changelog.yaml | 14 ++++++-------- galaxy.yml | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/changelogs/changelog.yaml b/changelogs/changelog.yaml index 8cf4ffcc3f..da5e00691f 100644 --- a/changelogs/changelog.yaml +++ b/changelogs/changelog.yaml @@ -1063,11 +1063,7 @@ releases: - Code change in user_role_manager module - Changes in dnac.py - inventory_workflow_manager.py - added attribute hostnames, serial_numbers and mac_addresses -<<<<<<< HEAD - inventory_workflow_manager.py - Removed attribute hostname_list, serial_number_list and mac_address_list -======= - - inventory_workflow_manager.py - Removed attribute hostname_list, serial_number_list and mac_address_list ->>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) 6.23.0: release_date: "2024-11-06" changes: @@ -1082,11 +1078,7 @@ releases: - Changes in site_workflow_manager - accesspoint_workflow_manager - added attribute bulk_update_aps - sda_fabric_devices_workflow_manager.py - added attribute route_distribution_protocol -<<<<<<< HEAD - - sda_fabric_sites_zones_workflow_manager.py - added attribute site_name_hierarchy and removed attribute site_name -======= - sda_fabric_sites_zones_workflow_manager.py - added attribute site_name_hierarchy and removed attribute site_name ->>>>>>> a03e1ea8b (Merge branch 'main' of https://github.com/cisco-en-programmability/catalyst-center-ansible-intg into network_settings) 6.24.0: release_date: "2024-11-12" changes: @@ -1114,3 +1106,9 @@ releases: - Changes in sda_extranet_policy_workflow_manager - Changes in site_workflow_manager - Modifications due to documentation errors + 6.25.1: + release_date: "2024-12-04" + changes: + release_summary: application of unapplied changes. + minor_changes: + - application of the changes made in pull request 207 diff --git a/galaxy.yml b/galaxy.yml index 941fd45212..c08001f0ae 100644 --- a/galaxy.yml +++ b/galaxy.yml @@ -1,7 +1,7 @@ --- namespace: cisco name: dnac -version: 6.25.0 +version: 6.25.1 readme: README.md authors: - Rafael Campos