diff --git a/plugins/cliconf/vyos.py b/plugins/cliconf/vyos.py
index 3033603..de9e93d 100644
--- a/plugins/cliconf/vyos.py
+++ b/plugins/cliconf/vyos.py
@@ -1,342 +1,341 @@
#
# (c) 2017 Red Hat Inc.
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
----
author: Ansible Networking Team
cliconf: vyos
short_description: Use vyos cliconf to run command on VyOS platform
description:
- - This vyos plugin provides low level abstraction apis for
- sending and receiving CLI commands from VyOS network devices.
-version_added: "2.4"
+- This vyos plugin provides low level abstraction apis for sending and receiving CLI
+ commands from VyOS network devices.
+version_added: 1.0.0
"""
import re
import json
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text
from ansible.module_utils.common._collections_compat import Mapping
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,
)
from ansible.plugins.cliconf import CliconfBase
class Cliconf(CliconfBase):
def get_device_info(self):
device_info = {}
device_info["network_os"] = "vyos"
reply = self.get("show version")
data = to_text(reply, errors="surrogate_or_strict").strip()
match = re.search(r"Version:\s*(.*)", data)
if match:
device_info["network_os_version"] = match.group(1)
match = re.search(r"HW model:\s*(\S+)", data)
if match:
device_info["network_os_model"] = match.group(1)
reply = self.get("show host name")
device_info["network_os_hostname"] = to_text(
reply, errors="surrogate_or_strict"
).strip()
return device_info
def get_config(self, flags=None, format=None):
if format:
option_values = self.get_option_values()
if format not in option_values["format"]:
raise ValueError(
"'format' value %s is invalid. Valid values of format are %s"
% (format, ", ".join(option_values["format"]))
)
if not flags:
flags = []
if format == "text":
command = "show configuration"
else:
command = "show configuration commands"
command += " ".join(to_list(flags))
command = command.strip()
out = self.send_command(command)
return out
def edit_config(
self, candidate=None, commit=True, replace=None, comment=None
):
resp = {}
operations = self.get_device_operations()
self.check_edit_config_capability(
operations, candidate, commit, replace, comment
)
results = []
requests = []
self.send_command("configure")
for cmd in to_list(candidate):
if not isinstance(cmd, Mapping):
cmd = {"command": cmd}
results.append(self.send_command(**cmd))
requests.append(cmd["command"])
out = self.get("compare")
out = to_text(out, errors="surrogate_or_strict")
diff_config = out if not out.startswith("No changes") else None
if diff_config:
if commit:
try:
self.commit(comment)
except AnsibleConnectionFailure as e:
msg = "commit failed: %s" % e.message
self.discard_changes()
raise AnsibleConnectionFailure(msg)
else:
self.send_command("exit")
else:
self.discard_changes()
else:
self.send_command("exit")
if (
to_text(
self._connection.get_prompt(), errors="surrogate_or_strict"
)
.strip()
.endswith("#")
):
self.discard_changes()
if diff_config:
resp["diff"] = diff_config
resp["response"] = results
resp["request"] = requests
return resp
def get(
self,
command=None,
prompt=None,
answer=None,
sendonly=False,
output=None,
newline=True,
check_all=False,
):
if not command:
raise ValueError("must provide value of command to execute")
if output:
raise ValueError(
"'output' value %s is not supported for get" % output
)
return self.send_command(
command=command,
prompt=prompt,
answer=answer,
sendonly=sendonly,
newline=newline,
check_all=check_all,
)
def commit(self, comment=None):
if comment:
command = 'commit comment "{0}"'.format(comment)
else:
command = "commit"
self.send_command(command)
def discard_changes(self):
self.send_command("exit discard")
def get_diff(
self,
candidate=None,
running=None,
diff_match="line",
diff_ignore_lines=None,
path=None,
diff_replace=None,
):
diff = {}
device_operations = self.get_device_operations()
option_values = self.get_option_values()
if candidate is None and device_operations["supports_generate_diff"]:
raise ValueError(
"candidate configuration is required to generate diff"
)
if diff_match not in option_values["diff_match"]:
raise ValueError(
"'match' value %s in invalid, valid values are %s"
% (diff_match, ", ".join(option_values["diff_match"]))
)
if diff_replace:
raise ValueError("'replace' in diff is not supported")
if diff_ignore_lines:
raise ValueError("'diff_ignore_lines' in diff is not supported")
if path:
raise ValueError("'path' in diff is not supported")
set_format = candidate.startswith("set") or candidate.startswith(
"delete"
)
candidate_obj = NetworkConfig(indent=4, contents=candidate)
if not set_format:
config = [c.line for c in candidate_obj.items]
commands = list()
# this filters out less specific lines
for item in config:
for index, entry in enumerate(commands):
if item.startswith(entry):
del commands[index]
break
commands.append(item)
candidate_commands = [
"set %s" % cmd.replace(" {", "") for cmd in commands
]
else:
candidate_commands = str(candidate).strip().split("\n")
if diff_match == "none":
diff["config_diff"] = list(candidate_commands)
return diff
running_commands = [
str(c).replace("'", "") for c in running.splitlines()
]
updates = list()
visited = set()
for line in candidate_commands:
item = str(line).replace("'", "")
if not item.startswith("set") and not item.startswith("delete"):
raise ValueError(
"line must start with either `set` or `delete`"
)
elif item.startswith("set") and item not in running_commands:
updates.append(line)
elif item.startswith("delete"):
if not running_commands:
updates.append(line)
else:
item = re.sub(r"delete", "set", item)
for entry in running_commands:
if entry.startswith(item) and line not in visited:
updates.append(line)
visited.add(line)
diff["config_diff"] = list(updates)
return diff
def run_commands(self, commands=None, check_rc=True):
if commands is None:
raise ValueError("'commands' value is required")
responses = list()
for cmd in to_list(commands):
if not isinstance(cmd, Mapping):
cmd = {"command": cmd}
output = cmd.pop("output", None)
if output:
raise ValueError(
"'output' value %s is not supported for run_commands"
% output
)
try:
out = self.send_command(**cmd)
except AnsibleConnectionFailure as e:
if check_rc:
raise
out = getattr(e, "err", e)
responses.append(out)
return responses
def get_device_operations(self):
return {
"supports_diff_replace": False,
"supports_commit": True,
"supports_rollback": False,
"supports_defaults": False,
"supports_onbox_diff": True,
"supports_commit_comment": True,
"supports_multiline_delimiter": False,
"supports_diff_match": True,
"supports_diff_ignore_lines": False,
"supports_generate_diff": False,
"supports_replace": False,
}
def get_option_values(self):
return {
"format": ["text", "set"],
"diff_match": ["line", "none"],
"diff_replace": [],
"output": [],
}
def get_capabilities(self):
result = super(Cliconf, self).get_capabilities()
result["rpc"] += [
"commit",
"discard_changes",
"get_diff",
"run_commands",
]
result["device_operations"] = self.get_device_operations()
result.update(self.get_option_values())
return json.dumps(result)
def set_cli_prompt_context(self):
"""
Make sure we are in the operational cli mode
:return: None
"""
if self._connection.connected:
self._update_cli_prompt_context(
config_context="#", exit_command="exit discard"
)
diff --git a/plugins/modules/vyos_interface.py b/plugins/modules/vyos_interface.py
index fe4fce3..be844c5 100644
--- a/plugins/modules/vyos_interface.py
+++ b/plugins/modules/vyos_interface.py
@@ -1,471 +1,473 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_interface
author: Ganesh Nalawade (@ganeshrn)
-short_description: (deprecated) Manage Interface on VyOS network devices
+short_description: (deprecated, removed after 2022-06-01) Manage Interface on VyOS
+ network devices
description:
- This module provides declarative management of Interfaces on VyOS network devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_interfaces
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the Interface.
required: true
description:
description:
- Description of Interface.
enabled:
description:
- Interface link status.
type: bool
speed:
description:
- Interface link speed.
mtu:
description:
- Maximum size of transmit packet.
duplex:
description:
- Interface link status.
default: auto
choices:
- full
- half
- auto
delay:
description:
- Time in seconds to wait before checking for the operational state on remote
device. This wait is applicable for operational state argument which are I(state)
with values C(up)/C(down) and I(neighbors).
default: 10
neighbors:
description:
- Check the operational state of given interface C(name) for LLDP neighbor.
- The following suboptions are available.
suboptions:
host:
description:
- LLDP neighbor host for given interface C(name).
port:
description:
- LLDP neighbor port to which given interface C(name) is connected.
aggregate:
description: List of Interfaces definitions.
state:
description:
- State of the Interface configuration, C(up) means present and operationally
up and C(down) means present and operationally C(down)
default: present
choices:
- present
- absent
- up
- down
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: configure interface
vyos.vyos.vyos_interface:
name: eth0
description: test-interface
- name: remove interface
vyos.vyos.vyos_interface:
name: eth0
state: absent
- name: make interface down
vyos.vyos.vyos_interface:
name: eth0
enabled: false
- name: make interface up
vyos.vyos.vyos_interface:
name: eth0
enabled: true
- name: Configure interface speed, mtu, duplex
vyos.vyos.vyos_interface:
name: eth5
state: present
speed: 100
mtu: 256
duplex: full
- name: Set interface using aggregate
vyos.vyos.vyos_interface:
aggregate:
- {name: eth1, description: test-interface-1, speed: 100, duplex: half, mtu: 512}
- {name: eth2, description: test-interface-2, speed: 1000, duplex: full, mtu: 256}
- name: Disable interface on aggregate
net_interface:
aggregate:
- name: eth1
- name: eth2
enabled: false
- name: Delete interface using aggregate
net_interface:
aggregate:
- name: eth1
- name: eth2
state: absent
- name: Check lldp neighbors intent arguments
vyos.vyos.vyos_interface:
name: eth0
neighbors:
- port: eth0
host: netdev
- name: Config + intent
vyos.vyos.vyos_interface:
name: eth1
enabled: false
state: down
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces ethernet eth0 description "test-interface"
- set interfaces ethernet eth0 speed 100
- set interfaces ethernet eth0 mtu 256
- set interfaces ethernet eth0 duplex full
"""
import re
from copy import deepcopy
from time import sleep
from ansible.module_utils._text import to_text
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import exec_command
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
conditional,
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
get_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates):
commands = list()
want, have = updates
params = ("speed", "description", "duplex", "mtu")
for w in want:
name = w["name"]
disable = w["disable"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
set_interface = "set interfaces ethernet " + name
delete_interface = "delete interfaces ethernet " + name
if state == "absent" and obj_in_have:
commands.append(delete_interface)
elif state in ("present", "up", "down"):
if obj_in_have:
for item in params:
value = w.get(item)
if value and value != obj_in_have.get(item):
if item == "description":
value = "'" + str(value) + "'"
commands.append(
set_interface + " " + item + " " + str(value)
)
if disable and not obj_in_have.get("disable", False):
commands.append(set_interface + " disable")
elif not disable and obj_in_have.get("disable", False):
commands.append(delete_interface + " disable")
else:
commands.append(set_interface)
for item in params:
value = w.get(item)
if value:
if item == "description":
value = "'" + str(value) + "'"
commands.append(
set_interface + " " + item + " " + str(value)
)
if disable:
commands.append(set_interface + " disable")
return commands
def map_config_to_obj(module):
data = get_config(module, flags=["| grep interface"])
obj = []
for line in data.split("\n"):
if line.startswith("set interfaces ethernet"):
match = re.search(r"set interfaces ethernet (\S+)", line, re.M)
name = match.group(1)
if name:
interface = {}
for item in obj:
if item["name"] == name:
interface = item
break
if not interface:
interface = {"name": name}
obj.append(interface)
match = re.search(r"%s (\S+)" % name, line, re.M)
if match:
param = match.group(1)
if param == "description":
match = re.search(r"description (.+)", line, re.M)
description = match.group(1).strip("'")
interface["description"] = description
elif param == "speed":
match = re.search(r"speed (\S+)", line, re.M)
speed = match.group(1).strip("'")
interface["speed"] = speed
elif param == "mtu":
match = re.search(r"mtu (\S+)", line, re.M)
mtu = match.group(1).strip("'")
interface["mtu"] = int(mtu)
elif param == "duplex":
match = re.search(r"duplex (\S+)", line, re.M)
duplex = match.group(1).strip("'")
interface["duplex"] = duplex
elif param.strip("'") == "disable":
interface["disable"] = True
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
d = item.copy()
if d["enabled"]:
d["disable"] = False
else:
d["disable"] = True
obj.append(d)
else:
params = {
"name": module.params["name"],
"description": module.params["description"],
"speed": module.params["speed"],
"mtu": module.params["mtu"],
"duplex": module.params["duplex"],
"delay": module.params["delay"],
"state": module.params["state"],
"neighbors": module.params["neighbors"],
}
if module.params["enabled"]:
params.update({"disable": False})
else:
params.update({"disable": True})
obj.append(params)
return obj
def check_declarative_intent_params(module, want, result):
failed_conditions = []
have_neighbors = None
for w in want:
want_state = w.get("state")
want_neighbors = w.get("neighbors")
if want_state not in ("up", "down") and not want_neighbors:
continue
if result["changed"]:
sleep(w["delay"])
command = "show interfaces ethernet %s" % w["name"]
rc, out, err = exec_command(module, command)
if rc != 0:
module.fail_json(
msg=to_text(err, errors="surrogate_then_replace"),
command=command,
rc=rc,
)
if want_state in ("up", "down"):
match = re.search(r"%s (\w+)" % "state", out, re.M)
have_state = None
if match:
have_state = match.group(1)
if have_state is None or not conditional(
want_state, have_state.strip().lower()
):
failed_conditions.append("state " + "eq(%s)" % want_state)
if want_neighbors:
have_host = []
have_port = []
if have_neighbors is None:
rc, have_neighbors, err = exec_command(
module, "show lldp neighbors detail"
)
if rc != 0:
module.fail_json(
msg=to_text(err, errors="surrogate_then_replace"),
command=command,
rc=rc,
)
if have_neighbors:
lines = have_neighbors.strip().split("Interface: ")
for line in lines:
field = line.split("\n")
if field[0].split(",")[0].strip() == w["name"]:
for item in field:
if item.strip().startswith("SysName:"):
have_host.append(item.split(":")[1].strip())
if item.strip().startswith("PortDescr:"):
have_port.append(item.split(":")[1].strip())
for item in want_neighbors:
host = item.get("host")
port = item.get("port")
if host and host not in have_host:
failed_conditions.append("host " + host)
if port and port not in have_port:
failed_conditions.append("port " + port)
return failed_conditions
def main():
""" main entry point for module execution
"""
neighbors_spec = dict(host=dict(), port=dict())
element_spec = dict(
name=dict(),
description=dict(),
speed=dict(),
mtu=dict(type="int"),
duplex=dict(choices=["full", "half", "auto"]),
enabled=dict(default=True, type="bool"),
neighbors=dict(type="list", elements="dict", options=neighbors_spec),
delay=dict(default=10, type="int"),
state=dict(
default="present", choices=["present", "absent", "up", "down"]
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
required_together = [["speed", "duplex"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
required_together=required_together,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have))
result["commands"] = commands
if commands:
commit = not module.check_mode
diff = load_config(module, commands, commit=commit)
if diff:
if module._diff:
result["diff"] = {"prepared": diff}
result["changed"] = True
failed_conditions = check_declarative_intent_params(module, want, result)
if failed_conditions:
msg = "One or more conditional statements have not been satisfied"
module.fail_json(msg=msg, failed_conditions=failed_conditions)
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_l3_interface.py b/plugins/modules/vyos_l3_interface.py
index 2feb824..676d6ec 100644
--- a/plugins/modules/vyos_l3_interface.py
+++ b/plugins/modules/vyos_l3_interface.py
@@ -1,329 +1,331 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_l3_interface
author: Ricardo Carrillo Cruz (@rcarrillocruz)
-short_description: (deprecated) Manage L3 interfaces on VyOS network devices
+short_description: (deprecated, removed after 2022-06-01) Manage L3 interfaces on
+ VyOS network devices
description:
- This module provides declarative management of L3 interfaces on VyOS network devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_l3_interfaces
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the L3 interface.
ipv4:
description:
- IPv4 of the L3 interface.
ipv6:
description:
- IPv6 of the L3 interface.
aggregate:
description: List of L3 interfaces definitions
state:
description:
- State of the L3 interface configuration.
default: present
choices:
- present
- absent
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: Set eth0 IPv4 address
vyos.vyos.vyos_l3_interface:
name: eth0
ipv4: 192.168.0.1/24
- name: Remove eth0 IPv4 address
vyos.vyos.vyos_l3_interface:
name: eth0
state: absent
- name: Set IP addresses on aggregate
vyos.vyos.vyos_l3_interface:
aggregate:
- {name: eth1, ipv4: 192.168.2.10/24}
- {name: eth2, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
- name: Remove IP addresses on aggregate
vyos.vyos.vyos_l3_interface:
aggregate:
- {name: eth1, ipv4: 192.168.2.10/24}
- {name: eth2, ipv4: 192.168.3.10/24, ipv6: fd5d:12c9:2201:1::1/64}
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces ethernet eth0 address '192.168.0.1/24'
"""
import socket
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
is_masklen,
validate_ip_address,
)
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def is_ipv4(value):
if value:
address = value.split("/")
if is_masklen(address[1]) and validate_ip_address(address[0]):
return True
return False
def is_ipv6(value):
if value:
address = value.split("/")
if 0 <= int(address[1]) <= 128:
try:
socket.inet_pton(socket.AF_INET6, address[0])
except socket.error:
return False
return True
return False
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
ipv4 = w["ipv4"]
ipv6 = w["ipv6"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent" and obj_in_have:
if (
not ipv4
and not ipv6
and (obj_in_have["ipv4"] or obj_in_have["ipv6"])
):
if name == "lo":
commands.append("delete interfaces loopback lo address")
else:
commands.append(
"delete interfaces ethernet " + name + " address"
)
else:
if ipv4 and ipv4 in obj_in_have["ipv4"]:
if name == "lo":
commands.append(
"delete interfaces loopback lo address " + ipv4
)
else:
commands.append(
"delete interfaces ethernet "
+ name
+ " address "
+ ipv4
)
if ipv6 and ipv6 in obj_in_have["ipv6"]:
if name == "lo":
commands.append(
"delete interfaces loopback lo address " + ipv6
)
else:
commands.append(
"delete interfaces ethernet "
+ name
+ " address "
+ ipv6
)
elif state == "present" and obj_in_have:
if ipv4 and ipv4 not in obj_in_have["ipv4"]:
if name == "lo":
commands.append(
"set interfaces loopback lo address " + ipv4
)
else:
commands.append(
"set interfaces ethernet " + name + " address " + ipv4
)
if ipv6 and ipv6 not in obj_in_have["ipv6"]:
if name == "lo":
commands.append(
"set interfaces loopback lo address " + ipv6
)
else:
commands.append(
"set interfaces ethernet " + name + " address " + ipv6
)
return commands
def map_config_to_obj(module):
obj = []
output = run_commands(module, ["show interfaces"])
lines = re.split(r"\n[e|l]", output[0])[1:]
if len(lines) > 0:
for line in lines:
splitted_line = line.split()
if len(splitted_line) > 0:
ipv4 = []
ipv6 = []
if splitted_line[0].lower().startswith("th"):
name = "e" + splitted_line[0].lower()
elif splitted_line[0].lower().startswith("o"):
name = "l" + splitted_line[0].lower()
for i in splitted_line[1:]:
if ("." in i or ":" in i) and "/" in i:
value = i.split(r"\n")[0]
if is_ipv4(value):
ipv4.append(value)
elif is_ipv6(value):
ipv6.append(value)
obj.append({"name": name, "ipv4": ipv4, "ipv6": ipv6})
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{
"name": module.params["name"],
"ipv4": module.params["ipv4"],
"ipv6": module.params["ipv6"],
"state": module.params["state"],
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
ipv4=dict(),
ipv6=dict(),
state=dict(default="present", choices=["present", "absent"]),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_linkagg.py b/plugins/modules/vyos_linkagg.py
index a68197b..4e63e2f 100644
--- a/plugins/modules/vyos_linkagg.py
+++ b/plugins/modules/vyos_linkagg.py
@@ -1,327 +1,329 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_linkagg
author: Ricardo Carrillo Cruz (@rcarrillocruz)
-short_description: (deprecated) Manage link aggregation groups on VyOS network devices
+short_description: (deprecated, removed after 2022-06-01) Manage link aggregation
+ groups on VyOS network devices
description:
- This module provides declarative management of link aggregation groups on VyOS network
devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_lag_interfaces
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the link aggregation group.
required: true
type: str
mode:
description:
- Mode of the link aggregation group.
choices:
- 802.3ad
- active-backup
- broadcast
- round-robin
- transmit-load-balance
- adaptive-load-balance
- xor-hash
- on
type: str
members:
description:
- List of members of the link aggregation group.
type: list
aggregate:
description: List of link aggregation definitions.
type: list
state:
description:
- State of the link aggregation group.
default: present
choices:
- present
- absent
- up
- down
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: configure link aggregation group
vyos.vyos.vyos_linkagg:
name: bond0
members:
- eth0
- eth1
- name: remove configuration
vyos.vyos.vyos_linkagg:
name: bond0
state: absent
- name: Create aggregate of linkagg definitions
vyos.vyos.vyos_linkagg:
aggregate:
- {name: bond0, members: [eth1]}
- {name: bond1, members: [eth2]}
- name: Remove aggregate of linkagg definitions
vyos.vyos.vyos_linkagg:
aggregate:
- name: bond0
- name: bond1
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set interfaces bonding bond0
- set interfaces ethernet eth0 bond-group 'bond0'
- set interfaces ethernet eth1 bond-group 'bond0'
"""
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
load_config,
run_commands,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
members = w.get("members") or []
mode = w["mode"]
if mode == "on":
mode = "802.3ad"
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent":
if obj_in_have:
for m in obj_in_have["members"]:
commands.append(
"delete interfaces ethernet " + m + " bond-group"
)
commands.append("delete interfaces bonding " + name)
else:
if not obj_in_have:
commands.append(
"set interfaces bonding " + name + " mode " + mode
)
for m in members:
commands.append(
"set interfaces ethernet " + m + " bond-group " + name
)
if state == "down":
commands.append(
"set interfaces bonding " + name + " disable"
)
else:
if mode != obj_in_have["mode"]:
commands.append(
"set interfaces bonding " + name + " mode " + mode
)
missing_members = list(
set(members) - set(obj_in_have["members"])
)
for m in missing_members:
commands.append(
"set interfaces ethernet " + m + " bond-group " + name
)
if state == "down" and obj_in_have["state"] == "up":
commands.append(
"set interfaces bonding " + name + " disable"
)
elif state == "up" and obj_in_have["state"] == "down":
commands.append(
"delete interfaces bonding " + name + " disable"
)
return commands
def map_config_to_obj(module):
obj = []
output = run_commands(module, ["show interfaces bonding slaves"])
lines = output[0].splitlines()
if len(lines) > 1:
for line in lines[1:]:
splitted_line = line.split()
name = splitted_line[0]
mode = splitted_line[1]
state = splitted_line[2]
if len(splitted_line) > 4:
members = splitted_line[4:]
else:
members = []
obj.append(
{
"name": name,
"mode": mode,
"members": members,
"state": state,
}
)
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{
"name": module.params["name"],
"mode": module.params["mode"],
"members": module.params["members"],
"state": module.params["state"],
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
mode=dict(
choices=[
"802.3ad",
"active-backup",
"broadcast",
"round-robin",
"transmit-load-balance",
"adaptive-load-balance",
"xor-hash",
"on",
],
default="802.3ad",
),
members=dict(type="list"),
state=dict(
default="present", choices=["present", "absent", "up", "down"]
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp.py b/plugins/modules/vyos_lldp.py
index 5b697c2..9b138c1 100644
--- a/plugins/modules/vyos_lldp.py
+++ b/plugins/modules/vyos_lldp.py
@@ -1,141 +1,143 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp
author: Ricardo Carrillo Cruz (@rcarrillocruz)
-short_description: (deprecated) Manage LLDP configuration on VyOS network devices
+short_description: (deprecated, removed after 2022-06-01) Manage LLDP configuration
+ on VyOS network devices
description:
- This module provides declarative management of LLDP service on VyOS network devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_lldp_global
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
interfaces:
description:
- Name of the interfaces.
type: list
state:
description:
- State of the link aggregation group.
default: present
choices:
- present
- absent
- enabled
- disabled
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: Enable LLDP service
vyos.vyos.vyos_lldp:
state: present
- name: Disable LLDP service
vyos.vyos.vyos_lldp:
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set service lldp
"""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def has_lldp(module):
config = get_config(module).splitlines()
if "set service 'lldp'" in config or "set service lldp" in config:
return True
else:
return False
def main():
""" main entry point for module execution
"""
argument_spec = dict(
interfaces=dict(type="list"),
state=dict(
default="present",
choices=["present", "absent", "enabled", "disabled"],
),
)
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(
argument_spec=argument_spec, supports_check_mode=True
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
HAS_LLDP = has_lldp(module)
commands = []
if module.params["state"] == "absent" and HAS_LLDP:
commands.append("delete service lldp")
elif module.params["state"] == "present" and not HAS_LLDP:
commands.append("set service lldp")
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_lldp_interface.py b/plugins/modules/vyos_lldp_interface.py
index 90e123d..2fd1bc5 100644
--- a/plugins/modules/vyos_lldp_interface.py
+++ b/plugins/modules/vyos_lldp_interface.py
@@ -1,264 +1,265 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_lldp_interface
author: Ricardo Carrillo Cruz (@rcarrillocruz)
-short_description: (deprecated) Manage LLDP interfaces configuration on VyOS network
- devices
+short_description: (deprecated, removed after 2022-06-01) Manage LLDP interfaces configuration
+ on VyOS network devices
description:
- This module provides declarative management of LLDP interfaces configuration on
VyOS network devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_lldp_interfaces
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VYOS 1.1.7
options:
name:
description:
- Name of the interface LLDP should be configured on.
type: str
aggregate:
description: List of interfaces LLDP should be configured on.
type: list
state:
description:
- State of the LLDP configuration.
default: present
choices:
- present
- absent
- enabled
- disabled
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: Enable LLDP on eth1
net_lldp_interface:
state: present
- name: Enable LLDP on specific interfaces
net_lldp_interface:
interfaces:
- eth1
- eth2
state: present
- name: Disable LLDP globally
net_lldp_interface:
state: disabled
- name: Create aggregate of LLDP interface configurations
vyos.vyos.vyos_lldp_interface:
aggregate:
- name: eth1
- name: eth2
state: present
- name: Delete aggregate of LLDP interface configurations
vyos.vyos.vyos_lldp_interface:
aggregate:
- name: eth1
- name: eth2
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always, except for the platforms that use Netconf transport to manage the device.
type: list
sample:
- set service lldp eth1
- set service lldp eth2 disable
"""
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def search_obj_in_list(name, lst):
for o in lst:
if o["name"] == name:
return o
return None
def map_obj_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
name = w["name"]
state = w["state"]
obj_in_have = search_obj_in_list(name, have)
if state == "absent" and obj_in_have:
commands.append("delete service lldp interface " + name)
elif state in ("present", "enabled"):
if not obj_in_have:
commands.append("set service lldp interface " + name)
elif (
obj_in_have
and obj_in_have["state"] == "disabled"
and state == "enabled"
):
commands.append(
"delete service lldp interface " + name + " disable"
)
elif state == "disabled":
if not obj_in_have:
commands.append("set service lldp interface " + name)
commands.append(
"set service lldp interface " + name + " disable"
)
elif obj_in_have and obj_in_have["state"] != "disabled":
commands.append(
"set service lldp interface " + name + " disable"
)
return commands
def map_config_to_obj(module):
obj = []
config = get_config(module).splitlines()
output = [c for c in config if c.startswith("set service lldp interface")]
for i in output:
splitted_line = i.split()
if len(splitted_line) > 5:
new_obj = {"name": splitted_line[4]}
if splitted_line[5] == "'disable'":
new_obj["state"] = "disabled"
else:
new_obj = {"name": splitted_line[4][1:-1]}
new_obj["state"] = "present"
obj.append(new_obj)
return obj
def map_params_to_obj(module):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
obj.append(item.copy())
else:
obj.append(
{"name": module.params["name"], "state": module.params["state"]}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
name=dict(),
state=dict(
default="present",
choices=["present", "absent", "enabled", "disabled"],
),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["name"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["name", "aggregate"]]
mutually_exclusive = [["name", "aggregate"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module)
have = map_config_to_obj(module)
commands = map_obj_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()
diff --git a/plugins/modules/vyos_static_route.py b/plugins/modules/vyos_static_route.py
index 4724d5f..67f9954 100644
--- a/plugins/modules/vyos_static_route.py
+++ b/plugins/modules/vyos_static_route.py
@@ -1,302 +1,304 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2017, Ansible by Red Hat, inc
#
# This file is part of Ansible by Red Hat
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see .
#
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = """
module: vyos_static_route
author: Trishna Guha (@trishnaguha)
-short_description: (deprecated) Manage static IP routes on Vyatta VyOS network devices
+short_description: (deprecated, removed after 2022-06-01) Manage static IP routes
+ on Vyatta VyOS network devices
description:
- This module provides declarative management of static IP routes on Vyatta VyOS network
devices.
version_added: 1.0.0
deprecated:
- removed_in: '2.13'
alternative: vyos_static_routes
why: Updated modules released with more functionality.
+ removed_at_date: '2022-06-01'
notes:
- Tested against VyOS 1.1.8 (helium).
- This module works with connection C(network_cli). See L(the VyOS OS Platform Options,../network/user_guide/platform_vyos.html).
options:
prefix:
description:
- Network prefix of the static route. C(mask) param should be ignored if C(prefix)
is provided with C(mask) value C(prefix/mask).
type: str
mask:
description:
- Network prefix mask of the static route.
type: str
next_hop:
description:
- Next hop IP of the static route.
type: str
admin_distance:
description:
- Admin distance of the static route.
type: int
aggregate:
description: List of static route definitions
type: list
state:
description:
- State of the static route configuration.
default: present
choices:
- present
- absent
type: str
extends_documentation_fragment:
- vyos.vyos.vyos
+
"""
EXAMPLES = """
- name: configure static route
vyos.vyos.vyos_static_route:
prefix: 192.168.2.0
mask: 24
next_hop: 10.0.0.1
- name: configure static route prefix/mask
vyos.vyos.vyos_static_route:
prefix: 192.168.2.0/16
next_hop: 10.0.0.1
- name: remove configuration
vyos.vyos.vyos_static_route:
prefix: 192.168.2.0
mask: 16
next_hop: 10.0.0.1
state: absent
- name: configure aggregates of static routes
vyos.vyos.vyos_static_route:
aggregate:
- {prefix: 192.168.2.0, mask: 24, next_hop: 10.0.0.1}
- {prefix: 192.168.3.0, mask: 16, next_hop: 10.0.2.1}
- {prefix: 192.168.3.0/16, next_hop: 10.0.2.1}
- name: Remove static route collections
vyos.vyos.vyos_static_route:
aggregate:
- {prefix: 172.24.1.0/24, next_hop: 192.168.42.64}
- {prefix: 172.24.3.0/24, next_hop: 192.168.42.64}
state: absent
"""
RETURN = """
commands:
description: The list of configuration mode commands to send to the device
returned: always
type: list
sample:
- set protocols static route 192.168.2.0/16 next-hop 10.0.0.1
"""
import re
from copy import deepcopy
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
remove_default_spec,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
get_config,
load_config,
)
from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import (
vyos_argument_spec,
)
def spec_to_commands(updates, module):
commands = list()
want, have = updates
for w in want:
prefix = w["prefix"]
mask = w["mask"]
next_hop = w["next_hop"]
admin_distance = w["admin_distance"]
state = w["state"]
del w["state"]
if state == "absent" and w in have:
commands.append(
"delete protocols static route %s/%s" % (prefix, mask)
)
elif state == "present" and w not in have:
cmd = "set protocols static route %s/%s next-hop %s" % (
prefix,
mask,
next_hop,
)
if admin_distance != "None":
cmd += " distance %s" % (admin_distance)
commands.append(cmd)
return commands
def config_to_dict(module):
data = get_config(module)
obj = []
for line in data.split("\n"):
if line.startswith("set protocols static route"):
match = re.search(r"static route (\S+)", line, re.M)
prefix = match.group(1).split("/")[0]
mask = match.group(1).split("/")[1]
if "next-hop" in line:
match_hop = re.search(r"next-hop (\S+)", line, re.M)
next_hop = match_hop.group(1).strip("'")
match_distance = re.search(r"distance (\S+)", line, re.M)
if match_distance is not None:
admin_distance = match_distance.group(1)[1:-1]
else:
admin_distance = None
if admin_distance is not None:
obj.append(
{
"prefix": prefix,
"mask": mask,
"next_hop": next_hop,
"admin_distance": admin_distance,
}
)
else:
obj.append(
{
"prefix": prefix,
"mask": mask,
"next_hop": next_hop,
"admin_distance": "None",
}
)
return obj
def map_params_to_obj(module, required_together=None):
obj = []
aggregate = module.params.get("aggregate")
if aggregate:
for item in aggregate:
for key in item:
if item.get(key) is None:
item[key] = module.params[key]
module._check_required_together(required_together, item)
d = item.copy()
if "/" in d["prefix"]:
d["mask"] = d["prefix"].split("/")[1]
d["prefix"] = d["prefix"].split("/")[0]
if "admin_distance" in d:
d["admin_distance"] = str(d["admin_distance"])
obj.append(d)
else:
prefix = module.params["prefix"].strip()
if "/" in prefix:
mask = prefix.split("/")[1]
prefix = prefix.split("/")[0]
else:
mask = module.params["mask"].strip()
next_hop = module.params["next_hop"].strip()
admin_distance = str(module.params["admin_distance"])
state = module.params["state"]
obj.append(
{
"prefix": prefix,
"mask": mask,
"next_hop": next_hop,
"admin_distance": admin_distance,
"state": state,
}
)
return obj
def main():
""" main entry point for module execution
"""
element_spec = dict(
prefix=dict(type="str"),
mask=dict(type="str"),
next_hop=dict(type="str"),
admin_distance=dict(type="int"),
state=dict(default="present", choices=["present", "absent"]),
)
aggregate_spec = deepcopy(element_spec)
aggregate_spec["prefix"] = dict(required=True)
# remove default in aggregate spec, to handle common arguments
remove_default_spec(aggregate_spec)
argument_spec = dict(
aggregate=dict(type="list", elements="dict", options=aggregate_spec),
)
argument_spec.update(element_spec)
argument_spec.update(vyos_argument_spec)
required_one_of = [["aggregate", "prefix"]]
required_together = [["prefix", "next_hop"]]
mutually_exclusive = [["aggregate", "prefix"]]
module = AnsibleModule(
argument_spec=argument_spec,
required_one_of=required_one_of,
required_together=required_together,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True,
)
warnings = list()
result = {"changed": False}
if warnings:
result["warnings"] = warnings
want = map_params_to_obj(module, required_together=required_together)
have = config_to_dict(module)
commands = spec_to_commands((want, have), module)
result["commands"] = commands
if commands:
commit = not module.check_mode
load_config(module, commands, commit=commit)
result["changed"] = True
module.exit_json(**result)
if __name__ == "__main__":
main()