Skip to content

Commit

Permalink
Merge pull request #92 from abimishr/PNP_Code_Abinash
Browse files Browse the repository at this point in the history
Code and playbook for config verify of device addition of PnP only
  • Loading branch information
madhansansel authored Jan 17, 2024
2 parents 0c89a8e + 58938b7 commit f5e0492
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 17 deletions.
12 changes: 7 additions & 5 deletions playbooks/PnP.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,18 @@
<<: *dnac_login
dnac_log: True
state: merged
config_verify: True
config:
- site_name: Global/USA/San Francisco/BGL_18
device_info:
- serial_number: CD2425L8M7
- device_info:
- serial_number: QD2425L8M7
state: Unclaimed
pid: c9300-24P
is_sudi_required: False

- serial_number: FTC2320E0H9
- serial_number: QTC2320E0H9
state: Unclaimed
pid: c9300-24P
hostname: Test-123

- serial_number: ETC2320E0HB
state: Unclaimed
Expand Down Expand Up @@ -101,8 +102,9 @@
<<: *dnac_login
dnac_log: True
state: deleted
config_verify: True
config:
- device_info:
- serial_number: FTC2320E0HB #Will get deleted
- serial_number: QD2425L8M7 #Will get deleted
- serial_number: FTC2320E0HA #Doesn't exist in the inventory
- serial_number: FKC2310E0HB #Doesn't exist in the inventory
23 changes: 22 additions & 1 deletion playbooks/discovery_intent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
cisco.dnac.discovery_intent:
<<: *dnac_login
state: merged
config_verify: True
config:
- devices_list:
- name: SJ-BN-9300
Expand All @@ -37,8 +38,28 @@
l2interface: TenGigabitEthernet1/1/6
ip: 204.1.2.3
discovery_type: "MULTI RANGE"
discovery_name: Multi_Range_Discovery_Test
protocol_order: ssh
start_index: 1
records_to_return: 25
snmp_version: v2


- name: Execute discovery devices using CDP/LLDP/CIDR
cisco.dnac.discovery_intent:
<<: *dnac_login
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
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
protocol_order: ssh
start_index: 1
records_to_return: 25
snmp_version: v2
121 changes: 113 additions & 8 deletions plugins/modules/discovery_intent.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -48,7 +52,7 @@
type: str
required: true
discovery_type:
description: Type of discovery (SINGLE/RANGE/MULTI RANGE/CDP/LLDP/CIDR)
description: Type of discovery (SINGLE/RANGE/MULTI RANGE/CDP/LLDP)
type: str
required: true
cdp_level:
Expand Down Expand Up @@ -81,7 +85,8 @@
elements: str
discovery_name:
description: Name of the discovery task
type: dict
type: str
required: true
netconf_port:
description: Port for the netconf credentials
type: str
Expand Down Expand Up @@ -176,7 +181,7 @@
dnac_log: True
state: merged
config:
- device_list:
- devices_list:
- name: string
ip: string
discovery_type: string
Expand Down Expand Up @@ -317,8 +322,9 @@ def validate_input(self):
'elements': 'str'},
'lldp_level': {'type': 'int', 'required': False,
'default': 16},
'discovery_name': {'type': 'dict', 'required': False,
'default': '{0}'.format(default_dicovery_name)},
'prefix_length': {'type': 'int', 'required': False,
'default': 30},
'discovery_name': {'type': 'str', 'required': True},
'netconf_port': {'type': 'str', 'required': False},
'password_list': {'type': 'list', 'required': False,
'elements': 'str'},
Expand Down Expand Up @@ -391,6 +397,7 @@ def get_dnac_global_credentials_v2_info(self):
params=self.validated_config[0].get('headers'),
)
response = response.get('response')
self.log(response)
for value in response.values():
if not value:
continue
Expand Down Expand Up @@ -436,8 +443,17 @@ def preprocessing_devices_info(self, devices_list=None):

ip_address_list = [device['ip'] for device in devices_list]

if self.validated_config[0].get('discovery_type') == "SINGLE":
ip_address_list = ip_address_list[0]
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.module.fail_json(msg="Device 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.module.fail_json(msg="Device list's length is longer than 1", response=[])
else:
ip_address_list = list(
map(
Expand All @@ -447,6 +463,7 @@ def preprocessing_devices_info(self, devices_list=None):
)
ip_address_list = ','.join(ip_address_list)

self.log("Collected IP address/addresses are {0}".format(ip_address_list))
return ip_address_list

def create_params(self, credential_ids=None, ip_address_list=None):
Expand Down Expand Up @@ -510,6 +527,7 @@ def create_params(self, credential_ids=None, ip_address_list=None):
new_object_params['snmpVersion'] = self.validated_config[0].get('snmp_version')
new_object_params['timeout'] = self.validated_config[0].get('timeout')
new_object_params['userNameList'] = self.validated_config[0].get('user_name_list')
self.log(new_object_params)

return new_object_params

Expand Down Expand Up @@ -541,6 +559,8 @@ def create_discovery(self, credential_ids=None, ip_address_list=None):
op_modifies=True,
)

self.log(result)

self.result.update(dict(discovery_result=result))
return result.response.get('taskId')

Expand All @@ -567,6 +587,7 @@ def get_task_status(self, task_id=None):
params=params,
)
response = response.response
self.log(response)
if response.get('isError') or re.search(
'failed', response.get('progress'), flags=re.IGNORECASE
):
Expand Down Expand Up @@ -605,6 +626,8 @@ def lookup_discovery_by_range_via_name(self):
params=params
)

