Skip to content

[0.8.0/0.8.1] Removing the final MCFG VRFs or networks silently skips deletion #846

Description

@dacasti2mx

Ansible Version

ansible [core 2.16.3]
  python version = 3.11.15
  jinja version = 3.1.6

Ansible Collection Versions

Collection          Version
------------------- --------
cisco.nac_dc_vxlan  0.8.1-dev (develop @ 11588f44)
cisco.dcnm          3.12.1-dev
cisco.nxos          10.2.0
ansible.netcommon   7.1.0
ansible.posix       2.0.0
ansible.utils       5.1.2
community.general   10.1.0

Cisco Nexus Dashboard Version

4.1.1g (NDFC 12.4.1.321)

Cisco NX-OS Version

10.6(2), virtual N9K-C9300v

Which role is this issue related to?

cisco.nac_dc_vxlan.dtc.remove

Which section of the data model is this issue related to?

vxlan.multisite

Expected Behavior

When the final remaining vxlan.multisite.overlay VRF or network entries are removed and the corresponding list becomes empty for an MCFG fabric, the remove pipeline should detect the change and send the corresponding VRF/network delete requests to NDFC — the same way any other declared-then-removed resource is handled in diff-run mode.

Actual Behavior

The collection keeps a small file on disk for each MCFG fabric (the sentinel file, _msite_overlay_sentinel.yml) to remember what the VRFs/networks looked like last time, so it can tell if anything changed.

The function that writes it — _detect_msite_overlay_changes() (plugins/action/dtc/build_resource_data.py) — doesn't save the data as plain values. Instead of writing name: VRF01, the file ends up with something like this for that same value:

? !!python/object/apply:ansible.utils.unsafe_proxy.AnsibleUnsafeText
  - VRF01

That's a Python-specific instruction embedded in the file, not just plain text. The function that reads the file back — _sentinel_had_key(), same file — deliberately refuses to load files containing that kind of instruction (a normal security precaution, so it never rebuilds arbitrary code from a file). Because of this mismatch, reading the file always fails.

That failure is silently ignored instead of being reported. So instead of saying "I couldn't read my saved data," the collection just assumes "there was nothing here before."

This becomes a problem specifically once every VRF/network is removed: the current data doesn't disappear, it just becomes an empty list. The only way to tell "these existed before and were removed" from "there was never anything here" is by checking that saved file — and that check is exactly what's silently failing.

End result: the collection always assumes nothing existed before, so it thinks nothing changed, and it never asks NDFC to delete the VRF/networks. Since nothing fails loudly, the playbook finishes normally as if the removal worked — but the VRF/networks are still attached in NDFC.

  • API method: none
  • API endpoint: none
  • Controller requests: 0

Scope

Affected:

  • MCFG fabrics where the final remaining vxlan.multisite.overlay.vrfs or .networks entries are removed, so that list becomes empty (e.g. removing the corresponding host_vars files or clearing the lists) in diff-run mode (force_run_all: false). Present since 0.8.0 (confirmed in the 0.8.0 tag's plugins/action/dtc/build_resource_data.py; the file did not exist prior to 0.7.2).
  • The failure occurs before any controller request and has no NDFC-version conditional — it is a client-side collection defect in how the collection serializes/deserializes its own local change-detection file. Confirmed on ND 4.1.1g / NDFC 12.4.1.321; expected on ND 4.2 as well, but ND 4.2 has not yet been tested directly.

Not affected:

  • Removing some, but not all, entries from an otherwise still-populated vrfs/networks list (e.g. removing 2 of 3 networks while one remains) — that case is handled correctly via the normal per-resource diff mechanism, independent of this defect.
  • Greenfield deployments (first-ever run for a given overlay) — there is no pre-existing sentinel to misread in that case.
  • MSD fabrics share the same overlay change-detection code path but have not been separately reproduced/validated here — that remains open.

Ansible Playbook

---
- name: Manage VXLAN MCFG Fabric
  hosts: all
  gather_facts: false
  roles:
    - role: cisco.nac_dc_vxlan.dtc.create
    - role: cisco.nac_dc_vxlan.dtc.deploy
    - role: cisco.nac_dc_vxlan.dtc.remove

Data Model

# Previous desired state (already deployed and attached in NDFC)
vxlan:
  multisite:
    overlay:
      vrfs:
        - name: VRF01
          vlan_id: 3001
          vrf_attach_group: all
      networks:
        - name: Net01
          vrf_name: VRF01
          vlan_id: 3101
          net_id: 130101
          network_attach_group: all
      vrf_attach_groups:
        - name: all
          switches: [...]
      network_attach_groups:
        - name: all
          switches: [...]

# Current desired state: VRF01/Net01 removed. The extended/rendered data
# model keeps every overlay key present with an empty list rather than
# removing the "overlay" key itself -- this truthy-dict-of-empty-lists
# shape is what actually reaches the pipeline, and is the specific
# condition that triggers the defect:
vxlan:
  multisite:
    overlay:
      vrfs: []
      networks: []
      vrf_attach_groups: []
      network_attach_groups: []

Steps to Reproduce

  1. Deploy an MCFG parent fabric with vxlan.multisite.overlay.vrfs/networks declared and successfully created/attached in NDFC.
  2. Remove every entry from vxlan.multisite.overlay.vrfs and .networks (or delete the corresponding data model files/host_vars entries), so both lists become empty.
  3. Run the create → deploy → remove workflow in diff-run mode (force_run_all: false).
  4. Observe the networks/vrfs remove steps report skipped with reason change flag ... is False, and the overall play reports success with no errors.
  5. Query NDFC directly and confirm the VRFs/networks are still present and attached.

Relevant Debug Output

{
    "module": "dcnm_network",
    "reason": "change flag 'changes_detected_networks' is False",
    "resource_name": "networks",
    "status": "skipped"
},
{
    "module": "dcnm_vrf",
    "reason": "change flag 'changes_detected_vrfs' is False",
    "resource_name": "vrfs",
    "status": "skipped"
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingwipwork-in-progress

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions