Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changes for typos, fixed CIDR issue and added delete all feature #132

Merged
merged 4 commits into from
Feb 3, 2024
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 79 additions & 47 deletions plugins/modules/discovery_intent.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,15 @@
start_index:
description: Start index for the header in fetching SNMP v2 credentials
type: int
default: 1
enable_password_list:
description: List of enable passwords for the CLI crfedentials
type: list
elements: str
records_to_return:
description: Number of records to return for the header in fetching global v2 credentials
type: int
default: 100
http_read_credential:
description: HTTP read credentials for hosting a device
type: dict
Expand Down Expand Up @@ -141,6 +143,10 @@
description: Specifies the total number of CLI credentials to be used, ranging from 1 to 5.
type: int
default: 1
delete_all:
description: Parameter to delete all the discoveries at one go
type: bool
default: False
requirements:
- dnacentersdk == 2.6.10
- python >= 3.5
Expand All @@ -152,6 +158,8 @@
discovery.Discovery.get_discoveries_by_range,
discovery.Discovery.get_discovered_network_devices_by_discovery_id',
discovery.Discovery.delete_discovery_by_id
discovery.Discovery.delete_all_discovery
discovery.Discovery.get_count_of_all_discovery_jobs

- Paths used are
get /dna/intent/api/v2/global-credential
Expand All @@ -160,6 +168,8 @@
get /dna/intent/api/v1/discovery/{startIndex}/{recordsToReturn}
get /dna/intent/api/v1/discovery/{id}/network-device
delete /dna/intent/api/v1/discovery/{id}
delete /dna/intent/api/v1/delete
get /dna/intent/api/v1/discovery/count

"""

Expand Down Expand Up @@ -206,7 +216,7 @@
snmp_version: string
timeout: integer
username_list: list
cli_cred_length: integer
cli_cred_len: integer
- name: Delete disovery by name
cisco.dnac.discovery_intent:
dnac_host: "{{dnac_host}}"
Expand All @@ -221,10 +231,7 @@
state: deleted
config_verify: True
config:
- ip_address_list: list
start_index: integer
records_to_return: integer
discovery_name: string
- discovery_name: string
"""

RETURN = r"""
Expand Down Expand Up @@ -293,7 +300,7 @@ def __init__(self, module):
super().__init__(module)
self.creds_ids_list = []

def validate_input(self):
def validate_input(self, state=None):
"""
Validate the fields provided in the playbook. Checks the
configuration provided in the playbook against a predefined
Expand Down Expand Up @@ -322,22 +329,18 @@ def validate_input(self):
discovery_spec = {
'cdp_level': {'type': 'int', 'required': False,
'default': 16},
'discovery_type': {'type': 'str', 'required': True},
'enable_password_list': {'type': 'list', 'required': False,
'elements': 'str'},
'ip_address_list': {'type': 'list', 'required': True,
'elements': 'str'},
'start_index': {'type': 'int', 'required': False,
'default': 25},
'records_to_return': {'type': 'int', 'required': False},
'default': 1},
'records_to_return': {'type': 'int', 'required': False,
'default': 100},
'http_read_credential': {'type': 'dict', 'required': False},
'http_write_credential': {'type': 'dict', 'required': False},
'ip_filter_list': {'type': 'list', 'required': False,
'elements': 'str'},
'lldp_level': {'type': 'int', 'required': False,
'default': 16},
'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,
Expand All @@ -364,6 +367,18 @@ def validate_input(self):
'default': 1}
}

if state == "merged":
discovery_spec["ip_address_list"] = {'type': 'list', 'required': True,
'elements': 'str'}
discovery_spec["discovery_type"] = {'type': 'str', 'required': True}

elif state == "deleted":
if self.config[0].get("delete_all") is True:
self.validated_config = [{"delete_all": True}]
self.msg = "Sucessfully collected input for deletion of all the discoveries"
self.log(self.msg, "WARNING")
return self