self.log(response)

return next(
filter(
lambda x: x['name'] == self.validated_config[0].get('discovery_name'),
Expand All @@ -630,6 +653,7 @@ def get_discoveries_by_range_until_success(self):
if not discovery:
msg = 'Cannot find any discovery task with name {0} -- Discovery result: {1}'.format(
self.validated_config[0].get("discovery_name"), discovery)
self.log(msg)
self.module.fail_json(msg=msg)

while True:
Expand All @@ -643,6 +667,7 @@ def get_discoveries_by_range_until_success(self):
if not result:
msg = 'Cannot find any discovery task with name {0} -- Discovery result: {1}'.format(
self.validated_config[0].get("discovery_name"), discovery)
self.log(msg)
self.module.fail_json(msg=msg)

self.result.update(dict(discovery_range=discovery))
Expand Down Expand Up @@ -677,6 +702,8 @@ def get_discovery_device_info(self, discovery_id=None, task_id=None):
params=params,
)
devices = response.response

self.log(devices)
if all(res.get('reachabilityStatus') == 'Success' for res in devices):
result = True
break
Expand Down Expand Up @@ -704,7 +731,6 @@ def get_exist_discovery(self):
returns None and updates the 'exist_discovery' entry in
the result dictionary to None.
"""

discovery = self.lookup_discovery_by_range_via_name()
if not discovery:
self.result.update(dict(exist_discovery=discovery))
Expand Down Expand Up @@ -732,6 +758,8 @@ def delete_exist_discovery(self, params):
function="delete_discovery_by_id",
params=params,
)

self.log(response)
self.result.update(dict(delete_discovery=response))
return response.response.get('taskId')

Expand Down Expand Up @@ -795,6 +823,81 @@ def get_diff_deleted(self):

return self

def verify_diff_merged(self, config):
"""
Verify the merged status(Creation/Updation) of Discovery in Cisco DNA Center.
Args:
- self (object): An instance of a class used for interacting with Cisco DNA Center.
- config (dict): The configuration details to be verified.
Return:
- self (object): An instance of a class used for interacting with Cisco DNA Center.
Description:
This method checks the merged status of a configuration in Cisco DNA Center by
retrieving the current state (have) and desired state (want) of the configuration,
logs the states, and validates whether the specified device(s) exists in the DNA
Center configuration's Discovery Database.
"""

self.log(str(self.have))
# Code to validate dnac config for merged state
discovery_task_info = self.get_discoveries_by_range_until_success()
discovery_id = discovery_task_info.get('id')
params = dict(
id=discovery_id
)
response = self.dnac_apply['exec'](
family="discovery",
function='get_discovery_by_id',
params=params
)

if response:
discovery_name = response.get('response').get('name')
self.log("Requested Discovery with name {0} is completed".format(discovery_name))

else:
self.log("Requested Discovery with name {0} is not completed".format(discovery_name))
self.status = "success"

return self

def verify_diff_deleted(self, config):
"""
Verify the deletion status of Discovery in Cisco DNA Center.
Args:
- self (object): An instance of a class used for interacting with Cisco DNA Center.
- config (dict): The configuration details to be verified.
Return:
- self (object): An instance of a class used for interacting with Cisco DNA Center.
Description:
This method checks the deletion status of a configuration in Cisco DNA Center.
It validates whether the specified discovery(s) exists in the DNA Center configuration's
Discovery Database.
"""

self.log(str(self.have))
# Code to validate dnac config for deleted state
discovery_task_info = self.get_discoveries_by_range_until_success()
discovery_id = discovery_task_info.get('id')
params = dict(
id=discovery_id
)
response = self.dnac_apply['exec'](
family="discovery",
function='get_discovery_by_id',
params=params
)

if response:
discovery_name = response.get('response').get('name')
self.log("Requested Discovery with name {0} is present".format(discovery_name))

else:
self.log("Requested Discovery with name {0} is not present and deleted".format(discovery_name))
self.status = "success"

return self


def main():
""" main entry point for module execution
Expand All @@ -809,6 +912,7 @@ def main():
'dnac_debug': {'type': 'bool', 'default': False},
'dnac_log': {'type': 'bool', 'default': False},
'validate_response_schema': {'type': 'bool', 'default': True},
'config_verify': {"type": 'bool', "default": False},
'config': {'required': True, 'type': 'list', 'elements': 'dict'},
'state': {'default': 'merged', 'choices': ['merged', 'deleted']}
}
Expand All @@ -817,6 +921,7 @@ def main():
supports_check_mode=False)

dnac_discovery = DnacDiscovery(module)
config_verify = dnac_discovery.params.get("config_verify")

state = dnac_discovery.params.get("state")
if state not in dnac_discovery.supported_states:
Expand Down
Loading

0 comments on commit f5e0492

Please sign in to comment.