diff --git a/plugins/modules/network_settings_workflow_manager.py b/plugins/modules/network_settings_workflow_manager.py index dcc2d5ab0..db6be1003 100644 --- a/plugins/modules/network_settings_workflow_manager.py +++ b/plugins/modules/network_settings_workflow_manager.py @@ -100,6 +100,13 @@ The former identifier for the global pool. It should be used exclusively when you need to update the global pool's name. type: str + force_delete: + description: > + Forcefully delete all IP pools from the global level of the global pool. + The default value is false. + type: bool + required: false + default: false reserve_pool_details: description: Reserved IP subpool details from the global pool. @@ -227,6 +234,14 @@ Allows devices on IPv6 networks to self-configure their IP addresses autonomously, eliminating the need for manual setup. type: bool + force_delete: + description: > + Forcefully delete all IP pools from the reserve level of the IP sub-pool. + The default value is false. + type: bool + required: false + default: false + network_management_details: description: Set default network settings for the site type: list @@ -445,10 +460,10 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - global_pool_details: settings: @@ -470,25 +485,25 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - reserve_pool_details: - site_name: string name: string pool_type: LAN - ipv6_address_space: True + ipv6_address_space: true ipv4_global_pool: string - ipv4_prefix: True + ipv4_prefix: true ipv4_prefix_length: 9 ipv4_subnet: string - ipv6_prefix: True + ipv6_prefix: true ipv6_prefix_length: 64 ipv6_global_pool: string ipv6_subnet: string - slaac_support: True + slaac_support: true - name: Create reserve an ip pool using global pool name cisco.dnac.network_settings_workflow_manager: @@ -499,25 +514,25 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - reserve_pool_details: - name: string site_name: string pool_type: LAN - ipv6_address_space: True + ipv6_address_space: true ipv4_global_pool_name: string - ipv4_prefix: True + ipv4_prefix: true ipv4_prefix_length: 9 ipv4_subnet: string - ipv6_prefix: True + ipv6_prefix: true ipv6_prefix_length: 64 ipv6_global_pool_name: string ipv6_subnet: string - slaac_support: True + slaac_support: true - name: Delete reserved pool cisco.dnac.network_settings_workflow_manager: @@ -528,10 +543,10 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: deleted - config_verify: True + config_verify: true config: - reserve_pool_details: - site_name: string @@ -565,10 +580,10 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - network_management_details: - site_name: string @@ -587,10 +602,10 @@ ip_address: string port: 443 snmp_server: - configure_dnac_ip: True + configure_dnac_ip: true ip_addresses: list syslog_server: - configure_dnac_ip: True + configure_dnac_ip: true ip_addresses: list - name: Adding the network_aaa and client_and_endpoint_aaa AAA server @@ -602,10 +617,10 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - network_management_details: - site_name: string @@ -630,10 +645,10 @@ dnac_port: "{{dnac_port}}" dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" - dnac_log: True + dnac_log: true dnac_log_level: "{{ dnac_log_level }}" state: merged - config_verify: True + config_verify: true config: - network_management_details: - site_name: string @@ -751,6 +766,7 @@ def validate_input(self): "name": {"type": 'string'}, "prev_name": {"type": 'string'}, "pool_type": {"type": 'string', "choices": ["Generic", "Tunnel"]}, + 'force_delete': {'type': 'bool', 'required': False, 'default': True}, } } }, @@ -782,6 +798,7 @@ def validate_input(self): "type": 'string', "choices": ["Generic", "LAN", "Management", "Service", "WAN"] }, + 'force_delete': {'type': 'bool', 'required': False, 'default': True}, }, "network_management_details": { "type": 'list', @@ -1745,7 +1762,7 @@ def get_network_params_v2(self, site_name, site_id): return network_details - def get_reserved_ip_subpool(self, site_id): + def get_reserved_ip_subpool(self, site_name, site_id): """ Retrieve all the reserved IP subpool details from the Cisco Catalyst Center. @@ -1757,7 +1774,7 @@ def get_reserved_ip_subpool(self, site_id): self (object) - The current object with updated desired reserved subpool information. """ - value = 1 + offset = 1 self.all_reserved_pool_details.update({site_id: []}) start_time = time.time() while True: @@ -1768,14 +1785,12 @@ def get_reserved_ip_subpool(self, site_id): op_modifies=True, params={ "site_id": site_id, - "offset": value + "offset": offset } ) except Exception as msg: - self.msg = ( - "Exception occurred while getting the reserved pool details " - "from Cisco Catalyst Center: {msg}".format(msg=msg) - ) + self.msg = "Exception occurred while fetching reserved pool details for site '{0}': {1}".format( + site_name, site_id) self.log(str(msg), "ERROR") self.status = "failed" return self @@ -1787,12 +1802,19 @@ def get_reserved_ip_subpool(self, site_id): reserve_pool_details = response.get("response") if not reserve_pool_details: - self.log("No subpools are reserved in the site with ID - '{0}'." - .format(site_id), "DEBUG") + self.log("No subpools are reserved in the site with ID - '{0}': '{1}'." + .format(site_id, site_name), "DEBUG") return self self.all_reserved_pool_details.get(site_id).extend(reserve_pool_details) - value += 25 + + if len(reserve_pool_details) < 25: + self.log("Found {0} record(s), No more record available for the next offset" + .format(str(len(reserve_pool_details))), "INFO") + break + + offset += 25 + end_time = time.time() if (end_time - start_time) >= self.max_timeout: self.msg = ( @@ -1823,13 +1845,14 @@ def global_pool_exists(self, name): "details": None, "id": None } - value = 1 + all_global_pool = [] + offset = 1 while True: try: response = self.dnac._exec( family="network_settings", function="get_global_pool", - params={"offset": value} + params={"offset": offset} ) except Exception as msg: self.msg = ( @@ -1848,18 +1871,43 @@ def global_pool_exists(self, name): all_global_pool_details = response.get("response") if not all_global_pool_details: + if name == "": + self.log("Global pool '{0}' does not exist".format(all_global_pool), "INFO") + return all_global_pool self.log("Global pool '{0}' does not exist".format(name), "INFO") return global_pool - global_pool_details = get_dict_result(all_global_pool_details, "ipPoolName", name) - if global_pool_details: + global_pool_details = None + if name == "": + global_pool_details = all_global_pool_details + else: + global_pool_details = get_dict_result(all_global_pool_details, "ipPoolName", name) + + if global_pool_details and isinstance(global_pool_details, dict): self.log("Global pool found with name '{0}': {1}".format(name, global_pool_details), "INFO") global_pool.update({"exists": True}) global_pool.update({"id": global_pool_details.get("id")}) global_pool["details"] = self.get_global_pool_params(global_pool_details) break - value += 25 + if global_pool_details and isinstance(global_pool_details, list) and name == "": + self.log("Global pool found {0}".format( + self.pprint(global_pool_details)), "INFO") + + for each_pool in global_pool_details: + global_del_pool = { + "exists": True, + "id": each_pool.get("id"), + "details": self.get_global_pool_params(each_pool) + } + all_global_pool.append(global_del_pool) + + if len(all_global_pool_details) < 25: + self.log("Found {0} record(s), No more record available for the next offset" + .format(str(len(all_global_pool_details))), "INFO") + break + + offset += 25 self.log("Formatted global pool details: {0}".format(global_pool), "DEBUG") return global_pool @@ -1895,24 +1943,48 @@ def reserve_pool_exists(self, name, site_name): return reserve_pool if not self.all_reserved_pool_details.get(site_id): - self.get_reserved_ip_subpool(site_id) + self.get_reserved_ip_subpool(site_name, site_id) if not self.all_reserved_pool_details.get(site_id): self.log("Reserved pool {0} does not exist in the site {1}" .format(name, site_name), "DEBUG") return reserve_pool - reserve_pool_details = get_dict_result(self.all_reserved_pool_details.get(site_id), "groupName", name) - if reserve_pool_details: - self.log("Reserve pool found with name '{0}' in the site '{1}': {2}" + reserve_pool_details = None + if name == "": + reserve_pool_details = self.all_reserved_pool_details.get(site_id) + else: + reserve_pool_details = get_dict_result( + self.all_reserved_pool_details.get(site_id), "groupName", name) + + if reserve_pool_details and isinstance(reserve_pool_details, dict): + self.log("Reserve pool found with name {0} in the site '{1}': {2}" .format(name, site_name, reserve_pool_details), "INFO") reserve_pool.update({"exists": True}) reserve_pool.update({"id": reserve_pool_details.get("id")}) reserve_pool.update({"details": self.get_reserve_pool_params(reserve_pool_details)}) + self.log("Reserved pool details: {0}".format(reserve_pool.get("details")), "DEBUG") + self.log("Reserved pool id: {0}".format(reserve_pool.get("id")), "DEBUG") + return reserve_pool - self.log("Reserved pool details: {0}".format(reserve_pool.get("details")), "DEBUG") - self.log("Reserved pool id: {0}".format(reserve_pool.get("id")), "DEBUG") - return reserve_pool + if reserve_pool_details and isinstance(reserve_pool_details, list): + self.log("Found reserve pools for site '{0}': {1}" + .format(site_name, self.pprint(reserve_pool_details)), "INFO") + all_reserve_pool = [] + for each_pool in reserve_pool_details: + reserve_del_pool = { + "exists": True, + "id": each_pool.get("id"), + "details": self.get_reserve_pool_params(each_pool), + "success": True + } + all_reserve_pool.append(reserve_del_pool) + + self.log("Reserved pool list details: {0}".format( + self.pprint(all_reserve_pool)), "DEBUG") + return all_reserve_pool + + return None def get_have_global_pool(self, global_pool_details): """ @@ -1944,22 +2016,26 @@ def get_have_global_pool(self, global_pool_details): errors = [] # To collect all error messages for pool_details in global_pool_ippool: + delete_all = pool_details.get("force_delete") name = pool_details.get("name") - if name is None: - errors.append("Missing required parameter 'name' in global_pool_details: {}".format(pool_details)) - continue + if delete_all: + name = "" + else: + if name is None: + errors.append("Missing required parameter 'name' in global_pool_details: {}".format(pool_details)) + continue - name_length = len(name) - if name_length > 100: - errors.append("The length of the 'name' in global_pool_details should be less or equal to 100. Invalid_config: {}".format(pool_details)) + name_length = len(name) + if name_length > 100: + errors.append("The length of the 'name' in global_pool_details should be less or equal to 100. Invalid_config: {}".format(pool_details)) - if " " in name: - errors.append("The 'name' in global_pool_details should not contain any spaces. Invalid_config: {}".format(pool_details)) + if " " in name: + errors.append("The 'name' in global_pool_details should not contain any spaces. Invalid_config: {}".format(pool_details)) - pattern = r'^[\w\-./]+$' - if not re.match(pattern, name): - errors.append("The 'name' in global_pool_details should contain only letters, numbers, and -_./ characters. Invalid_config: {}" - .format(pool_details)) + pattern = r'^[\w\-./]+$' + if not re.match(pattern, name): + errors.append("The 'name' in global_pool_details should contain only letters, numbers, and -_./ characters. Invalid_config: {}" + .format(pool_details)) if errors: # If there are errors, return a failure status with all messages @@ -1967,19 +2043,32 @@ def get_have_global_pool(self, global_pool_details): self.status = "failed" return self + if name == "": + global_pool.append(self.global_pool_exists(name)) + self.log("Global pool details: {0}".format(global_pool), "DEBUG") + self.have.update({"globalPool": global_pool}) + self.msg = "Collected all global pool details from the Cisco Catalyst Center." + self.status = "success" + return self + for pool_details in global_pool_ippool: - name = pool_details.get("name") # If the Global Pool doesn't exist and a previous name is provided # Else try using the previous name + name = pool_details.get("name") + if pool_details.get("force_delete"): + name = "" + global_pool.append(self.global_pool_exists(name)) - self.log("Global pool details of '{0}': {1}".format(name, global_pool[global_pool_index]), "DEBUG") + self.log("Global pool details of '{0}': {1}".format( + name, global_pool[global_pool_index]), "DEBUG") prev_name = pool_details.get("prev_name") if global_pool[global_pool_index].get("exists") is False and \ prev_name is not None: global_pool.pop() global_pool.append(self.global_pool_exists(prev_name)) if global_pool[global_pool_index].get("exists") is False: - self.msg = "Prev name {0} doesn't exist in global_pool_details".format(prev_name) + self.msg = "Prev name {0} doesn't exist in global_pool_details".format( + prev_name) self.status = "failed" return self @@ -2008,28 +2097,33 @@ def get_have_reserve_pool(self, reserve_pool_details): reserve_pool = [] reserve_pool_index = 0 for item in reserve_pool_details: + delete_all = item.get("force_delete") name = item.get("name") - if name is None: - self.msg = "Missing required parameter 'name' in reserve_pool_details." - self.status = "failed" - return self + site_name = item.get("site_name") + if delete_all: + name = "" + else: + if name is None: + self.msg = "Missing required parameter 'name' in reserve_pool_details." + self.status = "failed" + return self - name_length = len(name) - if name_length > 100: - self.msg = "The length of the 'name' in reserve_pool_details should be less or equal to 100." - self.status = "failed" - return self + name_length = len(name) + if name_length > 100: + self.msg = "The length of the 'name' in reserve_pool_details should be less or equal to 100." + self.status = "failed" + return self - if " " in name: - self.msg = "The 'name' in reserve_pool_details should not contain any spaces." - self.status = "failed" - return self + if " " in name: + self.msg = "The 'name' in reserve_pool_details should not contain any spaces." + self.status = "failed" + return self - pattern = r'^[\w\-./]+$' - if not re.match(pattern, name): - self.msg = "The 'name' in reserve_pool_details should contain only letters, numbers and -_./ characters." - self.status = "failed" - return self + pattern = r'^[\w\-./]+$' + if not re.match(pattern, name): + self.msg = "The 'name' in reserve_pool_details should contain only letters, numbers and -_./ characters." + self.status = "failed" + return self site_name = item.get("site_name") self.log("Site Name: {0}".format(site_name), "DEBUG") @@ -2040,37 +2134,48 @@ def get_have_reserve_pool(self, reserve_pool_details): # Check if the Reserved Pool exists in Cisco Catalyst Center # based on the provided name and site name - reserve_pool.append(self.reserve_pool_exists(name, site_name)) - if not reserve_pool[reserve_pool_index].get("success"): + if self.reserve_pool_exists(name, site_name): + reserve_pool.append(self.reserve_pool_exists(name, site_name)) + else: + self.have.update({"reservePool": reserve_pool}) + return self + + if name != "" and not reserve_pool[reserve_pool_index].get("success"): + return self.check_return_status() + elif name == "" and len(reserve_pool[reserve_pool_index]) < 1: return self.check_return_status() - self.log("Reserved pool details for '{0}': {1}".format(name, reserve_pool[reserve_pool_index]), "DEBUG") - # If the Reserved Pool doesn't exist and a previous name is provided - # Else try using the previous name - prev_name = item.get("prev_name") - if reserve_pool[reserve_pool_index].get("exists") is False and \ - prev_name is not None: - reserve_pool.pop() - reserve_pool.append(self.reserve_pool_exists(prev_name, site_name)) - if not reserve_pool[reserve_pool_index].get("success"): - return self.check_return_status() - - # If the previous name doesn't exist in Cisco Catalyst Center, return with error - if reserve_pool[reserve_pool_index].get("exists") is False: - self.msg = "Prev name {0} doesn't exist in reserve_pool_details".format(prev_name) - self.status = "failed" - return self + self.log("Reserved pool details for {0}: {1}" + .format(name, reserve_pool[reserve_pool_index]), "DEBUG") + + if name != "": + # If the Reserved Pool doesn't exist and a previous name is provided + # Else try using the previous name + prev_name = item.get("prev_name") + if reserve_pool[reserve_pool_index].get("exists") is False and prev_name is not None: + self.log("Current pool does not exist. Checking for previous name '{0}'." + .format(prev_name), "DEBUG") + reserve_pool.pop() + reserve_pool.append(self.reserve_pool_exists(prev_name, site_name)) + if not reserve_pool[reserve_pool_index].get("success"): + return self.check_return_status() + + # If the previous name doesn't exist in Cisco Catalyst Center, return with error + if reserve_pool[reserve_pool_index].get("exists") is False: + self.msg = "Prev name {0} doesn't exist in reserve_pool_details".format(prev_name) + self.status = "failed" + return self - self.log("Reserved pool exists: {0}".format(reserve_pool[reserve_pool_index].get("exists")), "DEBUG") - self.log("Reserved pool: {0}".format(reserve_pool[reserve_pool_index].get("details")), "DEBUG") + self.log("Reserved pool exists: {0}".format(reserve_pool[reserve_pool_index].get("exists")), "DEBUG") + self.log("Reserved pool: {0}".format(reserve_pool[reserve_pool_index].get("details")), "DEBUG") - # If reserve pool exist, convert ipv6AddressSpace to the required format (boolean) - if reserve_pool[reserve_pool_index].get("exists"): - reserve_pool_info = reserve_pool[reserve_pool_index].get("details") - if reserve_pool_info.get("ipv6AddressSpace") == "False": - reserve_pool_info.update({"ipv6AddressSpace": False}) - else: - reserve_pool_info.update({"ipv6AddressSpace": True}) + # If reserve pool exist, convert ipv6AddressSpace to the required format (boolean) + if reserve_pool[reserve_pool_index].get("exists"): + reserve_pool_info = reserve_pool[reserve_pool_index].get("details") + if reserve_pool_info.get("ipv6AddressSpace") == "False": + reserve_pool_info.update({"ipv6AddressSpace": False}) + else: + reserve_pool_info.update({"ipv6AddressSpace": True}) reserve_pool_index += 1 @@ -3719,6 +3824,73 @@ def get_diff_merged(self, config): self.update_network(network_management).check_return_status() return self + def delete_ip_pool(self, name, pool_id, function_name, pool_type): + """ + Delete single reserve/global pool from based on the pool ID and return + execution id and status message. + + Parameters: + name (str) - name contains ip pool name for release ip pool + pool_id (str) - ID contails IP pool id from get ip pool + function_name (str) - contains execution of sdk function name either + release_reserve_ip_subpool or delete_global_ip_pool + pool_type (str) - contains string message for log either Reserve or Global + + Returns: + execution_details (dict) - contains response for the delete execution + contains name, execution id and status message. + """ + self.log("{0} IP pool scheduled for deletion: {1}".format(pool_type, name), "INFO") + self.log("{0} pool '{1}' id: {2}".format(pool_type, name, pool_id), "DEBUG") + try: + response = self.dnac._exec( + family="network_settings", + function=function_name, + op_modifies=True, + params={"id": pool_id}, + ) + self.check_execution_response_status(response, + function_name).check_return_status() + self.log("Response received from delete {0} pool API: {1}". + format(pool_type, self.pprint(response)), "DEBUG") + execution_id = response.get("executionId") + success_msg, failed_msg = None, None + + if pool_type == "Global": + success_msg = "Global pool deleted successfully" + failed_msg = "Unable to delete global pool reservation" + else: + success_msg = "Ip subpool reservation released successfully" + failed_msg = "Unable to release subpool reservation" + + if execution_id: + return { + "name": name, + "execution_id": execution_id, + "msg": success_msg, + "status": "success" + } + self.log("No execution ID received for '{name}'".format(name=name), "ERROR") + return { + "name": name, + "execution_id": None, + "msg": failed_msg, + "status": "failed" + } + + except Exception as e: + error_msg = ( + "Exception occurred while deleting the {type} pool with the name '{name}': {error}" + .format(name=name, error=str(e), type=pool_type) + ) + self.log(error_msg, "ERROR") + return { + "name": name, + "execution_id": None, + "msg": error_msg, + "status": "failed" + } + def delete_reserve_pool(self, reserve_pool_details): """ Delete a Reserve Pool by name in Cisco Catalyst Center @@ -3729,47 +3901,68 @@ def delete_reserve_pool(self, reserve_pool_details): Returns: self - The current object with Global Pool, Reserved Pool, Network Servers information. """ - reserve_pool_index = -1 for item in reserve_pool_details: reserve_pool_index += 1 - name = item.get("name") - reserve_pool_exists = self.have.get("reservePool")[reserve_pool_index].get("exists") + delete_all = item.get("force_delete") + site_name = item.get("site_name") result_reserve_pool = self.result.get("response")[1].get("reservePool") - - if not reserve_pool_exists: - result_reserve_pool.get("msg").update({name: "Reserve Pool not found"}) - self.log("Reserved Ip Subpool '{0}' not found".format(name), "INFO") - continue - - self.log("Reserved IP pool scheduled for deletion: {0}" - .format(self.have.get("reservePool")[reserve_pool_index].get("name")), "INFO") - _id = self.have.get("reservePool")[reserve_pool_index].get("id") - self.log("Reserved pool '{0}' id: {1}".format(name, _id), "DEBUG") - try: - response = self.dnac._exec( - family="network_settings", - function="release_reserve_ip_subpool", - op_modifies=True, - params={"id": _id}, - ) - except Exception as msg: - self.msg = ( - "Exception occurred while updating the reserved pool with the name '{name}': {msg}" - .format(name=name, msg=msg) + if delete_all: + self.log("Delete all reserved pools operation initiated for site '{0}'" + .format(site_name), "INFO") + have_reserve_pool = reserve_pool = self.have.get("reservePool")[reserve_pool_index] + result_reserve_pool.get("response").update({site_name: []}) + if have_reserve_pool and len(have_reserve_pool) > 0: + if isinstance(have_reserve_pool, dict): + self.log("Found reserved pools for site '{0}': {1}" + .format(site_name, self.pprint(have_reserve_pool)), "DEBUG") + reserve_pool = have_reserve_pool.get("exists") + if not reserve_pool: + result_reserve_pool.get("msg").update({site_name: "Reserve Pool not found"}) + self.log("Reserved IP Subpool '{0}' not found".format(site_name), "INFO") + continue + + for each_pool in have_reserve_pool: + if not each_pool.get("exists"): + result_reserve_pool["msg"].update({site_name: "Reserve Pool not found"}) + self.log("Reserved IP Subpool '{0}' not found for deletion".format(site_name), "INFO") + continue + + pool_name = each_pool.get("details", {}).get("name") + pool_id = each_pool.get("id") + self.log("Processing deletion for reserved pool '{0}' with ID '{1}'" + .format(pool_name, pool_id), "INFO") + execution_details = self.delete_ip_pool(pool_name, pool_id, + "release_reserve_ip_subpool", + "Reserve") + result_reserve_pool["response"][site_name].append(execution_details) + self.log("Deletion completed for reserved pool '{0}' with ID '{1}'" + .format(pool_name, pool_id), "DEBUG") + else: + result_reserve_pool["msg"].update({site_name: "No Reserve Pools available"}) + self.log("No Reserved IP Subpools found for site '{0}'. Skipping deletion." + .format(site_name), "INFO") + else: + pool_name = item.get("name") + pool_id = self.have.get("reservePool")[reserve_pool_index].get("id") + self.log("Delete operation initiated for specific reserved pool '{0}'".format(pool_name), "INFO") + + reserve_pool = None + if self.have.get("reservePool"): + reserve_pool = self.have.get("reservePool")[reserve_pool_index].get("exists") + + if not reserve_pool: + result_reserve_pool.get("msg").update({pool_name: "Reserve Pool not found"}) + self.log("Reserved IP Subpool '{0}' not found. Skipping deletion.".format(pool_name), "INFO") + continue + + self.log("Reserved IP pool '{0}' scheduled for deletion".format(pool_name), "INFO") + self.log("Reserved pool '{0}' ID: {1}".format(pool_name, pool_id), "DEBUG") + execution_details = self.delete_ip_pool( + pool_name, pool_id, "release_reserve_ip_subpool", "Reserve" ) - self.log(str(msg), "ERROR") - self.status = "failed" - return self - - self.check_execution_response_status(response, "release_reserve_ip_subpool").check_return_status() - executionid = response.get("executionId") - result_reserve_pool = self.result.get("response")[1].get("reservePool") - result_reserve_pool.get("response").update({name: {}}) - result_reserve_pool.get("response").get(name) \ - .update({"Execution Id": executionid}) - result_reserve_pool.get("msg") \ - .update({name: "Ip subpool reservation released successfully"}) + self.log("Deletion completed for reserved pool '{0}' with ID '{1}'".format(pool_name, pool_id), "DEBUG") + result_reserve_pool["response"].update({pool_name: execution_details}) self.msg = "Reserved pool(s) released successfully" self.status = "success" @@ -3789,40 +3982,41 @@ def delete_global_pool(self, global_pool_details): result_global_pool = self.result.get("response")[0].get("globalPool") global_pool_index = 0 for item in self.have.get("globalPool"): - global_pool_exists = item.get("exists") - name = global_pool_details.get("settings").get("ip_pool")[global_pool_index].get("name") - global_pool_index += 1 - if not global_pool_exists: - result_global_pool.get("msg").update({name: "Global Pool not found"}) - self.log("Global pool '{0}' not found".format(name), "INFO") - continue - - id = item.get("id") - try: - response = self.dnac._exec( - family="network_settings", - function="delete_global_ip_pool", - op_modifies=True, - params={"id": id}, - ) - except Exception as msg: - self.msg = ( - "Exception occurred while deleting the global pool with '{name}': {msg}" - .format(name=name, msg=msg) - ) - self.log(str(msg), "ERROR") - self.status = "failed" - return self - - # Check the execution status - self.check_execution_response_status(response, "delete_global_ip_pool").check_return_status() - executionid = response.get("executionId") - - # Update result information - result_global_pool = self.result.get("response")[0].get("globalPool") - result_global_pool.get("response").update({name: {}}) - result_global_pool.get("response").get(name).update({"Execution Id": executionid}) - result_global_pool.get("msg").update({name: "Global pool deleted successfully"}) + if isinstance(item, list): + self.log("Processing global pool deletion for a list of items", "INFO") + for each_item in item: + global_pool_exists = each_item.get("exists") + pool_name = each_item.get("details").get("ipPoolName") + global_pool_index += 1 + + if not global_pool_exists: + result_global_pool.get("msg").update({pool_name: "Global Pool not found"}) + self.log("Global pool '{0}' not found".format(pool_name), "INFO") + continue + + execution_details = {} + pool_id = each_item.get("id") + execution_details = self.delete_ip_pool(pool_name, pool_id, + "delete_global_ip_pool", + "Global") + self.log("Deletion completed for global pool '{0}'".format(pool_name), "DEBUG") + result_global_pool["response"][pool_name].append(execution_details) + else: + self.log("Processing global pool deletion for a single item", "INFO") + global_pool_exists = item.get("exists") + pool_name = global_pool_details.get("settings").get("ip_pool")[global_pool_index].get("name") + global_pool_index += 1 + if not global_pool_exists: + result_global_pool.get("msg").update({pool_name: "Global Pool not found"}) + self.log("Global pool '{0}' not found. Skipping deletion.".format(pool_name), "INFO") + continue + + self.log("Global pool '{0}' exists. Proceeding with deletion.".format(pool_name), "INFO") + id = item.get("id") + execution_details = self.delete_ip_pool(pool_name, id, + "delete_global_ip_pool", + "Global") + result_global_pool.get("response").update({pool_name: execution_details}) self.msg = "Global pools deleted successfully" self.status = "success" @@ -3949,37 +4143,105 @@ def verify_diff_deleted(self, config): self.get_have(config) self.log("Current State (have): {0}".format(self.have), "INFO") self.log("Desired State (want): {0}".format(self.want), "INFO") + delete_all = [] if config.get("global_pool_details") is not None: + self.log("Starting validation for Global Pool absence.", "INFO") global_pool_index = 0 global_pool_details = self.have.get("globalPool") for item in global_pool_details: - global_pool_exists = item.get("exists") - name = config.get("global_pool_details").get("settings") \ - .get("ip_pool")[global_pool_index].get("name") - if global_pool_exists: - self.msg = "Global Pool Config '{0}' is not applied to the Cisco Catalyst Center" \ - .format(name) - self.status = "failed" - return self + if isinstance(item, dict): + global_pool_exists = item.get("exists") + name = config.get("global_pool_details").get("settings")\ + .get("ip_pool")[global_pool_index].get("name") + if global_pool_exists: + self.msg = "Global Pool Config '{0}' is not applied to the Cisco Catalyst Center".format(name) + self.status = "failed" + return self + + self.log("Successfully validated absence of Global Pool '{0}'.". + format(name), "INFO") + else: + if len(item) > 0: + for each_ip_pool in item: + each_pool_validation = {} + global_pool_exists = each_ip_pool.get("exists") + name = each_ip_pool.get("details", {}).get("ipPoolName") + if global_pool_exists: + each_pool_validation = { + "name": name, + "msg": "Global Pool Config is not applied to the Catalyst Center", + "validation": "failed", + } + delete_all.append(each_pool_validation) - self.log("Successfully validated absence of Global Pool '{0}'.".format(name), "INFO") global_pool_index += 1 + + if len(delete_all) > 0: + self.msg = "Global Pool Config is not applied to the Catalyst Center" + self.set_operation_result("failed", False, self.msg, + "ERROR", delete_all).check_return_status() + self.result.get("response")[0].get("globalPool").update({"Validation": "Success"}) + self.msg = "Successfully validated the absence of Global Pool." + self.log(self.msg, "INFO") + self.log("Last Check {0}".format(self.result.get("response")[0].get("globalPool")), "INFO") + del_response = self.result.get("response")[0].get("globalPool").get("response") + delete_all.append(del_response) + self.set_operation_result("success", True, self.msg, + "INFO", del_response).check_return_status() + if config.get("reserve_pool_details") is not None: + self.log("Starting validation for Reserve Pool absence.", "INFO") reserve_pool_index = 0 reserve_pool_details = self.have.get("reservePool") + for item in reserve_pool_details: - reserve_pool_exists = item.get("exists") - name = config.get("reserve_pool_details")[reserve_pool_index].get("name") - if reserve_pool_exists: - self.msg = "Reserved Pool Config '{0}' is not applied to the Catalyst Center" \ - .format(name) - self.status = "failed" - return self + site_name = config.get("reserve_pool_details")[reserve_pool_index].get("site_name") + if isinstance(item, dict): + reserve_pool_exists = item.get("exists") + name = config.get("reserve_pool_details")[reserve_pool_index].get("name") + + if reserve_pool_exists: + self.msg = "Reserved Pool Config '{0}' is not applied to the Catalyst Center"\ + .format(name) + self.set_operation_result("failed", False, self.msg, "ERROR").check_return_status() + + self.log("Successfully validated the absence of Reserve Pool '{0}'.".format(name), "INFO") + reserve_pool_index += 1 - self.log("Successfully validated the absence of Reserve Pool '{0}'.".format(name), "INFO") - self.result.get("response")[1].get("reservePool").update({"Validation": "Success"}) + if self.result.get("response"): + self.result.get("response")[1].get("reservePool").update({"Validation": "Success"}) + + else: + if len(item) > 0: + for each_ip_pool in item: + each_pool_validation = {} + reserve_pool_exists = each_ip_pool.get("exists") + if reserve_pool_exists: + self.log("Reserve Pool '{0}' found. Marking as failed validation." + .format(name), "ERROR") + each_pool_validation = { + "site_name": site_name, + "name": each_ip_pool.get("details", {}).get("name"), + "msg": "Reserved Pool Config is not applied to the Catalyst Center", + "validation": "failed", + } + delete_all.append(each_pool_validation) + + reserve_pool_index += 1 + + if len(delete_all) > 0: + self.msg = "Reserved Pool Config is not applied to the Catalyst Center" + self.set_operation_result("failed", False, self.msg, + "ERROR", delete_all).check_return_status() + + self.msg = "Successfully validated the absence of Reserve Pool." + self.log(self.msg, "INFO") + del_response = self.result.get("response")[1].get("reservePool").get("response") + delete_all.append(del_response) + self.set_operation_result("success", True, self.msg, + "INFO", del_response).check_return_status() self.msg = "Successfully validated the absence of Global Pool/Reserve Pool" self.status = "success"