summaryrefslogtreecommitdiffstats
path: root/ansible_collections/cisco/ios/plugins/module_utils
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/cisco/ios/plugins/module_utils')
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py14
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py11
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py7
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py157
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py59
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py28
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py10
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py45
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py548
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py55
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py14
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py13
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py12
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py268
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py0
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py0
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py82
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py0
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py147
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py203
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py163
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py66
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py32
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py210
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py57
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py2
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py56
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py6
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py2
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py43
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py11
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/vlans.py131
-rw-r--r--ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py24
33 files changed, 1037 insertions, 1439 deletions
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py
index 21bb051c3..6e82c99c2 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/bgp_global/bgp_global.py
@@ -163,6 +163,20 @@ class Bgp_globalArgs(object): # pylint: disable=R0903
"route_map": {"type": "str"},
},
},
+ "default": {
+ "type": "dict",
+ "apply_defaults": True,
+ "options": {
+ "ipv4_unicast": {"type": "bool", "default": True},
+ "route_target": {
+ "type": "dict",
+ "apply_defaults": True,
+ "options": {
+ "filter": {"type": "bool", "default": True},
+ },
+ },
+ },
+ },
"deterministic_med": {"type": "bool"},
"dmzlink_bw": {"type": "bool"},
"enforce_first_as": {"type": "bool"},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py
index 455261409..82ae98ee6 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/l3_interfaces/l3_interfaces.py
@@ -36,6 +36,8 @@ class L3_interfacesArgs(object): # pylint: disable=R0903
"type": "list",
"elements": "dict",
"options": {
+ "autostate": {"type": "bool"},
+ "mac_address": {"type": "str"},
"name": {"type": "str", "required": True},
"ipv4": {
"type": "list",
@@ -54,6 +56,14 @@ class L3_interfacesArgs(object): # pylint: disable=R0903
},
},
"pool": {"type": "str"},
+ "source_interface": {
+ "type": "dict",
+ "options": {
+ "name": {"type": "str"},
+ "poll": {"type": "bool"},
+ "point_to_point": {"type": "bool"},
+ },
+ },
},
},
"ipv6": {
@@ -72,6 +82,7 @@ class L3_interfacesArgs(object): # pylint: disable=R0903
"rapid_commit": {"type": "bool"},
},
},
+ "enable": {"type": "bool"},
"anycast": {"type": "bool"},
"cga": {"type": "bool"},
"eui": {"type": "bool"},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py
index 9db593dcc..65e631817 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/argspec/vlans/vlans.py
@@ -51,7 +51,10 @@ class VlansArgs(object):
"private_vlan": {
"type": "dict",
"options": {
- "type": {"type": "str", "choices": ["primary", "community", "isolated"]},
+ "type": {
+ "type": "str",
+ "choices": ["primary", "community", "isolated"],
+ },
"associated": {"type": "list", "elements": "int"},
},
},
@@ -65,7 +68,6 @@ class VlansArgs(object):
},
"type": "list",
},
- "configuration": {"type": "bool"},
"running_config": {"type": "str"},
"state": {
"choices": [
@@ -75,6 +77,7 @@ class VlansArgs(object):
"deleted",
"rendered",
"parsed",
+ "purged",
"gathered",
],
"default": "merged",
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py
index 164c75c40..f604e4360 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/acls/acls.py
@@ -119,6 +119,16 @@ class Acls(ResourceModule):
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")
+ # 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(
+ "no " + self.acl_name_cmd(wname, afi, hentry.get("acl_type", "")),
+ )
+ hentry.pop(
+ "aces",
+ {},
+ ) # We remove ACEs because we have previously add a command to suppress completely the ACL
+
begin = len(self.commands) # to determine the index for acl command
self._compare_aces(
wentry.pop("aces", {}),
@@ -159,20 +169,22 @@ class Acls(ResourceModule):
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."
@@ -180,55 +192,78 @@ class Acls(ResourceModule):
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
- if hentry != wentry:
- self.addcmd(add_afi(hentry, afi), "aces", negate=True)
+ ) # 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",
- )
+ self.addcmd(
+ {
+ "remarks": wrems,
+ "sequence": wentry.get("sequence", ""),
+ },
+ "remarks",
+ )
+
# 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,
- )
- else: # remove extra aces
- self.addcmd(add_afi(hseq, afi), "aces", 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,
+ )
def sanitize_protocol_options(self, wace, hace):
"""handles protocol and protocol options as optional attribute"""
@@ -262,7 +297,7 @@ class Acls(ResourceModule):
def list_to_dict(self, param):
"""converts list attributes to dict"""
- temp, count = dict(), 0
+ temp = dict()
if param:
for each in param: # ipv4 and ipv6 acl
temp_acls = {}
@@ -271,7 +306,9 @@ class Acls(ResourceModule):
temp_aces = {}
if acl.get("aces"):
rem_idx = 0 # remarks if defined in an ace
- for ace in acl.get("aces"): # each ace turned to dict
+ for count, ace in enumerate(
+ acl.get("aces"),
+ ): # each ace turned to dict
if (
ace.get("destination")
and ace.get("destination", {}).get(
@@ -285,9 +322,9 @@ class Acls(ResourceModule):
for k, v in (
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 [
@@ -311,13 +348,14 @@ class Acls(ResourceModule):
# temp_rem.extend(ace.pop("remarks"))
for remks in ace.get("remarks"):
rem_ace[remks.replace(" ", "_")] = remks
- rem_idx += 1
ace["remarks"] = rem_ace
if ace.get("sequence"):
temp_aces.update({ace.get("sequence"): ace})
+ elif ace.get("remarks"):
+ temp_aces.update({"__{0}".format(rem_idx): ace})
+ rem_idx += 1
elif ace:
- count += 1
temp_aces.update({"_" + to_text(count): ace})
# if temp_rem: # add remarks to the temp ace
@@ -325,7 +363,12 @@ class Acls(ResourceModule):
if acl.get("acl_type"): # update acl dict with req info
temp_acls.update(
- {acl.get("name"): {"aces": temp_aces, "acl_type": acl["acl_type"]}},
+ {
+ acl.get("name"): {
+ "aces": temp_aces,
+ "acl_type": acl["acl_type"],
+ },
+ },
)
else: # if no acl type then here eg: ipv6
temp_acls.update({acl.get("name"): {"aces": temp_aces}})
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py
index c48cac946..77112ffa2 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/bgp_global/bgp_global.py
@@ -61,6 +61,8 @@ class Bgp_global(ResourceModule):
"route_server_context.description",
"synchronization",
"table_map",
+ "template.peer_policy",
+ "template.peer_session",
"timers",
"bgp.additional_paths",
"bgp.advertise_best_external",
@@ -79,6 +81,8 @@ class Bgp_global(ResourceModule):
"bgp.consistency_checker.auto_repair",
"bgp.consistency_checker.error_message",
"bgp.dampening",
+ "bgp.default.ipv4_unicast",
+ "bgp.default.route_target.filter",
"bgp.deterministic_med",
"bgp.dmzlink_bw",
"bgp.enforce_first_as",
@@ -168,7 +172,10 @@ class Bgp_global(ResourceModule):
if self.state == "deleted":
# deleted, clean up global params
if not self.want or (self.have.get("as_number") == self.want.get("as_number")):
- self._compare(want={}, have=self.have)
+ if "as_number" not in self.want:
+ self.want["as_number"] = self.have.get("as_number")
+ self._set_bgp_defaults(self.want)
+ self._compare(self.want, have=self.have)
elif self.state == "purged":
# delete as_number takes down whole bgp config
@@ -190,10 +197,16 @@ class Bgp_global(ResourceModule):
for the Bgp_global network resource.
"""
self.generic_list_parsers = ["distributes", "aggregate_addresses", "networks"]
+
if self._has_bgp_inject_maps(want):
self.generic_list_parsers.insert(0, "inject_maps")
cmd_len = len(self.commands) # holds command length to add as_number
+
+ # for clean bgp global setup
+ if not have.get("bgp", {}).get("default"):
+ self._set_bgp_defaults(have)
+
# for dict type attributes
self.compare(parsers=self.parsers, want=want, have=have)
@@ -206,17 +219,40 @@ class Bgp_global(ResourceModule):
_parse,
)
else:
- self._compare_generic_lists(want.get(_parse, {}), have.get(_parse, {}), _parse)
+ self._compare_generic_lists(
+ want.get(_parse, {}),
+ have.get(_parse, {}),
+ _parse,
+ )
# for neighbors
- self._compare_neighbor_lists(want.get("neighbors", {}), have.get("neighbors", {}))
+ self._compare_neighbor_lists(
+ want.get("neighbors", {}),
+ have.get("neighbors", {}),
+ )
# for redistribute
- self._compare_redistribute_lists(want.get("redistribute", {}), have.get("redistribute", {}))
+ self._compare_redistribute_lists(
+ want.get("redistribute", {}),
+ have.get("redistribute", {}),
+ )
- # add as_number in the begining fo command set if commands generated
+ # add as_number in the beginning of commands set if commands generated
if len(self.commands) != cmd_len or (not have and want):
- self.commands.insert(0, self._tmplt.render(want or have, "as_number", False))
+ self.commands.insert(
+ 0,
+ self._tmplt.render(want or have, "as_number", False),
+ )
+
+ def _set_bgp_defaults(self, bgp_dict):
+ bgp_dict.setdefault("bgp", {}).setdefault("default", {}).setdefault(
+ "ipv4_unicast",
+ True,
+ )
+ bgp_dict.setdefault("bgp", {}).setdefault("default", {}).setdefault(
+ "route_target",
+ {},
+ ).setdefault("filter", True)
def _has_bgp_inject_maps(self, want):
if want.get("bgp", {}).get("inject_maps", {}):
@@ -324,10 +360,21 @@ class Bgp_global(ResourceModule):
]
for name, w_neighbor in want.items():
+ handle_shutdown_default = False
have_nbr = have.pop(name, {})
want_route = w_neighbor.pop("route_maps", {})
have_route = have_nbr.pop("route_maps", {})
+ if (
+ not w_neighbor.get("shutdown", {}).get("set")
+ and have_nbr.get("shutdown", {}).get("set")
+ and self.state in ["merged", "replaced", "overridden"]
+ ):
+ neig_parses.remove("shutdown")
+ handle_shutdown_default = True
self.compare(parsers=neig_parses, want=w_neighbor, have=have_nbr)
+ if handle_shutdown_default:
+ self.addcmd(have_nbr, "shutdown", True)
+
if want_route:
for k_rmps, w_rmps in want_route.items():
have_rmps = have_route.pop(k_rmps, {})
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py
index b4b874d6e..b862ac466 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/l3_interfaces/l3_interfaces.py
@@ -29,7 +29,6 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates
)
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.utils.utils import (
normalize_interface,
- validate_ipv6,
validate_n_expand_ipv4,
)
@@ -48,12 +47,18 @@ class L3_interfaces(ResourceModule):
tmplt=L3_interfacesTemplate(),
)
self.parsers = [
+ "mac_address",
"ipv4.address",
"ipv4.pool",
"ipv4.dhcp",
+ "ipv4.source_interface",
"ipv6.address",
"ipv6.autoconfig",
"ipv6.dhcp",
+ "ipv6.enable",
+ ]
+ self.gen_parsers = [
+ "autostate",
]
def execute_module(self):
@@ -101,10 +106,16 @@ class L3_interfaces(ResourceModule):
self._compare(want={}, have=have)
for k, want in wantd.items():
- self._compare(want=want, have=haved.pop(k, {}))
+ have = haved.pop(k, {})
+ # New interface (doesn't use fact file)
+ if k[:4] == "Vlan":
+ have.setdefault("autostate", True)
+ want.setdefault("autostate", True)
+ self._compare(want=want, have=have)
def _compare(self, want, have):
begin = len(self.commands)
+ self.compare(parsers=self.gen_parsers, want=want, have=have)
self._compare_lists(want=want, have=have)
if len(self.commands) != begin:
self.commands.insert(begin, self._tmplt.render(want or have, "name", False))
@@ -141,7 +152,12 @@ class L3_interfaces(ResourceModule):
# hacl is set as primary, if wacls has no other primary entry we must keep
# this entry as primary (so we'll compare entry to hacl and not
# generate commands)
- if list(filter(lambda w: w.get("secondary", False) is False, wacls.values())):
+ if list(
+ filter(
+ lambda w: w.get("secondary", False) is False,
+ wacls.values(),
+ ),
+ ):
# another primary is in wacls
hacl = {}
self.validate_ips(afi, want=entry, have=hacl)
@@ -168,17 +184,11 @@ class L3_interfaces(ResourceModule):
v4_addr = validate_n_expand_ipv4(self._module, want) if want.get("address") else {}
if v4_addr:
want["address"] = v4_addr
- elif afi == "ipv6" and want:
- if want.get("address"):
- validate_ipv6(want["address"], self._module)
if afi == "ipv4" and have:
v4_addr_h = validate_n_expand_ipv4(self._module, have) if have.get("address") else {}
if v4_addr_h:
have["address"] = v4_addr_h
- elif afi == "ipv6" and have:
- if have.get("address"):
- validate_ipv6(have["address"], self._module)
def list_to_dict(self, param):
if param:
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py
index d8a1bec9f..f02fbfdfb 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/route_maps/route_maps.py
@@ -304,11 +304,11 @@ class Route_maps(ResourceModule):
"acl",
)
elif match["ip"][each_ip_param].get("prefix_lists"):
- match["ip"][each_ip_param][
- "prefix_lists"
- ] = convert_to_dict(
- match["ip"][each_ip_param]["prefix_lists"],
- "prefix_list",
+ match["ip"][each_ip_param]["prefix_lists"] = (
+ convert_to_dict(
+ match["ip"][each_ip_param]["prefix_lists"],
+ "prefix_list",
+ )
)
if match.get("local_preference") and match.get("local_preference").get(
"value",
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py
index 187d0779d..d61a70a10 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/snmp_server/snmp_server.py
@@ -237,39 +237,20 @@ class Snmp_server(ResourceModule):
def _compare_lists_attrs(self, want, have):
"""Compare list of dict"""
for _parser in self.list_parsers:
- if _parser == "users":
- i_want = want.get(_parser, {})
- i_have = have.get(_parser, {})
- for key, wanting in iteritems(i_want):
- wanting_compare = deepcopy(wanting)
- if (
- "authentication" in wanting_compare
- and "password" in wanting_compare["authentication"]
- ):
- wanting_compare["authentication"].pop("password")
- if (
- "encryption" in wanting_compare
- and "password" in wanting_compare["encryption"]
- ):
- wanting_compare["encryption"].pop("password")
- haveing = i_have.pop(key, {})
- if wanting_compare != haveing:
- if haveing and self.state in ["overridden", "replaced"]:
+ i_want = want.get(_parser, {})
+ i_have = have.get(_parser, {})
+ for key, wanting in iteritems(i_want):
+ haveing = i_have.pop(key, {})
+ if wanting != haveing:
+ if haveing and self.state in ["overridden", "replaced"]:
+ if not (
+ _parser == "users"
+ and wanting.get("username") == haveing.get("username")
+ ):
self.addcmd(haveing, _parser, negate=True)
- self.addcmd(wanting, _parser)
- for key, haveing in iteritems(i_have):
- self.addcmd(haveing, _parser, negate=True)
- else:
- i_want = want.get(_parser, {})
- i_have = have.get(_parser, {})
- for key, wanting in iteritems(i_want):
- haveing = i_have.pop(key, {})
- if wanting != haveing:
- if haveing and self.state in ["overridden", "replaced"]:
- self.addcmd(haveing, _parser, negate=True)
- self.addcmd(wanting, _parser)
- for key, haveing in iteritems(i_have):
- self.addcmd(haveing, _parser, negate=True)
+ self.addcmd(wanting, _parser)
+ for key, haveing in iteritems(i_have):
+ self.addcmd(haveing, _parser, negate=True)
def _snmp_list_to_dict(self, data):
"""Convert all list of dicts to dicts of dicts"""
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py
index ad95b680a..d195c317e 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/config/vlans/vlans.py
@@ -1,62 +1,60 @@
#
# -*- coding: utf-8 -*-
-# Copyright 2019 Red Hat
+# Copyright 2024 Red Hat
# GNU General Public License v3.0+
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-"""
-The ios_vlans class
-It is in this file where the current configuration (as dict)
-is compared to the provided configuration (as dict) and the command set
-necessary to bring the current configuration to it's desired end-state is
-created
-"""
+#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
+"""
+The ios_vlans config file.
+It is in this file where the current configuration (as dict)
+is compared to the provided configuration (as dict) and the command set
+necessary to bring the current configuration to its desired end-state is
+created.
+"""
+
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import (
- ConfigBase,
+from ansible.module_utils.six import iteritems
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.resource_module import (
+ ResourceModule,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
- remove_empties,
- to_list,
+ 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.utils.utils import dict_to_set
+from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vlans import (
+ VlansTemplate,
+)
-class Vlans(ConfigBase):
+class Vlans(ResourceModule):
"""
- The ios_vlans class
+ The ios_vlans config class
"""
- gather_subset = ["!all", "!min"]
-
- gather_network_resources = ["vlans"]
-
def __init__(self, module):
- super(Vlans, self).__init__(module)
-
- def get_vlans_facts(self, data=None):
- """Get the 'facts' (the current configuration)
-
- :rtype: A dictionary
- :returns: The current configuration as a dictionary
- """
-
- facts, _warnings = Facts(self._module).get_facts(
- self.gather_subset,
- self.gather_network_resources,
- data=data,
+ super(Vlans, self).__init__(
+ empty_fact_val={},
+ facts_module=Facts(module),
+ module=module,
+ resource="vlans",
+ tmplt=VlansTemplate(),
)
- vlans_facts = facts["ansible_network_resources"].get("vlans")
- if not vlans_facts:
- return []
- return vlans_facts
+ self.parsers = [
+ "name",
+ "state",
+ "mtu",
+ "remote_span",
+ "private_vlan.type",
+ "private_vlan.associated",
+ "member",
+ ]
def execute_module(self):
"""Execute the module
@@ -64,394 +62,106 @@ class Vlans(ConfigBase):
:rtype: A dictionary
:returns: The result from module execution
"""
- result = {"changed": False}
- commands = list()
- warnings = list()
- self.have_now = list()
- self.configuration = self._module.params["configuration"]
- if not self.configuration:
- self.vlan_parent = "vlan {0}"
- else:
- self.vlan_parent = "vlan configuration {0}"
- if self.state in self.ACTION_STATES:
- existing_vlans_facts = self.get_vlans_facts()
- else:
- existing_vlans_facts = []
-
- if self.state in self.ACTION_STATES or self.state == "rendered":
- commands.extend(self.set_config(existing_vlans_facts))
- if commands and self.state in self.ACTION_STATES:
- if not self._module.check_mode:
- self._connection.edit_config(commands)
- result["changed"] = True
- if self.state in self.ACTION_STATES:
- result["commands"] = commands
-
- if self.state in self.ACTION_STATES or self.state == "gathered":
- changed_vlans_facts = self.get_vlans_facts()
- elif self.state == "rendered":
- result["rendered"] = commands
- elif self.state == "parsed":
- running_config = self._module.params["running_config"]
- if not running_config:
- self._module.fail_json(
- msg="value of running_config parameter must not be empty for state parsed",
+ if self.state not in ["parsed", "gathered"]:
+ self.segregate_resource()
+ self.run_commands()
+ return self.result
+
+ def segregate_resource(self):
+ self.want_vlan_config = []
+ self.have_vlan_config = []
+ for vlan_data in self.want:
+ if vlan_data.get("member"):
+ self.want_vlan_config.append(
+ {
+ "vlan_id": vlan_data.get("vlan_id"),
+ "member": vlan_data.pop("member"),
+ },
)
- result["parsed"] = self.get_vlans_facts(data=running_config)
- else:
- changed_vlans_facts = []
-
- if self.state in self.ACTION_STATES:
- result["before"] = existing_vlans_facts
- if result["changed"]:
- result["after"] = changed_vlans_facts
- elif self.state == "gathered":
- result["gathered"] = changed_vlans_facts
-
- result["warnings"] = warnings
- return result
-
- def set_config(self, existing_vlans_facts):
- """Collect the configuration from the args passed to the module,
- collect the current configuration (as a dict from facts)
-
- :rtype: A list
- :returns: the commands necessary to migrate the current configuration
- to the desired configuration
- """
- want = []
- if self._module.params.get("config"):
- for cfg in self._module.params["config"]:
- want.append(remove_empties(cfg))
- have = existing_vlans_facts
- resp = self.set_state(want, have)
- return to_list(resp)
-
- def set_state(self, want, have):
- """Select the appropriate function based on the state provided
-
- :param want: the desired configuration as a dictionary
- :param have: the current configuration as a dictionary
- :rtype: A list
- :returns: the commands necessary to migrate the current configuration
- to the desired configuration
- """
-
- if self.state in ("overridden", "merged", "replaced", "rendered") and not want:
- self._module.fail_json(
- msg="value of config parameter must not be empty for state {0}".format(self.state),
- )
-
- if self.state == "overridden":
- commands = self._state_overridden(want, have)
- elif self.state == "deleted":
- commands = self._state_deleted(want, have)
- elif self.state in ("merged", "rendered"):
- commands = self._state_merged(want, have)
- elif self.state == "replaced":
- commands = self._state_replaced(want, have)
- return commands
-
- def _state_replaced(self, want, have):
- """The command generator when state is replaced
-
- :rtype: A list
- :returns: the commands necessary to migrate the current configuration
- to the desired configuration
- """
- commands = []
-
- check = False
- for each in want:
- for every in have:
- if every["vlan_id"] == each["vlan_id"]:
- check = True
- break
- continue
- if check:
- commands.extend(self._set_config(each, every))
- else:
- commands.extend(self._set_config(each, dict()))
-
- return commands
-
- def _state_overridden(self, want, have):
- """The command generator when state is overridden
-
- :rtype: A list
- :returns: the commands necessary to migrate the current configuration
- to the desired configuration
- """
- commands = []
-
- want_local = want
- self.have_now = have.copy()
- for each in have:
- count = 0
- for every in want_local:
- if each["vlan_id"] == every["vlan_id"]:
- break
- count += 1
else:
- # We didn't find a matching desired state, which means we can
- # pretend we received an empty desired state.
- commands.extend(self._clear_config(every, each))
- continue
- commands.extend(self._set_config(every, each))
- # as the pre-existing VLAN are now configured by
- # above set_config call, deleting the respective
- # VLAN entry from the want_local list
- del want_local[count]
-
- # Iterating through want_local list which now only have new VLANs to be
- # configured
- for each in want_local:
- commands.extend(self._set_config(each, dict()))
-
- return commands
-
- def _state_merged(self, want, have):
- """The command generator when state is merged
-
- :rtype: A list
- :returns: the commands necessary to merge the provided into
- the current configuration
- """
- commands = []
-
- check = False
- for each in want:
- for every in have:
- if each.get("vlan_id") == every.get("vlan_id"):
- check = True
- break
- continue
- if check:
- commands.extend(self._set_config(each, every))
+ self.want_vlan_config.append(
+ {"vlan_id": vlan_data.get("vlan_id")},
+ )
+ for vlan_data in self.have:
+ if vlan_data.get("member"):
+ self.have_vlan_config.append(
+ {
+ "vlan_id": vlan_data.get("vlan_id"),
+ "member": vlan_data.pop("member"),
+ },
+ )
else:
- commands.extend(self._set_config(each, dict()))
-
- return commands
-
- def _state_deleted(self, want, have):
- """The command generator when state is deleted
+ self.have_vlan_config.append(
+ {"vlan_id": vlan_data.get("vlan_id")},
+ )
+ if self.want or self.have:
+ self.generate_commands(self.want, self.have, "vlans")
+ if self.want_vlan_config or self.have_vlan_config:
+ self.generate_commands(
+ self.want_vlan_config,
+ self.have_vlan_config,
+ "vlan_configuration",
+ )
- :rtype: A list
- :returns: the commands necessary to remove the current configuration
- of the provided objects
+ def generate_commands(self, conf_want, conf_have, resource=None):
+ """Generate configuration commands to send based on
+ want, have and desired state.
"""
- commands = []
-
- if want:
- check = False
- for each in want:
- for every in have:
- if each.get("vlan_id") == every.get("vlan_id"):
- check = True
- break
- check = False
- continue
- if check:
- commands.extend(self._clear_config(each, every))
+ wantd = {entry["vlan_id"]: entry for entry in conf_want}
+ haved = {entry["vlan_id"]: entry for entry in conf_have}
+
+ # if state is merged, merge want onto have and then compare
+ if self.state == "merged":
+ wantd = dict_merge(haved, wantd)
+
+ # if state is deleted, empty out wantd and set haved to wantd
+ if self.state == "deleted":
+ haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd}
+ wantd = {}
+
+ # if state is deleted, empty out wantd and set haved to wantd
+ if self.state in ["deleted", "purged"]:
+ haved = {k: v for k, v in iteritems(haved) if k in wantd or not wantd}
+ wantd = {}
+
+ # remove superfluous config for overridden and deleted
+ if self.state in ["overridden", "deleted"]:
+ for k, have in iteritems(haved):
+ if k not in wantd:
+ self._compare(want={}, have=have, resource=resource)
+
+ if self.state == "purged":
+ for k, have in iteritems(haved):
+ self.purge(have, resource)
else:
- for each in have:
- commands.extend(self._clear_config(dict(), each))
-
- return commands
-
- def remove_command_from_config_list(self, vlan_id, cmd, commands):
- if vlan_id not in commands and cmd != "vlan":
- commands.insert(0, vlan_id)
- elif cmd == "vlan":
- commands.append("no %s" % vlan_id)
- return commands
- commands.append("no %s" % cmd)
- return commands
-
- def add_command_to_config_list(self, vlan_id, cmd, commands):
- if vlan_id not in commands:
- commands.insert(0, vlan_id)
- if cmd not in commands:
- commands.append(cmd)
-
- def _set_config(self, want, have):
- # Set the interface config based on the want and have config
- commands = []
-
- vlan = self.vlan_parent.format(want.get("vlan_id"))
-
- def negate_have_config(want_diff, have_diff, vlan, commands):
- name = dict(have_diff).get("name")
- if name and not dict(want_diff).get("name"):
- self.remove_command_from_config_list(vlan, "name {0}".format(name), commands)
- state = dict(have_diff).get("state")
- if state and not dict(want_diff).get("state"):
- self.remove_command_from_config_list(vlan, "state {0}".format(state), commands)
- shutdown = dict(have_diff).get("shutdown")
- if shutdown and not dict(want_diff).get("shutdown"):
- self.remove_command_from_config_list(vlan, "shutdown", commands)
- mtu = dict(have_diff).get("mtu")
- if mtu and not dict(want_diff).get("mtu"):
- self.remove_command_from_config_list(vlan, "mtu {0}".format(mtu), commands)
- remote_span = dict(have_diff).get("remote_span")
- if remote_span and not dict(want_diff).get("remote_span"):
- self.remove_command_from_config_list(vlan, "remote-span", commands)
- private_vlan = dict(have_diff).get("private_vlan")
- if private_vlan and not dict(want_diff).get("private_vlan"):
- private_vlan_type = dict(private_vlan).get("type")
- self.remove_command_from_config_list(
- vlan,
- "private-vlan {0}".format(private_vlan_type),
- commands,
- )
- if private_vlan_type == "primary" and dict(private_vlan).get("associated"):
- self.remove_command_from_config_list(vlan, "private-vlan association", commands)
-
- # Get the diff b/w want n have
-
- want_dict = dict_to_set(want, sort_dictionary=True)
- have_dict = dict_to_set(have, sort_dictionary=True)
- diff = want_dict - have_dict
- have_diff = have_dict - want_dict
-
- if diff:
- if have_diff and (self.state == "replaced" or self.state == "overridden"):
- negate_have_config(diff, have_diff, vlan, commands)
-
- if not self.configuration:
- name = dict(diff).get("name")
- state = dict(diff).get("state")
- shutdown = dict(diff).get("shutdown")
- mtu = dict(diff).get("mtu")
- remote_span = dict(diff).get("remote_span")
- private_vlan = dict(diff).get("private_vlan")
-
- if name:
- self.add_command_to_config_list(vlan, "name {0}".format(name), commands)
- if state:
- self.add_command_to_config_list(vlan, "state {0}".format(state), commands)
- if mtu:
- self.add_command_to_config_list(vlan, "mtu {0}".format(mtu), commands)
- if remote_span:
- self.add_command_to_config_list(vlan, "remote-span", commands)
-
- if private_vlan:
- private_vlan_type = dict(private_vlan).get("type")
- private_vlan_associated = dict(private_vlan).get("associated")
- if private_vlan_type:
- self.add_command_to_config_list(
- vlan,
- "private-vlan {0}".format(private_vlan_type),
- commands,
- )
- if private_vlan_associated:
- associated_list = ",".join(
- str(e) for e in private_vlan_associated
- ) # Convert python list to string with elements separated by a comma
- self.add_command_to_config_list(
- vlan,
- "private-vlan association {0}".format(associated_list),
- commands,
- )
- if shutdown == "enabled":
- self.add_command_to_config_list(vlan, "shutdown", commands)
- elif shutdown == "disabled":
- self.add_command_to_config_list(vlan, "no shutdown", commands)
+ for k, want in iteritems(wantd):
+ self._compare(want=want, have=haved.pop(k, {}), resource=resource)
+
+ def _compare(self, want, have, resource=None):
+ """Leverages the base class `compare()` method and
+ populates the list of commands to be run by comparing
+ the `want` and `have` data with the `parsers` defined
+ for the Vlans network resource.
+ """
+ begin = len(self.commands)
+ self.compare(parsers=self.parsers, want=want, have=have)
+ if want.get("shutdown") != have.get("shutdown"):
+ if want.get("shutdown"):
+ self.addcmd(want, "shutdown", True)
else:
- member_dict = dict(diff).get("member")
- if member_dict:
- member_dict = dict(member_dict)
- member_vni = member_dict.get("vni")
- member_evi = member_dict.get("evi")
- commands.extend(
- self._remove_vlan_vni_evi_mapping(
- want,
- ),
- )
- commands.extend(
- [
- vlan,
- self._get_member_cmds(member_dict),
- ],
- )
-
- elif have_diff and (self.state == "replaced" or self.state == "overridden"):
- negate_have_config(diff, have_diff, vlan, commands)
- return commands
-
- def _clear_config(self, want, have):
- # Delete the interface config based on the want and have config
- commands = []
- vlan = self.vlan_parent.format(have.get("vlan_id"))
-
- if (
- have.get("vlan_id")
- and "default" not in have.get("name", "")
- and (have.get("vlan_id") != want.get("vlan_id") or self.state == "deleted")
- ):
- self.remove_command_from_config_list(vlan, "vlan", commands)
- if self.configuration and self.state == "overridden":
- self.have_now.remove(have)
- elif "default" not in have.get("name", ""):
- if not self.configuration:
- if have.get("mtu") != want.get("mtu"):
- self.remove_command_from_config_list(vlan, "mtu", commands)
- if have.get("remote_span") != want.get("remote_span") and want.get("remote_span"):
- self.remove_command_from_config_list(vlan, "remote-span", commands)
- if have.get("shutdown") != want.get("shutdown") and want.get("shutdown"):
- self.remove_command_from_config_list(vlan, "shutdown", commands)
- if have.get("state") != want.get("state") and want.get("state"):
- self.remove_command_from_config_list(vlan, "state", commands)
- return commands
-
- def _remove_vlan_vni_evi_mapping(self, want_dict):
- commands = []
- have_copy = self.have_now.copy()
- vlan = want_dict["vlan_id"]
- for vlan_dict in have_copy:
- if vlan_dict["vlan_id"] == vlan:
- if "member" in vlan_dict:
- commands.extend(
- [
- self.vlan_parent.format(vlan),
- self._get_member_cmds(
- vlan_dict.get("member", {}),
- prefix="no",
- ),
- ],
- )
- vlan_dict.pop("member")
- if vlan_dict["vlan_id"] != vlan:
- delete_member = False
- have_vni = vlan_dict.get("member", {}).get("vni")
- have_evi = vlan_dict.get("member", {}).get("evi")
- if have_vni and (have_vni == want_dict["member"].get("vni")):
- delete_member = True
- if have_evi and (have_evi == want_dict["member"].get("evi")):
- delete_member = True
- if delete_member:
- commands.extend(
- [
- self.vlan_parent.format(vlan_dict["vlan_id"]),
- self._get_member_cmds(
- vlan_dict.get("member", {}),
- prefix="no",
- ),
- ],
- )
- self.have_now.remove(vlan_dict)
- return commands
-
- def _get_member_cmds(self, member_dict, prefix=""):
- cmd = ""
- if prefix:
- prefix = prefix + " "
- member_vni = member_dict.get("vni")
- member_evi = member_dict.get("evi")
-
- if member_evi:
- cmd = prefix + "member evpn-instance {0} vni {1}".format(member_evi, member_vni)
- elif member_vni:
- cmd = prefix + "member vni {0}".format(member_vni)
+ if want:
+ self.addcmd(want, "shutdown", False)
+ elif have.get("shutdown"):
+ # handles deleted as want be blank and only
+ # negates if no shutdown
+ self.addcmd(have, "shutdown", False)
+ if len(self.commands) != begin:
+ self.commands.insert(
+ begin,
+ self._tmplt.render(want or have, resource, False),
+ )
- return cmd
+ def purge(self, have, resource):
+ """Handle operation for purged state"""
+ if resource == "vlan_configuration":
+ self.commands.append(self._tmplt.render(have, resource, True))
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py
index 2be369a7a..341491f2e 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/acls/acls.py
@@ -105,7 +105,10 @@ class AclsFacts(object):
if namedata:
# parse just names to update empty acls
- templateObjName = NetworkTemplate(lines=namedata.splitlines(), tmplt=AclsTemplate())
+ templateObjName = NetworkTemplate(
+ lines=namedata.splitlines(),
+ tmplt=AclsTemplate(),
+ )
raw_acl_names = templateObjName.parse()
raw_acls = self.populate_empty_acls(raw_acls, raw_acl_names)
@@ -114,7 +117,10 @@ class AclsFacts(object):
if raw_acls.get("acls"):
for k, v in iteritems(raw_acls.get("acls")):
- if v.get("afi") == "ipv4" and v.get("acl_type") in ["standard", "extended"]:
+ if v.get("afi") == "ipv4" and v.get("acl_type") in [
+ "standard",
+ "extended",
+ ]:
del v["afi"]
temp_v4.append(v)
elif v.get("afi") == "ipv6":
@@ -142,10 +148,15 @@ class AclsFacts(object):
def process_protocol_options(each):
for each_ace in each.get("aces"):
if each.get("acl_type") == "standard":
- if len(each_ace.get("source", {})) == 1 and each_ace.get("source", {}).get(
+ if len(each_ace.get("source", {})) == 1 and each_ace.get(
+ "source",
+ {},
+ ).get(
"address",
):
- each_ace["source"]["host"] = each_ace["source"].pop("address")
+ each_ace["source"]["host"] = each_ace["source"].pop(
+ "address",
+ )
if each_ace.get("source", {}).get("address"):
addr = each_ace.get("source", {}).get("address")
if addr[-1] == ",":
@@ -159,7 +170,10 @@ class AclsFacts(object):
if each_ace.get("icmp_igmp_tcp_protocol"):
each_ace["protocol_options"] = {
each_ace["protocol"]: {
- each_ace.pop("icmp_igmp_tcp_protocol").replace("-", "_"): True,
+ each_ace.pop("icmp_igmp_tcp_protocol").replace(
+ "-",
+ "_",
+ ): True,
},
}
if each_ace.get("protocol_number"):
@@ -172,13 +186,21 @@ class AclsFacts(object):
ace_entry = []
ace_rem = []
rem = {}
+ # every remarks is one list item which has a sequence number
+ # every ace remark is preserved and ordered
+ # at the end of each sequence it is flushed to a ace entry
for i in aces:
+ # i here denotes an ace, which would be populated with remarks entries
if i.get("is_remark_for"):
if not rem.get(i.get("is_remark_for")):
rem[i.get("is_remark_for")] = {"remarks": []}
- rem[i.get("is_remark_for")]["remarks"].append(i.get("the_remark"))
+ rem[i.get("is_remark_for")]["remarks"].append(
+ i.get("the_remark"),
+ )
else:
- rem[i.get("is_remark_for")]["remarks"].append(i.get("the_remark"))
+ rem[i.get("is_remark_for")]["remarks"].append(
+ i.get("the_remark"),
+ )
else:
if rem:
if rem.get(i.get("sequence")):
@@ -187,12 +209,27 @@ class AclsFacts(object):
ace_entry.append(i)
if rem: # pending remarks
- pending_rem = rem.get("remark")
- ace_entry.append({"remarks": pending_rem.get("remarks")})
+ for pending_rem_seq, pending_rem_val in rem.items():
+ # there can be ace entry with just a remarks and no ace actually
+ # 10 remarks I am a remarks
+ # 20 ..... so onn
+ if pending_rem_seq != "remark":
+ ace_entry.append(
+ {
+ "sequence": pending_rem_seq,
+ "remarks": pending_rem_val.get("remarks"),
+ },
+ )
+ else:
+ # this handles the classic set of remarks at the end, which is not tied to
+ # any sequence number
+ pending_rem = rem.get("remark", {})
+ ace_entry.append({"remarks": pending_rem.get("remarks")})
return ace_entry
for each in temp_v4:
if each.get("aces"):
+ # handling remarks for each ace entry
each["aces"] = collect_remarks(each.get("aces"))
process_protocol_options(each)
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py
index 37bbfabfd..c5b90d9e7 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/bgp_global/bgp_global.py
@@ -35,6 +35,17 @@ class Bgp_globalFacts(object):
def get_bgp_global_data(self, connection):
return connection.get("show running-config | section ^router bgp")
+ def _set_defaults(self, objs):
+ """makes data as per the facts after data obtained from parsers"""
+
+ if objs.get("as_number"):
+ objs.setdefault("bgp", {}).setdefault("default", {}).setdefault("ipv4_unicast", True)
+ objs.setdefault("bgp", {}).setdefault("default", {}).setdefault(
+ "route_target",
+ {},
+ ).setdefault("filter", True)
+ return objs
+
def populate_facts(self, connection, ansible_facts, data=None):
"""Populate the facts for Bgp_global network resource
@@ -56,6 +67,9 @@ class Bgp_globalFacts(object):
module=self._module,
)
objs = bgp_global_parser.parse()
+ if objs:
+ objs = self._set_defaults(objs)
+
neighbor_list = objs.get("neighbors", {})
if neighbor_list:
objs["neighbors"] = sorted(
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py
index 7718b474d..583c86b51 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/facts.py
@@ -104,7 +104,12 @@ from ansible_collections.cisco.ios.plugins.module_utils.network.ios.facts.vxlan_
)
-FACT_LEGACY_SUBSETS = dict(default=Default, hardware=Hardware, interfaces=Interfaces, config=Config)
+FACT_LEGACY_SUBSETS = dict(
+ default=Default,
+ hardware=Hardware,
+ interfaces=Interfaces,
+ config=Config,
+)
FACT_RESOURCE_SUBSETS = dict(
interfaces=InterfacesFacts,
@@ -155,7 +160,11 @@ class Facts(FactsBase):
:return: the facts gathered
"""
if self.VALID_RESOURCE_SUBSETS:
- self.get_network_resources_facts(FACT_RESOURCE_SUBSETS, resource_facts_type, data)
+ self.get_network_resources_facts(
+ FACT_RESOURCE_SUBSETS,
+ resource_facts_type,
+ data,
+ )
if self.VALID_LEGACY_GATHER_SUBSETS:
self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type)
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py
index bea49159b..1b36db57a 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/l3_interfaces/l3_interfaces.py
@@ -40,6 +40,14 @@ class L3_InterfacesFacts(object):
def get_l3_interfaces_data(self, connection):
return connection.get("show running-config | section ^interface")
+ def _set_defaults(self, objs):
+ """Set default parameters"""
+
+ for intf in objs:
+ if intf.get("name") and intf["name"][:4] == "Vlan":
+ intf.setdefault("autostate", True)
+ return objs
+
def populate_facts(self, connection, ansible_facts, data=None):
"""Populate the facts for l3 interfaces
:param connection: the device connection
@@ -71,6 +79,10 @@ class L3_InterfacesFacts(object):
temp = sorted(temp, key=lambda k, sk="name": k[sk])
objs = temp
+
+ if objs:
+ objs = self._set_defaults(objs)
+
facts = {}
if objs:
facts["l3_interfaces"] = []
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py
index 9b506fa90..7bfed68d5 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/facts/vlans/vlans.py
@@ -16,8 +16,6 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
-import re
-
from copy import deepcopy
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils
@@ -25,6 +23,9 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common i
from ansible_collections.cisco.ios.plugins.module_utils.network.ios.argspec.vlans.vlans import (
VlansArgs,
)
+from ansible_collections.cisco.ios.plugins.module_utils.network.ios.rm_templates.vlans import (
+ VlansTemplate,
+)
class VlansFacts(object):
@@ -44,154 +45,60 @@ class VlansFacts(object):
self.generated_spec = utils.generate_dict(facts_argument_spec)
- def get_vlans_data(self, connection, configuration):
+ def get_vlans_data(self, connection):
"""Checks device is L2/L3 and returns
facts gracefully. Does not fail module.
"""
- if configuration:
- cmd = "show running-config | sec ^vlan configuration .+"
- else:
- cmd = "show vlan"
check_os_type = connection.get_device_info()
if check_os_type.get("network_os_type") == "L3":
return ""
- return connection.get(cmd)
+ return connection.get("show vlan")
+
+ def get_vlan_conf_data(self, connection):
+ return connection.get("show running-config | section ^vlan configuration .+")
+
+ def populate_vlans_config_facts(self, connection, data=None):
+ """Process config for Vlans Configurations
- def populate_facts(self, connection, ansible_facts, data=None):
- """Populate the facts for vlans
:param connection: the device connection
:param ansible_facts: Facts dictionary
:param data: previously collected conf
+
:rtype: dictionary
:returns: facts
"""
- configuration = self._module.params["configuration"]
- objs = []
if not data:
- data = self.get_vlans_data(connection, configuration)
- if not configuration:
- objs = self.parse_vlan(data)
- else:
- objs = self.parse_vlan_config(data)
+ data = self.get_vlan_conf_data(connection)
- facts = {}
- if objs:
- facts["vlans"] = []
- params = utils.validate_config(self.argument_spec, {"config": objs})
-
- for cfg in params["config"]:
- facts["vlans"].append(utils.remove_empties(cfg))
- ansible_facts["ansible_network_resources"].update(facts)
- return ansible_facts
+ # parse native config using the Vlan_configurations template
+ vlan_configurations_parser = VlansTemplate(
+ lines=data.splitlines(),
+ module=self._module,
+ )
+ return vlan_configurations_parser.parse()
- def render_config(self, spec, conf, vlan_info):
- """
- Render config as dictionary structure and delete keys
- from spec for null values
-
- :param spec: The facts tree, generated from the argspec
- :param conf: The configuration
+ def populate_facts(self, connection, ansible_facts, data=None):
+ """Populate the facts for vlans
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
:rtype: dictionary
- :returns: The generated config
+ :returns: facts
"""
- config = deepcopy(spec)
-
- if vlan_info == "Name" and "VLAN Name" not in conf:
- conf = list(filter(None, conf.split(" ")))
- config["vlan_id"] = int(conf[0])
- config["name"] = conf[1]
- state_idx = 2
- for i in range(2, len(conf)): # check for index where state starts
- if conf[i] in ["suspended", "active"]:
- state_idx = i
- break
- elif conf[i].split("/")[0] in ["sus", "act"]:
- state_idx = i
- break
- config["name"] += " " + conf[i]
- try:
- if len(conf[state_idx].split("/")) > 1:
- _state = conf[state_idx].split("/")[0]
- if _state == "sus":
- config["state"] = "suspend"
- elif _state == "act":
- config["state"] = "active"
- config["shutdown"] = "enabled"
- else:
- if conf[state_idx] == "suspended":
- config["state"] = "suspend"
- elif conf[state_idx] == "active":
- config["state"] = "active"
- config["shutdown"] = "disabled"
- except IndexError:
- pass
- elif vlan_info == "Type" and "VLAN Type" not in conf:
- conf = list(filter(None, conf.split(" ")))
- config["mtu"] = int(conf[3])
- elif vlan_info == "Remote":
- if len(conf.split(",")) > 1 or conf.isdigit():
- remote_span_vlan = []
- if len(conf.split(",")) > 1:
- remote_span_vlan = conf.split(",")
- else:
- remote_span_vlan.append(conf)
- remote_span = []
- for each in remote_span_vlan:
- split_sp_list = each.split("-")
- if len(split_sp_list) > 1: # break range
- for r_sp in range(int(split_sp_list[0]), int(split_sp_list[1]) + 1):
- remote_span.append(r_sp)
- else:
- remote_span.append(int(each))
- config["remote_span"] = remote_span
-
- elif vlan_info == "Private" and "Primary Secondary" not in conf:
- conf = list(filter(None, conf.split(" ")))
-
- pri_idx = 0
- sec_idx = 1
- priv_type_idx = 2
-
- config["tmp_pvlans"] = {
- "primary": conf[pri_idx],
- "secondary": conf[sec_idx],
- "sec_type": conf[priv_type_idx],
- }
- return utils.remove_empties(config)
-
- def parse_vlan_config(self, vlan_conf):
- vlan_list = list()
-
- re1 = re.compile(r"^vlan configuration +(?P<vlan>\d+)$")
- re2 = re.compile(r"^member +(evpn\-instance +(?P<evi>\d+) )?vni (?P<vni>[\d\-]+)$")
-
- for line in vlan_conf.splitlines():
- line = line.strip()
- m = re1.match(line)
- if m:
- vlan = m.groupdict()["vlan"]
- vlan_dict = {"vlan_id": vlan}
- continue
-
- m = re2.match(line)
- if m:
- group = m.groupdict()
- vlan_dict.update({"member": {}})
- vlan_dict["member"].update({"vni": group["vni"]})
- if group["evi"]:
- vlan_dict["member"].update({"evi": group["evi"]})
- vlan_list.append(vlan_dict)
-
- return vlan_list
-
- def parse_vlan(self, data):
objs = []
mtu_objs = []
remote_objs = []
final_objs = []
pvlan_objs = []
+ conf_data = {}
+
+ if not data:
+ data = self.get_vlans_data(connection)
+
+ # deals with vlan configuration config only
+ conf_data = self.populate_vlans_config_facts(connection, data)
# operate on a collection of resource x
config = data.split("\n")
@@ -199,6 +106,7 @@ class VlansFacts(object):
vlan_info = ""
temp = ""
vlan_name = True
+
for conf in config:
if len(list(filter(None, conf.split(" ")))) <= 2 and vlan_name:
temp = temp + conf
@@ -231,6 +139,7 @@ class VlansFacts(object):
pvlan_objs.append(obj)
elif obj:
objs.append(obj)
+
# Appending MTU value to the retrieved dictionary
for o, m in zip(objs, mtu_objs):
o.update(m)
@@ -250,7 +159,6 @@ class VlansFacts(object):
pvlan_final = {}
if len(pvlan_objs) > 0:
# Sanitize and structure everything
-
for data in pvlan_objs:
pvdata = data.get("tmp_pvlans")
privlan = pvdata.get("primary")
@@ -270,7 +178,9 @@ class VlansFacts(object):
"private_vlan": {"type": "primary", "associated": []},
}
if secvlan and (isinstance(secvlan, int) or secvlan.isnumeric()):
- pvlan_final[privlan]["private_vlan"]["associated"].append(int(secvlan))
+ pvlan_final[privlan]["private_vlan"]["associated"].append(
+ int(secvlan),
+ )
# Associate with the proper VLAN in final_objs
for vlan_id, data in pvlan_final.items():
@@ -278,7 +188,103 @@ class VlansFacts(object):
if vlan_id == every.get("vlan_id"):
every.update(data)
- if final_objs:
- return objs
- else:
- return {}
+ facts = {}
+
+ if conf_data:
+ for vlan in objs:
+ if conf_data.get(vlan.get("vlan_id")):
+ member_data = conf_data.pop(vlan.get("vlan_id"))
+ vlan.update(member_data)
+
+ if conf_data: # if any vlan configuration data is pending add it to facts
+ for vlanid, conf in conf_data.items():
+ objs.append(conf)
+
+ if objs:
+ facts["vlans"] = []
+ params = utils.validate_config(self.argument_spec, {"config": objs})
+
+ for cfg in params["config"]:
+ facts["vlans"].append(utils.remove_empties(cfg))
+ ansible_facts["ansible_network_resources"].update(facts)
+
+ return ansible_facts
+
+ def render_config(self, spec, conf, vlan_info):
+ """
+ Render config as dictionary structure and delete keys
+ from spec for null values
+
+ :param spec: The facts tree, generated from the argspec
+ :param conf: The configuration
+ :rtype: dictionary
+ :returns: The generated config
+ """
+ config = deepcopy(spec)
+
+ if vlan_info == "Name" and "VLAN Name" not in conf:
+ conf = list(filter(None, conf.split(" ")))
+ config["vlan_id"] = int(conf[0])
+ config["name"] = conf[1]
+ state_idx = 2
+ for i in range(2, len(conf)): # check for index where state starts
+ if conf[i] in ["suspended", "active"]:
+ state_idx = i
+ break
+ elif conf[i].split("/")[0] in ["sus", "act"]:
+ state_idx = i
+ break
+ config["name"] += " " + conf[i]
+ try:
+ if len(conf[state_idx].split("/")) > 1:
+ _state = conf[state_idx].split("/")[0]
+ if _state == "sus":
+ config["state"] = "suspend"
+ elif _state == "act":
+ config["state"] = "active"
+ config["shutdown"] = "enabled"
+ else:
+ if conf[state_idx] == "suspended":
+ config["state"] = "suspend"
+ elif conf[state_idx] == "active":
+ config["state"] = "active"
+ config["shutdown"] = "disabled"
+ except IndexError:
+ pass
+ elif vlan_info == "Type" and "VLAN Type" not in conf:
+ conf = list(filter(None, conf.split(" ")))
+ config["mtu"] = int(conf[3])
+ elif vlan_info == "Remote":
+ if len(conf.split(",")) > 1 or conf.isdigit():
+ remote_span_vlan = []
+ if len(conf.split(",")) > 1:
+ remote_span_vlan = conf.split(",")
+ else:
+ remote_span_vlan.append(conf)
+ remote_span = []
+ for each in remote_span_vlan:
+ split_sp_list = each.split("-")
+ if len(split_sp_list) > 1: # break range
+ for r_sp in range(
+ int(split_sp_list[0]),
+ int(split_sp_list[1]) + 1,
+ ):
+ remote_span.append(r_sp)
+ else:
+ remote_span.append(int(each))
+ config["remote_span"] = remote_span
+
+ elif vlan_info == "Private" and "Primary Secondary" not in conf:
+ conf = list(filter(None, conf.split(" ")))
+
+ pri_idx = 0
+ sec_idx = 1
+ priv_type_idx = 2
+
+ config["tmp_pvlans"] = {
+ "primary": conf[pri_idx],
+ "secondary": conf[sec_idx],
+ "sec_type": conf[priv_type_idx],
+ }
+
+ return utils.remove_empties(config)
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/__init__.py
+++ /dev/null
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/__init__.py
+++ /dev/null
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py
deleted file mode 100644
index f1b9cdb90..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/base.py
+++ /dev/null
@@ -1,82 +0,0 @@
-#
-# (c) 2019, Ansible by Red Hat, inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-from __future__ import absolute_import, division, print_function
-
-
-__metaclass__ = type
-from ansible.module_utils.six import iteritems
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.config import (
- NetworkConfig,
-)
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
-
-
-class ConfigBase(object):
- argument_spec = {}
-
- mutually_exclusive = []
-
- identifier = ()
-
- def __init__(self, **kwargs):
- self.values = {}
- self._rendered_configuration = {}
- self.active_configuration = None
-
- for item in self.identifier:
- self.values[item] = kwargs.pop(item)
-
- for key, value in iteritems(kwargs):
- if key in self.argument_spec:
- setattr(self, key, value)
-
- for key, value in iteritems(self.argument_spec):
- if value.get("default"):
- if not getattr(self, key, None):
- setattr(self, key, value.get("default"))
-
- def __getattr__(self, key):
- if key in self.argument_spec:
- return self.values.get(key)
-
- def __setattr__(self, key, value):
- if key in self.argument_spec:
- if key in self.identifier:
- raise TypeError("cannot set value")
- elif value is not None:
- self.values[key] = value
- else:
- super(ConfigBase, self).__setattr__(key, value)
-
- def context_config(self, cmd):
- if "context" not in self._rendered_configuration:
- self._rendered_configuration["context"] = list()
- self._rendered_configuration["context"].extend(to_list(cmd))
-
- def global_config(self, cmd):
- if "global" not in self._rendered_configuration:
- self._rendered_configuration["global"] = list()
- self._rendered_configuration["global"].extend(to_list(cmd))
-
- def get_rendered_configuration(self):
- config = list()
- for section in ("context", "global"):
- config.extend(self._rendered_configuration.get(section, []))
- return config
-
- def set_active_configuration(self, config):
- self.active_configuration = config
-
- def render(self, config=None):
- raise NotImplementedError
-
- def get_section(self, config, section):
- if config is not None:
- netcfg = NetworkConfig(indent=1, contents=config)
- try:
- config = netcfg.get_block_config(to_list(section))
- except ValueError:
- config = None
- return config
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/__init__.py
+++ /dev/null
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py
deleted file mode 100644
index e4c2bd803..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/address_family.py
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# (c) 2019, Ansible by Red Hat, inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-from __future__ import absolute_import, division, print_function
-
-
-__metaclass__ = type
-import re
-
-from ansible.module_utils.common.network import to_netmask
-from ansible.module_utils.six import iteritems
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
-
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import (
- AFNeighbors,
-)
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import (
- CliProvider,
-)
-
-
-class AddressFamily(CliProvider):
- def render(self, config=None):
- commands = list()
- safe_list = list()
-
- router_context = "router bgp %s" % self.get_value("config.bgp_as")
- context_config = None
-
- for item in self.get_value("config.address_family"):
- context = "address-family %s" % item["afi"]
- if item["safi"] != "unicast":
- context += " %s" % item["safi"]
- context_commands = list()
-
- if config:
- context_path = [router_context, context]
- context_config = self.get_config_context(config, context_path, indent=1)
-
- for key, value in iteritems(item):
- if value is not None:
- meth = getattr(self, "_render_%s" % key, None)
- if meth:
- resp = meth(item, context_config)
- if resp:
- context_commands.extend(to_list(resp))
-
- if context_commands:
- commands.append(context)
- commands.extend(context_commands)
- commands.append("exit-address-family")
-
- safe_list.append(context)
-
- if self.params["operation"] == "replace":
- if config:
- resp = self._negate_config(config, safe_list)
- commands.extend(resp)
-
- return commands
-
- def _negate_config(self, config, safe_list=None):
- commands = list()
- matches = re.findall(r"(address-family .+)$", config, re.M)
- for item in set(matches).difference(safe_list):
- commands.append("no %s" % item)
- return commands
-
- def _render_auto_summary(self, item, config=None):
- cmd = "auto-summary"
- if item["auto_summary"] is False:
- cmd = "no %s" % cmd
- if not config or cmd not in config:
- return cmd
-
- def _render_synchronization(self, item, config=None):
- cmd = "synchronization"
- if item["synchronization"] is False:
- cmd = "no %s" % cmd
- if not config or cmd not in config:
- return cmd
-
- def _render_networks(self, item, config=None):
- commands = list()
- safe_list = list()
-
- for entry in item["networks"]:
- network = entry["prefix"]
- cmd = "network %s" % network
- if entry["masklen"]:
- cmd += " mask %s" % to_netmask(entry["masklen"])
- network += " mask %s" % to_netmask(entry["masklen"])
- if entry["route_map"]:
- cmd += " route-map %s" % entry["route_map"]
- network += " route-map %s" % entry["route_map"]
-
- safe_list.append(network)
-
- if not config or cmd not in config:
- commands.append(cmd)
-
- if self.params["operation"] == "replace":
- if config:
- matches = re.findall(r"network (.*)", config, re.M)
- for entry in set(matches).difference(safe_list):
- commands.append("no network %s" % entry)
-
- return commands
-
- def _render_redistribute(self, item, config=None):
- commands = list()
- safe_list = list()
-
- for entry in item["redistribute"]:
- option = entry["protocol"]
-
- cmd = "redistribute %s" % entry["protocol"]
-
- if entry["id"] and entry["protocol"] in ("ospf", "ospfv3", "eigrp"):
- cmd += " %s" % entry["id"]
- option += " %s" % entry["id"]
-
- if entry["metric"]:
- cmd += " metric %s" % entry["metric"]
-
- if entry["route_map"]:
- cmd += " route-map %s" % entry["route_map"]
-
- if not config or cmd not in config:
- commands.append(cmd)
-
- safe_list.append(option)
-
- if self.params["operation"] == "replace":
- if config:
- matches = re.findall(r"redistribute (\S+)(?:\s*)(\d*)", config, re.M)
- for i in range(0, len(matches)):
- matches[i] = " ".join(matches[i]).strip()
- for entry in set(matches).difference(safe_list):
- commands.append("no redistribute %s" % entry)
-
- return commands
-
- def _render_neighbors(self, item, config):
- """generate bgp neighbor configuration"""
- return AFNeighbors(self.params).render(config, nbr_list=item["neighbors"])
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py
deleted file mode 100644
index 4ee337b00..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/neighbors.py
+++ /dev/null
@@ -1,203 +0,0 @@
-#
-# (c) 2019, Ansible by Red Hat, inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-from __future__ import absolute_import, division, print_function
-
-
-__metaclass__ = type
-import re
-
-from ansible.module_utils.six import iteritems
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
-
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import (
- CliProvider,
-)
-
-
-class Neighbors(CliProvider):
- def render(self, config=None, nbr_list=None):
- commands = list()
- safe_list = list()
- if not nbr_list:
- nbr_list = self.get_value("config.neighbors")
-
- for item in nbr_list:
- neighbor_commands = list()
- context = "neighbor %s" % item["neighbor"]
- cmd = "%s remote-as %s" % (context, item["remote_as"])
-
- if not config or cmd not in config:
- neighbor_commands.append(cmd)
-
- for key, value in iteritems(item):
- if value is not None:
- meth = getattr(self, "_render_%s" % key, None)
- if meth:
- resp = meth(item, config)
- if resp:
- neighbor_commands.extend(to_list(resp))
-
- commands.extend(neighbor_commands)
- safe_list.append(context)
-
- if self.params["operation"] == "replace":
- if config and safe_list:
- commands.extend(self._negate_config(config, safe_list))
-
- return commands
-
- def _negate_config(self, config, safe_list=None):
- commands = list()
- matches = re.findall(r"(neighbor \S+)", config, re.M)
- for item in set(matches).difference(safe_list):
- commands.append("no %s" % item)
- return commands
-
- def _render_local_as(self, item, config=None):
- cmd = "neighbor %s local-as %s" % (item["neighbor"], item["local_as"])
- if not config or cmd not in config:
- return cmd
-
- def _render_port(self, item, config=None):
- cmd = "neighbor %s port %s" % (item["neighbor"], item["port"])
- if not config or cmd not in config:
- return cmd
-
- def _render_description(self, item, config=None):
- cmd = "neighbor %s description %s" % (item["neighbor"], item["description"])
- if not config or cmd not in config:
- return cmd
-
- def _render_enabled(self, item, config=None):
- cmd = "neighbor %s shutdown" % item["neighbor"]
- if item["enabled"] is True:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_update_source(self, item, config=None):
- cmd = "neighbor %s update-source %s" % (item["neighbor"], item["update_source"])
- if not config or cmd not in config:
- return cmd
-
- def _render_password(self, item, config=None):
- cmd = "neighbor %s password %s" % (item["neighbor"], item["password"])
- if not config or cmd not in config:
- return cmd
-
- def _render_ebgp_multihop(self, item, config=None):
- cmd = "neighbor %s ebgp-multihop %s" % (item["neighbor"], item["ebgp_multihop"])
- if not config or cmd not in config:
- return cmd
-
- def _render_peer_group(self, item, config=None):
- cmd = "neighbor %s peer-group %s" % (item["neighbor"], item["peer_group"])
- if not config or cmd not in config:
- return cmd
-
- def _render_timers(self, item, config):
- """generate bgp timer related configuration"""
- keepalive = item["timers"]["keepalive"]
- holdtime = item["timers"]["holdtime"]
- min_neighbor_holdtime = item["timers"]["min_neighbor_holdtime"]
- neighbor = item["neighbor"]
-
- if keepalive and holdtime:
- cmd = "neighbor %s timers %s %s" % (neighbor, keepalive, holdtime)
- if min_neighbor_holdtime:
- cmd += " %s" % min_neighbor_holdtime
- if not config or cmd not in config:
- return cmd
-
-
-class AFNeighbors(CliProvider):
- def render(self, config=None, nbr_list=None):
- commands = list()
- if not nbr_list:
- return
-
- for item in nbr_list:
- neighbor_commands = list()
- for key, value in iteritems(item):
- if value is not None:
- meth = getattr(self, "_render_%s" % key, None)
- if meth:
- resp = meth(item, config)
- if resp:
- neighbor_commands.extend(to_list(resp))
-
- commands.extend(neighbor_commands)
-
- return commands
-
- def _render_advertisement_interval(self, item, config=None):
- cmd = "neighbor %s advertisement-interval %s" % (
- item["neighbor"],
- item["advertisement_interval"],
- )
- if not config or cmd not in config:
- return cmd
-
- def _render_route_reflector_client(self, item, config=None):
- cmd = "neighbor %s route-reflector-client" % item["neighbor"]
- if item["route_reflector_client"] is False:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_route_server_client(self, item, config=None):
- cmd = "neighbor %s route-server-client" % item["neighbor"]
- if item["route_server_client"] is False:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_remove_private_as(self, item, config=None):
- cmd = "neighbor %s remove-private-as" % item["neighbor"]
- if item["remove_private_as"] is False:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_next_hop_self(self, item, config=None):
- cmd = "neighbor %s next-hop-self" % item["neighbor"]
- if item["next_hop_self"] is False:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_activate(self, item, config=None):
- cmd = "neighbor %s activate" % item["neighbor"]
- if item["activate"] is False:
- if not config or cmd in config:
- cmd = "no %s" % cmd
- return cmd
- elif not config or cmd not in config:
- return cmd
-
- def _render_maximum_prefix(self, item, config=None):
- cmd = "neighbor %s maximum-prefix %s" % (item["neighbor"], item["maximum_prefix"])
- if not config or cmd not in config:
- return cmd
-
- def _render_prefix_list_in(self, item, config=None):
- cmd = "neighbor %s prefix-list %s in" % (item["neighbor"], item["prefix_list_in"])
- if not config or cmd not in config:
- return cmd
-
- def _render_prefix_list_out(self, item, config=None):
- cmd = "neighbor %s prefix-list %s out" % (item["neighbor"], item["prefix_list_out"])
- if not config or cmd not in config:
- return cmd
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py
deleted file mode 100644
index 2b54daa35..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/cli/config/bgp/process.py
+++ /dev/null
@@ -1,163 +0,0 @@
-#
-# (c) 2019, Ansible by Red Hat, inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-from __future__ import absolute_import, division, print_function
-
-
-__metaclass__ = type
-import re
-
-from ansible.module_utils.common.network import to_netmask
-from ansible.module_utils.six import iteritems
-from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import to_list
-
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.address_family import (
- AddressFamily,
-)
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.cli.config.bgp.neighbors import (
- Neighbors,
-)
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers.providers import (
- CliProvider,
- register_provider,
-)
-
-
-REDISTRIBUTE_PROTOCOLS = [
- "ospf",
- "ospfv3",
- "eigrp",
- "isis",
- "static",
- "connected",
- "odr",
- "lisp",
- "mobile",
- "rip",
-]
-
-
-@register_provider("ios", "ios_bgp")
-class Provider(CliProvider):
- def render(self, config=None):
- commands = list()
-
- existing_as = None
- if config:
- match = re.search(r"router bgp (\d+)", config, re.M)
- if match:
- existing_as = match.group(1)
-
- operation = self.params["operation"]
-
- context = None
- if self.params["config"]:
- context = "router bgp %s" % self.get_value("config.bgp_as")
-
- if operation == "delete":
- if existing_as:
- commands.append("no router bgp %s" % existing_as)
- elif context:
- commands.append("no %s" % context)
-
- else:
- self._validate_input(config)
- if operation == "replace":
- if existing_as and int(existing_as) != self.get_value("config.bgp_as"):
- commands.append("no router bgp %s" % existing_as)
- config = None
-
- elif operation == "override":
- if existing_as:
- commands.append("no router bgp %s" % existing_as)
- config = None
-
- context_commands = list()
-
- for key, value in iteritems(self.get_value("config")):
- if value is not None:
- meth = getattr(self, "_render_%s" % key, None)
- if meth:
- resp = meth(config)
- if resp:
- context_commands.extend(to_list(resp))
-
- if context and context_commands:
- commands.append(context)
- commands.extend(context_commands)
- commands.append("exit")
- return commands
-
- def _render_router_id(self, config=None):
- cmd = "bgp router-id %s" % self.get_value("config.router_id")
- if not config or cmd not in config:
- return cmd
-
- def _render_log_neighbor_changes(self, config=None):
- cmd = "bgp log-neighbor-changes"
- log_neighbor_changes = self.get_value("config.log_neighbor_changes")
- if log_neighbor_changes is True:
- if not config or cmd not in config:
- return cmd
- elif log_neighbor_changes is False:
- if config and cmd in config:
- return "no %s" % cmd
-
- def _render_networks(self, config=None):
- commands = list()
- safe_list = list()
-
- for entry in self.get_value("config.networks"):
- network = entry["prefix"]
- cmd = "network %s" % network
- if entry["masklen"] and entry["masklen"] not in (24, 16, 8):
- cmd += " mask %s" % to_netmask(entry["masklen"])
- network += " mask %s" % to_netmask(entry["masklen"])
-
- if entry["route_map"]:
- cmd += " route-map %s" % entry["route_map"]
- network += " route-map %s" % entry["route_map"]
-
- safe_list.append(network)
-
- if not config or cmd not in config:
- commands.append(cmd)
-
- if self.params["operation"] == "replace":
- if config:
- matches = re.findall(r"network (.*)", config, re.M)
- for entry in set(matches).difference(safe_list):
- commands.append("no network %s" % entry)
-
- return commands
-
- def _render_neighbors(self, config):
- """generate bgp neighbor configuration"""
- return Neighbors(self.params).render(config)
-
- def _render_address_family(self, config):
- """generate address-family configuration"""
- return AddressFamily(self.params).render(config)
-
- def _validate_input(self, config=None):
- def device_has_AF(config):
- return re.search(r"address-family (?:.*)", config)
-
- address_family = self.get_value("config.address_family")
- root_networks = self.get_value("config.networks")
- operation = self.params["operation"]
-
- if operation == "replace":
- if address_family and root_networks:
- for item in address_family:
- if item["networks"]:
- raise ValueError(
- "operation is replace but provided both root level network(s) and network(s) under %s %s address family"
- % (item["afi"], item["safi"]),
- )
-
- if root_networks and config and device_has_AF(config):
- raise ValueError(
- "operation is replace and device has one or more address family activated but root level network(s) provided",
- )
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py
deleted file mode 100644
index 4de464795..000000000
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/providers/module.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#
-# (c) 2019, Ansible by Red Hat, inc
-# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
-#
-from __future__ import absolute_import, division, print_function
-
-
-__metaclass__ = type
-from ansible.module_utils._text import to_text
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.connection import Connection
-
-from ansible_collections.cisco.ios.plugins.module_utils.network.ios.providers import providers
-
-
-class NetworkModule(AnsibleModule):
- fail_on_missing_provider = True
-
- def __init__(self, connection=None, *args, **kwargs):
- super(NetworkModule, self).__init__(*args, **kwargs)
-
- if connection is None:
- connection = Connection(self._socket_path)
-
- self.connection = connection
-
- @property
- def provider(self):
- if not hasattr(self, "_provider"):
- capabilities = self.from_json(self.connection.get_capabilities())
-
- network_os = capabilities["device_info"]["network_os"]
- network_api = capabilities["network_api"]
-
- if network_api == "cliconf":
- connection_type = "network_cli"
-
- cls = providers.get(network_os, self._name.split(".")[-1], connection_type)
-
- if not cls:
- msg = "unable to find suitable provider for network os %s" % network_os
- if self.fail_on_missing_provider:
- self.fail_json(msg=msg)
- else:
- self.warn(msg)
-
- obj = cls(self.params, self.connection, self.check_mode)
-
- setattr(self, "_provider", obj)
-
- return getattr(self, "_provider")
-
- def get_facts(self, subset=None):
- try:
- self.provider.get_facts(subset)
- except Exception as exc:
- self.fail_json(msg=to_text(exc))
-
- def edit_config(self, config_filter=None):
- current_config = self.connection.get_config(flags=config_filter)
- try:
- commands = self.provider.edit_config(current_config)
- changed = bool(commands)
- return {"commands": commands, "changed": changed}
- except Exception as exc:
- self.fail_json(msg=to_text(exc))
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py
index b3afd65f9..54d082c2e 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/acls.py
@@ -23,9 +23,9 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.r
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
@@ -191,7 +191,7 @@ class AclsTemplate(NetworkTemplate):
"name": "{{ acl_name }}",
"aces": [
{
- "the_remark": "{{ remarks }}",
+ "the_remark": "'{{ remarks }}'",
"order": "{{ order }}",
"is_remark_for": "{{ sequence }}",
},
@@ -206,14 +206,14 @@ class AclsTemplate(NetworkTemplate):
r"""(?P<order>^\d+)\s*remark\s(?P<remarks>.+)$""",
re.VERBOSE,
),
- "setval": "{{ sequence }} remark",
+ "setval": remarks_with_sequence,
"result": {
"acls": {
"{{ acl_name|d() }}": {
"name": "{{ acl_name }}",
"aces": [
{
- "the_remark": "{{ remarks }}",
+ "the_remark": "'{{ remarks }}'",
"order": "{{ order }}",
"is_remark_for": "remark",
},
@@ -238,7 +238,7 @@ class AclsTemplate(NetworkTemplate):
"aces": [
{
"sequence": "{{ sequence }}",
- "remarks": ["{{ remarks }}"],
+ "remarks": ["'{{ remarks }}'"],
},
],
},
@@ -248,7 +248,7 @@ class AclsTemplate(NetworkTemplate):
{
"name": "aces_ipv4_standard",
"getval": re.compile(
- r"""(\s*(?P<sequence>\d+))?
+ r"""^\s*((?P<sequence>\d+))?
(\s(?P<grant>deny|permit))
(\s+(?P<address>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))?
(\s(?P<wildcard>((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)))?
@@ -281,17 +281,17 @@ class AclsTemplate(NetworkTemplate):
{
"name": "aces",
"getval": re.compile(
- r"""(\s*(?P<sequence>\d+))?
- (\s*sequence\s(?P<sequence_ipv6>\d+))?
- (\s*(?P<grant>deny|permit))
+ r"""^\s*((?P<sequence>\d+))?
+ (\ssequence\s(?P<sequence_ipv6>\d+))?
+ (\s(?P<grant>deny|permit))
(\sevaluate\s(?P<evaluate>\S+))?
- (\s(?P<protocol_num>\d+))?
+ (\s(?P<protocol_num>\d+)\s)?
(\s*(?P<protocol>ahp|eigrp|esp|gre|icmp|igmp|ipinip|ipv6|ip|nos|ospf|pcp|pim|sctp|tcp|ip|udp))?
- ((\s(?P<source_any>any))|
- (\sobject-group\s(?P<source_obj_grp>\S+))|
- (\shost\s(?P<source_host>\S+))|
- (\s(?P<ipv6_source_address>\S+/\d+))|
- (\s(?P<source_address>(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s\S+)))?
+ ((\s*(?P<source_any>any))|
+ (\s*object-group\s(?P<source_obj_grp>\S+))|
+ (\s*host\s(?P<source_host>\S+))|
+ (\s*(?P<ipv6_source_address>\S+/\d+))|
+ (\s*(?P<source_address>(\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3})\s\S+)))?
(\seq\s(?P<seq>(\S+|\d+)))?
(\sgt\s(?P<sgt>(\S+|\d+)))?
(\slt\s(?P<slt>(\S+|\d+)))?
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py
index ca85440b2..eb5a08a37 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_address_family.py
@@ -28,7 +28,11 @@ UNIQUE_NEIB_ADD = "{{ neighbor_address }}"
class Bgp_address_familyTemplate(NetworkTemplate):
def __init__(self, lines=None, module=None):
- super(Bgp_address_familyTemplate, self).__init__(lines=lines, tmplt=self, module=module)
+ super(Bgp_address_familyTemplate, self).__init__(
+ lines=lines,
+ tmplt=self,
+ module=module,
+ )
PARSERS = [
{
@@ -61,7 +65,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' vrf ' + vrf) if vrf is defined else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"afi": "{{ afi }}", "safi": "{{ safi }}", "vrf": "{{ vrf }}"},
+ UNIQUE_AFI: {
+ "afi": "{{ afi }}",
+ "safi": "{{ safi }}",
+ "vrf": "{{ vrf }}",
+ },
},
},
"shared": True,
@@ -136,7 +144,10 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "table_map": {"name": "{{ name }}", "filter": "{{ not not filter }}"},
+ "table_map": {
+ "name": "{{ name }}",
+ "filter": "{{ not not filter }}",
+ },
},
},
},
@@ -149,7 +160,10 @@ class Bgp_address_familyTemplate(NetworkTemplate):
},
{
"name": "default_information",
- "getval": re.compile(r"""\s\sdefault-information\soriginate$""", re.VERBOSE),
+ "getval": re.compile(
+ r"""\s\sdefault-information\soriginate$""",
+ re.VERBOSE,
+ ),
"setval": "default-information originate",
"result": {"address_family": {UNIQUE_AFI: {"default_information": True}}},
},
@@ -162,7 +176,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
re.VERBOSE,
),
"setval": "default-metric {{ default_metric|string }}",
- "result": {"address_family": {UNIQUE_AFI: {"default_metric": "{{ default_metric }}"}}},
+ "result": {
+ "address_family": {
+ UNIQUE_AFI: {"default_metric": "{{ default_metric }}"},
+ },
+ },
},
{
"name": "distance",
@@ -234,7 +252,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
),
"setval": "bgp additional-paths select install",
"result": {
- "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"install": True}}}},
+ "address_family": {
+ UNIQUE_AFI: {"bgp": {"additional_paths": {"install": True}}},
+ },
},
},
{
@@ -246,7 +266,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
),
"setval": "bgp additional-paths select receive",
"result": {
- "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"receive": True}}}},
+ "address_family": {
+ UNIQUE_AFI: {"bgp": {"additional_paths": {"receive": True}}},
+ },
},
},
{
@@ -258,7 +280,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
),
"setval": "bgp additional-paths select send",
"result": {
- "address_family": {UNIQUE_AFI: {"bgp": {"additional_paths": {"send": True}}}},
+ "address_family": {
+ UNIQUE_AFI: {"bgp": {"additional_paths": {"send": True}}},
+ },
},
},
{
@@ -316,7 +340,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "bgp nexthop trigger delay {{ bgp.nexthop.trigger.delay|string }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"bgp": {"nexthop": {"trigger": {"delay": "{{ delay }}"}}}},
+ UNIQUE_AFI: {
+ "bgp": {"nexthop": {"trigger": {"delay": "{{ delay }}"}}},
+ },
},
},
},
@@ -330,7 +356,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
),
"setval": "bgp nexthop trigger delay enable",
"result": {
- "address_family": {UNIQUE_AFI: {"bgp": {"nexthop": {"trigger": {"enable": True}}}}},
+ "address_family": {
+ UNIQUE_AFI: {"bgp": {"nexthop": {"trigger": {"enable": True}}}},
+ },
},
},
{
@@ -342,7 +370,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
re.VERBOSE,
),
"setval": "bgp redistribute-internal",
- "result": {"address_family": {UNIQUE_AFI: {"bgp": {"redistribute_internal": True}}}},
+ "result": {
+ "address_family": {UNIQUE_AFI: {"bgp": {"redistribute_internal": True}}},
+ },
},
{
"name": "bgp.route_map",
@@ -365,7 +395,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
re.VERBOSE,
),
"setval": "bgp scan-time {{ bgp.scan_time }}",
- "result": {"address_family": {UNIQUE_AFI: {"bgp": {"scan_time": "{{ scan_time }}"}}}},
+ "result": {
+ "address_family": {
+ UNIQUE_AFI: {"bgp": {"scan_time": "{{ scan_time }}"}},
+ },
+ },
},
{
"name": "bgp.soft_reconfig_backup",
@@ -376,7 +410,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
re.VERBOSE,
),
"setval": "bgp soft-reconfig-backup",
- "result": {"address_family": {UNIQUE_AFI: {"bgp": {"soft_reconfig_backup": True}}}},
+ "result": {
+ "address_family": {UNIQUE_AFI: {"bgp": {"soft_reconfig_backup": True}}},
+ },
},
{
"name": "bgp.update_group",
@@ -429,7 +465,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "bgp slow-peer detection",
"result": {
"address_family": {
- UNIQUE_AFI: {"bgp": {"slow_peer_options": {"detection": {"enable": True}}}},
+ UNIQUE_AFI: {
+ "bgp": {"slow_peer_options": {"detection": {"enable": True}}},
+ },
},
},
},
@@ -447,7 +485,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"address_family": {
UNIQUE_AFI: {
"bgp": {
- "slow_peer_options": {"detection": {"threshold": "{{ threshold }}"}},
+ "slow_peer_options": {
+ "detection": {"threshold": "{{ threshold }}"},
+ },
},
},
},
@@ -464,7 +504,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "bgp": {"slow_peer_options": {"split_update_group": {"dynamic": True}}},
+ "bgp": {
+ "slow_peer_options": {
+ "split_update_group": {"dynamic": True},
+ },
+ },
},
},
},
@@ -480,7 +524,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "bgp": {"slow_peer_options": {"split_update_group": {"permanent": True}}},
+ "bgp": {
+ "slow_peer_options": {
+ "split_update_group": {"permanent": True},
+ },
+ },
},
},
},
@@ -543,7 +591,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' ' + remote_as|string) if remote_as is defined else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"remote_as": "{{ number }}"}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"remote_as": "{{ number }}"}},
+ },
},
},
},
@@ -692,7 +742,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"advertises": {"best-external": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"advertises": {"best-external": True}},
+ },
},
},
},
@@ -792,7 +844,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "{{ ('neighbor ' + neighbor_address + ' aigp') if aigp.enable|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"aigp": {"enable": True}}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"aigp": {"enable": True}}},
+ },
},
},
},
@@ -848,7 +902,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' aigp send med') if aigp.send.med|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"aigp": {"send": {"med": True}}}}},
+ UNIQUE_AFI: {
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"aigp": {"send": {"med": True}}},
+ },
+ },
},
},
},
@@ -863,7 +921,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "{{ ('neighbor ' + neighbor_address + ' allow-policy') if allow_policy|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"allow_policy": True}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"allow_policy": True}},
+ },
},
},
},
@@ -881,7 +941,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"allowas_in": "{{ allowas_in }}"}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"allowas_in": "{{ allowas_in }}"},
+ },
},
},
},
@@ -973,7 +1035,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"cluster_id": "{{ cluster_id }}"}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"cluster_id": "{{ cluster_id }}"},
+ },
},
},
},
@@ -989,7 +1053,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"default_originate": {"set": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"default_originate": {"set": True}},
+ },
},
},
},
@@ -1031,7 +1097,7 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"neighbors": {
UNIQUE_NEIB_ADD: {
"neighbor_address": UNIQUE_NEIB_ADD,
- "description": "{{ description }}",
+ "description": "'{{ description }}'",
},
},
},
@@ -1260,7 +1326,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' ' + inherit) if inherit is defined else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"inherit": "{{ inherit }}"}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"inherit": "{{ inherit }}"}},
+ },
},
},
},
@@ -1276,7 +1344,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "neighbor {{ neighbor_address }} internal-vpn-client",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"internal_vpn_client": True}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"internal_vpn_client": True}},
+ },
},
},
},
@@ -1354,7 +1424,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self') if nexthop_self.set|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"set": True}}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"set": True}}},
+ },
},
},
},
@@ -1369,7 +1441,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "{{ ('neighbor ' + neighbor_address + ' next-hop-self all') if nexthop_self.all|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"all": True}}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"nexthop_self": {"all": True}}},
+ },
},
},
},
@@ -1385,7 +1459,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"set": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"set": True}},
+ },
},
},
},
@@ -1402,7 +1478,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"allpaths": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"next_hop_unchanged": {"allpaths": True}},
+ },
},
},
},
@@ -1460,7 +1538,10 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"path_attribute": {
"discard": {
"type": "{{ type }}",
- "range": {"start": "{{ start }}", "end": "{{ end }}"},
+ "range": {
+ "start": "{{ start }}",
+ "end": "{{ end }}",
+ },
"in": "{{ not not in }}",
},
},
@@ -1493,7 +1574,10 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"path_attribute": {
"treat_as_withdraw": {
"type": "{{ type }}",
- "range": {"start": "{{ start }}", "end": "{{ end }}"},
+ "range": {
+ "start": "{{ start }}",
+ "end": "{{ end }}",
+ },
"in": "{{ not not in }}",
},
},
@@ -1581,7 +1665,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"set": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"remove_private_as": {"set": True}},
+ },
},
},
},
@@ -1598,7 +1684,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"all": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"remove_private_as": {"all": True}},
+ },
},
},
},
@@ -1615,7 +1703,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"remove_private_as": {"replace_as": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"remove_private_as": {"replace_as": True}},
+ },
},
},
},
@@ -1675,7 +1765,11 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"setval": "{{ ('neighbor ' + neighbor_address + ' send-community') if send_community.set|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"set": True}}}},
+ UNIQUE_AFI: {
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"send_community": {"set": True}},
+ },
+ },
},
},
},
@@ -1692,7 +1786,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"both": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"send_community": {"both": True}},
+ },
},
},
},
@@ -1710,7 +1806,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"extended": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"send_community": {"extended": True}},
+ },
},
},
},
@@ -1728,7 +1826,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"send_community": {"standard": True}}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"send_community": {"standard": True}},
+ },
},
},
},
@@ -1840,7 +1940,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' soft-reconfiguration inbound') if soft_reconfiguration|d(False) else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"soft_reconfiguration": True}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"soft_reconfiguration": True}},
+ },
},
},
},
@@ -1986,7 +2088,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"ttl_security": "{{ ttl_security }}"}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"ttl_security": "{{ ttl_security }}"},
+ },
},
},
},
@@ -2005,7 +2109,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"result": {
"address_family": {
UNIQUE_AFI: {
- "neighbors": {UNIQUE_NEIB_ADD: {"unsuppress_map": "{{ unsuppress_map }}"}},
+ "neighbors": {
+ UNIQUE_NEIB_ADD: {"unsuppress_map": "{{ unsuppress_map }}"},
+ },
},
},
},
@@ -2047,7 +2153,9 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"{{ (' ' + weight|string) if weight is defined else '' }}",
"result": {
"address_family": {
- UNIQUE_AFI: {"neighbors": {UNIQUE_NEIB_ADD: {"weight": "{{ weight }}"}}},
+ UNIQUE_AFI: {
+ "neighbors": {UNIQUE_NEIB_ADD: {"weight": "{{ weight }}"}},
+ },
},
},
},
@@ -2130,7 +2238,10 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"name": "{{ context }}",
"user": {
"name": "{{ user }}",
- "access": {"acl": "{{ acl }}", "ipv6": "{{ aclv6 }}"},
+ "access": {
+ "acl": "{{ acl }}",
+ "ipv6": "{{ aclv6 }}",
+ },
"auth": {"md5": "{{ md5 }}", "sha": "{{ sha }}"},
"priv": {
"des56": "{{ des56 }}",
@@ -2663,7 +2774,12 @@ class Bgp_address_familyTemplate(NetworkTemplate):
"address_family": {
UNIQUE_AFI: {
"redistribute": [
- {"vrf": {"name": "{{ name }}", "global": "{{ not not global }}"}},
+ {
+ "vrf": {
+ "name": "{{ name }}",
+ "global": "{{ not not global }}",
+ },
+ },
],
},
},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py
index 39fd548bc..2ce959fcd 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/bgp_global.py
@@ -447,7 +447,7 @@ class Bgp_globalTemplate(NetworkTemplate):
re.VERBOSE,
),
"setval": "description {{ route_server_context.description }}",
- "result": {"route_server_context": {"description": "{{ description }}"}},
+ "result": {"route_server_context": {"description": "'{{ description }}'"}},
},
{
"name": "synchronization",
@@ -476,6 +476,36 @@ class Bgp_globalTemplate(NetworkTemplate):
},
},
{
+ "name": "template.peer_policy",
+ "getval": re.compile(
+ r"""
+ \stemplate\speer-policy
+ (\s(?P<peer_policy>\S+))
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "template peer-policy"
+ "{{ (' ' + template.peer_policy) if template.peer_policy is defined else '' }}",
+ "result": {
+ "template": {"peer_policy": "{{ peer_policy }}"},
+ },
+ },
+ {
+ "name": "template.peer_session",
+ "getval": re.compile(
+ r"""
+ \stemplate\speer-session
+ (\s(?P<peer_session>\S+))
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "template peer-session"
+ "{{ (' ' + template.peer_session) if template.peer_session is defined else '' }}",
+ "result": {
+ "template": {"peer_session": "{{ peer_session }}"},
+ },
+ },
+ {
"name": "timers",
"getval": re.compile(
r"""
@@ -745,6 +775,21 @@ class Bgp_globalTemplate(NetworkTemplate):
},
},
{
+ "name": "bgp.default.ipv4_unicast",
+ "getval": re.compile(r"""\sno\sbgp\sdefault\sipv4\-unicast""", re.VERBOSE),
+ "setval": "bgp default ipv4-unicast",
+ "result": {"bgp": {"default": {"ipv4_unicast": False}}},
+ },
+ {
+ "name": "bgp.default.route_target.filter",
+ "getval": re.compile(
+ r"""\sno\sbgp\sdefault\sroute\-target\sfilter""",
+ re.VERBOSE,
+ ),
+ "setval": "bgp default route-target filter",
+ "result": {"bgp": {"default": {"route_target": {"filter": False}}}},
+ },
+ {
"name": "bgp.deterministic_med",
"getval": re.compile(r"""\s(bgp\sdeterministic-med)""", re.VERBOSE),
"setval": "bgp deterministic-med",
@@ -1064,7 +1109,9 @@ class Bgp_globalTemplate(NetworkTemplate):
"setval": "bgp nopeerup-delay nsf-switchover {{ bgp.nopeerup_delay_options.nsf_switchover|string }}",
"result": {
"bgp": {
- "nopeerup_delay_options": {"nsf_switchover": "{{ nsf_switchover }}"},
+ "nopeerup_delay_options": {
+ "nsf_switchover": "{{ nsf_switchover }}",
+ },
},
},
},
@@ -1080,7 +1127,9 @@ class Bgp_globalTemplate(NetworkTemplate):
"setval": "bgp nopeerup-delay user-initiated {{ bgp.nopeerup_delay_options.user_initiated|string }}",
"result": {
"bgp": {
- "nopeerup_delay_options": {"user_initiated": "{{ user_initiated }}"},
+ "nopeerup_delay_options": {
+ "user_initiated": "{{ user_initiated }}",
+ },
},
},
},
@@ -1433,7 +1482,7 @@ class Bgp_globalTemplate(NetworkTemplate):
"neighbors": {
"{{ neighbor_address }}": {
"neighbor_address": "{{ neighbor_address }}",
- "description": "{{ description }}",
+ "description": "'{{ description }}'",
},
},
},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py
index 084945143..6cd1eb84c 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/interfaces.py
@@ -53,7 +53,7 @@ class InterfacesTemplate(NetworkTemplate):
"setval": "description {{ description }}",
"result": {
'{{ name }}': {
- 'description': '{{ description }}',
+ 'description': "'{{ description }}'",
},
},
},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py
index d57a8ded8..bd337befa 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/l3_interfaces.py
@@ -66,6 +66,23 @@ class L3_interfacesTemplate(NetworkTemplate):
# fmt: off
PARSERS = [
{
+ "name": "autostate",
+ "getval": re.compile(r"""\s+no\s+autostate$""", re.VERBOSE),
+ "setval": "autostate",
+ "result": {"{{ name }}": {"autostate": False}},
+ },
+ {
+ "name": "mac_address",
+ "getval": re.compile(
+ r"""^mac-address
+ (\s(?P<mac_address>\S+))
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "mac-address {{ mac_address }}",
+ "result": {"{{ name }}": {"mac_address": "{{ mac_address }}"}},
+ },
+ {
"name": "name",
"getval": re.compile(
r"""^interface
@@ -146,6 +163,33 @@ class L3_interfacesTemplate(NetworkTemplate):
},
},
{
+ "name": "ipv4.source_interface",
+ "getval": re.compile(
+ r"""\s+ip\sunnumbered
+ (\s(?P<name>\S+))
+ (\s(?P<poll>poll))?
+ (\s(?P<point_to_point>point-to-point))?
+ $""",
+ re.VERBOSE,
+ ),
+ "setval": "ip unnumbered {{ ipv4.source_interface.name }}"
+ "{{ ' poll' if ipv4.source_interface.poll|d(False) else ''}}"
+ "{{ ' point-to-point' if ipv4.source_interface.point_to_point|d(False) else ''}}",
+ "result": {
+ "{{ name }}": {
+ "ipv4": [
+ {
+ "source_interface": {
+ "name": "{{ True if name is defined }}",
+ "poll": "{{ True if poll is defined }}",
+ "point_to_point": "{{ True if point_to_point is defined }}",
+ },
+ },
+ ],
+ },
+ },
+ },
+ {
"name": "ipv6.address",
"getval": re.compile(
r"""\s+ipv6\saddress
@@ -227,5 +271,17 @@ class L3_interfacesTemplate(NetworkTemplate):
},
},
},
+ {
+ "name": "ipv6.enable",
+ "getval": re.compile(r"""\s+ipv6\s+enable$""", re.VERBOSE),
+ "setval": "ipv6 enable",
+ "result": {
+ "{{ name }}": {
+ "ipv6": [
+ {"enable": True},
+ ],
+ },
+ },
+ },
]
# fmt: on
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py
index 74b380cc5..80548254e 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/ospfv2.py
@@ -721,9 +721,9 @@ class Ospfv2Template(NetworkTemplate):
$""",
re.VERBOSE,
),
- "setval": "distance {{ admin_distance.distance }} "
- "{{ ( admin_distance.address + ' ' + admin_distance.wildcard_bits ) if admin_distance.address is defined else '' }}"
- "{{ ' ' + admin_distance.acl if admin_distance.acl is defined else '' }}",
+ "setval": "distance {{ distance.admin_distance.distance }} "
+ "{{ ( distance.admin_distance.address + ' ' + distance.admin_distance.wildcard_bits ) if distance.admin_distance.address is defined else '' }}"
+ "{{ ' ' + distance.admin_distance.acl if distance.admin_distance.acl is defined else '' }}",
"result": {
"processes": {
"{{ pid }}": {
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py
index 43d163c5d..f19e58e27 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/prefix_lists.py
@@ -80,7 +80,7 @@ class Prefix_listsTemplate(NetworkTemplate):
"{{ afi + name }}": {
"name": "{{ name }}",
"afi": "{{ 'ipv4' if afi is defined and afi=='ip' else 'ipv6' }}",
- "description": "{{ description }}",
+ "description": "'{{ description }}'",
},
},
},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py
index 1e569ccca..580987069 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/route_maps.py
@@ -39,11 +39,15 @@ def _tmplt_route_map_match(config_data):
cmd += " best {best}".format(**config_data["match"]["additional_paths"])
if config_data["match"]["additional_paths"].get("best_range"):
cmd += " best-range"
- if config_data["match"]["additional_paths"]["best_range"].get("lower_limit"):
+ if config_data["match"]["additional_paths"]["best_range"].get(
+ "lower_limit",
+ ):
cmd += " lower-limit {lower_limit}".format(
**config_data["match"]["additional_paths"]["best_range"],
)
- if config_data["match"]["additional_paths"]["best_range"].get("upper_limit"):
+ if config_data["match"]["additional_paths"]["best_range"].get(
+ "upper_limit",
+ ):
cmd += " upper-limit {upper_limit}".format(
**config_data["match"]["additional_paths"]["best_range"],
)
@@ -231,7 +235,10 @@ def _tmplt_route_map_match_ip(config_data):
config_data["match"]["ip"]["address"]["prefix_lists"],
)
elif config_data["match"]["ip"]["address"].get("acls"):
- cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["address"]["acls"])
+ cmd = construct_cmd_from_list(
+ cmd,
+ config_data["match"]["ip"]["address"]["acls"],
+ )
if config_data["match"]["ip"].get("flowspec"):
cmd += " flowspec"
if config_data["match"]["ip"]["flowspec"].get("dest_pfx"):
@@ -245,7 +252,10 @@ def _tmplt_route_map_match_ip(config_data):
config_data["match"]["ip"]["flowspec"]["prefix_lists"],
)
elif config_data["match"]["ip"]["flowspec"].get("acls"):
- cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["flowspec"]["acls"])
+ cmd = construct_cmd_from_list(
+ cmd,
+ config_data["match"]["ip"]["flowspec"]["acls"],
+ )
if config_data["match"]["ip"].get("next_hop"):
cmd += " next-hop"
if config_data["match"]["ip"]["next_hop"].get("prefix_lists"):
@@ -255,7 +265,10 @@ def _tmplt_route_map_match_ip(config_data):
config_data["match"]["ip"]["next_hop"]["prefix_lists"],
)
elif config_data["match"]["ip"]["next_hop"].get("acls"):
- cmd = construct_cmd_from_list(cmd, config_data["match"]["ip"]["next_hop"]["acls"])
+ cmd = construct_cmd_from_list(
+ cmd,
+ config_data["match"]["ip"]["next_hop"]["acls"],
+ )
if config_data["match"]["ip"].get("redistribution_source"):
cmd += " redistribution-source"
if config_data["match"]["ip"]["redistribution_source"].get("prefix_lists"):
@@ -411,7 +424,9 @@ def _tmplt_route_map_set(config_data):
cmd += " additive"
command.append(cmd)
if set["extcommunity"].get("soo"):
- command.append("set extcommunity soo {soo}".format(**set["extcommunity"]))
+ command.append(
+ "set extcommunity soo {soo}".format(**set["extcommunity"]),
+ )
if set["extcommunity"].get("vpn_distinguisher"):
cmd = "set extcommunity vpn-distinguisher"
if set["extcommunity"]["vpn_distinguisher"].get("range"):
@@ -419,7 +434,9 @@ def _tmplt_route_map_set(config_data):
**set["extcommunity"]["vpn_distinguisher"]["range"],
)
elif set["extcommunity"]["vpn_distinguisher"].get("address"):
- cmd += " {address}".format(**set["extcommunity"]["vpn_distinguisher"])
+ cmd += " {address}".format(
+ **set["extcommunity"]["vpn_distinguisher"],
+ )
if set["extcommunity"]["vpn_distinguisher"].get("additive"):
cmd += " additive"
command.append(cmd)
@@ -525,7 +542,10 @@ def _tmplt_route_map_set_ip(config_data):
command.append("{0} dynamic dhcp".format(cmd))
if set_ip["next_hop"].get("encapsulate"):
command.append(
- "{0} encapsulate l3vpn {encapsulate}".format(cmd, **set_ip["next_hop"]),
+ "{0} encapsulate l3vpn {encapsulate}".format(
+ cmd,
+ **set_ip["next_hop"],
+ ),
)
if set_ip["next_hop"].get("peer_address"):
command.append("{0} peer-address".format(cmd))
@@ -649,7 +669,10 @@ class Route_mapsTemplate(NetworkTemplate):
"{{ route_map }}": {
"route_map": "{{ route_map }}",
"{{ action|d() + '_' + sequence|d() }}": {
- "entries": {"action": "{{ action }}", "sequence": "{{ sequence }}"},
+ "entries": {
+ "action": "{{ action }}",
+ "sequence": "{{ sequence }}",
+ },
},
},
},
@@ -691,7 +714,7 @@ class Route_mapsTemplate(NetworkTemplate):
"result": {
"{{ route_map }}": {
"{{ action|d() + '_' + sequence|d() }}": {
- "entries": {"description": "{{ description }}"},
+ "entries": {"description": "'{{ description }}'"},
},
},
},
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py
index 5107612eb..45cabaae9 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/snmp_server.py
@@ -283,8 +283,7 @@ class Snmp_serverTemplate(NetworkTemplate):
(\sread\s(?P<read>\S+))?
(\swrite\s(?P<write>\S+))?
(\snotify\s(?P<notify>\S+))?
- (\saccess\s(?P<acl_v4>\S+))?
- (\saccess\sipv6\s(?P<acl_v6>\S+))?
+ (\saccess(\sipv6\s(?P<acl_v6>\S+))?(\s(?P<acl_v4>\S+|\d+))?)?
""", re.VERBOSE,
),
"setval": "snmp-server group "
@@ -296,8 +295,9 @@ class Snmp_serverTemplate(NetworkTemplate):
"{{ (' read ' + read) if read is defined else '' }}"
"{{ (' write ' + write) if write is defined else '' }}"
"{{ (' notify ' + notify) if notify is defined else '' }}"
- "{{ (' access ' + acl_v4) if acl_v4 is defined else '' }}"
- "{{ (' access ipv6 ' + acl_v6) if acl_v6 is defined else '' }}",
+ "{{ (' access') if acl_v6 is defined or acl_v4 is defined else '' }}"
+ "{{ (' ipv6 ' + acl_v6) if acl_v6 is defined else '' }}"
+ "{{ (' ' + acl_v4|string) if acl_v4 is defined else '' }}",
"result": {
"groups": [
{
@@ -398,8 +398,7 @@ class Snmp_serverTemplate(NetworkTemplate):
(\sudp-port\s(?P<udp_port>\d+))?
(\s(?P<version>v1|v3|v2c))?
(\s(?P<version_option>encrypted))?
- (\saccess\sipv6\s(?P<acl_v6>\S+))?
- (\saccess\s(?P<acl_v4>\S+|\d+))?
+ (\saccess(\sipv6\s(?P<acl_v6>\S+))?(\s(?P<acl_v4>\S+|\d+))?)?
(\svrf\s(?P<vrf>\S+))?
""", re.VERBOSE,
),
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/vlans.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/vlans.py
new file mode 100644
index 000000000..24907c868
--- /dev/null
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/rm_templates/vlans.py
@@ -0,0 +1,131 @@
+# -*- coding: utf-8 -*-
+# Copyright 2024 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+
+
+__metaclass__ = type
+
+"""
+The Vlans parser templates file. This contains
+a list of parser definitions and associated functions that
+facilitates both facts gathering and native command generation for
+the given network resource.
+"""
+
+import re
+
+from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.rm_base.network_template import (
+ NetworkTemplate,
+)
+
+
+def vlan_associated_config(config):
+ cmd = ""
+ if len(config.get("private_vlan", {}).get("associated")) > 1:
+ for vlan in config.get("private_vlan", {}).get("associated"):
+ cmd += str(vlan) + ","
+ cmd = cmd[:-1]
+ else:
+ cmd = config.get("private_vlan", {}).get("associated")[0]
+ return "private-vlan association " + cmd
+
+
+class VlansTemplate(NetworkTemplate):
+ def __init__(self, lines=None, module=None):
+ super(VlansTemplate, self).__init__(
+ lines=lines,
+ tmplt=self,
+ module=module,
+ )
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "vlan_configuration",
+ "getval": re.compile(
+ r"""
+ ^vlan\sconfiguration\s(?P<vlan_id>\d+)
+ $""", re.VERBOSE,
+ ),
+ "setval": "vlan configuration {{ vlan_id|string }}",
+ "result": {
+ "{{ vlan_id }}": {
+ "vlan_id": "{{ vlan_id }}",
+ },
+ },
+ "shared": True,
+ },
+ {
+ "name": "member",
+ "getval": re.compile(
+ r"""
+ \s*member
+ (\sevpn-instance\s(?P<inst_vlan_id>\d+))?
+ (\svni\s(?P<vni>\d+))?
+ $""", re.VERBOSE,
+ ),
+ "setval": "member"
+ "{{ (' evpn-instance ' + member.evi|string) if member.evi is defined else '' }}"
+ "{{ (' vni ' + member.vni|string) if member.vni is defined else '' }}",
+ "result": {
+ "{{ vlan_id }}": {
+ "member": {
+ "evi": "{{ inst_vlan_id }}",
+ "vni": "{{ vni }}",
+ },
+ },
+ },
+ },
+ {
+ "name": "vlans",
+ "getval": "",
+ "setval": "vlan {{ vlan_id|string }}",
+ "result": {},
+ },
+ {
+ "name": "name",
+ "getval": "",
+ "setval": "name {{ name|string }}",
+ "result": {},
+ },
+ {
+ "name": "state",
+ "getval": "",
+ "setval": "state {{ state }}",
+ "result": {},
+ },
+ {
+ "name": "mtu",
+ "getval": "",
+ "setval": "mtu {{ mtu|string }}",
+ "result": {},
+ },
+ {
+ "name": "remote_span",
+ "getval": "",
+ "setval": "remote-span",
+ "result": {},
+ },
+ {
+ "name": "private_vlan.type",
+ "getval": "",
+ "setval": "private-vlan {{ private_vlan.type if private_vlan.type is defined }}",
+ "result": {},
+ },
+ {
+ "name": "private_vlan.associated",
+ "getval": "",
+ "setval": vlan_associated_config,
+ "result": {},
+ },
+ {
+ "name": "shutdown",
+ "getval": "",
+ "setval": "shutdown",
+ "result": {},
+ },
+ ]
+ # fmt: on
diff --git a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py
index f3038f7f2..1deea535b 100644
--- a/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py
+++ b/ansible_collections/cisco/ios/plugins/module_utils/network/ios/utils/utils.py
@@ -219,31 +219,19 @@ def validate_ipv4(value, module):
address = value.split("/")
if len(address) != 2:
module.fail_json(
- msg="address format is <ipv4 address>/<mask>, got invalid format {0}".format(value),
+ msg="address format is <ipv4 address>/<mask>, got invalid format {0}".format(
+ value,
+ ),
)
if not is_masklen(address[1]):
module.fail_json(
- msg="invalid value for mask: {0}, mask should be in range 0-32".format(address[1]),
+ msg="invalid value for mask: {0}, mask should be in range 0-32".format(
+ address[1],
+ ),
)
-def validate_ipv6(value, module):
- if value:
- address = value.split("/")
- if len(address) != 2:
- module.fail_json(
- msg="address format is <ipv6 address>/<mask>, got invalid format {0}".format(value),
- )
- else:
- if not 0 <= int(address[1]) <= 128:
- module.fail_json(
- msg="invalid value for mask: {0}, mask should be in range 0-128".format(
- address[1],
- ),
- )
-
-
def validate_n_expand_ipv4(module, want):
# Check if input IPV4 is valid IP and expand IPV4 with its subnet mask
ip_addr_want = want.get("address")