-
Couldn't load subscription status.
- Fork 1
Swim bugs completed #141
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
Swim bugs completed #141
Changes from 2 commits
f06ed05
a2703a8
999dabe
1afa78a
6d272d3
c006930
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -411,6 +411,15 @@ | |
| description: Specify the name of the device | ||
| family such as Switches and Hubs, etc. | ||
| type: str | ||
| api_task_timeout: | ||
|
||
| description: | | ||
| Timeout duration in seconds for image distribution API operations. Controls how long | ||
| the system waits for image distribution tasks to complete, including image transfer | ||
| to target devices, network propagation, and distribution validation. Default of 2400 | ||
| seconds (40 minutes) accounts for large image files, multiple target devices, and | ||
| network latency. Adjust based on distribution scope, network bandwidth, and image size. | ||
| type: int | ||
| default: 1800 | ||
| site_name: | ||
| description: Used to get device details | ||
| associated to this site. | ||
|
|
@@ -467,6 +476,15 @@ | |
| ACCESS, BORDER ROUTER, DISTRIBUTION, and | ||
| CORE. | ||
| type: str | ||
| api_task_timeout: | ||
|
||
| description: | | ||
| Timeout duration in seconds for image activation API operations. Controls how long | ||
| the system waits for image upload, installation, and activation processes to complete | ||
| before timing out. Default of 2400 seconds (40 minutes) accommodates large image | ||
| transfers and device processing time. Adjust based on network speed, image size, | ||
| and device capabilities. | ||
| type: int | ||
| default: 1800 | ||
| device_family_name: | ||
| description: Specify the name of the device | ||
| family such as Switches and Hubs, etc. | ||
|
|
@@ -831,6 +849,7 @@ | |
| device_family_name: Switches and Hubs | ||
| device_series_name: Cisco Catalyst 9300 Series | ||
| Switches | ||
|
|
||
| - name: Activate the given image on devices associated | ||
| to that site with specified role. | ||
| cisco.dnac.swim_workflow_manager: | ||
|
|
@@ -850,7 +869,52 @@ | |
| device_role: ALL | ||
| device_family_name: Switches and Hubs | ||
| device_series_name: Cisco Catalyst 9300 Series Switches | ||
| scehdule_validate: false | ||
| schedule_validate: false | ||
| activate_lower_image_version: true | ||
| distribute_if_needed: true | ||
|
|
||
| - name: Activate the golden image on devices associated | ||
| to that site with specified role. | ||
| cisco.dnac.swim_workflow_manager: | ||
| dnac_host: "{{dnac_host}}" | ||
| dnac_username: "{{dnac_username}}" | ||
| dnac_password: "{{dnac_password}}" | ||
| dnac_verify: "{{dnac_verify}}" | ||
| dnac_port: "{{dnac_port}}" | ||
| dnac_version: "{{dnac_version}}" | ||
| dnac_debug: "{{dnac_debug}}" | ||
| dnac_log_level: "{{dnac_log_level}}" | ||
| dnac_log: true | ||
| config: | ||
| - image_activation_details: | ||
| site_name: Global/USA/San Francisco/BGL_18 | ||
| device_role: ALL | ||
| device_family_name: Switches and Hubs | ||
| device_series_name: Cisco Catalyst 9300 Series Switches | ||
| schedule_validate: false | ||
| activate_lower_image_version: true | ||
| distribute_if_needed: true | ||
|
|
||
| - name: distribute the golden image on devices associated | ||
| to that site with specified role with custom api timeout. | ||
| cisco.dnac.swim_workflow_manager: | ||
| dnac_host: "{{dnac_host}}" | ||
| dnac_username: "{{dnac_username}}" | ||
| dnac_password: "{{dnac_password}}" | ||
| dnac_verify: "{{dnac_verify}}" | ||
| dnac_port: "{{dnac_port}}" | ||
| dnac_version: "{{dnac_version}}" | ||
| dnac_debug: "{{dnac_debug}}" | ||
| dnac_log_level: "{{dnac_log_level}}" | ||
| dnac_log: true | ||
| config: | ||
| - image_activation_details: | ||
| site_name: Global/USA/San Francisco/BGL_18 | ||
| api_task_timeout: 2500 | ||
| device_role: ALL | ||
| device_family_name: Switches and Hubs | ||
| device_series_name: Cisco Catalyst 9300 Series Switches | ||
| schedule_validate: false | ||
| activate_lower_image_version: true | ||
| distribute_if_needed: true | ||
|
|
||
|
|
@@ -1365,11 +1429,18 @@ def get_device_uuids( | |
| device_uuid_list = [] | ||
| device_id_list, site_response_list = [], [] | ||
| if not site_name: | ||
| site_names = "Global/.*" | ||
| self.log( | ||
| "Site name not specified; defaulting to 'Global' to fetch all devices under this category", | ||
| "INFO", | ||
| ) | ||
| if self.compare_dnac_versions(self.get_ccc_version(), "2.3.5.3") <= 0: | ||
|
||
| site_name = "Global/.*" | ||
| self.log( | ||
| "Site name not specified; defaulting to 'Global/.*' to fetch all devices under this category", | ||
| "INFO", | ||
| ) | ||
| else: | ||
| site_name = "Global" | ||
| self.log( | ||
| "Site name not specified; defaulting to 'Global' to fetch all devices under this category", | ||
| "INFO", | ||
| ) | ||
|
|
||
| (site_exists, site_id) = self.site_exists(site_name) | ||
| if not site_exists: | ||
|
|
@@ -1428,7 +1499,11 @@ def get_device_uuids( | |
| for item_dict in item["response"]: | ||
| site_response_list.append(item_dict) | ||
| else: | ||
| self.log(site_name, "DEBUG") | ||
|
||
| if site_name: | ||
| self.log( | ||
| "Fetching devices for site '{0}'".format(site_name), "DEBUG" | ||
| ) | ||
| site_type = self.get_sites_type(site_name) | ||
| self.log("Determined site type: {0}".format(site_type), "DEBUG") | ||
| site_info = {} | ||
|
|
@@ -1493,7 +1568,7 @@ def get_device_uuids( | |
| self.log("No child site data found under: {0}".format(wildcard_site_name), "DEBUG") | ||
| site_names = site_name | ||
|
|
||
| elif site_type == "area": | ||
| elif site_type in ["area", "global"]: | ||
| self.log( | ||
| "Processing site as an area: {site_name}".format(site_name=site_name), | ||
| "DEBUG", | ||
|
|
@@ -1528,7 +1603,7 @@ def get_device_uuids( | |
| "ERROR", | ||
| ) | ||
|
|
||
| if site_type in ["area", "floor"]: | ||
| if site_type in ["area", "floor", "global"]: | ||
| self.log("Fetching site names for pattern: {0}".format(site_names), "DEBUG") | ||
| get_site_names = self.get_site(site_names) | ||
| self.log("Fetched site names: {0}".format(str(get_site_names)), "DEBUG") | ||
|
|
@@ -2999,7 +3074,7 @@ def get_diff_distribution(self): | |
| device_family = distribution_details.get("device_family_name") | ||
| device_role = distribution_details.get("device_role", "ALL") | ||
| device_series_name = distribution_details.get("device_series_name") | ||
|
|
||
| self.max_timeout = distribution_details.get("api_task_timeout", 1800) | ||
| self.log( | ||
| "Fetching device UUIDs for site '{0}', family '{1}', role '{2}', and series '{3}'.".format( | ||
| site_name, device_family, device_role, device_series_name | ||
|
|
@@ -3087,6 +3162,7 @@ def get_diff_distribution(self): | |
| ) | ||
| self.set_operation_result("success", False, self.msg, "INFO") | ||
| return self | ||
|
|
||
| if ( | ||
| self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.9") | ||
| <= 0 | ||
|
|
@@ -3213,7 +3289,7 @@ def get_diff_distribution(self): | |
| "DEBUG", | ||
| ) | ||
|
|
||
| self.check_tasks_response_status( | ||
| self.check_swim_tasks_response_status( | ||
| response, "distribute_images_on_the_network_device" | ||
| ) | ||
|
|
||
|
|
@@ -3401,7 +3477,7 @@ def get_diff_distribution(self): | |
|
|
||
| self.log("API response from 'bulk_distribute_images_on_network_devices': {0}".format(str(response)), "DEBUG") | ||
|
|
||
| self.check_tasks_response_status( | ||
| self.check_swim_tasks_response_status( | ||
| response, "bulk_distribute_images_on_network_devices" | ||
| ) | ||
|
|
||
|
|
@@ -3559,6 +3635,7 @@ def get_diff_activation(self): | |
| device_family = activation_details.get("device_family_name") | ||
| device_role = activation_details.get("device_role", "ALL") | ||
| device_series_name = activation_details.get("device_series_name") | ||
| self.max_timeout = activation_details.get("api_task_timeout", 1800) | ||
|
|
||
| self.log( | ||
| "Fetching device UUIDs for site '{0}', family '{1}', role '{2}', and series '{3}'.".format( | ||
|
|
@@ -3653,7 +3730,7 @@ def get_diff_activation(self): | |
| activation_payload_list = [] | ||
|
|
||
| # OLD FLOW (for DNAC < 2.3.7.9) | ||
| if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.9") < 0: | ||
| if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.9") <= 0: | ||
| for image_name, image_id in image_ids.items(): | ||
| payload = [ | ||
| { | ||
|
|
@@ -3715,7 +3792,7 @@ def get_diff_activation(self): | |
| self.log(failed_msg, "ERROR") | ||
| break | ||
|
|
||
| # NEW FLOW (for Catalyst Center >= 2.3.7.9) | ||
| # NEW FLOW (for Catalyst Center > 2.3.7.9) | ||
| else: | ||
| self.log("Using new SWIM API for image activation (after 2.3.7.9)", "INFO") | ||
|
|
||
|
|
@@ -3751,7 +3828,7 @@ def get_diff_activation(self): | |
| ) | ||
|
|
||
| self.log("API response from 'update_images_on_the_network_device': {0}".format(str(response)), "DEBUG") | ||
| self.check_tasks_response_status(response, "update_images_on_the_network_device") | ||
| self.check_swim_tasks_response_status(response, "update_images_on_the_network_device") | ||
|
|
||
| device_ip = self.get_device_ip_from_id(activation_device_id) | ||
| if response and self.status not in ["failed", "exited"]: | ||
|
|
@@ -3816,8 +3893,8 @@ def get_diff_activation(self): | |
| elg_device_list = [] | ||
| device_ip_for_not_elg_list = [] | ||
|
|
||
| # OLD FLOW (for DNAC < 2.3.7.9) | ||
| if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.9") < 0: | ||
| # OLD FLOW (for DNAC <= 2.3.7.9) | ||
| if self.compare_dnac_versions(self.get_ccc_version(), "2.3.7.9") <= 0: | ||
| for device_uuid in device_uuid_list: | ||
| device_ip = self.get_device_ip_from_id(device_uuid) | ||
| activated = False | ||
|
|
@@ -3903,7 +3980,7 @@ def get_diff_activation(self): | |
| "{} to {}".format(img, ", ".join(devices)) for img, devices in failed_image_map.items() | ||
| ] | ||
|
|
||
| # NEW FLOW (for DNAC >= 2.3.7.9) | ||
| # NEW FLOW (for DNAC > 2.3.7.9) | ||
| else: | ||
| image_id_base = self.have.get("activation_image_id") | ||
| # Resolve sub-package ids (if any) | ||
|
|
@@ -3914,7 +3991,6 @@ def get_diff_activation(self): | |
|
|
||
| for device_uuid in device_uuid_list: | ||
| device_ip = self.get_device_ip_from_id(device_uuid) | ||
| device_ips.append(device_ip) | ||
| self.log("Processing device: {0}".format(device_ip), "DEBUG") | ||
|
|
||
| # Aggregate all image ids for this device | ||
|
|
@@ -3934,6 +4010,8 @@ def get_diff_activation(self): | |
| device_ip_for_not_elg_list.append(device_ip) | ||
| continue | ||
|
|
||
| device_ips.append(elg_device_ip) | ||
|
|
||
| activation_payload = {} | ||
| if device_id: | ||
| activation_payload["id"] = device_id | ||
|
|
@@ -3968,7 +4046,7 @@ def get_diff_activation(self): | |
| params={"payload": activation_payload_list}, | ||
| ) | ||
| self.log("API response from 'bulk_update_images_on_network_devices': {0}".format(str(response)), "DEBUG") | ||
| self.check_tasks_response_status(response, "bulk_update_images_on_network_devices") | ||
| self.check_swim_tasks_response_status(response, "bulk_update_images_on_network_devices") | ||
|
|
||
| if response and self.status not in ["failed", "exited"]: | ||
| self.msg = "All eligible images activated successfully on the devices {0}.".format(", ".join(device_ips)) | ||
|
|
@@ -4014,6 +4092,71 @@ def get_diff_activation(self): | |
| self.complete_successful_activation = True | ||
| return self | ||
|
|
||
| def check_swim_tasks_response_status(self, response, api_name): | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shall we add operation_details as well?
|
||
| """ | ||
| Get the task response status from taskId | ||
| Args: | ||
| self: The current object details. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| response (dict): API response. | ||
| api_name (str): API name. | ||
| Returns: | ||
| self (object): The current object with updated desired Fabric Transits information. | ||
| Description: | ||
| Poll the function 'get_tasks_by_id' until it returns either 'SUCCESS' or 'FAILURE' | ||
| state or till it reaches the maximum timeout. | ||
| Log the task details and return self. | ||
| """ | ||
|
|
||
|
||
| if not response: | ||
| self.msg = "response is empty" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add a detailed message..
|
||
| self.status = "exited" | ||
| return self | ||
|
|
||
| if not isinstance(response, dict): | ||
| self.msg = "response is not a dictionary" | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| self.status = "exited" | ||
| return self | ||
|
|
||
| task_info = response.get("response") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. first check the task_info, if not then return.. |
||
| if task_info.get("errorcode") is not None: | ||
| self.msg = response.get("response").get("detail") | ||
| self.status = "failed" | ||
| return self | ||
|
|
||
| task_id = task_info.get("taskId") | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if task_id is none, then return?? |
||
| start_time = time.time() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is good to determine the timeout for this operation.. |
||
| while True: | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a sample code to check the task_status.. But you can refine and update if required.. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| elapsed_time = time.time() - start_time | ||
| if elapsed_time >= self.max_timeout: | ||
| self.msg = "Max timeout of {0} sec has reached for the task id '{1}'. " \ | ||
| .format(self.max_timeout, task_id) + \ | ||
| "Exiting the loop due to unexpected API '{0}' status.".format(api_name) | ||
| self.log(self.msg, "WARNING") | ||
| self.status = "failed" | ||
| break | ||
|
|
||
| task_details = self.get_tasks_by_id(task_id) | ||
| self.log('Getting tasks details from task ID {0}: {1}' | ||
| .format(task_id, task_details), "DEBUG") | ||
|
|
||
| task_status = task_details.get("status") | ||
| if task_status == "FAILURE": | ||
| details = self.get_task_details_by_id(task_id) | ||
| self.msg = details.get("failureReason") | ||
| self.status = "failed" | ||
| break | ||
|
|
||
| elif task_status == "SUCCESS": | ||
| self.result["changed"] = True | ||
| self.log("The task with task ID '{0}' is executed successfully." | ||
| .format(task_id), "INFO") | ||
| break | ||
|
|
||
| self.log("Progress is {0} for task ID: {1}" | ||
| .format(task_status, task_id), "DEBUG") | ||
|
|
||
| return self | ||
|
|
||
| def get_diff_merged(self, config): | ||
| """ | ||
| Get tagging details and then trigger distribution followed by activation if specified in the playbook. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name "api_task_timeout" may get confused with the existing global dnac_api_task_timeout parameter. Please note that the parameter name should reflect its specific operation scope.
Can we say
OR
OR
image_distribution_timeout. .. image_activation_timeout ...Check with @DNACENSolutions and update...
type: int
default: 1800