324324RETURN = r"""
325325# Case_1: Success Scenario
326326response_1:
327- description: >-
328- A dictionary with the response returned by the Cisco Catalyst
329- Center Python SDK
327+ description: A dictionary with the response returned by the Cisco Catalyst Center Python SDK
330328 returned: always
331329 type: dict
332330 sample: >
333331 {
334- "response ": {
335- "YAML config generation Task succeeded for module
336- 'accesspoint_workflow_manager'.": {
337- "file_path":
338- "tmp/accesspoint_workflow_playbook_templatebase.yml"
339- }
332+ "msg ": {
333+ "configurations_count": 7,
334+ "file_mode": "overwrite",
335+ "file_path": "tmp/accesspoint_workflow_playbook.yml",
336+ "message": "YAML configuration file generated successfully for module 'accesspoint_workflow_manager'",
337+ "status": "success"
340338 },
341- "msg": {
342- "YAML config generation Task succeeded for module
343- 'accesspoint_workflow_manager'.": {
344- "file_path":
345- "tmp/accesspoint_workflow_playbook_templatebase.yml"
346- }
347- }
339+ "response": {
340+ "configurations_count": 7,
341+ "file_mode": "overwrite",
342+ "file_path": "tmp/accesspoint_workflow_playbook.yml",
343+ "message": "YAML configuration file generated successfully for module 'accesspoint_workflow_manager'",
344+ "status": "success"
345+ },
346+ "status": "success"
348347 }
349-
350348# Case_2: Error Scenario
351349response_2:
352- description: >-
353- A string with the response returned by the Cisco Catalyst
354- Center Python SDK
350+ description: A string with the response returned by the Cisco Catalyst Center Python SDK
355351 returned: always
356352 type: dict
357353 sample: >
358354 {
359- "response": "No configurations or components to process for
360- module 'accesspoint_workflow_manager'.
361- Verify input filters or configuration.",
362- "msg": "No configurations or components to process for module
363- 'accesspoint_workflow_manager'.
364- Verify input filters or configuration."
355+ "msg": "Validation failed: global_filters is required when config is provided.",
356+ "response": "Validation failed: global_filters is required when config is provided."
365357 }
366358"""
367359
@@ -2215,7 +2207,7 @@ def get_diff_gathered(self):
22152207 "DEBUG"
22162208 )
22172209
2218- # Check for unprocessed APs and set failure status if any exist
2210+ # Check for unprocessed APs and log warning without failing the module
22192211 if self .have .get ("unprocessed" ):
22202212 unprocessed_count = len (self .have .get ("unprocessed" ))
22212213 self .msg = (
@@ -2225,10 +2217,9 @@ def get_diff_gathered(self):
22252217 f"{ str (self .have .get ('unprocessed' ))} . Verify filter values match existing APs "
22262218 "and check Catalyst Center inventory for missing configurations."
22272219 )
2228- self .set_operation_result ("failed" , False , self .msg , "WARNING" )
22292220
22302221 self .log (
2231- f"Setting workflow status to 'failed' due to { unprocessed_count } unprocessed AP(s). "
2222+ f"Continuing workflow with warnings due to { unprocessed_count } unprocessed AP(s). "
22322223 "This indicates filter mismatches or incomplete AP data in Catalyst Center. Review "
22332224 f"unprocessed list for troubleshooting: { self .have .get ('unprocessed' )} " ,
22342225 "WARNING"
@@ -2438,19 +2429,17 @@ def yaml_config_generator(self, yaml_config_generator):
24382429
24392430 # Validate final_list is not empty
24402431 if not final_list :
2441- self .msg = (
2442- f"No configurations or components found to process for module ' { self . module_name } '. "
2443- "This indicates either : (1) No access points discovered in Catalyst Center, "
2444- "(2) Global filters didn't match any APs, or (3) Invalid configuration parameters. "
2445- "Verify input filters match existing AP inventory and check Catalyst Center has "
2446- "onboarded access points. No YAML file will be generated."
2447- )
2448- self .set_operation_result ("success " , False , self .msg , "INFO" )
2432+ self .msg = {
2433+ "status" : "ok" ,
2434+ "message" : (
2435+ "No configurations found for module '{0}'. Verify filters applied and access "
2436+ "point data in Catalyst Center." . format ( self . module_name )
2437+ )
2438+ }
2439+ self .set_operation_result ("ok " , False , self .msg , "INFO" )
24492440
24502441 self .log (
2451- "YAML generation workflow completed with no-op status. Final configuration list "
2452- "is empty - no YAML playbook generated. Check filter criteria or Catalyst Center "
2453- "AP inventory for troubleshooting." ,
2442+ "YAML generation workflow completed. Final configuration list is empty." ,
24542443 "INFO"
24552444 )
24562445 return self
@@ -2473,30 +2462,45 @@ def yaml_config_generator(self, yaml_config_generator):
24732462 "DEBUG"
24742463 )
24752464
2476- if self .write_dict_to_yaml (final_dict , file_path , file_mode ):
2465+ file_written = self .write_dict_to_yaml (final_dict , file_path , file_mode )
2466+
2467+ if file_written :
24772468 self .msg = {
2478- f"YAML config generation task succeeded for module '{ self .module_name } '." : {"file_path" : file_path }
2469+ "status" : "success" ,
2470+ "message" : "YAML configuration file generated successfully for module '{0}'" .format (
2471+ self .module_name
2472+ ),
2473+ "file_path" : file_path ,
2474+ "file_mode" : file_mode ,
2475+ "configurations_count" : len (final_list )
24792476 }
24802477 self .set_operation_result ("success" , True , self .msg , "INFO" )
24812478
24822479 self .log (
2483- f"YAML playbook file successfully created at '{ file_path } '. File contains "
2484- f"{ len (final_list )} access point configuration(s) in Ansible-compatible format. "
2485- "Playbook ready for automation workflows." ,
2480+ "YAML configuration generation completed. File: {0}, Configs: {1}" .format (
2481+ file_path ,
2482+ len (final_list ),
2483+ ),
24862484 "INFO"
24872485 )
24882486 else :
24892487 self .msg = {
2490- f"YAML config generation task failed for module '{ self .module_name } '." : {"file_path" : file_path }
2488+ "status" : "ok" ,
2489+ "message" : "YAML configuration file already up-to-date for module '{0}'. No changes written." .format (
2490+ self .module_name
2491+ ),
2492+ "file_path" : file_path ,
2493+ "file_mode" : file_mode ,
2494+ "configurations_count" : len (final_list )
24912495 }
2492- self .set_operation_result ("failed " , True , self .msg , "ERROR " )
2496+ self .set_operation_result ("ok " , False , self .msg , "INFO " )
24932497
24942498 self .log (
2495- f "YAML playbook file creation FAILED for path ' { file_path } '. write_dict_to_yaml() "
2496- "returned False, indicating file write error. Verify: (1) Parent directory exists, "
2497- "(2) Write permissions sufficient, (3) Disk space available, (4) Path is valid. "
2498- "Check system logs for detailed error information." ,
2499- "ERROR "
2499+ "YAML configuration unchanged. File: {0}, Configs: {1}" . format (
2500+ file_path ,
2501+ len ( final_list ),
2502+ ) ,
2503+ "INFO "
25002504 )
25012505
25022506 return self
@@ -2631,14 +2635,11 @@ def process_global_filters(self, global_filters):
26312635
26322636 # Validate AP configuration inventory exists
26332637 if not self .have .get ("all_ap_config" ):
2634- self .msg = (
2635- "No access points configuration found in Cisco Catalyst Center inventory. "
2636- "self.have['all_ap_config'] is empty or None. Cannot proceed with filter processing. "
2637- "Verify: (1) APs onboarded to Catalyst Center, (2) get_current_config() executed "
2638- "successfully, (3) API permissions sufficient. Halting workflow with critical error."
2638+ self .log (
2639+ "No access point configurations available for filter processing. Returning empty." ,
2640+ "DEBUG"
26392641 )
2640- self .log (self .msg , "WARNING" )
2641- self .fail_and_exit (self .msg )
2642+ return []
26422643
26432644 self .log (
26442645 f"AP configuration inventory validation passed. Found { len (self .have .get ('all_ap_config' ))} "
@@ -2680,7 +2681,7 @@ def process_global_filters(self, global_filters):
26802681 f"Site filter { site_index } /{ len (site_list )} : Site hierarchy '{ floor } ' NOT "
26812682 f"found in AP configurations. No APs assigned to this site. Adding to "
26822683 f"unprocessed list." ,
2683- "WARNING "
2684+ "DEBUG "
26842685 )
26852686 unprocessed_aps .append (f"{ floor } : Unable to find the configuration for the site hierarchy in the catalyst center." )
26862687 continue
@@ -2722,11 +2723,10 @@ def process_global_filters(self, global_filters):
27222723 if not ap_exist :
27232724 self .log (
27242725 "No provisioned access points found in Catalyst Center inventory. No APs "
2725- "have rf_profile='HIGH'. Halting workflow with critical error ." ,
2726- "WARNING "
2726+ "have rf_profile='HIGH'. Returning empty ." ,
2727+ "DEBUG "
27272728 )
2728- self .msg = "No provisioned access points found in the catalyst center."
2729- self .fail_and_exit (self .msg )
2729+ return []
27302730
27312731 provisioned_aps = []
27322732 for ap_index , each_ap in enumerate (ap_exist , start = 1 ):
@@ -2765,27 +2765,49 @@ def process_global_filters(self, global_filters):
27652765 self .log (
27662766 f"Provision hostname filter { host_index } /{ len (provision_hostname_list )} : "
27672767 f"Hostname '{ each_host } ' NOT found in AP configurations. Adding to unprocessed list." ,
2768- "WARNING "
2768+ "DEBUG "
27692769 )
27702770 unprocessed_aps .append (f"{ each_host } : Unable to find the hostname in the catalyst center." )
27712771 continue
27722772
2773+ selected_ap = ap_exist [0 ]
2774+ if selected_ap .get ("rf_profile" ) is None or selected_ap .get ("site" ) is None :
2775+ self .log (
2776+ f"Provision hostname filter { host_index } /{ len (provision_hostname_list )} : "
2777+ f"Found AP '{ each_host } ', but it is not provisioned to any floor site. "
2778+ "Adding to unprocessed list." ,
2779+ "DEBUG"
2780+ )
2781+ unprocessed_aps .append (
2782+ f"{ each_host } : AP found but not provisioned to any floor site."
2783+ )
2784+ continue
2785+
27732786 self .log (
27742787 f"Provision hostname filter { host_index } /{ len (provision_hostname_list )} : "
27752788 f"Found AP '{ each_host } '. Extracting minimal provision config." ,
27762789 "INFO"
27772790 )
27782791
27792792 provisioned_aps .append ({
2780- "mac_address" : ap_exist [ 0 ] .get ("mac_address" ),
2781- "rf_profile" : ap_exist [ 0 ] .get ("rf_profile" ),
2782- "site" : ap_exist [ 0 ] .get ("site" )
2793+ "mac_address" : selected_ap .get ("mac_address" ),
2794+ "rf_profile" : selected_ap .get ("rf_profile" ),
2795+ "site" : selected_ap .get ("site" )
27832796 })
27842797
27852798 if not provisioned_aps :
2786- self .msg = "No provisioned access points found in the catalyst center."
2787- self .log (self .msg , "WARNING" )
2788- self .fail_and_exit (self .msg )
2799+ self .log (
2800+ "No provisioned APs matched provision_hostname_list. Returning empty." ,
2801+ "DEBUG"
2802+ )
2803+
2804+ if unprocessed_aps :
2805+ self .msg = {
2806+ "The following access points could not be processed:" : unprocessed_aps
2807+ }
2808+ self .have ["unprocessed" ] = unprocessed_aps
2809+
2810+ return []
27892811
27902812 final_list = provisioned_aps
27912813
@@ -2847,7 +2869,7 @@ def process_global_filters(self, global_filters):
28472869 self .log (
28482870 f"AP config filter { ap_name_index } /{ len (accesspoint_config_list )} : AP name "
28492871 f"'{ each_ap_name } ' NOT found in configurations. Adding to unprocessed list." ,
2850- "WARNING "
2872+ "DEBUG "
28512873 )
28522874 unprocessed_aps .append (f"{ each_ap_name } : Unable to find the hostname in the catalyst center." )
28532875 continue
@@ -2866,9 +2888,19 @@ def process_global_filters(self, global_filters):
28662888 ap_config_list .extend (ap_exist )
28672889
28682890 if not ap_config_list :
2869- self .msg = f"No access points found matching the provided list. { accesspoint_config_list } ."
2870- self .log (self .msg , "WARNING" )
2871- self .fail_and_exit (self .msg )
2891+ self .log (
2892+ f"No access points found matching the provided list: { accesspoint_config_list } . "
2893+ "Returning empty." ,
2894+ "DEBUG"
2895+ )
2896+
2897+ if unprocessed_aps :
2898+ self .msg = {
2899+ "The following access points could not be processed:" : unprocessed_aps
2900+ }
2901+ self .have ["unprocessed" ] = unprocessed_aps
2902+
2903+ return []
28722904
28732905 final_list = ap_config_list
28742906
@@ -2915,7 +2947,7 @@ def process_global_filters(self, global_filters):
29152947 self .log (
29162948 f"Combined filter { host_index } /{ len (accesspoint_provision_config_list )} : "
29172949 f"Hostname '{ each_host_name } ' NOT found in configurations. Adding to unprocessed list." ,
2918- "WARNING "
2950+ "DEBUG "
29192951 )
29202952 unprocessed_aps .append (f"{ each_host_name } : Unable to find the hostname in the catalyst center." )
29212953 continue
@@ -2929,9 +2961,18 @@ def process_global_filters(self, global_filters):
29292961 collected_aps .extend (ap_exist )
29302962
29312963 if not collected_aps :
2932- self .msg = "No access points found matching the provided hostname list."
2933- self .log (self .msg , "WARNING" )
2934- self .fail_and_exit (self .msg )
2964+ self .log (
2965+ "No access points found matching the provided hostname list. Returning empty." ,
2966+ "DEBUG"
2967+ )
2968+
2969+ if unprocessed_aps :
2970+ self .msg = {
2971+ "The following access points could not be processed:" : unprocessed_aps
2972+ }
2973+ self .have ["unprocessed" ] = unprocessed_aps
2974+
2975+ return []
29352976
29362977 final_list = collected_aps
29372978
@@ -2978,7 +3019,7 @@ def process_global_filters(self, global_filters):
29783019 self .log (
29793020 f"MAC filter { mac_index } /{ len (accesspoint_provision_config_mac_list )} : MAC "
29803021 f"address '{ each_mac } ' NOT found in configurations. Adding to unprocessed list." ,
2981- "WARNING "
3022+ "DEBUG "
29823023 )
29833024 unprocessed_aps .append (
29843025 f"{ each_mac } : Unable to find configuration for the MAC address in the catalyst center." )
@@ -2993,9 +3034,18 @@ def process_global_filters(self, global_filters):
29933034 collected_aps .extend (ap_exist )
29943035
29953036 if not collected_aps :
2996- self .msg = "No access points found matching the provided mac address list."
2997- self .log (self .msg , "WARNING" )
2998- self .fail_and_exit (self .msg )
3037+ self .log (
3038+ "No access points found matching the provided mac address list. Returning empty." ,
3039+ "DEBUG"
3040+ )
3041+
3042+ if unprocessed_aps :
3043+ self .msg = {
3044+ "The following access points could not be processed:" : unprocessed_aps
3045+ }
3046+ self .have ["unprocessed" ] = unprocessed_aps
3047+
3048+ return []
29993049
30003050 final_list = collected_aps
30013051
@@ -3025,7 +3075,7 @@ def process_global_filters(self, global_filters):
30253075 f"Filter processing completed with { len (unprocessed_aps )} unprocessed AP identifier(s). "
30263076 "These APs/sites/MACs did not match any configurations in Catalyst Center inventory. "
30273077 f"Unprocessed list: { self .msg } " ,
3028- "WARNING "
3078+ "DEBUG "
30293079 )
30303080 self .have ["unprocessed" ] = unprocessed_aps
30313081
@@ -3035,7 +3085,7 @@ def process_global_filters(self, global_filters):
30353085 "No access points matched the provided global filter criteria. Final filtered list "
30363086 "is empty. This may indicate: (1) Filter values don't match existing APs, "
30373087 "(2) Filters too restrictive, or (3) No APs in Catalyst Center. Returning None." ,
3038- "WARNING "
3088+ "DEBUG "
30393089 )
30403090 return None
30413091
0 commit comments