@@ -2295,281 +2295,6 @@ def get_diff_merged(self, config):
22952295
22962296 return self
22972297
2298- def is_update_required_for_application_policy (self ):
2299- """
2300- Check if updates are required for the application policy and trigger necessary updates.
2301-
2302- Args:
2303- self (object): An instance of a class used for interacting with Cisco Catalyst Center.
2304-
2305- Returns:
2306- bool:
2307- - True if updates are required for the application policy.
2308- - False if no updates are necessary.
2309-
2310- Description:
2311- This function evaluates whether updates are required for the application policy configuration in Cisco Catalyst Center
2312- based on the desired state provided in the configuration. It validates the existence of the application policy,
2313- identifies mismatches in queuing profiles and site mappings, and checks for missing application set names
2314- categorized as BUSINESS_RELEVANT, BUSINESS_IRRELEVANT, or DEFAULT.
2315-
2316- The function ensures the application policy aligns with the desired configuration while minimizing unnecessary updates.
2317- """
2318-
2319- application_policy = self .have
2320- application_policy_name = self .want .get ("application_policy" , {}).get ("name" )
2321-
2322- req_application_policy_details = self .config .get ("application_policy" )
2323-
2324- if not req_application_policy_details :
2325- self .log (
2326- "No application policy details found in the configuration." , "INFO"
2327- )
2328- return False
2329-
2330- application_queuing_profile_name = req_application_policy_details .get (
2331- "application_queuing_profile_name"
2332- )
2333- site_names = req_application_policy_details .get ("site_names" )
2334- site_ids = [self .get_site_id (site_name )[1 ] for site_name in site_names ]
2335- current_application_policy = application_policy .get (
2336- "current_application_policy"
2337- )
2338-
2339- self .log ("Checking if updates are required for the application policy." , "INFO" )
2340-
2341- queuing_profile_needs_update = False
2342-
2343- for contract in current_application_policy :
2344-
2345- if contract .get (
2346- "contract"
2347- ) and application_queuing_profile_name not in contract .get ("name" ):
2348- queuing_profile_needs_update = True
2349- break
2350-
2351- # Check if update is required for site
2352- site_scope_does_not_match = False
2353- for application_policy in current_application_policy :
2354- advanced_policy_scope = application_policy .get ("advancedPolicyScope" , {})
2355- scope_elements = advanced_policy_scope .get ("advancedPolicyScopeElement" , [])
2356-
2357- if scope_elements :
2358- group_ids = set (scope_elements [0 ].get ("groupId" , []))
2359-
2360- if set (site_ids ) != group_ids :
2361- site_scope_does_not_match = True
2362- break
2363-
2364- is_update_required_for_queuing_profile = queuing_profile_needs_update
2365- is_update_required_for_site = site_scope_does_not_match
2366-
2367- if is_update_required_for_queuing_profile :
2368- self .log (
2369- "Update required for queuing profile: {0}" .format (
2370- application_queuing_profile_name
2371- ),
2372- "INFO" ,
2373- )
2374- else :
2375- self .log (
2376- "No update required for queuing profile: {0}" .format (
2377- application_queuing_profile_name
2378- ),
2379- "INFO" ,
2380- )
2381-
2382- if is_update_required_for_site :
2383- self .log ("Update required for site(s): {0}" .format (site_names ), "INFO" )
2384- else :
2385- self .log ("No update required for site(s): {0}" .format (site_names ), "INFO" )
2386-
2387- other_check_names = ["application_queuing_profile" , "site_names" ]
2388- no_update_require = []
2389-
2390- if not is_update_required_for_queuing_profile :
2391- no_update_require .append ("application_queuing_profile" )
2392-
2393- if not is_update_required_for_site :
2394- no_update_require .append ("site_names" )
2395-
2396- update_not_required = True
2397-
2398- for check in other_check_names :
2399-
2400- if check not in no_update_require :
2401- update_not_required = False
2402- break
2403-
2404- if all (
2405- check in no_update_require
2406- for check in ["application_queuing_profile" , "site_names" ]
2407- ):
2408- self .log ("No update required for application policy" , "INFO" )
2409-
2410- self .log ("update required for application policy" , "INFO" )
2411- (
2412- want_business_relevant_set_name ,
2413- want_business_irrelevant_set_name ,
2414- want_default_set_name ,
2415- ) = ([], [], [])
2416- (
2417- have_business_relevant_set_name ,
2418- have_business_irrelevant_set_name ,
2419- have_default_set_name ,
2420- ) = ([], [], [])
2421-
2422- application_set_names = req_application_policy_details .get ("clause" )
2423-
2424- for item in application_set_names :
2425- for relevance in item ["relevance_details" ]:
2426-
2427- if relevance ["relevance" ] == "BUSINESS_RELEVANT" :
2428- want_business_relevant_set_name .extend (
2429- relevance ["application_set_name" ]
2430- )
2431-
2432- elif relevance ["relevance" ] == "BUSINESS_IRRELEVANT" :
2433- want_business_irrelevant_set_name .extend (
2434- relevance ["application_set_name" ]
2435- )
2436-
2437- elif relevance ["relevance" ] == "DEFAULT" :
2438- want_default_set_name .extend (relevance ["application_set_name" ])
2439-
2440- self .log (
2441- "Collected application set names: {0} for relevance: {1}" .format (
2442- application_set_names , relevance
2443- ),
2444- "DEBUG" ,
2445- )
2446-
2447- for application_sets in current_application_policy :
2448- clause = application_sets .get ("exclusiveContract" , {}).get ("clause" )
2449-
2450- if clause and clause [0 ].get ("relevanceLevel" ):
2451- current_relevance_type = clause [0 ].get ("relevanceLevel" )
2452- app_set_name = self ._extract_app_set_name (application_sets )
2453-
2454- if current_relevance_type == "BUSINESS_RELEVANT" :
2455- have_business_relevant_set_name .append (app_set_name )
2456-
2457- elif current_relevance_type == "BUSINESS_IRRELEVANT" :
2458- have_business_irrelevant_set_name .append (app_set_name )
2459-
2460- elif current_relevance_type == "DEFAULT" :
2461- have_default_set_name .append (app_set_name )
2462-
2463- self .log (
2464- "Existing application set: {0} categorized under {1}" .format (
2465- app_set_name , current_relevance_type
2466- ),
2467- "DEBUG" ,
2468- )
2469-
2470- (
2471- final_business_relevant_set_name ,
2472- final_business_irrelevant_set_name ,
2473- final_default_set_name ,
2474- ) = ([], [], [])
2475- for want_item , have_item , final_item in [
2476- (
2477- want_business_relevant_set_name ,
2478- have_business_relevant_set_name ,
2479- final_business_relevant_set_name ,
2480- ),
2481- (
2482- want_business_irrelevant_set_name ,
2483- have_business_irrelevant_set_name ,
2484- final_business_irrelevant_set_name ,
2485- ),
2486- (want_default_set_name , have_default_set_name , final_default_set_name ),
2487- ]:
2488- final_item .extend (item for item in want_item if item not in have_item )
2489-
2490- if not want_default_set_name :
2491- final_default_set_name = []
2492-
2493- if not want_business_relevant_set_name :
2494- final_business_relevant_set_name = []
2495-
2496- if not want_business_irrelevant_set_name :
2497- final_business_irrelevant_set_name = []
2498-
2499- if final_business_relevant_set_name :
2500- self .log (
2501- "Missing business relevant application sets: {0}" .format (
2502- final_business_relevant_set_name
2503- ),
2504- "INFO" ,
2505- )
2506-
2507- if final_business_irrelevant_set_name :
2508- self .log (
2509- "Missing business irrelevant application sets: {0}" .format (
2510- final_business_irrelevant_set_name
2511- ),
2512- "INFO" ,
2513- )
2514-
2515- if final_default_set_name :
2516- self .log (
2517- "Missing default application sets: {0}" .format (final_default_set_name ),
2518- "INFO" ,
2519- )
2520-
2521- if update_not_required :
2522- if not any (
2523- [
2524- final_business_relevant_set_name ,
2525- final_business_irrelevant_set_name ,
2526- final_default_set_name ,
2527- ]
2528- ):
2529- self .log (
2530- "No update required for application policy: {}" .format (
2531- application_policy_name
2532- ),
2533- "INFO" ,
2534- )
2535- return False
2536-
2537- relevance_differences = {
2538- "BUSINESS_RELEVANT" : final_business_relevant_set_name ,
2539- "BUSINESS_IRRELEVANT" : final_business_irrelevant_set_name ,
2540- "DEFAULT" : final_default_set_name ,
2541- }
2542-
2543- relevance_types_with_differences = []
2544- for relevance_type , missing_app_sets in relevance_differences .items ():
2545- if missing_app_sets :
2546- relevance_types_with_differences .append (relevance_type )
2547-
2548- if len (relevance_types_with_differences ) == 0 :
2549- return False
2550-
2551- if len (relevance_types_with_differences ) == 1 :
2552- differing_type = relevance_types_with_differences [0 ]
2553-
2554- if (
2555- differing_type == "BUSINESS_RELEVANT"
2556- and not want_business_relevant_set_name
2557- ) or (
2558- differing_type == "BUSINESS_IRRELEVANT"
2559- and not want_business_irrelevant_set_name
2560- ) or (
2561- differing_type == "DEFAULT"
2562- and not want_default_set_name
2563- ):
2564- self .log (
2565- "No update required: Only '{0}' set is empty in config. Ignoring difference." .format (differing_type ),
2566- "INFO"
2567- )
2568- return False
2569-
2570- self .log ("Updates are required for the application policy." , "INFO" )
2571- return True
2572-
25732298 def get_diff_application_policy (self ):
25742299 """
25752300 Get the differences in application policy configuration and trigger necessary updates.
@@ -2973,24 +2698,14 @@ def get_diff_application_policy(self):
29732698
29742699 total_current_app_set .append (app_set_name )
29752700
2976- # Determine if update is required
2977- update_not_required = False
2978-
2979- if not expected_set_names :
2980- update_not_required = True
2701+ if app_set_name in expected_set_names :
29812702 self .log (
2982- "No update required (empty expected set list) for application set: {0}" .format (app_set_name ),
2703+ "Application set '{0}' is already present under "
2704+ "relevance '{1}'." .format (
2705+ app_set_name , current_relevance_type
2706+ ),
29832707 "INFO" ,
29842708 )
2985- else :
2986- for set_name in expected_set_names :
2987- if set_name == app_set_name :
2988- update_not_required = True
2989- self .log (
2990- "No update required for application set: {0}" .format (app_set_name ),
2991- "INFO" ,
2992- )
2993- break # Exit loop early
29942709
29952710 self .log (
29962711 "Total Current Application Sets: {0}" .format (total_current_app_set ),
@@ -3123,22 +2838,35 @@ def get_diff_application_policy(self):
31232838 "Final want Default (Diff): {0}" .format (final_want_default ), "INFO"
31242839 )
31252840
2841+ # Idempotency decision: skip the update API call when nothing has
2842+ # actually changed. The three `final_*_set_name` lists hold
2843+ # `want - have` per relevance group, so a non-empty list means
2844+ # at least one requested application set is missing from that
2845+ # relevance group and must be added. Anything else (a strict
2846+ # subset of what is already deployed, or an exact match) leaves
2847+ # all three lists empty and is treated as a no-op, provided the
2848+ # site scope and queuing profile have not changed either. This
2849+ # prevents wireless policies from hitting NCAS10112 ("All
2850+ # GroupBasedPolicy objects of the same policy should have the
2851+ # same value for SSID name") when the playbook lists only a
2852+ # subset of the currently deployed application sets.
2853+ update_not_required = (
2854+ not is_update_required_for_site
2855+ and not is_update_required_for_queuing_profile
2856+ and not final_business_irrelevant_set_name
2857+ and not final_business_relevant_set_name
2858+ and not final_default_set_name
2859+ )
2860+
31262861 if update_not_required :
3127- if not (
3128- final_business_irrelevant_set_name
3129- or final_business_relevant_set_name
3130- or final_default_set_name
3131- or is_update_required_for_site
3132- or is_update_required_for_queuing_profile
3133- ):
3134- self .msg = (
3135- "Application policy '{0}' does not need any update. " .format (
3136- application_policy_name
3137- )
2862+ self .msg = (
2863+ "Application policy '{0}' does not need any update. " .format (
2864+ application_policy_name
31382865 )
3139- self .no_update_application_policy .append (application_policy_name )
3140- self .set_operation_result ("success" , False , self .msg , "INFO" )
3141- continue
2866+ )
2867+ self .no_update_application_policy .append (application_policy_name )
2868+ self .set_operation_result ("success" , False , self .msg , "INFO" )
2869+ continue
31422870
31432871 for application_sets in current_application_policy :
31442872 group_id = site_ids if is_update_required_for_site else current_site_ids
0 commit comments