summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/cliconf/vyos.py5
-rw-r--r--plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py3
-rw-r--r--plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py107
-rw-r--r--plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py80
-rw-r--r--plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py443
-rw-r--r--plugins/module_utils/network/vyos/config/interfaces/interfaces.py2
-rw-r--r--plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py28
-rw-r--r--plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py18
-rw-r--r--plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py180
-rw-r--r--plugins/module_utils/network/vyos/facts/interfaces/interfaces.py20
-rw-r--r--plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py2
-rw-r--r--plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py51
-rw-r--r--plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py650
-rw-r--r--plugins/module_utils/network/vyos/utils/version.py13
-rw-r--r--plugins/module_utils/network/vyos/vyos.py8
-rw-r--r--plugins/modules/vyos_firewall_global.py13
-rw-r--r--plugins/modules/vyos_firewall_rules.py180
-rw-r--r--plugins/modules/vyos_ospf_interfaces.py2
18 files changed, 1592 insertions, 213 deletions
diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py
index 7e6b0b17..5beffaa1 100644
--- a/plugins/cliconf/vyos.py
+++ b/plugins/cliconf/vyos.py
@@ -80,6 +80,11 @@ class Cliconf(CliconfBase):
if match:
device_info["network_os_version"] = match.group(1)
+ if device_info["network_os_version"]:
+ match = re.search(r"VyOS\s*(\d+\.\d+)", device_info["network_os_version"])
+ if match:
+ device_info["network_os_major_version"] = match.group(1)
+
match = re.search(r"(?:HW|Hardware) model:\s*(\S+)", data)
if match:
device_info["network_os_model"] = match.group(1)
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
index 2326bea1..f79454ed 100644
--- a/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/argspec/firewall_global/firewall_global.py
@@ -134,6 +134,9 @@ class Firewall_globalArgs(object): # pylint: disable=R0903
"type": "str",
},
"log": {"type": "bool"},
+ "log_level": {
+ "choices": ["emerg", "alert", "crit", "err", "warn", "notice", "info", "debug"]
+ }
},
"type": "list",
},
diff --git a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
index eb285cfd..4d0973e3 100644
--- a/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/argspec/firewall_rules/firewall_rules.py
@@ -50,11 +50,16 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
"elements": "dict",
"options": {
"default_action": {
- "choices": ["drop", "reject", "accept"],
+ "choices": ["drop", "reject", "accept", "jump"],
"type": "str",
},
+ "default_jump_target": {"type": "str"},
"description": {"type": "str"},
"enable_default_log": {"type": "bool"},
+ "filter": {
+ "choices": ["input", "output", "forward"],
+ "type": "str"
+ },
"name": {"type": "str"},
"rules": {
"elements": "dict",
@@ -65,6 +70,11 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
"reject",
"accept",
"inspect",
+ "continue",
+ "return",
+ "jump",
+ "queue",
+ "synproxy",
],
"type": "str",
},
@@ -147,9 +157,23 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
},
"type": "dict",
},
+ "inbound_interface": {
+ "options": {
+ "group": {
+ "type": "str",
+ },
+ "name": {
+ "type": "str",
+ },
+ },
+ "type": "dict",
+ },
"ipsec": {
- "choices": ["match-ipsec", "match-none"],
- "type": "str",
+ "choices": ["match-ipsec", "match-none", "match-ipsec-in", "match-ipsec-out", "match-none-in", "match-none-out"],
+ "type": "str"
+ },
+ "jump_target": {
+ "type": "str"
},
"limit": {
"options": {
@@ -169,6 +193,17 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
"choices": ["enable", "disable"],
},
"number": {"required": True, "type": "int"},
+ "outbound_interface": {
+ "options": {
+ "group": {
+ "type": "str",
+ },
+ "name": {
+ "type": "str",
+ },
+ },
+ "type": "dict",
+ },
"p2p": {
"elements": "dict",
"options": {
@@ -185,19 +220,52 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
"type": "str",
},
},
+ "type": "list"
+ },
+ "packet_length": {
+ "elements": "dict",
+ "options": {
+ "length": {
+ "type": "str",
+ },
+ },
+ "type": "list"
+ },
+ "packet_length_exclude": {
+ "elements": "dict",
+ "options": {
+ "length": {
+ "type": "str",
+ }
+ },
"type": "list",
},
+ "packet_type": {
+ "choices": [
+ "broadcast",
+ "multicast",
+ "host",
+ "other"
+ ],
+ "type": "str"
+ },
"protocol": {"type": "str"},
+ "queue": {"type": "str"},
+ "queue_options": {
+ "choices": ["bypass", "fanout"],
+ "type": "str"
+ },
"recent": {
"options": {
"count": {"type": "int"},
- "time": {"type": "int"},
+ "time": {"type": "str"},
},
"type": "dict",
},
"source": {
"options": {
"address": {"type": "str"},
+ "fqdn": {"type": "str"},
"group": {
"options": {
"address_group": {"type": "str"},
@@ -220,8 +288,37 @@ class Firewall_rulesArgs(object): # pylint: disable=R0903
},
"type": "dict",
},
+ "synproxy": {
+ "options": {
+ "mss": {"type": "int"},
+ "window_scale": {"type": "int"},
+ },
+ "type": "dict",
+ },
"tcp": {
- "options": {"flags": {"type": "str"}},
+ "options": {
+ "flags": {
+ "elements": "dict",
+ "options": {
+ "flag": {
+ "choices": [
+ "ack",
+ "cwr",
+ "ecn",
+ "fin",
+ "psh",
+ "rst",
+ "syn",
+ "urg",
+ "all",
+ ],
+ "type": "str"
+ },
+ "invert": {"type": "bool"}
+ },
+ "type": "list"
+ }
+ },
"type": "dict",
},
"time": {
diff --git a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
index 8694f11b..7e978ff9 100644
--- a/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/config/firewall_global/firewall_global.py
@@ -31,6 +31,10 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils
list_diff_want_only,
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion
+
class Firewall_global(ConfigBase):
"""
@@ -255,7 +259,7 @@ class Firewall_global(ConfigBase):
continue
if (
key in l_set
- and not (h and self._in_target(h, key))
+ and not self._in_target(h, key)
and not self._is_del(l_set, h)
):
commands.append(
@@ -455,7 +459,10 @@ class Firewall_global(ConfigBase):
"""
commands = []
have = []
- l_set = ("log", "action", "connection_type")
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ l_set = ("log", "action", "connection_type", "log_level")
+ else:
+ l_set = ("log", "action", "connection_type")
if not opr and self._is_root_del(h, w, attr):
commands.append(self._form_attr_cmd(attr=attr, opr=opr))
else:
@@ -478,25 +485,23 @@ class Firewall_global(ConfigBase):
),
)
elif not opr and key in l_set:
- if not (h and self._in_target(h, key)) and not self._is_del(
- l_set,
- h,
- ):
- if key == "action":
- commands.append(
- self._form_attr_cmd(
- attr=attr + " " + w["connection_type"],
- opr=opr,
- ),
- )
- else:
- commands.append(
- self._form_attr_cmd(
- attr=attr + " " + w["connection_type"],
- val=self._bool_to_str(val),
- opr=opr,
- ),
- )
+ if not h:
+ commands.append(
+ self._form_attr_cmd(
+ attr=attr + " " + w["connection_type"],
+ opr=opr,
+ ),
+ )
+ break # delete the whole thing and move on
+ if (not self._in_target(h, key) or h[key] is None) and (self._in_target(w, key) and w[key]):
+ # delete if not being replaced and value currently exists
+ commands.append(
+ self._form_attr_cmd(
+ attr=attr + " " + w["connection_type"] + " " + key,
+ val=self._bool_to_str(val),
+ opr=opr,
+ ),
+ )
return commands
def _render_route_redirects(self, attr, w, h, opr):
@@ -520,6 +525,14 @@ class Firewall_global(ConfigBase):
if want:
for w in want:
h = self.search_attrib_in_have(have, w, "afi")
+ if 'afi' in w:
+ afi = w['afi']
+ else:
+ if h and 'afi' in h:
+ afi = h['afi']
+ else:
+ afi = None
+ afi = None
for key, val in iteritems(w):
if val and key != "afi":
if opr and key in l_set and not (h and self._is_w_same(w, h, key)):
@@ -528,6 +541,7 @@ class Firewall_global(ConfigBase):
attr=key,
val=self._bool_to_str(val),
opr=opr,
+ type=afi
),
)
elif not opr and key in l_set:
@@ -537,6 +551,7 @@ class Firewall_global(ConfigBase):
attr=key,
val=self._bool_to_str(val),
opr=opr,
+ type=afi
),
)
continue
@@ -546,6 +561,7 @@ class Firewall_global(ConfigBase):
attr=key,
val=self._bool_to_str(val),
opr=opr,
+ type=afi
),
)
elif key == "icmp_redirects":
@@ -565,20 +581,27 @@ class Firewall_global(ConfigBase):
commands = []
h_red = {}
l_set = ("send", "receive")
+ if w and 'afi' in w:
+ afi = w['afi']
+ else:
+ if h and 'afi' in h:
+ afi = h['afi']
+ else:
+ afi = None
if w[attr]:
if h and attr in h.keys():
h_red = h.get(attr) or {}
for item, value in iteritems(w[attr]):
if opr and item in l_set and not (h_red and self._is_w_same(w[attr], h_red, item)):
commands.append(
- self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr),
+ self._form_attr_cmd(attr=item, val=self._bool_to_str(value), opr=opr, type=afi)
)
elif (
not opr
and item in l_set
and not (h_red and self._is_w_same(w[attr], h_red, item))
):
- commands.append(self._form_attr_cmd(attr=item, opr=opr))
+ commands.append(self._form_attr_cmd(attr=item, opr=opr, type=afi))
return commands
def search_attrib_in_have(self, have, want, attr):
@@ -595,16 +618,17 @@ class Firewall_global(ConfigBase):
return h
return None
- def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True):
+ def _form_attr_cmd(self, key=None, attr=None, val=None, opr=True, type=None):
"""
This function forms the command for leaf attribute.
:param key: parent key.
:param attr: attribute name
:param value: value
:param opr: True/False.
+ :param type: AF type of attribute.
:return: generated command.
"""
- command = self._compute_command(key=key, attr=self._map_attrib(attr), val=val, opr=opr)
+ command = self._compute_command(key=key, attr=self._map_attrib(attr, type=type), val=val, opr=opr)
return command
def _compute_command(self, key=None, attr=None, val=None, remove=False, opr=True):
@@ -621,13 +645,15 @@ class Firewall_global(ConfigBase):
cmd = "delete firewall "
else:
cmd = "set firewall "
+ if key != "group" and LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ cmd += "global-options "
if key:
cmd += key.replace("_", "-") + " "
if attr:
cmd += attr.replace("_", "-")
if val and opr:
cmd += " '" + str(val) + "'"
- return cmd
+ return cmd.strip()
def _bool_to_str(self, val):
"""
@@ -698,7 +724,7 @@ class Firewall_global(ConfigBase):
:param key: number.
:return: True/False.
"""
- return key in b_set and not (h and self._in_target(h, key))
+ return key in b_set and not self._in_target(h, key)
def _map_attrib(self, attrib, type=None):
"""
diff --git a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
index 09e19d70..106b2b8b 100644
--- a/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/config/firewall_rules/firewall_rules.py
@@ -15,8 +15,6 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
-import re
-
from copy import deepcopy
from ansible.module_utils.six import iteritems
@@ -33,6 +31,10 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils
list_diff_want_only,
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion
+
class Firewall_rules(ConfigBase):
"""
@@ -171,12 +173,8 @@ class Firewall_rules(ConfigBase):
# In the desired configuration, search for the rule set we
# already have (to be replaced by our desired
# configuration's rule set).
- wanted_rule_set = self.search_r_sets_in_have(
- want,
- rs["name"],
- "r_list",
- h["afi"],
- )
+ rs_id = self._rs_id(rs, h["afi"])
+ wanted_rule_set = self.search_r_sets_in_have(want, rs_id, "r_list")
if wanted_rule_set is not None:
# Remove the rules that we already have if the wanted
# rules exist under the same name.
@@ -202,11 +200,12 @@ class Firewall_rules(ConfigBase):
commands = []
if have:
for h in have:
- r_sets = self._get_r_sets(h)
- for rs in r_sets:
- w = self.search_r_sets_in_have(want, rs["name"], "r_list", h["afi"])
+ have_r_sets = self._get_r_sets(h)
+ for rs in have_r_sets:
+ rs_id = self._rs_id(rs, h["afi"])
+ w = self.search_r_sets_in_have(want, rs_id, "r_list")
if not w:
- commands.append(self._compute_command(h["afi"], rs["name"], remove=True))
+ commands.append(self._compute_command(rs_id, remove=True))
else:
commands.extend(self._add_r_sets(h["afi"], rs, w, opr=False))
commands.extend(self._state_merged(want, have))
@@ -223,7 +222,8 @@ class Firewall_rules(ConfigBase):
for w in want:
r_sets = self._get_r_sets(w)
for rs in r_sets:
- h = self.search_r_sets_in_have(have, rs["name"], "r_list", w["afi"])
+ rs_id = self._rs_id(rs, w["afi"])
+ h = self.search_r_sets_in_have(have, rs_id, "r_list")
commands.extend(self._add_r_sets(w["afi"], rs, h))
return commands
@@ -240,18 +240,21 @@ class Firewall_rules(ConfigBase):
r_sets = self._get_r_sets(w)
if r_sets:
for rs in r_sets:
- h = self.search_r_sets_in_have(have, rs["name"], "r_list", w["afi"])
+ rs_id = self._rs_id(rs, w["afi"])
+ h = self.search_r_sets_in_have(have, rs_id, "r_list")
if h:
- commands.append(self._compute_command(w["afi"], h["name"], remove=True))
+ commands.append(self._compute_command(rs_id, remove=True))
elif have:
for h in have:
if h["afi"] == w["afi"]:
- commands.append(self._compute_command(w["afi"], remove=True))
+ commands.append(
+ self._compute_command(self._rs_id(None, w["afi"]), remove=True)
+ )
elif have:
for h in have:
r_sets = self._get_r_sets(h)
if r_sets:
- commands.append(self._compute_command(afi=h["afi"], remove=True))
+ commands.append(self._compute_command(self._rs_id(None, h["afi"]), remove=True))
return commands
def _add_r_sets(self, afi, want, have, opr=True):
@@ -265,11 +268,12 @@ class Firewall_rules(ConfigBase):
:return: generated commands list.
"""
commands = []
- l_set = ("description", "default_action", "enable_default_log")
+ l_set = ("description", "default_action", "default_jump_target", "enable_default_log")
h_rs = {}
h_rules = {}
w_rs = deepcopy(remove_empties(want))
w_rules = w_rs.pop("rules", None)
+ rs_id = self._rs_id(want, afi=afi)
if have:
h_rs = deepcopy(remove_empties(have))
h_rules = h_rs.pop("rules", None)
@@ -278,9 +282,9 @@ class Firewall_rules(ConfigBase):
if opr and key in l_set and not (h_rs and self._is_w_same(w_rs, h_rs, key)):
if key == "enable_default_log":
if val and (not h_rs or key not in h_rs or not h_rs[key]):
- commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs))
+ commands.append(self._add_rs_base_attrib(rs_id, key, w_rs))
else:
- commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs))
+ commands.append(self._add_rs_base_attrib(rs_id, key, w_rs))
elif not opr and key in l_set:
if (
key == "enable_default_log"
@@ -288,22 +292,24 @@ class Firewall_rules(ConfigBase):
and h_rs
and (key not in h_rs or not h_rs[key])
):
- commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs, opr))
+ commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr))
elif not (h_rs and self._in_target(h_rs, key)):
- commands.append(self._add_rs_base_attrib(afi, want["name"], key, w_rs, opr))
- commands.extend(self._add_rules(afi, want["name"], w_rules, h_rules, opr))
+ commands.append(self._add_rs_base_attrib(rs_id, key, w_rs, opr))
+ commands.extend(self._add_rules(rs_id, w_rules, h_rules, opr))
if h_rules:
have["rules"] = h_rules
if w_rules:
want["rules"] = w_rules
return commands
- def _add_rules(self, afi, name, w_rules, h_rules, opr=True):
+ def _add_rules(self, rs_id, w_rules, h_rules, opr=True):
"""
This function forms the set/delete commands based on the 'opr' type
for rules attributes.
- :param want: desired config.
- :param have: target config.
+ :param rs_id: rule-set identifier.
+ :param w_rules: desired config.
+ :param h_rules: target config.
+ :param opr: True/False.
:return: generated commands list.
"""
commands = []
@@ -316,31 +322,70 @@ class Firewall_rules(ConfigBase):
"disable",
"description",
"log",
+ "jump_target",
)
if w_rules:
for w in w_rules:
- cmd = self._compute_command(afi, name, w["number"], opr=opr)
- h = self.search_r_sets_in_have(h_rules, w["number"], type="rules")
+ cmd = self._compute_command(rs_id, w["number"], opr=opr)
+ h = self.search_rules_in_have_rs(h_rules, w["number"])
for key, val in iteritems(w):
if val:
if opr and key in l_set and not (h and self._is_w_same(w, h, key)):
if key == "disable":
if not (not val and (not h or key not in h or not h[key])):
- commands.append(self._add_r_base_attrib(afi, name, key, w))
+ commands.append(self._add_r_base_attrib(rs_id, key, w))
else:
- commands.append(self._add_r_base_attrib(afi, name, key, w))
+ commands.append(self._add_r_base_attrib(rs_id, key, w))
elif not opr:
+ # Note: if you are experiencing sticky configuration on replace
+ # you may need to add an explicit check for the key here. Anything that
+ # doesn't have a custom operation is taken care of by the `l_set` check
+ # below, but I'm not sure how any of the others work.
+ # It's possible that historically the delete was forced (but now it's
+ # checked).
if key == "number" and self._is_del(l_set, h):
- commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
+ commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr))
continue
- if key == "disable" and val and h and (key not in h or not h[key]):
- commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
+ if (
+ key == "tcp"
+ and val
+ and h
+ and (key not in h or not h[key] or h[key] != w[key])
+ ):
+ commands.extend(self._add_tcp(key, w, h, cmd, opr))
+ if (
+ key == "state"
+ and val
+ and h
+ and (key not in h or not h[key] or h[key] != w[key])
+ ):
+ commands.extend(self._add_state(key, w, h, cmd, opr))
+ if (
+ key == "icmp"
+ and val
+ and h
+ and (key not in h or not h[key] or h[key] != w[key])
+ ):
+ commands.extend(self._add_icmp(key, w, h, cmd, opr))
+ if (
+ key in ("packet_length", "packet_length_exclude")
+ and val
+ and h
+ and (key not in h or not h[key] or h[key] != w[key])
+ ):
+ commands.extend(self._add_packet_length(key, w, h, cmd, opr))
+ elif key == "disable" and val and h and (key not in h or not h[key]):
+ commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr))
+ elif key in ("inbound_interface", "outbound_interface") and not (
+ h and self._is_w_same(w, h, key)
+ ):
+ commands.extend(self._add_interface(key, w, h, cmd, opr))
elif (
key in l_set
and not (h and self._in_target(h, key))
and not self._is_del(l_set, h)
):
- commands.append(self._add_r_base_attrib(afi, name, key, w, opr=opr))
+ commands.append(self._add_r_base_attrib(rs_id, key, w, opr=opr))
elif key == "p2p":
commands.extend(self._add_p2p(key, w, h, cmd, opr))
elif key == "tcp":
@@ -357,6 +402,10 @@ class Firewall_rules(ConfigBase):
commands.extend(self._add_recent(key, w, h, cmd, opr))
elif key == "destination" or key == "source":
commands.extend(self._add_src_or_dest(key, w, h, cmd, opr))
+ elif key in ("packet_length", "packet_length_exclude"):
+ commands.extend(self._add_packet_length(key, w, h, cmd, opr))
+ elif key in ("inbound_interface", "outbound_interface"):
+ commands.extend(self._add_interface(key, w, h, cmd, opr))
return commands
def _add_p2p(self, attr, w, h, cmd, opr):
@@ -405,8 +454,11 @@ class Firewall_rules(ConfigBase):
and item in l_set
and not (h_state and self._is_w_same(w[attr], h_state, item))
):
- commands.append(cmd + (" " + attr + " " + item + " " + self._bool_to_str(val)))
- elif not opr and item in l_set and not (h_state and self._in_target(h_state, item)):
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ commands.append(cmd + (" " + attr + " " + item))
+ else:
+ commands.append(cmd + (" " + attr + " " + item + " " + self._bool_to_str(val)))
+ elif not opr and item in l_set and not self._in_target(h_state, item):
commands.append(cmd + (" " + attr + " " + item))
return commands
@@ -460,26 +512,50 @@ class Firewall_rules(ConfigBase):
and not (h_icmp and self._is_w_same(w[attr], h_icmp, item))
):
if item == "type_name":
- os_version = self._get_os_version()
- ver = re.search(
- "vyos ([\\d\\.]+)-?.*", # noqa: W605
- os_version,
- re.IGNORECASE,
- )
- if ver.group(1) >= "1.4":
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.3"):
param_name = "type-name"
else:
param_name = "type"
- if "ipv6-name" in cmd:
+ if "ipv6" in cmd: # ipv6-name or ipv6
commands.append(cmd + (" " + "icmpv6" + " " + param_name + " " + val))
else:
commands.append(
cmd + (" " + attr + " " + item.replace("_", "-") + " " + val),
)
else:
- commands.append(cmd + (" " + attr + " " + item + " " + str(val)))
- elif not opr and item in l_set and not (h_icmp and self._in_target(h_icmp, item)):
- commands.append(cmd + (" " + attr + " " + item))
+ if "ipv6" in cmd: # ipv6-name or ipv6
+ commands.append(cmd + (" " + "icmpv6" + " " + item + " " + str(val)))
+ else:
+ commands.append(cmd + (" " + attr + " " + item + " " + str(val)))
+ elif not opr and item in l_set and not self._in_target(h_icmp, item):
+ commands.append(cmd + (" " + attr + " " + item.replace("_", "-") + " " + str(val)))
+ return commands
+
+ def _add_interface(self, attr, w, h, cmd, opr):
+ """
+ This function forms the commands for 'interface' attributes based on the 'opr'.
+ :param attr: attribute name.
+ :param w: base config.
+ :param h: target config.
+ :param cmd: commands to be prepend.
+ :return: generated list of commands.
+ """
+ commands = []
+ h_if = {}
+ l_set = ("name", "group")
+ if w[attr]:
+ if h and attr in h.keys():
+ h_if = h.get(attr) or {}
+ for item, val in iteritems(w[attr]):
+ if opr and item in l_set and not (h_if and self._is_w_same(w[attr], h_if, item)):
+ commands.append(
+ cmd
+ + (" " + attr.replace("_", "-") + " " + item.replace("_", "-") + " " + val)
+ )
+ elif not opr and item in l_set and not (h_if and self._in_target(h_if, item)):
+ commands.append(
+ cmd + (" " + attr.replace("_", "-") + " " + item.replace("_", "-"))
+ )
return commands
def _add_time(self, attr, w, h, cmd, opr):
@@ -524,15 +600,107 @@ class Firewall_rules(ConfigBase):
commands.append(cmd + (" " + attr + " " + item))
return commands
+ def _add_tcp_1_4(self, attr, w, h, cmd, opr):
+ """
+ This function forms the commands for 'tcp' attributes based on the 'opr'.
+ Version 1.4+
+ :param attr: attribute name.
+ :param w: base config.
+ :param h: target config.
+ :param cmd: commands to be prepend.
+ :return: generated list of commands.
+ """
+ commands = []
+ have = []
+ key = "flags"
+ want = []
+
+ if w:
+ if w.get(attr):
+ want = w.get(attr).get(key) or []
+ if h:
+ if h.get(attr):
+ have = h.get(attr).get(key) or []
+ if want:
+ if opr:
+ flags = list_diff_want_only(want, have)
+ for flag in flags:
+ invert = flag.get("invert", False)
+ commands.append(
+ cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"])
+ )
+ elif not opr:
+ flags = list_diff_want_only(want, have)
+ for flag in flags:
+ invert = flag.get("invert", False)
+ commands.append(
+ cmd + (" " + attr + " flags " + ("not " if invert else "") + flag["flag"])
+ )
+ return commands
+
+ def _add_packet_length(self, attr, w, h, cmd, opr):
+ """
+ This function forms the commands for 'packet_length[_exclude]' attributes based on the 'opr'.
+ If < 1.4, handle tcp attributes.
+ :param attr: attribute name.
+ :param w: base config.
+ :param h: target config.
+ :param cmd: commands to be prepend.
+ :return: generated list of commands.
+ """
+ commands = []
+ have = []
+ want = []
+
+ if w:
+ if w.get(attr):
+ want = w.get(attr) or []
+ if h:
+ if h.get(attr):
+ have = h.get(attr) or []
+ attr = attr.replace("_", "-")
+ if want:
+ if opr:
+ lengths = list_diff_want_only(want, have)
+ for l_rec in lengths:
+ commands.append(cmd + " " + attr + " " + str(l_rec["length"]))
+ elif not opr:
+ lengths = list_diff_want_only(want, have)
+ for l_rec in lengths:
+ commands.append(cmd + " " + attr + " " + str(l_rec["length"]))
+ return commands
+
+ def _tcp_flags_string(self, flags):
+ """
+ This function forms the tcp flags string.
+ :param flags: flags list.
+ :return: flags string or None.
+ """
+ if not flags:
+ return ""
+ flag_str = ""
+ for flag in flags:
+ this_flag = flag["flag"].upper()
+ if flag.get("invert", False):
+ this_flag = "!" + this_flag
+ if len(flag_str) > 0:
+ flag_str = ",".join([flag_str, this_flag])
+ else:
+ flag_str = this_flag
+ return flag_str
+
def _add_tcp(self, attr, w, h, cmd, opr):
"""
This function forms the commands for 'tcp' attributes based on the 'opr'.
+ If < 1.4, handle tcp attributes.
:param attr: attribute name.
:param w: base config.
:param h: target config.
:param cmd: commands to be prepend.
:return: generated list of commands.
"""
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ return self._add_tcp_1_4(attr, w, h, cmd, opr)
h_tcp = {}
commands = []
if w[attr]:
@@ -542,10 +710,11 @@ class Firewall_rules(ConfigBase):
if h and key in h[attr].keys():
h_tcp = h[attr].get(key) or {}
if flags:
- if opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)):
- commands.append(cmd + (" " + attr + " " + key + " " + flags))
- if not opr and not (h_tcp and self._is_w_same(w[attr], h[attr], key)):
- commands.append(cmd + (" " + attr + " " + key + " " + flags))
+ flag_str = self._tcp_flags_string(flags)
+ if opr and not (h_tcp and flags == h_tcp):
+ commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str))
+ if not opr and not (h_tcp and flags == h_tcp):
+ commands.append(cmd + (" " + attr + " " + "flags" + " " + flag_str))
return commands
def _add_limit(self, attr, w, h, cmd, opr):
@@ -671,43 +840,68 @@ class Firewall_rules(ConfigBase):
)
return commands
- def search_r_sets_in_have(self, have, w_name, type="rule_sets", afi=None):
+ def search_rules_in_have_rs(self, have_rules, r_number):
+ """
+ This function returns the rule if it is present in target config.
+ :param have: target config.
+ :param rs_id: rule-set identifier.
+ :param r_number: rule-number.
+ :return: rule.
+ """
+ if have_rules:
+ for h in have_rules:
+ key = "number"
+ for r in have_rules:
+ if key in r and r[key] == r_number:
+ return r
+ return None
+
+ def search_r_sets_in_have(self, have, rs_id, type="rule_sets"):
"""
This function returns the rule-set/rule if it is present in target config.
:param have: target config.
- :param w_name: rule-set name.
- :param type: rule_sets/rule/r_list.
- :param afi: address family (when type is r_list).
+ :param rs_id: rule-identifier.
+ :param type: rule_sets if searching a rule_set and r_list if searching from a rule_list.
:return: rule-set/rule.
"""
- if have:
+ if "afi" in rs_id:
+ afi = rs_id["afi"]
+ else:
+ afi = None
+ if rs_id["filter"]:
+ key = "filter"
+ w_value = rs_id["filter"]
+ elif rs_id["name"]:
key = "name"
- if type == "rules":
- key = "number"
- for r in have:
- if r[key] == w_name:
- return r
- elif type == "r_list":
+ w_value = rs_id["name"]
+ else:
+ raise ValueError("id must be specific to name or filter")
+
+ if type not in ("r_list", "rule_sets"):
+ raise ValueError("type must be rule_sets or r_list")
+ if have:
+ if type == "r_list":
for h in have:
if h["afi"] == afi:
r_sets = self._get_r_sets(h)
for rs in r_sets:
- if rs[key] == w_name:
+ if key in rs and rs[key] == w_value:
return rs
else:
+ # searching a ruleset
for rs in have:
- if rs[key] == w_name:
+ if key in rs and rs[key] == w_value:
return rs
return None
- def _get_r_sets(self, item, type="rule_sets"):
+ def _get_r_sets(self, item):
"""
- This function returns the list of rule-sets/rules.
+ This function returns the list of rule-sets.
:param item: config dictionary.
- :param type: rule_sets/rule/r_list.
:return: list of rule-sets/rules.
"""
rs_list = []
+ type = "rule_sets"
r_sets = item[type]
if r_sets:
for rs in r_sets:
@@ -716,8 +910,7 @@ class Firewall_rules(ConfigBase):
def _compute_command(
self,
- afi,
- name=None,
+ rs_id,
number=None,
attrib=None,
value=None,
@@ -726,46 +919,53 @@ class Firewall_rules(ConfigBase):
):
"""
This function construct the add/delete command based on passed attributes.
- :param afi: address type.
- :param name: rule-set name.
+ :param rs_id: rule-set identifier.
:param number: rule-number.
:param attrib: attribute name.
:param value: value.
:param remove: True if delete command needed to be construct.
- :param opr: opeeration flag.
+ :param opr: operation flag.
:return: generated command.
"""
+ if rs_id["name"] and rs_id["filter"]:
+ raise ValueError("name and filter cannot be used together")
if remove or not opr:
- cmd = "delete firewall " + self._get_fw_type(afi)
+ cmd = "delete firewall " + self._get_fw_type(rs_id["afi"])
else:
- cmd = "set firewall " + self._get_fw_type(afi)
- if name:
- cmd += " " + name
+ cmd = "set firewall " + self._get_fw_type(rs_id["afi"])
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ if rs_id["name"]:
+ cmd += " name " + rs_id["name"]
+ elif rs_id["filter"]:
+ cmd += " " + rs_id["filter"] + " filter"
+ elif rs_id["name"]:
+ cmd += " " + rs_id["name"]
if number:
cmd += " rule " + str(number)
if attrib:
- cmd += " " + attrib.replace("_", "-")
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4") and attrib == "enable_default_log":
+ cmd += " " + "default-log"
+ else:
+ cmd += " " + attrib.replace("_", "-")
if value and opr and attrib != "enable_default_log" and attrib != "disable":
cmd += " '" + str(value) + "'"
return cmd
- def _add_r_base_attrib(self, afi, name, attr, rule, opr=True):
+ def _add_r_base_attrib(self, rs_id, attr, rule, opr=True):
"""
This function forms the command for 'rules' attributes which doesn't
have further sub attributes.
- :param afi: address type.
- :param name: rule-set name
+ :param rs_id: rule-set identifier.
:param attrib: attribute name
:param rule: rule config dictionary.
:param opr: True/False.
:return: generated command.
"""
if attr == "number":
- command = self._compute_command(afi=afi, name=name, number=rule["number"], opr=opr)
+ command = self._compute_command(rs_id, number=rule["number"], opr=opr)
else:
command = self._compute_command(
- afi=afi,
- name=name,
+ rs_id=rs_id,
number=rule["number"],
attrib=attr,
value=rule[attr],
@@ -773,21 +973,54 @@ class Firewall_rules(ConfigBase):
)
return command
- def _add_rs_base_attrib(self, afi, name, attrib, rule, opr=True):
+ def _rs_id(self, have, afi, name=None, filter=None):
"""
+ This function returns the rule-set identifier based on
+ the example rule, overriding the components as specified.
- This function forms the command for 'rule-sets' attributes which doesn't
- have further sub attributes.
+ :param have: example rule.
:param afi: address type.
- :param name: rule-set name
+ :param name: rule-set name.
+ :param filter: filter name.
+ :return: rule-set identifier.
+ """
+ identifier = {"name": None, "filter": None}
+ if afi:
+ identifier["afi"] = afi
+ else:
+ raise ValueError("afi must be provided")
+
+ if name:
+ identifier["name"] = name
+ return identifier
+ elif filter:
+ identifier["filter"] = filter
+ return identifier
+ if have:
+ if "name" in have and have["name"]:
+ identifier["name"] = have["name"]
+ return identifier
+ if "filter" in have and have["filter"]:
+ identifier["filter"] = have["filter"]
+ return identifier
+ # raise ValueError("name or filter must be provided or present in have")
+ # unless we want a wildcard
+ return identifier
+
+ def _add_rs_base_attrib(self, rs_id, attrib, rule, opr=True):
+ """
+
+ This function forms the command for 'rule-sets' attributes which don't
+ have further sub attributes.
+
+ :param rs_id: rule-set identifier.
:param attrib: attribute name
:param rule: rule config dictionary.
:param opr: True/False.
:return: generated command.
"""
command = self._compute_command(
- afi=afi,
- name=name,
+ rs_id=rs_id,
attrib=attrib,
value=rule[attrib],
opr=opr,
@@ -808,6 +1041,8 @@ class Firewall_rules(ConfigBase):
:param afi: address type
:return: rule-set type.
"""
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ return "ipv6" if afi == "ipv6" else "ipv4"
return "ipv6-name" if afi == "ipv6" else "name"
def _is_del(self, l_set, h, key="number"):
@@ -834,37 +1069,9 @@ class Firewall_rules(ConfigBase):
def _in_target(self, h, key):
"""
- This function checks whether the target nexist and key present in target config.
+ This function checks whether the target exists and key present in target config.
:param h: target config.
:param key: attribute name.
:return: True/False.
"""
return True if h and key in h else False
-
- def _is_base_attrib(self, key):
- """
- This function checks whether key is present in predefined
- based attribute set.
- :param key:
- :return: True/False.
- """
- r_set = (
- "p2p",
- "ipsec",
- "log",
- "action",
- "fragment",
- "protocol",
- "disable",
- "description",
- "mac_address",
- "default_action",
- "enable_default_log",
- )
- return True if key in r_set else False
-
- def _get_os_version(self):
- os_version = "1.2"
- if self._connection:
- os_version = self._connection.get_device_info()["network_os_version"]
- return os_version
diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
index 731014cd..62e4f922 100644
--- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py
@@ -275,7 +275,7 @@ class Interfaces(ConfigBase):
commands.append(
self._compute_commands(key=key, interface=want_copy["name"], remove=True),
)
- if have_copy["enabled"] is False:
+ if have_copy["enabled"] is False and not ('enabled' in want_copy and want_copy["enabled"] is False):
commands.append(
self._compute_commands(key="enabled", value=True, interface=want_copy["name"]),
)
diff --git a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py
index a7652a6b..51b47494 100644
--- a/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py
+++ b/plugins/module_utils/network/vyos/config/ospf_interfaces/ospf_interfaces.py
@@ -27,9 +27,17 @@ from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.u
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import Facts
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import (
- Ospf_interfacesTemplate,
+ Ospf_interfacesTemplate
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces_14 import (
+ Ospf_interfacesTemplate14
+)
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion
+
class Ospf_interfaces(ResourceModule):
"""
@@ -61,12 +69,30 @@ class Ospf_interfaces(ResourceModule):
"passive",
]
+ def _validate_template(self):
+ version = get_os_version(self._module)
+ if LooseVersion(version) >= LooseVersion("1.4"):
+ self._tmplt = Ospf_interfacesTemplate14()
+ else:
+ self._tmplt = Ospf_interfacesTemplate()
+
+ def parse(self):
+ """ override parse to check template """
+ self._validate_template()
+ return super().parse()
+
+ def get_parser(self, name):
+ """get_parsers"""
+ self._validate_template()
+ return super().get_parser(name)
+
def execute_module(self):
"""Execute the module
:rtype: A dictionary
:returns: The result from module execution
"""
+ self._validate_template()
if self.state not in ["parsed", "gathered"]:
self.generate_commands()
self.run_commands()
diff --git a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
index 5b472222..3f4da3ea 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_global/firewall_global.py
@@ -174,9 +174,8 @@ class Firewall_globalFacts(object):
:return: generated rule list configuration.
"""
sp_lst = []
- attrib = "state-policy"
- policies = findall(r"^set firewall " + attrib + " (\\S+)", conf, M)
-
+ policies = findall(r"^set firewall (?:global-options )state-policy (\S+)", conf, M)
+ policies = list(set(policies)) # remove redundancies
if policies:
rules_lst = []
for sp in set(policies):
@@ -197,7 +196,7 @@ class Firewall_globalFacts(object):
:param attrib: connection type.
:return: generated rule configuration dictionary.
"""
- a_lst = ["action", "log"]
+ a_lst = ["action", "log", "log_level"]
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
@@ -304,16 +303,15 @@ class Firewall_globalFacts(object):
regex = match + " " + regex
if conf:
if self.is_bool(attrib):
- attr = self.map_regex(attrib, type)
- out = conf.find(attr.replace("_", "-"))
- dis = conf.find(attr.replace("_", "-") + " 'disable'")
- if out >= 1:
- if dis >= 1:
+ # fancy regex to make sure we don't get a substring
+ out = search(r"^.*" + regex + r"( 'disable')?(?=\s|$)", conf, M)
+ if out:
+ if out.group(1):
config[attrib] = False
else:
config[attrib] = True
else:
- out = search(r"^.*" + regex + " (.+)", conf, M)
+ out = search(r"^.*" + regex + r" (.+)", conf, M)
if out:
val = out.group(1).strip("'")
if self.is_num(attrib):
diff --git a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
index ead038a5..1fc70255 100644
--- a/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
+++ b/plugins/module_utils/network/vyos/facts/firewall_rules/firewall_rules.py
@@ -14,8 +14,6 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
-import re
-
from copy import deepcopy
from re import M, findall, search
@@ -61,15 +59,31 @@ class Firewall_rulesFacts(object):
data = self.get_device_data(connection)
# split the config into instances of the resource
objs = []
- v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M)
- v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M)
+ # check 1.4+ first
+ new_rules = True
+ v6_rules = findall(r"^set firewall ipv6 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M)
+ if not v6_rules:
+ v6_rules = findall(r"^set firewall ipv6-name (?:\'*)(\S+)(?:\'*)", data, M)
+ if v6_rules:
+ new_rules = False
+ v4_rules = findall(r"^set firewall ipv4 (name|forward|input|output) (?:\'*)(\S+)(?:\'*)", data, M)
+ if not v4_rules:
+ v4_rules = findall(r"^set firewall name (?:\'*)(\S+)(?:\'*)", data, M)
+ if v4_rules:
+ new_rules = False
if v6_rules:
- config = self.get_rules(data, v6_rules, type="ipv6")
+ if new_rules:
+ config = self.get_rules_post_1_4(data, v6_rules, type="ipv6")
+ else:
+ config = self.get_rules(data, v6_rules, type="ipv6")
if config:
config = utils.remove_empties(config)
objs.append(config)
if v4_rules:
- config = self.get_rules(data, v4_rules, type="ipv4")
+ if new_rules:
+ config = self.get_rules_post_1_4(data, v4_rules, type="ipv4")
+ else:
+ config = self.get_rules(data, v4_rules, type="ipv4")
if config:
config = utils.remove_empties(config)
objs.append(config)
@@ -113,6 +127,39 @@ class Firewall_rulesFacts(object):
config = {"afi": "ipv6", "rule_sets": r_v6}
return config
+ def get_rules_post_1_4(self, data, rules, type):
+ """
+ This function performs following:
+ - Form regex to fetch 'rule-sets' specific config from data.
+ - Form the rule-set list based on ip address.
+ Specifically for v1.4+ version.
+ :param data: configuration.
+ :param rules: list of rule-sets.
+ :param type: ip address type.
+ :return: generated rule-sets configuration.
+ """
+ r_v4 = []
+ r_v6 = []
+ for kind, name in set(rules):
+ rule_regex = r" %s %s %s .+$" % (type, kind, name.strip("'"))
+ cfg = findall(rule_regex, data, M)
+ fr = self.render_config(cfg, name.strip("'"))
+ if kind == "name":
+ fr["name"] = name.strip("'")
+ elif kind in ("forward", "input", "output"):
+ fr["filter"] = kind
+ else:
+ raise ValueError("Unknown rule kind: %s %s" % kind, name)
+ if type == "ipv6":
+ r_v6.append(fr)
+ else:
+ r_v4.append(fr)
+ if r_v4:
+ config = {"afi": "ipv4", "rule_sets": r_v4}
+ if r_v6:
+ config = {"afi": "ipv6", "rule_sets": r_v6}
+ return config
+
def render_config(self, conf, match):
"""
Render config as dictionary structure and delete keys
@@ -124,10 +171,12 @@ class Firewall_rulesFacts(object):
:returns: The generated config
"""
conf = "\n".join(filter(lambda x: x, conf))
- a_lst = ["description", "default_action", "enable_default_log"]
+ a_lst = ["description", "default_action", "default_jump_target", "enable_default_log", "default_log"]
config = self.parse_attr(conf, a_lst, match)
if not config:
config = {}
+ if 'default_log' in config:
+ config['enable_default_log'] = config.pop('default_log')
config["rules"] = self.parse_rules_lst(conf)
return config
@@ -169,11 +218,14 @@ class Firewall_rulesFacts(object):
"disable",
"description",
"icmp",
+ "jump_target",
+ "queue",
+ "queue_options",
]
rule = self.parse_attr(conf, a_lst)
r_sub = {
"p2p": self.parse_p2p(conf),
- "tcp": self.parse_tcp(conf, "tcp"),
+ "tcp": self.parse_tcp(conf),
"icmp": self.parse_icmp(conf, "icmp"),
"time": self.parse_time(conf, "time"),
"limit": self.parse_limit(conf, "limit"),
@@ -181,10 +233,42 @@ class Firewall_rulesFacts(object):
"recent": self.parse_recent(conf, "recent"),
"source": self.parse_src_or_dest(conf, "source"),
"destination": self.parse_src_or_dest(conf, "destination"),
+ "inbound_interface": self.parse_interface(conf, "inbound-interface"),
+ "outbound_interface": self.parse_interface(conf, "outbound-interface"),
+ "packet_length": self.parse_packet_length(conf, "packet-length"),
+ "packet_length_exclude": self.parse_packet_length(conf, "packet-length-exclude"),
}
rule.update(r_sub)
return rule
+ def parse_interface(self, conf, attrib):
+ """
+ This function triggers the parsing of 'interface' attributes.
+ :param conf: configuration.
+ :param attrib: 'interface'.
+ :return: generated config dictionary.
+ """
+ a_lst = ["name", "group"]
+ cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
+ return cfg_dict
+
+ def parse_packet_length(self, conf, attrib=None):
+ """
+ This function triggers the parsing of 'packet-length' attributes.
+ :param conf: configuration.
+ :param attrib: 'packet-length'.
+ :return: generated config dictionary.
+ """
+ lengths = []
+ rule_regex = r"%s (\d+)" % attrib
+ found_lengths = findall(rule_regex, conf, M)
+ if found_lengths:
+ lengths = []
+ for l in set(found_lengths):
+ obj = {"length": l.strip("'")}
+ lengths.append(obj)
+ return lengths
+
def parse_p2p(self, conf):
"""
This function forms the regex to fetch the 'p2p' with in
@@ -226,15 +310,41 @@ class Firewall_rulesFacts(object):
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
- def parse_tcp(self, conf, attrib=None):
+ def parse_tcp(self, conf):
"""
This function triggers the parsing of 'tcp' attributes.
:param conf: configuration.
:param attrib: 'tcp'.
:return: generated config dictionary.
"""
- cfg_dict = self.parse_attr(conf, ["flags"], match=attrib)
- return cfg_dict
+ f_lst = []
+ flags = findall(r"tcp flags (not )?(?:\'*)([\w!,]+)(?:\'*)", conf, M)
+ # for pre 1.4, this is a string including possible commas
+ # and ! as an inverter. For 1.4+ this is a single flag per
+ # command and 'not' as the inverter
+ if flags:
+ flag_lst = []
+ for n, f in set(flags):
+ f = f.strip("'").lower()
+ if "," in f:
+ # pre 1.4 version with multiple flags
+ fs = f.split(",")
+ for f in fs:
+ if "!" in f:
+ obj = {"flag": f.strip("'!"), "invert": True}
+ else:
+ obj = {"flag": f.strip("'")}
+ flag_lst.append(obj)
+ elif "!" in f:
+ obj = {"flag": f.strip("'!"), "invert": True}
+ flag_lst.append(obj)
+ else:
+ obj = {"flag": f.strip("'")}
+ if n:
+ obj["invert"] = True
+ flag_lst.append(obj)
+ f_lst = sorted(flag_lst, key=lambda i: i["flag"])
+ return {"flags": f_lst}
def parse_time(self, conf, attrib=None):
"""
@@ -276,6 +386,44 @@ class Firewall_rulesFacts(object):
cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
return cfg_dict
+ def parse_icmp_attr(self, conf, match):
+ """
+ This function peforms the following:
+ - parse ICMP arguemnts for firewall rules
+ - consider that older versions may need numbers or letters
+ in type, newer ones are more specific
+ :param conf: configuration.
+ :param match: parent node/attribute name.
+ :return: generated config dictionary.
+ """
+ config = {}
+ if not conf:
+ return config
+
+ for attrib in ("code", "type", "type-name"):
+ regex = self.map_regex(attrib)
+ if match:
+ regex = match + " " + regex
+ out = search(r"^.*" + regex + " (.+)", conf, M)
+ if out:
+ val = out.group(1).strip("'")
+ if attrib == 'type-name':
+ config['type_name'] = val
+ if attrib == 'code':
+ config['code'] = int(val)
+ if attrib == 'type':
+ # <1.3 could be # (type), #/# (type/code) or 'type' (type_name)
+ # recent this is only for strings
+ if "/" in val: # type/code
+ (type_no, code) = val.split(".")
+ config['type'] = type_no
+ config['code'] = code
+ elif val.isnumeric():
+ config['type'] = type_no
+ else:
+ config['type_name'] = val
+ return config
+
def parse_icmp(self, conf, attrib=None):
"""
This function triggers the parsing of 'icmp' attributes.
@@ -283,11 +431,9 @@ class Firewall_rulesFacts(object):
:param attrib: 'icmp'.
:return: generated config dictionary.
"""
- a_lst = ["code", "type", "type_name"]
- if attrib == "icmp":
- attrib = "icmpv6"
- conf = re.sub("icmpv6 type", "icmpv6 type-name", conf)
- cfg_dict = self.parse_attr(conf, a_lst, match=attrib)
+ cfg_dict = self.parse_icmp_attr(conf, "icmp")
+ if (len(cfg_dict) == 0):
+ cfg_dict = self.parse_icmp_attr(conf, "icmpv6")
return cfg_dict
def parse_limit(self, conf, attrib=None):
@@ -330,7 +476,6 @@ class Firewall_rulesFacts(object):
if conf:
if self.is_bool(attrib):
out = conf.find(attrib.replace("_", "-"))
-
dis = conf.find(attrib.replace("_", "-") + " 'disable'")
if out >= 1:
if dis >= 1:
@@ -375,6 +520,7 @@ class Firewall_rulesFacts(object):
"disabled",
"established",
"enable_default_log",
+ "default_log",
)
return True if attrib in bool_set else False
diff --git a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
index 995be911..cd8008c6 100644
--- a/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/interfaces/interfaces.py
@@ -17,7 +17,7 @@ __metaclass__ = type
from copy import deepcopy
-from re import M, findall
+from re import M, findall, search
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common import utils
@@ -66,7 +66,7 @@ class InterfacesFacts(object):
)
if interface_names:
for interface in set(interface_names):
- intf_regex = r" %s .+$" % interface.strip("'")
+ intf_regex = r" %s (.+$)" % interface.strip("'")
cfg = findall(intf_regex, data, M)
obj = self.render_config(cfg)
obj["name"] = interface.strip("'")
@@ -106,7 +106,7 @@ class InterfacesFacts(object):
if vif_names:
vifs_list = []
for vif in set(vif_names):
- vif_regex = r" %s .+$" % vif
+ vif_regex = r"%s (.+$)" % vif
cfg = "\n".join(findall(vif_regex, conf, M))
obj = self.parse_attribs(["description", "mtu"], cfg)
obj["vlan_id"] = int(vif)
@@ -117,6 +117,14 @@ class InterfacesFacts(object):
return vifs_list
def parse_attribs(self, attribs, conf):
+ """
+ Parse the attributes of a network interface.
+
+ :param attribs: List of attribute names.
+ :param conf: Configuration string.
+ :rtype: dict
+ :returns: Parsed configuration dictionary.
+ """
config = {}
for item in attribs:
value = utils.parse_conf_arg(conf, item)
@@ -126,7 +134,11 @@ class InterfacesFacts(object):
config[item] = value.strip("'")
else:
config[item] = None
- if "disable" in conf:
+
+ # match only on disable next to the interface name
+ # there are other sub-commands that can be disabled
+ match = search(r"^ *disable", conf, M)
+ if match:
config["enabled"] = False
else:
config["enabled"] = True
diff --git a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
index be467a0a..7d4d1a08 100644
--- a/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/l3_interfaces/l3_interfaces.py
@@ -62,7 +62,7 @@ class L3_interfacesFacts(object):
# operate on a collection of resource x
objs = []
interface_names = re.findall(
- r"set interfaces (?:ethernet|bonding|bridge|dummy|tunnel|vti|vxlan) (?:\'*)(\S+)(?:\'*)",
+ r"set interfaces (?:ethernet|bonding|bridge|dummy|tunnel|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)",
data,
re.M,
)
diff --git a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py
index af6c5770..c0e74895 100644
--- a/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py
+++ b/plugins/module_utils/network/vyos/facts/ospf_interfaces/ospf_interfaces.py
@@ -23,9 +23,17 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.osp
Ospf_interfacesArgs,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces import (
- Ospf_interfacesTemplate,
+ Ospf_interfacesTemplate
)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.rm_templates.ospf_interfaces_14 import (
+ Ospf_interfacesTemplate14
+)
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_os_version
+
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.version import LooseVersion
+
class Ospf_interfacesFacts(object):
"""The vyos ospf_interfaces facts class"""
@@ -35,9 +43,30 @@ class Ospf_interfacesFacts(object):
self.argument_spec = Ospf_interfacesArgs.argument_spec
def get_device_data(self, connection):
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ # use set protocols ospf in order to get both ospf and ospfv3
+ return connection.get("show configuration commands | match 'set protocols ospf'")
return connection.get('show configuration commands | match "set interfaces"')
- def get_config_set(self, data):
+ def get_config_set_1_4(self, data):
+ """To classify the configurations beased on interface"""
+ interface_list = []
+ config_set = []
+ int_string = ""
+ for config_line in data.splitlines():
+ ospf_int = re.search(r"set protocols (?:ospf|ospfv3) interface (\S+) .*", config_line)
+ if ospf_int:
+ if ospf_int.group(1) not in interface_list:
+ if int_string:
+ config_set.append(int_string)
+ interface_list.append(ospf_int.group(1))
+ int_string = ""
+ int_string = int_string + config_line + "\n"
+ if int_string:
+ config_set.append(int_string)
+ return config_set
+
+ def get_config_set_1_2(self, data):
"""To classify the configurations beased on interface"""
interface_list = []
config_set = []
@@ -55,6 +84,12 @@ class Ospf_interfacesFacts(object):
config_set.append(int_string)
return config_set
+ def get_config_set(self, data, connection):
+ """To classify the configurations beased on interface"""
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ return self.get_config_set_1_4(data)
+ return self.get_config_set_1_2(data)
+
def populate_facts(self, connection, ansible_facts, data=None):
"""Populate the facts for Ospf_interfaces network resource
@@ -67,16 +102,20 @@ class Ospf_interfacesFacts(object):
"""
facts = {}
objs = []
- ospf_interfaces_parser = Ospf_interfacesTemplate(lines=[], module=self._module)
+ if LooseVersion(get_os_version(self._module)) >= LooseVersion("1.4"):
+ ospf_interface_class = Ospf_interfacesTemplate14
+ else:
+ ospf_interface_class = Ospf_interfacesTemplate
+ ospf_interfaces_parser = ospf_interface_class(lines=[], module=self._module)
if not data:
data = self.get_device_data(connection)
# parse native config using the Ospf_interfaces template
ospf_interfaces_facts = []
- resources = self.get_config_set(data)
+ resources = self.get_config_set(data, connection)
for resource in resources:
- ospf_interfaces_parser = Ospf_interfacesTemplate(
+ ospf_interfaces_parser = ospf_interface_class(
lines=resource.split("\n"),
module=self._module,
)
@@ -93,7 +132,7 @@ class Ospf_interfacesFacts(object):
self.argument_spec,
{"config": ospf_interfaces_facts},
redact=True,
- ),
+ )
)
if params.get("config"):
for cfg in params["config"]:
diff --git a/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py
new file mode 100644
index 00000000..7f49d47a
--- /dev/null
+++ b/plugins/module_utils/network/vyos/rm_templates/ospf_interfaces_14.py
@@ -0,0 +1,650 @@
+# -*- coding: utf-8 -*-
+# Copyright 2020 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 Ospf_interfaces 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 _get_parameters(data):
+ if data["afi"] == "ipv6":
+ val = ["ospfv3", "ipv6"]
+ else:
+ val = ["ospf", "ip"]
+ return val
+
+
+def _tmplt_ospf_int_delete(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols " + params[0] + " interface {name}".format(**config_data)
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_cost(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " cost {cost}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_auth_password(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " authentication plaintext-password {plaintext_password}".format(
+ **config_data["address_family"]["authentication"]
+ )
+ )
+ return command
+
+
+def _tmplt_ospf_int_auth_md5(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " authentication md5 key-id {key_id} ".format(
+ **config_data["address_family"]["authentication"]["md5_key"]
+ )
+ + "md5-key {key}".format(**config_data["address_family"]["authentication"]["md5_key"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_auth_md5_delete(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " authentication"
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_bw(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " bandwidth {bandwidth}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_hello_interval(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " hello-interval {hello_interval}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_dead_interval(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " dead-interval {dead_interval}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_mtu_ignore(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " mtu-ignore"
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_network(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " network {network}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_priority(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " priority {priority}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_retransmit_interval(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " retransmit-interval {retransmit_interval}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_transmit_delay(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " transmit-delay {transmit_delay}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_ifmtu(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " ifmtu {ifmtu}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_instance(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " instance-id {instance}".format(**config_data["address_family"])
+ )
+
+ return command
+
+
+def _tmplt_ospf_int_passive(config_data):
+ params = _get_parameters(config_data["address_family"])
+ command = (
+ "protocols "
+ + params[0]
+ + " interface {name}".format(**config_data)
+ + " passive"
+ )
+
+ return command
+
+
+class Ospf_interfacesTemplate14(NetworkTemplate):
+ def __init__(self, lines=None, module=None):
+ prefix = {"set": "set", "remove": "delete"}
+ super(Ospf_interfacesTemplate14, self).__init__(
+ lines=lines, tmplt=self, prefix=prefix, module=module
+ )
+
+ # fmt: off
+ PARSERS = [
+ {
+ "name": "ip_ospf",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "remval": _tmplt_ospf_int_delete,
+ "compval": "address_family",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ }
+ }
+ }
+ },
+ {
+ "name": "authentication_password",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+authentication
+ \s+plaintext-password
+ \s+(?P<text>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_auth_password,
+ "compval": "address_family.authentication",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "authentication": {
+ "plaintext_password": "{{ text }}"
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "authentication_md5",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+authentication
+ \s+md5
+ \s+key-id
+ \s+(?P<id>\d+)
+ \s+md5-key
+ \s+(?P<text>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_auth_md5,
+ "remval": _tmplt_ospf_int_auth_md5_delete,
+ "compval": "address_family.authentication",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "authentication": {
+ "md5_key": {
+ "key_id": "{{ id }}",
+ "key": "{{ text }}"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "name": "bandwidth",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+bandwidth
+ \s+(?P<bw>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_bw,
+ "compval": "address_family.bandwidth",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "bandwidth": "{{ bw }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "cost",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+cost
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_cost,
+ "compval": "address_family.cost",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "cost": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "hello_interval",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+hello-interval
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_hello_interval,
+ "compval": "address_family.hello_interval",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "hello_interval": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "dead_interval",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+dead-interval
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_dead_interval,
+ "compval": "address_family.dead_interval",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "dead_interval": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "mtu_ignore",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+(?P<mtu>\'mtu-ignore\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_mtu_ignore,
+ "compval": "address_family.mtu_ignore",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "mtu_ignore": "{{ True if mtu is defined }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "network",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+network
+ \s+(?P<val>\S+)
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_network,
+ "compval": "address_family.network",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "network": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "priority",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+priority
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_priority,
+ "compval": "address_family.priority",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "priority": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "retransmit_interval",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+retransmit-interval
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_retransmit_interval,
+ "compval": "address_family.retransmit_interval",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "retransmit_interval": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "transmit_delay",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+transmit-delay
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_transmit_delay,
+ "compval": "address_family.transmit_delay",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "transmit_delay": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "ifmtu",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+ifmtu
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_ifmtu,
+ "compval": "address_family.ifmtu",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "ifmtu": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "instance",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+instance-id
+ \s+(?P<val>\'\d+\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_instance,
+ "compval": "address_family.instance",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "instance": "{{ val }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "passive",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ \s+(?P<pass>\'passive\')
+ *$""",
+ re.VERBOSE,
+ ),
+ "setval": _tmplt_ospf_int_passive,
+ "compval": "address_family.passive",
+ "result": {
+ "name": "{{ name }}",
+ "address_family": {
+ '{{ "ipv4" if proto == "ospf" else "ipv6" }}': {
+ "afi": '{{ "ipv4" if proto == "ospf" else "ipv6" }}',
+ "passive": "{{ True if pass is defined }}"
+ }
+ }
+ }
+ },
+ {
+ "name": "interface_name",
+ "getval": re.compile(
+ r"""
+ ^set
+ \s+protocols
+ \s+(?P<proto>ospf|ospfv3)
+ \s+interface
+ \s+(?P<name>\S+)
+ .*$""",
+ re.VERBOSE,
+ ),
+ "setval": "set protocols {{ proto }} interface {{ name }}",
+ "result": {
+ "name": "{{ name }}",
+ }
+ },
+ ]
+ # fmt: on
diff --git a/plugins/module_utils/network/vyos/utils/version.py b/plugins/module_utils/network/vyos/utils/version.py
new file mode 100644
index 00000000..cc3028c3
--- /dev/null
+++ b/plugins/module_utils/network/vyos/utils/version.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2021, Felix Fontein <felix@fontein.de>
+# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
+# SPDX-License-Identifier: GPL-3.0-or-later
+
+"""Provide version object to compare version numbers."""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+from ansible.module_utils.compat.version import LooseVersion # pylint: disable=unused-import
diff --git a/plugins/module_utils/network/vyos/vyos.py b/plugins/module_utils/network/vyos/vyos.py
index 4fcb3317..1430b1b1 100644
--- a/plugins/module_utils/network/vyos/vyos.py
+++ b/plugins/module_utils/network/vyos/vyos.py
@@ -34,7 +34,6 @@ import json
from ansible.module_utils._text import to_text
from ansible.module_utils.connection import Connection, ConnectionError
-
_DEVICE_CONFIGS = {}
@@ -100,3 +99,10 @@ def load_config(module, commands, commit=False, comment=None):
module.fail_json(msg=to_text(exc, errors="surrogate_then_replace"))
return response.get("diff")
+
+
+def get_os_version(module):
+ connection = get_connection(module)
+ if connection.get_device_info():
+ os_version = connection.get_device_info()["network_os_major_version"]
+ return os_version
diff --git a/plugins/modules/vyos_firewall_global.py b/plugins/modules/vyos_firewall_global.py
index 205ef136..befe5e73 100644
--- a/plugins/modules/vyos_firewall_global.py
+++ b/plugins/modules/vyos_firewall_global.py
@@ -253,6 +253,19 @@ options:
description:
- Enable logging of packets part of an established connection.
type: bool
+ log_level:
+ description:
+ - Only available in 1.4+
+ type: str
+ choices:
+ - emerg
+ - alert
+ - crit
+ - err
+ - warn
+ - notice
+ - info
+ - debug
running_config:
description:
- The module, by default, will connect to the remote device and retrieve the current
diff --git a/plugins/modules/vyos_firewall_rules.py b/plugins/modules/vyos_firewall_rules.py
index 06a300f5..fd2e7d55 100644
--- a/plugins/modules/vyos_firewall_rules.py
+++ b/plugins/modules/vyos_firewall_rules.py
@@ -31,6 +31,11 @@ from __future__ import absolute_import, division, print_function
__metaclass__ = type
+ANSIBLE_METADATA = {
+ 'metadata_version': '1.1',
+ 'status': ['preview'],
+ 'supported_by': 'network'
+}
DOCUMENTATION = """
module: vyos_firewall_rules
@@ -62,9 +67,16 @@ options:
type: list
elements: dict
suboptions:
+ filter:
+ description:
+ - Filter type (exclusive to "name").
+ - Supported in 1.4 and later.
+ type: str
+ choices: ['input', 'output', 'forward']
name:
description:
- Firewall rule set name.
+ - Required for 1.3- and optional for 1.4+.
type: str
default_action:
description:
@@ -72,11 +84,15 @@ options:
- drop (Drop if no prior rules are hit (default))
- reject (Drop and notify source if no prior rules are hit)
- accept (Accept if no prior rules are hit)
+ - jump (Jump to another rule-set, 1.4+)
+ type: str
+ choices: ['drop', 'reject', 'accept', 'jump']
+ default_jump_target:
+ description:
+ - Default jump target if the default action is jump.
+ - Only valid in 1.4 and later.
+ - Only valid when default_action = jump.
type: str
- choices:
- - drop
- - reject
- - accept
description:
description:
- Rule set description.
@@ -103,12 +119,19 @@ options:
action:
description:
- Specifying the action.
+ - inspect is available < 1.4
+ - continue, return, jump, queue, synproxy are available >= 1.4
type: str
choices:
- drop
- reject
- accept
- inspect
+ - continue
+ - return
+ - jump
+ - queue
+ - synproxy
destination:
description:
- Specifying the destination parameters.
@@ -148,6 +171,7 @@ options:
disable:
description:
- Option to disable firewall rule.
+ - aliased to disabled
type: bool
aliases: ["disabled"]
fragment:
@@ -215,6 +239,21 @@ options:
description:
- ICMP type.
type: int
+ inbound_interface:
+ description:
+ - Inbound interface.
+ - Only valid in 1.4 and later.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - Interface name.
+ - Can have wildcards
+ type: str
+ group:
+ description:
+ - Interface group.
+ type: str
ipsec:
description:
- Inbound ip sec packets.
@@ -222,13 +261,16 @@ options:
choices:
- match-ipsec
- match-none
- log:
+ - match-ipsec-in
+ - match-ipsec-out
+ - match-none-in
+ - match-none-out
+ jump_target:
description:
- - Option to log packets matching rule
+ - Jump target if the action is jump.
+ - Only valid in 1.4 and later.
+ - Only valid when action = jump.
type: str
- choices:
- - disable
- - enable
limit:
description:
- Rate limit using a token bucket filter.
@@ -255,6 +297,55 @@ options:
description:
- This is the time unit.
type: str
+ log:
+ description:
+ - Log matching packets.
+ type: str
+ choices: ['disable', 'enable']
+ outbound_interface:
+ description:
+ - Match outbound interface.
+ - Only valid in 1.4 and later.
+ type: dict
+ suboptions:
+ name:
+ description:
+ - Interface name.
+ - Can have wildcards
+ type: str
+ group:
+ description:
+ - Interface group.
+ type: str
+ packet_length:
+ description:
+ - Packet length match.
+ - Only valid in 1.4 and later.
+ - Multiple values from 1 to 65535 and ranges are supported
+ type: list
+ elements: dict
+ suboptions:
+ length:
+ description:
+ - Packet length or range.
+ type: str
+ packet_length_exclude:
+ description:
+ - Packet length match.
+ - Only valid in 1.4 and later.
+ - Multiple values from 1 to 65535 and ranges are supported
+ type: list
+ elements: dict
+ suboptions:
+ length:
+ description:
+ - Packet length or range.
+ type: str
+ packet_type:
+ description:
+ - Packet type match.
+ type: str
+ choices: ['broadcast', 'multicast', 'host', 'other']
p2p:
description:
- P2P application packets.
@@ -283,6 +374,20 @@ options:
- all All IP protocols.
- (!)All IP protocols except for the specified name or number.
type: str
+ queue:
+ description:
+ - Queue options.
+ - Only valid in 1.4 and later.
+ - Only valid when action = queue.
+ - Can be a queue number or range.
+ type: str
+ queue_options:
+ description:
+ - Queue options.
+ - Only valid in 1.4 and later.
+ - Only valid when action = queue.
+ type: str
+ choices: ['bypass', 'fanout']
recent:
description:
- Parameters for matching recently seen sources.
@@ -295,7 +400,8 @@ options:
time:
description:
- Source addresses seen in the last N seconds.
- type: int
+ - Since 1.4, this is a string of second/minute/hour
+ type: str
source:
description:
- Source parameters.
@@ -337,6 +443,12 @@ options:
- <MAC address> MAC address to match.
- <!MAC address> Match everything except the specified MAC address.
type: str
+ fqdn:
+ description:
+ - Fully qualified domain name.
+ - Available in 1.4 and later.
+ type: str
+
state:
description:
- Session state.
@@ -358,6 +470,21 @@ options:
description:
- Related state.
type: bool
+ synproxy:
+ description:
+ - SYN proxy options.
+ - Only valid in 1.4 and later.
+ - Only valid when action = synproxy.
+ type: dict
+ suboptions:
+ mss:
+ description:
+ - Adjust MSS (501-65535)
+ type: int
+ window_scale:
+ description:
+ - Window scale (1-14).
+ type: int
tcp:
description:
- TCP flags to match.
@@ -365,8 +492,22 @@ options:
suboptions:
flags:
description:
- - TCP flags to be matched.
- type: str
+ - list of tcp flags to be matched
+ - 5.0 breaking change to support 1.4+ and 1.3-
+ type: list
+ elements: dict
+ suboptions:
+ flag:
+ description:
+ - TCP flag to be matched.
+ - syn, ack, fin, rst, urg, psh, all (1.3-)
+ - syn, ack, fin, rst, urg, psh, cwr, ecn (1.4+)
+ type: str
+ choices: ['ack', 'cwr', 'ecn', 'fin', 'psh', 'rst', 'syn', 'urg', 'all']
+ invert:
+ description:
+ - Invert the match.
+ type: bool
time:
description:
- Time to match rule.
@@ -1460,14 +1601,14 @@ RETURN = """
before:
description: The configuration prior to the model invocation.
returned: always
- type: list
+ type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
after:
description: The resulting configuration model invocation.
returned: when changed
- type: list
+ type: dict
sample: >
The configuration returned will always be in the same format
of the parameters above.
@@ -1486,17 +1627,14 @@ commands:
from ansible.module_utils.basic import AnsibleModule
-from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import (
- Firewall_rulesArgs,
-)
-from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import (
- Firewall_rules,
-)
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.firewall_rules.firewall_rules import Firewall_rulesArgs
+from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.firewall_rules.firewall_rules import Firewall_rules
def main():
"""
Main entry point for module execution
+
:returns: the result form module invocation
"""
required_if = [
@@ -1518,5 +1656,5 @@ def main():
module.exit_json(**result)
-if __name__ == "__main__":
+if __name__ == '__main__':
main()
diff --git a/plugins/modules/vyos_ospf_interfaces.py b/plugins/modules/vyos_ospf_interfaces.py
index c2326895..33290581 100644
--- a/plugins/modules/vyos_ospf_interfaces.py
+++ b/plugins/modules/vyos_ospf_interfaces.py
@@ -901,7 +901,7 @@ def main():
argument_spec=Ospf_interfacesArgs.argument_spec,
mutually_exclusive=[],
required_if=[],
- supports_check_mode=False,
+ supports_check_mode=True,
)
result = Ospf_interfaces(module).execute_module()