Skip to content

Commit

Permalink
update tests and config code
Browse files Browse the repository at this point in the history
  • Loading branch information
KB-perByte committed Mar 25, 2024
1 parent fa318e4 commit 87cda46
Show file tree
Hide file tree
Showing 5 changed files with 693 additions and 134 deletions.
167 changes: 89 additions & 78 deletions plugins/module_utils/network/ios/config/acls/acls.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
dict_merge,
)

from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import Facts
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.facts import (
Facts,
)
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.acls import (
AclsTemplate,
)
Expand Down Expand Up @@ -83,7 +85,9 @@ def generate_commands(self):
wplists = wvalue.get("acls", {})
hplists = hvalue.get("acls", {})
hvalue["acls"] = {
k: v for k, v in iteritems(hplists) if k in wplists or not wplists
k: v
for k, v in iteritems(hplists)
if k in wplists or not wplists
}

# remove superfluous config for overridden and deleted
Expand Down Expand Up @@ -118,7 +122,9 @@ def rearrange_cmds(aces):
hplists = have.get("acls", {})
for wname, wentry in iteritems(wplists):
hentry = hplists.pop(wname, {})
acl_type = wentry["acl_type"] if wentry.get("acl_type") else hentry.get("acl_type")
acl_type = (
wentry["acl_type"] if wentry.get("acl_type") else hentry.get("acl_type")
)
# If ACLs type is different between existing and wanted ACL, we need first remove it
if acl_type != hentry.get("acl_type", acl_type):
self.commands.append(
Expand Down Expand Up @@ -169,101 +175,101 @@ def pop_remark(r_entry, afi):
else:
return {}

# case 1 - loop on want and compare with have data here
for wseq, wentry in iteritems(want):
hentry = have.pop(wseq, {})
rem_hentry, rem_wentry = {}, {}

if hentry:
if hentry: # if there is have information with same sequence
# the protocol options are processed here
hentry = self.sanitize_protocol_options(wentry, hentry)

if hentry != wentry: # will let in if ace is same but remarks is not same
if hentry:
if hentry != wentry: # if want and have is different
if hentry: # separate remarks from have in an ace entry
rem_hentry["remarks"] = pop_remark(hentry, afi)
if wentry:
if wentry: # separate remarks from want in an ace entry
rem_wentry["remarks"] = pop_remark(wentry, afi)

if hentry:
if hentry: # have aces processing starts here
if self.state == "merged":
self._module.fail_json(
msg="Cannot update existing sequence {0} of ACLs {1} with state merged."
" Please use state replaced or overridden.".format(
hentry.get("sequence", ""),
name,
),
)
else: # other action states
if rem_hentry.get("remarks"): # remove remark if not in want
for k_hrems, hrems in rem_hentry.get("remarks").items():
if k_hrems not in rem_wentry.get("remarks", {}).keys():
if self.state in ["replaced", "overridden"]:
self.addcmd(
{
"remarks": hrems,
"sequence": hentry.get("sequence", ""),
},
"remarks_no_data",
negate=True,
)
break
else:
self.addcmd(
{
"remarks": hrems,
"sequence": hentry.get("sequence", ""),
},
"remarks",
negate=True,
)
# remove ace if not in want
# we might think why not update it directly,
# if we try to update without negating the entry appliance
# reports % Duplicate sequence number
if hentry != wentry:
self.addcmd(add_afi(hentry, afi), "aces", negate=True)
# once an ace is negated intentionally emptying out have so that
# the remarks are repopulated, as the remarks and ace behavior is sticky
# if an ace is taken out all the remarks is removed automatically.
rem_hentry["remarks"] = {}
) # if merged then don't update anything and fail

# i.e if not merged
if rem_hentry.get("remarks") != rem_wentry.get("remarks"):
self.addcmd(
{
"sequence": hentry.get("sequence", None),
},
"remarks_no_data",
negate=True,
) # remove all remarks for a ace if want and have don't match
# as if we randomly add aces we cannot maintain order we have to
# add all of them again, for that ace
rem_hentry["remarks"] = {}
# and me empty our have as we would add back
# all our remarks for that ace anyways

