Skip to content

Network Wireless Profile site unassign issue fix#39

Open
shatagopasunil wants to merge 1 commit into
mainfrom
nw_wireless_bug_fix
Open

Network Wireless Profile site unassign issue fix#39
shatagopasunil wants to merge 1 commit into
mainfrom
nw_wireless_bug_fix

Conversation

@shatagopasunil

Copy link
Copy Markdown

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

PR Description

Issue:
When removing child sites from a wireless network profile, the module reports "Sites unassigned successfully" even when no sites were actually unassigned — because the parent site is still assigned to the profile and Catalyst Center's inheritance prevents the child from being independently removed.

RCA:
The _remove_site_names method blindly called the unassign API for all requested sites without checking the site hierarchy. Catalyst Center silently accepts the unassign request for child sites but does nothing if the parent is still assigned. The module then reported success based on the API returning without error, giving the user a false positive.

Fix:

  • Added is_parent_assigned method that recursively checks if any ancestor site is still assigned to the profile before attempting removal.
  • Sites whose parent is still assigned (and not being removed in the same operation) are now skipped with a clear warning instead of making a no-op API call.
  • The response now accurately reports which sites were actually removed vs. which were skipped due to parent inheritance.

Testing Done:

  • Manual testing
  • Unit tests
  • [] Integration tests

Test cases covered: [Mention test case IDs or brief points]

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • All the sanity checks have been completed and the sanity test cases have been executed

Ansible Best Practices

  • Tasks are idempotent (can be run multiple times without changing state)
  • Variables and secrets are handled securely (e.g., using ansible-vault or environment variables)
  • Playbooks are modular and reusable
  • Handlers are used for actions that need to run on change

Documentation

  • All options and parameters are documented clearly.
  • Examples are provided and tested.
  • Notes and limitations are clearly stated.

Screenshots (if applicable)

Notes to Reviewers

return False, None

parent = site_name.rsplit("/", 1)[0]
parent_exists, parent_id = self.get_site_id(parent)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we check if parent_exists and log a message, return?

"WARNING"
)
return True, parent

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if not parent_id... we'll check is_parent_assigned ? Is it correct?

unassign_sites = []
sites_removed = 0
sites_skipped = []

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Also reset the removed_sites and skipped_sites for multi-profiles...

    # Reset per-profile trackers so multi-profile playbooks don't accumulate
    self.removed_sites = []
    self.skipped_sites = []

)
return False, None

parent = site_name.rsplit("/", 1)[0]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

        # No "/" left (malformed path or already at top) - stop recursion safely
        if parent == site_name:
            self.log(
                "No further parent to check for site '{0}' - stopping recursion.".format(
                    site_name
                ),
                "DEBUG"
            )
            return False, None


# Build set of currently assigned site IDs for parent check
assigned_site_ids = set()
for site in each_have_profile.get("previous_sites", []):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

get the site id, check and assign.. to avoid chances of None..

        for site in each_have_profile.get("previous_sites", []):
            site_id = site.get("id")
            if site_id:
                assigned_site_ids.add(site_id)

"ap_zones_status", "feature_template_designs_status",
"additional_interfaces_status"
])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if sites are only skipped, no actual change happened. It should be changed=False.

            if remove_status and not removal_occurred and remove_status.get("site_skip_status"):
                self.msg = (
                    "No sites were unassigned from wireless profile '{0}'. "
                    "Sites '{1}' skipped due to parent inheritance."
                ).format(have_profile_name, "', '".join(self.skipped_sites))
                self.log(self.msg, "INFO")
                self.set_operation_result(
                    "success", False, self.msg, "INFO", remove_status
                ).check_return_status()
                return self

@@ -3994,21 +4069,36 @@
have_profile_name, have_profile_id, have_site_name, have_site_id
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

                if not unassign_response:
                    self.log(
                        "Failed to unassign site '{0}' from profile '{1}'.".format(
                            have_site_name, have_profile_name
                        ),
                        "ERROR"
                    )
                    continue

self.msg += " " + sites_message.strip()

# Process Day N template removal status
if remove_status.get("day_n_templates_status"):

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Kindly check and update network_proflies.py..otherwise task failure can still return a truthy response.

                    if task_status == "FAILURE":
                        self.result["changed"] = False
                        self.result["response"] = self.get_task_details_by_id(task_id)
                        self.log(
                            "Task {0} failed: {1}".format(task_id, self.result["response"]),
                            "ERROR"
                        )
                        return None

)

result = self.execute_module(changed=True, failed=False)
self.maxDiff = None

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

can we replace it?

        self.assertTrue(result.get("changed"))
        self.assertTrue(result.get("response", {}).get("site_remove_status"))
        self.assertTrue(result.get("response", {}).get("site_skip_status"))
        self.assertIn("SJ_BLD20", result.get("msg"))
        self.assertIn("FLOOR1", result.get("msg"))
        self.assertIn("FLOOR3", result.get("msg"))
        self.assertIn("FLOOR4", result.get("msg"))
        self.assertIn("skipped due to parent inheritance", result.get("msg"))

self.assertIn(
"Wireless profile data removed successfully",
result.get('msg')
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Can we add one more test where only child sites are requested for removal while the parent remains assigned. Expected result should be changed=False, site_remove_status=False, and site_skip_status=True.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants