Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions changelogs/fragments/nxos_prefix_lists.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
bugfixes:
- "nxos_prefix_lists - Fix idempotency issues caused by IP prefix notation discrepancies"
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@
created.
"""

try:
import ipaddress

HAS_IPADDRESS = True
except ImportError:
HAS_IPADDRESS = False

import copy

from ansible.module_utils.six import iteritems
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
ResourceModule,
Expand Down Expand Up @@ -117,7 +126,7 @@ def _compare(self, want, have):
def _compare_seqs(self, want, have):
for wseq, wentry in iteritems(want):
hentry = have.pop(wseq, {})
if hentry != wentry:
if not self._compare_entry(wentry, hentry):
if hentry:
if self.state == "merged":
self._module.fail_json(
Expand All @@ -134,6 +143,41 @@ def _compare_seqs(self, want, have):
for hseq in have.values():
self.addcmd(hseq, "entry", negate=True)

def _compare_entry(self, want_entry, have_entry):
if HAS_IPADDRESS:
want_entry_to_compare = copy.deepcopy(want_entry)
have_entry_to_compare = copy.deepcopy(have_entry)
if "prefix" in want_entry_to_compare and "prefix" in have_entry_to_compare:
try:
want_entry_to_compare["prefix"] = ipaddress.ip_network(
want_entry_to_compare["prefix"],
strict=False,
)
have_entry_to_compare["prefix"] = ipaddress.ip_network(
have_entry_to_compare["prefix"],
strict=False,
)
except ValueError as e:
self._module.fail_json(
msg="Cannot parse prefix. {0}.".format(str(e)),
)
return want_entry_to_compare == have_entry_to_compare
else:
self.warnings.append(
(
"The ipaddress package was not found. Prefix standardization "
"will not be performed. Please ensure that the prefix arguments in the "
"playbook and the running-config are exactly the same, or install the ipaddress "
"package. If there is a discrepancy in prefix notation between the playbook "
"and the running-config in this state, idempotency will not be maintained, "
"resulting in the deletion and re-addition of existing configurations. For "
"example, if the playbook specifies prefix: ::0/, the running-config will "
"display it as 0::0/0. In this case, running the playbook multiple times will "
"cause the existing config to be deleted and re-added each time."
),
)
return want_entry == have_entry

def _prefix_list_transform(self, entry):
for afi, value in iteritems(entry):
if "prefix_lists" in value:
Expand Down
91 changes: 91 additions & 0 deletions tests/unit/modules/network/nxos/test_nxos_prefix_lists.py
Original file line number Diff line number Diff line change
Expand Up @@ -619,3 +619,94 @@ def test_nxos_prefix_lists_gathered(self):
]
result = self.execute_module(changed=False)
self.assertEqual(result["gathered"], gathered)

def test_nxos_prefix_lists_idempotency_with_prefix_notation_discrepancy(self):
self.get_config.return_value = dedent(
"""\
ipv6 prefix-list plist1 seq 10 permit 0::/0
ipv6 prefix-list plist1 seq 15 permit 2001:db8::1/128
ipv6 prefix-list plist1 seq 20 permit 2001:db8::2:1/128
ipv6 prefix-list plist1 seq 25 permit 2001:db8:1:1:1::/128
ipv6 prefix-list plist1 seq 30 permit 2001:db8:0:1:1:1:1:1/128
ipv6 prefix-list plist1 seq 35 permit 2001:0:0:1::1/128
ipv6 prefix-list plist1 seq 40 permit 2001:db8::1:0:0:1/128
ipv6 prefix-list plist1 seq 45 permit 2001:db8::abcd:ef12/128
ip prefix-list plist2 seq 10 permit 10.0.0.0/24
""",
)
set_module_args(
dict(
config=[
dict(
afi="ipv6",
prefix_lists=[
dict(
name="plist1",
entries=[
dict(
sequence=10,
action="permit",
prefix="::/0",
),
dict(
sequence=15,
action="permit",
prefix="2001:0db8::0001/128",
),
dict(
sequence=20,
action="permit",
prefix="2001:db8:0:0:0:0:2:1/128",
),
dict(
sequence=25,
action="permit",
prefix="2001:db8:1:1:1::0/128",
),
dict(
sequence=30,
action="permit",
prefix="2001:db8::1:1:1:1:1/128",
),
dict(
sequence=35,
action="permit",
prefix="2001::1:0:0:0:1/128",
),
dict(
sequence=40,
action="permit",
prefix="2001:db8:0:0:1::1/128",
),
dict(
sequence=45,
action="permit",
prefix="2001:DB8::ABCD:EF12/128",
),
],
),
],
),
dict(
afi="ipv4",
prefix_lists=[
dict(
name="plist2",
entries=[
dict(
sequence=10,
action="permit",
prefix="10.0.0.8/24",
),
],
),
],
),
],
state="overridden",
),
ignore_provider_arg,
)
commands = []
result = self.execute_module(changed=False)
self.assertEqual(set(result["commands"]), set(commands))
Loading