# Validate discovery params
valid_discovery, invalid_params = validate_list_of_dicts(
self.config, discovery_spec
Expand Down Expand Up @@ -475,9 +490,12 @@ def preprocess_device_discovery(self, ip_address_list=None):
else:
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'))
if len(ip_address_list) == 1:
if len(ip_address_list[0].split("/")) == 2:
ip_address_list = ip_address_list[0]
else:
ip_address_list = str(ip_address_list[0]) + "/" + str("30")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we give a warning message? Say that prefix length is not given, hence taking 30 as default..

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

self.log("Prefix length is not given, hence taking 30 as default", "WARNING")
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add bit more detail?
self.log("CIDR notation is being used for discovery and it requires a prefix length to be specified, such as 1.1.1.1/24. As no prefix length was provided, it will default to 30.", "WARNING")

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

elif discovery_type == "CIDR":
    if len(ip_address_list) == 1:
        cidr_notation = ip_address_list[0]

        if len(cidr_notation.split("/")) == 2:
            # If CIDR notation is used and prefix length is provided
            ip_address_list = cidr_notation
        else:
            # If CIDR notation is used but prefix length is not given, default to 30
            ip_address_list = f"{cidr_notation}/30"
            self.log("CIDR notation is being used for discovery and it requires a prefix length to be specified, such as 1.1.1.1/24. As no prefix length was provided, it will default to 30.", "WARNING")
    else:
        # Handle error if multiple IP addresses are provided for CIDR discovery
        self.preprocess_device_discovery_handle_error()

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

else:
self.preprocess_device_discovery_handle_error()
elif discovery_type == "RANGE":
Expand All @@ -493,7 +511,9 @@ def preprocess_device_discovery(self, ip_address_list=None):
for ip in ip_address_list:
if len(str(ip).split("-")) != 2:
ip_collected = "{0}-{0}".format(ip)
new_ip_collected.append(ip_collected)
new_ip_collected.append(ip_collected)
else:
new_ip_collected.append(ip)
ip_address_list = ','.join(new_ip_collected)
self.log("Collected IP address/addresses are {0}".format(str(ip_address_list)), "INFO")
return str(ip_address_list)
Expand Down Expand Up @@ -567,7 +587,7 @@ def create_params(self, credential_ids=None, ip_address_list=None):
'snmp_username')
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')
new_object_params['userNameList'] = self.validated_config[0].get('username_list')
self.log("The payload/object created for calling the start discovery API is {0}".format(str(new_object_params)), "INFO")

return new_object_params
Expand Down Expand Up @@ -855,20 +875,43 @@ def get_diff_deleted(self):
- self: The instance of the class with updated attributes.
"""

exist_discovery = self.get_exist_discovery()
if not exist_discovery:
self.result['msg'] = "Discovery {0} Not Found".format(
self.validated_config[0].get("discovery_name"))
self.log(self.result['msg'], "ERROR")
return self
if self.validated_config[0].get("delete_all"):
count_discoveries = self.dnac_apply['exec'](
family="discovery",
function="get_count_of_all_discovery_jobs",
)
if count_discoveries.get("response") == 0:
msg = "There are no discoveries present in the Discovery Dashboard for deletion"
self.result['msg'] = msg
self.log(msg, "WARNING")
self.result['response'] = self.validated_config[0]
return self

delete_all_response = self.dnac_apply['exec'](
family="discovery",
function="delete_all_discovery",
)
discovery_task_id = delete_all_response.get('response').get('taskId')
self.result["changed"] = True
self.result['msg'] = "All of the Discoveries Deleted Successfully"
self.result['diff'] = self.validated_config

else:
exist_discovery = self.get_exist_discovery()
if not exist_discovery:
self.result['msg'] = "Discovery {0} Not Found".format(
self.validated_config[0].get("discovery_name"))
self.log(self.result['msg'], "ERROR")
return self

params = dict(id=exist_discovery.get('id'))
discovery_task_id = self.delete_exist_discovery(params=params)
complete_discovery = self.get_task_status(task_id=discovery_task_id)
self.result["changed"] = True
self.result['msg'] = "Successfully deleted discovery"
self.result['diff'] = self.validated_config
self.result['response'] = discovery_task_id

params = dict(id=exist_discovery.get('id'))
discovery_task_id = self.delete_exist_discovery(params=params)
complete_discovery = self.get_task_status(task_id=discovery_task_id)
self.result["changed"] = True
self.result['msg'] = "Discovery Deleted Successfully"
self.result['diff'] = self.validated_config
self.result['response'] = discovery_task_id
self.log(self.result['msg'], "INFO")
return self

Expand Down Expand Up @@ -900,9 +943,8 @@ def verify_diff_merged(self, config):
function='get_discovery_by_id',
params=params
)

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

else:
Expand All @@ -928,19 +970,9 @@ def verify_diff_deleted(self, config):
self.log("Current State (have): {0}".format(str(self.have)), "INFO")
self.log("Desired State (want): {0}".format(str(config)), "INFO")
# 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')
discovery_task_info = self.lookup_discovery_by_range_via_name()
discovery_name = config.get('discovery_name')
if discovery_task_info:
self.log("Requested Discovery with name {0} is present".format(discovery_name), "WARNING")

else:
Expand Down Expand Up @@ -981,7 +1013,7 @@ def main():
dnac_discovery.msg = "State {0} is invalid".format(state)
dnac_discovery.check_return_status()

dnac_discovery.validate_input().check_return_status()
dnac_discovery.validate_input(state=state).check_return_status()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reasons for this
state=state ?

Copy link
Collaborator Author

@abimishr abimishr Feb 3, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are passing the state value here to use in the validation part

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but why do we need to assign state = state when we call the function?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will it work if I don't pass the state?

for config in dnac_discovery.validated_config:
dnac_discovery.reset_values()
dnac_discovery.get_diff_state_apply[state]().check_return_status()
Expand Down
Loading