# remove ace if not in want
# we might think why not update it directly,
# if we try to update without negating the entry appliance
# reports % Duplicate sequence number
if hentry != wentry:
self.addcmd(add_afi(hentry, afi), "aces", negate=True)
# once an ace is negated intentionally emptying out have so that
# the remarks are repopulated, as the remarks and ace behavior is sticky
# if an ace is taken out all the remarks is removed automatically.
rem_hentry["remarks"] = {}

if rem_wentry.get("remarks"): # add remark if not in have
if rem_hentry.get("remarks"):
self.addcmd(
{
"sequence": hentry.get("sequence", None),
},
"remarks_no_data",
negate=True,
) # but delete all remarks before to protect order
for k_wrems, wrems in rem_wentry.get("remarks").items():
if k_wrems not in rem_hentry.get("remarks", {}).keys():
self.addcmd(
{
"remarks": wrems,
"sequence": hentry.get("sequence", ""),
},
"remarks",
)
else:
rem_hentry.get("remarks", {}).pop(k_wrems)
# We remove remarks that are not in the wentry for this ACE
for k_hrems, hrems in rem_hentry.get("remarks", {}).items():
self.addcmd(
{"remarks": hrems, "sequence": hentry.get("sequence", "")},
{
"remarks": wrems,
"sequence": wentry.get("sequence", ""),
},
"remarks",
negate=True,
)

# add ace if not in have
if hentry != wentry:
self.addcmd(add_afi(wentry, afi), "aces")

# remove remaining entries from have aces list
if len(wentry) == 1 and wentry.get(
"sequence"
): # if the ace entry just has sequence then do nothing
continue
else: # add normal ace entries from want
self.addcmd(add_afi(wentry, afi), "aces")

# case 2 - loop over remaining have and remove them
for hseq in have.values():
if hseq.get("remarks"): # remove remarks that are extra in have
for krems, rems in hseq.get("remarks").items():
self.addcmd(
{"remarks": rems, "sequence": hseq.get("sequence", "")},
"remarks",
negate=True,
)
if hseq.get("remarks"): # remove all remarks in that
self.addcmd(
{
"sequence": hseq.get("sequence", None),
},
"remarks_no_data",
negate=True,
)
hseq.pop("remarks")
# deal with the rest of ace entry
self.addcmd(
add_afi(hseq, afi),
"aces",
negate=True,
) # deal with the rest of ace entry
)

