Skip to content

Commit 0ecec0c

Browse files
authored
Merge pull request #17 from cisco-en-programmability/private/vivekraj2000/CSCwu53492
Fixed idempotency issue in application_policy_workflow_manager
2 parents 5d3a125 + 942b8b8 commit 0ecec0c

2 files changed

Lines changed: 32 additions & 339 deletions

File tree

plugins/modules/application_policy_workflow_manager.py

Lines changed: 32 additions & 304 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)