From d808ffaf134af1058611441d8c55417b0054f329 Mon Sep 17 00:00:00 2001 From: SHUBHAM VARFA Date: Thu, 1 Feb 2024 04:25:11 +0000 Subject: [PATCH 1/3] Removing config_verify, dnac_log_level from code along with fixing issues related to discovery --- playbooks/PnP.yml | 1 + plugins/modules/discovery_intent.py | 121 ++++++++++++++-------------- plugins/modules/pnp_intent.py | 9 --- 3 files changed, 62 insertions(+), 69 deletions(-) diff --git a/playbooks/PnP.yml b/playbooks/PnP.yml index 1c3dd206ad..b034bc3f81 100644 --- a/playbooks/PnP.yml +++ b/playbooks/PnP.yml @@ -16,6 +16,7 @@ dnac_port: "{{ dnac_port }}" dnac_version: "{{ dnac_version }}" dnac_debug: "{{ dnac_debug }}" + dnac_log_level: DEBUG tasks: diff --git a/plugins/modules/discovery_intent.py b/plugins/modules/discovery_intent.py index e43000d47e..e15df25204 100644 --- a/plugins/modules/discovery_intent.py +++ b/plugins/modules/discovery_intent.py @@ -22,15 +22,6 @@ author: Abinash Mishra (@abimishr) Phan Nguyen (phannguy) options: - config_verify: - description: Set to True to verify the Cisco DNA Center config after applying the playbook config. - type: bool - default: False - dnac_log_level: - description: Specifies the log level for Cisco Catalyst Center logging, categorizing logs by severity. - Options- [CRITICAL, ERROR, WARNING, INFO, DEBUG] - type: str - default: WARNING state: description: The state of DNAC after module completion. type: str @@ -43,19 +34,11 @@ elements: dict required: true suboptions: - devices_list: - description: List of devices with details necessary for discovering the devices. + ip_address_list: + description: List of IP addresses to be discoverred. type: list - elements: dict + elements: str required: true - suboptions: - name: - description: Hostname of the device - type: str - ip: - description: Management IP address of the device - type: str - required: true discovery_type: description: Type of discovery (SINGLE/RANGE/MULTI RANGE/CDP/LLDP) type: str @@ -147,10 +130,13 @@ snmp_version: description: Version of SNMP (v2/v3) type: str - required: true timeout: description: Time to wait for device response in seconds type: int + cli_cred_len: + description: Total CLI credentials that needs to be used. This value can vary from 1 to 5. + type: int + default: 1 requirements: - dnacentersdk == 2.6.10 - python >= 3.5 @@ -186,10 +172,9 @@ dnac_log: True dnac_log_level: "{{dnac_log_level}}" state: merged + config_verify: True config: - - devices_list: - - name: string - ip: string + - ip_address_list: list discovery_type: string cdp_level: string lldp_level: string @@ -217,6 +202,7 @@ snmp_version: string timeout: integer username_list: list + cli_cred_length: integer - name: Delete disovery by name cisco.dnac.discovery_intent: dnac_host: "{{dnac_host}}" @@ -227,11 +213,11 @@ dnac_version: "{{dnac_version}}" dnac_debug: "{{dnac_debug}}" dnac_log: True + dnac_log_level: "{{dnac_log_level}}" state: deleted + config_verify: True config: - - devices_list: - - name: string - ip: string + - ip_address_list: list start_index: integer records_to_return: integer discovery_name: string @@ -335,8 +321,8 @@ def validate_input(self): 'discovery_type': {'type': 'str', 'required': True}, 'enable_password_list': {'type': 'list', 'required': False, 'elements': 'str'}, - 'devices_list': {'type': 'list', 'required': True, - 'elements': 'dict'}, + 'ip_address_list': {'type': 'list', 'required': True, + 'elements': 'str'}, 'start_index': {'type': 'int', 'required': False, 'default': 25}, 'records_to_return': {'type': 'int', 'required': False}, @@ -366,10 +352,12 @@ def validate_input(self): 'snmp_rw_community': {'type': 'str', 'required': False}, 'snmp_rw_community_desc': {'type': 'str', 'required': False}, 'snmp_username': {'type': 'str', 'required': False}, - 'snmp_version': {'type': 'str', 'required': True}, + 'snmp_version': {'type': 'str', 'required': False}, 'timeout': {'type': 'str', 'required': False}, 'username_list': {'type': 'list', 'required': False, - 'elements': 'str'} + 'elements': 'str'}, + 'cli_cred_len': {'type': 'int', 'required': False, + 'default': 1} } # Validate discovery params @@ -399,7 +387,7 @@ def get_creds_ids_list(self): the class instance. """ - self.log("Credential IDs list is {0}".format(str(self.creds_ids_list)), "INFO") + self.log("Credential Ids list passed is {0}".format(str(self.creds_ids_list)), "INFO") return self.creds_ids_list def get_dnac_global_credentials_v2_info(self): @@ -422,11 +410,19 @@ def get_dnac_global_credentials_v2_info(self): params=self.validated_config[0].get('headers'), ) response = response.get('response') + cli_len_inp = self.validated_config[0].get("cli_cred_len") + if cli_len_inp > 5: + cli_len_inp = 5 self.log("The Global credentials response from 'get all global credentials v2' API is {0}".format(str(response)), "DEBUG") - for value in response.values(): - if not value: - continue - self.creds_ids_list.extend(element.get('id') for element in value) + cli_len = 0 + for key in response.keys(): + if key == "cliCredential": + for element in response.get(key): + while cli_len < cli_len_inp: + self.creds_ids_list.append(element.get('id')) + cli_len += 1 + else: + self.creds_ids_list.extend(element.get('id') for element in response.get(key)) if not self.creds_ids_list: msg = 'Not found any credentials to perform discovery' @@ -441,22 +437,22 @@ def get_devices_list_info(self): It then updates the result attribute with this list. Returns: - - devices_list: The list of devices extracted from the + - ip_address_list: The list of devices extracted from the 'validated_config' attribute. """ - devices_list = self.validated_config[0].get('devices_list') - self.result.update(dict(devices_info=devices_list)) - self.log("Devices list info passed is {0}".format(str(devices_list)), "INFO") - return devices_list + ip_address_list = self.validated_config[0].get('ip_address_list') + self.result.update(dict(devices_info=ip_address_list)) + self.log("Devices list info passed: {0}".format(str(ip_address_list)), "INFO") + return ip_address_list - def preprocessing_devices_info(self, devices_list=None): + def preprocessing_devices_info(self, ip_address_list=None): """ Preprocess the devices' information. Extract the IP addresses from the list of devices and perform additional processing based on the 'discovery_type' in the validated configuration. Parameters: - - devices_list: The list of devices to preprocess. If not + - ip_address_list: The list of devices to preprocess. If not provided, an empty list is used. Returns: @@ -465,36 +461,41 @@ def preprocessing_devices_info(self, devices_list=None): of IP ranges separated by commas. """ - if devices_list is None: - devices_list = [] - - ip_address_list = [device['ip'] for device in devices_list] + if ip_address_list is None: + ip_address_list = [] self.log("Discovery type passed for the discovery is {0}".format(self.validated_config[0].get('discovery_type')), "INFO") if self.validated_config[0].get('discovery_type') in ["SINGLE", "CDP", "LLDP"]: if len(ip_address_list) == 1: ip_address_list = ip_address_list[0] else: - self.log("Device list's length is longer than 1", "ERROR") - self.module.fail_json(msg="Device list's length is longer than 1", response=[]) + self.log("IP Address list's length is longer than 1", "ERROR") + self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) elif self.validated_config[0].get('discovery_type') == "CIDR": if len(ip_address_list) == 1 and self.validated_config[0].get('prefix_length'): ip_address_list = ip_address_list[0] ip_address_list = str(ip_address_list) + "/" + str(self.validated_config[0].get('prefix_length')) else: - self.log("Device list's length is longer than 1", "ERROR") - self.module.fail_json(msg="Device list's length is longer than 1", response=[]) + self.log("IP Address list's length is longer than 1", "ERROR") + self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) + elif self.validated_config[0].get('discovery_type') == "RANGE": + if len(ip_address_list) == 1: + if len(str(ip_address_list[0]).split("-")) == 2: + ip_address_list = ip_address_list[0] + else: + ip_address_list = "{0}-{1}".format(ip_address_list[0], ip_address_list[0]) + else: + self.log("IP Address list's length is longer than 1", "ERROR") + self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) else: - ip_address_list = list( - map( - lambda x: '{0}-{value}'.format(x, value=x), - ip_address_list - ) - ) - ip_address_list = ','.join(ip_address_list) - + new_ip_collected = [] + for ip in ip_address_list: + if len(str(ip).split("-")) != 2: + ip_collected = "{0}-{0}".format(ip) + new_ip_collected.append(ip_collected) + ip_address_list = ','.join(new_ip_collected) self.log("Collected IP address/addresses are {0}".format(str(ip_address_list)), "INFO") - return ip_address_list + return str(ip_address_list) def create_params(self, credential_ids=None, ip_address_list=None): """ diff --git a/plugins/modules/pnp_intent.py b/plugins/modules/pnp_intent.py index 18b06a3a23..43ac5e8d76 100644 --- a/plugins/modules/pnp_intent.py +++ b/plugins/modules/pnp_intent.py @@ -24,15 +24,6 @@ Rishita Chowdhary (@rishitachowdhary) Abinash Mishra (@abimishr) options: - config_verify: - description: Set to True to verify the Cisco DNA Center config after applying the playbook config. - type: bool - default: False - dnac_log_level: - description: Specifies the log level for Cisco Catalyst Center logging, categorizing logs by severity. - Options- [CRITICAL, ERROR, WARNING, INFO, DEBUG] - type: str - default: WARNING state: description: The state of DNAC after module completion. type: str From 9ef7277068580eca2697254a4504051ea40e3d18 Mon Sep 17 00:00:00 2001 From: SHUBHAM VARFA Date: Thu, 1 Feb 2024 04:36:42 +0000 Subject: [PATCH 2/3] Removing config_verify, dnac_log_level from code along with fixing issues related to discovery --- playbooks/discovery_intent.yml | 28 ++++++++-------------------- plugins/modules/discovery_intent.py | 4 ++++ plugins/modules/pnp_intent.py | 4 ++++ 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/playbooks/discovery_intent.yml b/playbooks/discovery_intent.yml index f285d71cdd..b8917b4423 100644 --- a/playbooks/discovery_intent.yml +++ b/playbooks/discovery_intent.yml @@ -16,6 +16,8 @@ dnac_port: "{{ dnac_port }}" dnac_version: "{{ dnac_version }}" dnac_debug: "{{ dnac_debug }}" + dnac_log: True + dnac_log_level: DEBUG tasks: - name: Execute discovery devices using MULTI RANGE @@ -24,21 +26,11 @@ state: merged config_verify: True config: - - devices_list: - - name: SJ-BN-9300 - site: Global/USA/SAN JOSE/BLD23 - role: MAPSERVER,BORDERNODE,INTERNAL,EXTERNAL,SDATRANSIT - l2interface: TenGigabitEthernet1/1/8 - ip: 204.1.2.1 - - name: NY-BN-9300 - site: Global/USA/New York/BLDNYC - role: MAPSERVER,BORDERNODE,INTERNAL,EXTERNAL,SDATRANSIT,NOIPTRANSIT,ECA - managed_ap_site: Global/USA/New York/BLDNYC/FLOOR1 - rolling_ap_count: 25 - l2interface: TenGigabitEthernet1/1/6 - ip: 204.1.2.3 + - ip_address_list: + - 204.1.2.1 #It will be taken as 204.1.2.1 - 204.1.2.1 + - 205.2.1.1-205.2.1.10 discovery_type: "MULTI RANGE" - discovery_name: Multi_Range_Discovery_Test + discovery_name: File_111 protocol_order: ssh start_index: 1 records_to_return: 25 @@ -50,12 +42,8 @@ state: merged config_verify: True config: - - devices_list: #List length should be one - - name: SJ-BN-9300 - site: Global/USA/SAN JOSE/BLD23 - role: MAPSERVER,BORDERNODE,INTERNAL,EXTERNAL,SDATRANSIT - l2interface: TenGigabitEthernet1/1/8 - ip: 204.1.2.1 + - ip_address_list: #List length should be one + - 204.1.2.1 discovery_type: "CDP" #Can be LLDP and CIDR cdp_level: 16 #Instead use lldp for LLDP and prefix length for CIDR discovery_name: CDP_Test_1 diff --git a/plugins/modules/discovery_intent.py b/plugins/modules/discovery_intent.py index e15df25204..65be8144a9 100644 --- a/plugins/modules/discovery_intent.py +++ b/plugins/modules/discovery_intent.py @@ -22,6 +22,10 @@ author: Abinash Mishra (@abimishr) Phan Nguyen (phannguy) options: + config_verify: + description: Set to True to verify the Cisco DNA Center config after applying the playbook config. + type: bool + default: False state: description: The state of DNAC after module completion. type: str diff --git a/plugins/modules/pnp_intent.py b/plugins/modules/pnp_intent.py index 43ac5e8d76..489568cff1 100644 --- a/plugins/modules/pnp_intent.py +++ b/plugins/modules/pnp_intent.py @@ -24,6 +24,10 @@ Rishita Chowdhary (@rishitachowdhary) Abinash Mishra (@abimishr) options: + config_verify: + description: Set to True to verify the Cisco DNA Center config after applying the playbook config. + type: bool + default: False state: description: The state of DNAC after module completion. type: str From d7a7b533e32a205ea8ba13e083914a07b610989d Mon Sep 17 00:00:00 2001 From: Abinash Date: Thu, 1 Feb 2024 09:42:40 +0000 Subject: [PATCH 3/3] Removing dnac_log_level from code along with fixing issues related to discovery --- plugins/modules/discovery_intent.py | 46 ++++++++++++++++------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/plugins/modules/discovery_intent.py b/plugins/modules/discovery_intent.py index 65be8144a9..4d951f29b8 100644 --- a/plugins/modules/discovery_intent.py +++ b/plugins/modules/discovery_intent.py @@ -138,7 +138,7 @@ description: Time to wait for device response in seconds type: int cli_cred_len: - description: Total CLI credentials that needs to be used. This value can vary from 1 to 5. + description: Specifies the total number of CLI credentials to be used, ranging from 1 to 5. type: int default: 1 requirements: @@ -446,51 +446,48 @@ def get_devices_list_info(self): """ ip_address_list = self.validated_config[0].get('ip_address_list') self.result.update(dict(devices_info=ip_address_list)) - self.log("Devices list info passed: {0}".format(str(ip_address_list)), "INFO") + self.log("Details of the device list passed: {0}".format(str(ip_address_list)), "INFO") return ip_address_list - def preprocessing_devices_info(self, ip_address_list=None): + def preprocess_device_discovery(self, ip_address_list=None): """ Preprocess the devices' information. Extract the IP addresses from the list of devices and perform additional processing based on the 'discovery_type' in the validated configuration. Parameters: - - ip_address_list: The list of devices to preprocess. If not - provided, an empty list is used. + - ip_address_list: The list of devices' IP addresses intended for preprocessing. + If not provided, an empty list will be used. Returns: - - ip_address_list: If 'discovery_type' is "SINGLE", it returns the - first IP address. Otherwise, it returns a string - of IP ranges separated by commas. + - ip_address_list: It returns IP address list for the API to process. The value passed + for single, CDP, LLDP, CIDR, Range and Multi Range varies depending + on the need. """ if ip_address_list is None: ip_address_list = [] - - self.log("Discovery type passed for the discovery is {0}".format(self.validated_config[0].get('discovery_type')), "INFO") - if self.validated_config[0].get('discovery_type') in ["SINGLE", "CDP", "LLDP"]: + discovery_type = self.validated_config[0].get('discovery_type') + self.log("Discovery type passed for the discovery is {0}".format(discovery_type), "INFO") + if discovery_type in ["SINGLE", "CDP", "LLDP"]: if len(ip_address_list) == 1: ip_address_list = ip_address_list[0] else: - self.log("IP Address list's length is longer than 1", "ERROR") - self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) - elif self.validated_config[0].get('discovery_type') == "CIDR": + self.preprocess_device_discovery_handle_error() + elif discovery_type == "CIDR": if len(ip_address_list) == 1 and self.validated_config[0].get('prefix_length'): ip_address_list = ip_address_list[0] ip_address_list = str(ip_address_list) + "/" + str(self.validated_config[0].get('prefix_length')) else: - self.log("IP Address list's length is longer than 1", "ERROR") - self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) - elif self.validated_config[0].get('discovery_type') == "RANGE": + self.preprocess_device_discovery_handle_error() + elif discovery_type == "RANGE": if len(ip_address_list) == 1: if len(str(ip_address_list[0]).split("-")) == 2: ip_address_list = ip_address_list[0] else: ip_address_list = "{0}-{1}".format(ip_address_list[0], ip_address_list[0]) else: - self.log("IP Address list's length is longer than 1", "ERROR") - self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) + self.preprocess_device_discovery_handle_error() else: new_ip_collected = [] for ip in ip_address_list: @@ -501,6 +498,15 @@ def preprocessing_devices_info(self, ip_address_list=None): self.log("Collected IP address/addresses are {0}".format(str(ip_address_list)), "INFO") return str(ip_address_list) + def preprocess_device_discovery_handle_error(self): + """ + Method for failing discovery based on the length of list of IP Addresses passed + for performing discovery. + """ + + self.log("IP Address list's length is longer than 1", "ERROR") + self.module.fail_json(msg="IP Address list's length is longer than 1", response=[]) + def create_params(self, credential_ids=None, ip_address_list=None): """ Create a new parameter object based on the validated configuration, @@ -817,7 +823,7 @@ def get_diff_merged(self): self.get_dnac_global_credentials_v2_info() devices_list_info = self.get_devices_list_info() - ip_address_list = self.preprocessing_devices_info(devices_list_info) + ip_address_list = self.preprocess_device_discovery(devices_list_info) exist_discovery = self.get_exist_discovery() if exist_discovery: params = dict(id=exist_discovery.get('id'))