def sanitize_protocol_options(self, wace, hace):
"""handles protocol and protocol options as optional attribute"""
Expand Down Expand Up @@ -320,20 +326,25 @@ def list_to_dict(self, param):
.get("range")
):
for k, v in (
ace.get("destination", {}).get("port_protocol", {}).items()
ace.get("destination", {})
.get("port_protocol", {})
.items()
):
ace["destination"]["port_protocol"][
k
] = self.port_protocl_no_to_protocol(v)
ace["destination"]["port_protocol"][k] = (
self.port_protocl_no_to_protocol(v)
)
if acl.get("acl_type") == "standard":
for ks in list(ace.keys()):
if ks not in [
"sequence",
"grant",
"source",
"remarks",
"log",
]: # failing for mutually exclusive standard acl key
if (
ks
not in [
"sequence",
"grant",
"source",
"remarks",
"log",
]
): # failing for mutually exclusive standard acl key
self._module.fail_json(
"Unsupported attribute for standard ACL - {0}.".format(
ks,
Expand Down
6 changes: 3 additions & 3 deletions plugins/module_utils/network/ios/rm_templates/acls.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@


def remarks_with_sequence(remarks_data):
cmd = "remark "
cmd = "remark"
if remarks_data.get("remarks"):
cmd += remarks_data.get("remarks")
cmd += " " + remarks_data.get("remarks")
if remarks_data.get("sequence"):
cmd = to_text(remarks_data.get("sequence")) + " " + cmd
return cmd
Expand Down Expand Up @@ -206,7 +206,7 @@ def __init__(self, lines=None):
r"""(?P<order>^\d+)\s*remark\s(?P<remarks>.+)$""",
re.VERBOSE,
),
"setval": "{{ sequence }} remark",
"setval": remarks_with_sequence,
"result": {
"acls": {
"{{ acl_name|d() }}": {
Expand Down
133 changes: 133 additions & 0 deletions tests/integration/targets/ios_acls/tests/cli/remarks_states.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
---
- ansible.builtin.debug:
msg: START ios_acls round trip integration tests on connection={{ ansible_connection }}

- ansible.builtin.include_tasks: _remove_config.yaml

- ansible.builtin.include_tasks: _populate_config.yaml

- block:
- name: Apply the configuration with remarks
register: base_config
cisco.ios.ios_acls:
config:
- afi: ipv4
acls:
- name: TEST
acl_type: extended
aces:
- sequence: 10
remarks:
- "FIRST REMARK BEFORE SEQUENCE 10"
- "============"
- "REMARKS FOR SEQUENCE 10 NO FOLLOWING ACE"
- sequence: 20
remarks:
- "FIRST REMARK BEFORE SEQUENCE 20"
- "============"
- "ALLOW HOST FROM SEQUENCE 20"
grant: permit
protocol: ip
source:
host: 1.1.1.1
destination:
any: true
- sequence: 30
remarks:
- "FIRST REMARK BEFORE SEQUENCE 30"
- "============"
- "ALLOW HOST FROM SEQUENCE 30"
grant: permit
protocol: ip
source:
host: 2.2.2.2
destination:
any: true
- sequence: 40
remarks:
- "FIRST REMARK BEFORE SEQUENCE 40"
- "============"
- "ALLOW NEW HOST FROM SEQUENCE 40"
grant: permit
protocol: ip
source:
host: 3.3.3.3
destination:
any: true
- remarks:
- "Remark not specific to sequence"
- "============"
- "End Remarks"
state: merged

- ansible.builtin.assert:
that:
- base_config.changed == true
- base_config.commands|symmetric_difference(remarks_check.commands) == []
- base_config.before|symmetric_difference(remarks_check.before) == []
- base_config.after|symmetric_difference(remarks_check.after) == []

- name: Apply enhanced configuration
register: base_config_overridden
cisco.ios.ios_acls:
config:
- afi: ipv4
acls:
- name: TEST
acl_type: extended
aces:
- sequence: 10
remarks:
- "FIRST REMARK BEFORE SEQUENCE 10"
- "============"
- "REMARKS FOR SEQUENCE 10 NO FOLLOWING ACE"
grant: permit
protocol: ip
source:
host: 1.1.1.1
destination:
any: true
- sequence: 20
remarks:
- "FIRST REMARK BEFORE SEQUENCE 20"
- "============"
- "ALLOW HOST FROM SEQUENCE 20"
grant: permit
protocol: ip
source:
host: 192.168.0.1
destination:
any: true
- sequence: 30
remarks:
- "FIRST REMARK BEFORE SEQUENCE 30"
- "============"
- "ALLOW HOST FROM SEQUENCE 30 updated"
grant: permit
protocol: ip
source:
host: 2.2.2.2
destination:
any: true
- sequence: 40
remarks:
- "FIRST REMARK BEFORE SEQUENCE 40"
- "============"
- "ALLOW NEW HOST FROM SEQUENCE 40"
grant: permit
protocol: ip
source:
host: 3.3.3.3
destination:
any: true
- remarks:
- "Remark not specific to sequence"
- "============"
- "End Remarks updated"
state: overridden

- ansible.builtin.assert:
that:
- base_config_overridden.commands|symmetric_difference(remarks_check_override.commands) == []
always:
- ansible.builtin.include_tasks: _remove_config.yaml
Loading

0 comments on commit 87cda46

Please sign in to comment.