diff options
54 files changed, 2065 insertions, 379 deletions
diff --git a/plugins/module_utils/network/vyos/argspec/facts/facts.py b/plugins/module_utils/network/vyos/argspec/facts/facts.py index fc9d438..624d2fd 100644 --- a/plugins/module_utils/network/vyos/argspec/facts/facts.py +++ b/plugins/module_utils/network/vyos/argspec/facts/facts.py @@ -4,8 +4,6 @@ """ The arg spec for the vyos facts module. """ - - from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -24,6 +22,8 @@ class FactsArgs(object): # pylint: disable=R0903 "!interfaces", "l3_interfaces", "!l3_interfaces", + "lag_interfaces", + "!lag_interfaces", ] argument_spec = { diff --git a/plugins/module_utils/network/vyos/argspec/lag_interfaces/__init__.py b/plugins/module_utils/network/vyos/argspec/lag_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/lag_interfaces/__init__.py diff --git a/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py new file mode 100644 index 0000000..97c5d5a --- /dev/null +++ b/plugins/module_utils/network/vyos/argspec/lag_interfaces/lag_interfaces.py @@ -0,0 +1,80 @@ +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The arg spec for the vyos_lag_interfaces module +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + + +class Lag_interfacesArgs(object): # pylint: disable=R0903 + """The arg spec for the vyos_lag_interfaces module + """ + + def __init__(self, **kwargs): + pass + + argument_spec = { + "config": { + "elements": "dict", + "options": { + "arp_monitor": { + "options": { + "interval": {"type": "int"}, + "target": {"type": "list"}, + }, + "type": "dict", + }, + "hash_policy": { + "choices": ["layer2", "layer2+3", "layer3+4"], + "type": "str", + }, + "members": { + "elements": "dict", + "options": {"member": {"type": "str"}}, + "type": "list", + }, + "mode": { + "choices": [ + "802.3ad", + "active-backup", + "broadcast", + "round-robin", + "transmit-load-balance", + "adaptive-load-balance", + "xor-hash", + ], + "type": "str", + }, + "name": {"required": True, "type": "str"}, + "primary": {"type": "str"}, + }, + "type": "list", + }, + "state": { + "choices": ["merged", "replaced", "overridden", "deleted"], + "default": "merged", + "type": "str", + }, + } # pylint: disable=C0301 diff --git a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py index adf61d7..e5724f5 100644 --- a/plugins/module_utils/network/vyos/config/interfaces/interfaces.py +++ b/plugins/module_utils/network/vyos/config/interfaces/interfaces.py @@ -25,7 +25,6 @@ from ansible.module_utils.six import iteritems from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( Facts, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( search_obj_in_list, get_interface_type, diff --git a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py index a69db05..2bd04b6 100644 --- a/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py +++ b/plugins/module_utils/network/vyos/config/l3_interfaces/l3_interfaces.py @@ -23,7 +23,6 @@ from ansible.module_utils.six import iteritems from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( Facts, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( search_obj_in_list, get_interface_type, diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/__init__.py b/plugins/module_utils/network/vyos/config/lag_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/lag_interfaces/__init__.py diff --git a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py new file mode 100644 index 0000000..44a8a62 --- /dev/null +++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py @@ -0,0 +1,410 @@ +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos_lag_interfaces class +It is in this file where the current configuration (as dict) +is compared to the provided configuration (as dict) and the command set +necessary to bring the current configuration to it's desired end-state is +created +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from ansible.module_utils.network.common.cfg.base import ConfigBase +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( + Facts, +) +from ansible.module_utils.network.common.utils import to_list, dict_diff +from ansible.module_utils.six import iteritems +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.utils.utils import ( + search_obj_in_list, + get_lst_diff_for_dicts, + list_diff_want_only, + list_diff_have_only, +) + + +class Lag_interfaces(ConfigBase): + """ + The vyos_lag_interfaces class + """ + + gather_subset = ["!all", "!min"] + + gather_network_resources = ["lag_interfaces"] + + params = [ + "arp_monitor", + "hash_policy", + "members", + "mode", + "name", + "primary", + ] + + def __init__(self, module): + super(Lag_interfaces, self).__init__(module) + + def get_lag_interfaces_facts(self): + """ Get the 'facts' (the current configuration) + + :rtype: A dictionary + :returns: The current configuration as a dictionary + """ + facts, _warnings = Facts(self._module).get_facts( + self.gather_subset, self.gather_network_resources + ) + lag_interfaces_facts = facts["ansible_network_resources"].get( + "lag_interfaces" + ) + if not lag_interfaces_facts: + return [] + return lag_interfaces_facts + + def execute_module(self): + """ Execute the module + + :rtype: A dictionary + :returns: The result from module execution + """ + result = {"changed": False} + commands = list() + warnings = list() + existing_lag_interfaces_facts = self.get_lag_interfaces_facts() + commands.extend(self.set_config(existing_lag_interfaces_facts)) + if commands: + if self._module.check_mode: + resp = self._connection.edit_config(commands, commit=False) + else: + resp = self._connection.edit_config(commands) + result["changed"] = True + + result["commands"] = commands + + if self._module._diff: + result["diff"] = resp["diff"] if result["changed"] else None + + changed_lag_interfaces_facts = self.get_lag_interfaces_facts() + + result["before"] = existing_lag_interfaces_facts + if result["changed"]: + result["after"] = changed_lag_interfaces_facts + + result["warnings"] = warnings + return result + + def set_config(self, existing_lag_interfaces_facts): + """ Collect the configuration from the args passed to the module, + collect the current configuration (as a dict from facts) + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + want = self._module.params["config"] + have = existing_lag_interfaces_facts + resp = self.set_state(want, have) + return to_list(resp) + + def set_state(self, want, have): + """ Select the appropriate function based on the state provided + + :param want: the desired configuration as a dictionary + :param have: the current configuration as a dictionary + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + state = self._module.params["state"] + if state == "overridden": + commands.extend(self._state_overridden(want, have)) + elif state == "deleted": + if want: + for want_item in want: + name = want_item["name"] + obj_in_have = search_obj_in_list(name, have) + commands.extend(self._state_deleted(obj_in_have)) + else: + for have_item in have: + commands.extend(self._state_deleted(have_item)) + else: + for want_item in want: + name = want_item["name"] + obj_in_have = search_obj_in_list(name, have) + if state == "merged": + commands.extend(self._state_merged(want_item, obj_in_have)) + elif state == "replaced": + commands.extend( + self._state_replaced(want_item, obj_in_have) + ) + return commands + + def _state_replaced(self, want, have): + """ The command generator when state is replaced + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + if have: + commands.extend(self._render_del_commands(want, have)) + commands.extend(self._state_merged(want, have)) + return commands + + def _state_overridden(self, want, have): + """ The command generator when state is overridden + + :rtype: A list + :returns: the commands necessary to migrate the current configuration + to the desired configuration + """ + commands = [] + for have_item in have: + lag_name = have_item["name"] + obj_in_want = search_obj_in_list(lag_name, want) + if not obj_in_want: + commands.extend(self._purge_attribs(have_item)) + + for want_item in want: + name = want_item["name"] + obj_in_have = search_obj_in_list(name, have) + commands.extend(self._state_replaced(want_item, obj_in_have)) + return commands + + def _state_merged(self, want, have): + """ The command generator when state is merged + + :rtype: A list + :returns: the commands necessary to merge the provided into + the current configuration + """ + commands = [] + if have: + commands.extend(self._render_updates(want, have)) + else: + commands.extend(self._render_set_commands(want)) + return commands + + def _state_deleted(self, have): + """ The command generator when state is deleted + + :rtype: A list + :returns: the commands necessary to remove the current configuration + of the provided objects + """ + commands = [] + if have: + commands.extend(self._purge_attribs(have)) + return commands + + def _render_updates(self, want, have): + commands = [] + + temp_have_members = have.pop("members", None) + temp_want_members = want.pop("members", None) + + updates = dict_diff(have, want) + + if temp_have_members: + have["members"] = temp_have_members + if temp_want_members: + want["members"] = temp_want_members + + commands.extend(self._add_bond_members(want, have)) + + if updates: + for key, value in iteritems(updates): + if value: + if key == "arp_monitor": + commands.extend( + self._add_arp_monitor(updates, key, want, have) + ) + else: + commands.append( + self._compute_command( + have["name"], key, str(value) + ) + ) + return commands + + def _render_set_commands(self, want): + commands = [] + have = [] + + params = Lag_interfaces.params + + for attrib in params: + value = want[attrib] + if value: + if attrib == "arp_monitor": + commands.extend( + self._add_arp_monitor(want, attrib, want, have) + ) + elif attrib == "members": + commands.extend(self._add_bond_members(want, have)) + elif attrib != "name": + commands.append( + self._compute_command( + want["name"], attrib, value=str(value) + ) + ) + return commands + + def _purge_attribs(self, have): + commands = [] + for item in Lag_interfaces.params: + if have.get(item): + if item == "members": + commands.extend(self._delete_bond_members(have)) + elif item != "name": + commands.append( + self._compute_command( + have["name"], attrib=item, remove=True + ) + ) + return commands + + def _render_del_commands(self, want, have): + commands = [] + + params = Lag_interfaces.params + for attrib in params: + if attrib == "members": + commands.extend(self._update_bond_members(attrib, want, have)) + elif attrib == "arp_monitor": + commands.extend(self._update_arp_monitor(attrib, want, have)) + elif have.get(attrib) and not want.get(attrib): + commands.append( + self._compute_command(have["name"], attrib, remove=True) + ) + return commands + + def _add_bond_members(self, want, have): + commands = [] + diff_members = get_lst_diff_for_dicts(want, have, "members") + if diff_members: + for key in diff_members: + commands.append( + self._compute_command( + key["member"], + "bond-group", + want["name"], + type="ethernet", + ) + ) + return commands + + def _add_arp_monitor(self, updates, key, want, have): + commands = [] + arp_monitor = updates.get(key) or {} + diff_targets = self._get_arp_monitor_target_diff( + want, have, key, "target" + ) + + if "interval" in arp_monitor: + commands.append( + self._compute_command( + key, "interval", str(arp_monitor["interval"]) + ) + ) + if diff_targets: + for target in diff_targets: + commands.append(self._compute_commands(key, "target", target)) + return commands + + def _delete_bond_members(self, have): + commands = [] + for member in have["members"]: + commands.append( + self._compute_command( + member["member"], + "bond-group", + have["name"], + remove=True, + type="ethernet", + ) + ) + return commands + + def _update_arp_monitor(self, key, want, have): + commands = [] + want_arp_target = [] + have_arp_target = [] + want_arp_monitor = want.get(key) or {} + have_arp_monitor = have.get(key) or {} + del_cmd = "delete interface bonding " + have["name"] + + if want_arp_monitor and "target" in want_arp_monitor: + want_arp_target = want_arp_monitor["target"] + + if have_arp_monitor and "target" in have_arp_monitor: + have_arp_target = have_arp_monitor["target"] + + if "interval" in have_arp_monitor and not want_arp_monitor: + commands.append(del_cmd + " " + key + " interval") + if "target" in have_arp_monitor: + target_diff = list_diff_have_only(want_arp_target, have_arp_target) + if target_diff: + for target in target_diff: + commands.append(del_cmd + " " + key + " target " + target) + + return commands + + def _update_bond_members(self, key, want, have): + commands = [] + want_members = want.get(key) or [] + have_members = have.get(key) or [] + + members_diff = list_diff_have_only(want_members, have_members) + if members_diff: + for member in members_diff: + commands.append( + self._compute_command( + member[key], + "bond-group", + have["name"], + False, + "ethernet", + ) + ) + return commands + + def _get_arp_monitor_target_diff( + self, want_list, have_list, dict_name, lst + ): + want_arp_target = [] + have_arp_target = [] + + want_arp_monitor = want_list.get(dict_name) or {} + if want_arp_monitor and lst in want_arp_monitor: + want_arp_target = want_arp_monitor[lst] + + if not have_list: + diff = want_arp_target + else: + have_arp_monitor = have_list.get(dict_name) or {} + if have_arp_monitor and lst in have_arp_monitor: + have_arp_target = have_arp_monitor[lst] + + diff = list_diff_want_only(want_arp_target, have_arp_target) + return diff + + def _compute_command( + self, key, attrib, value=None, remove=False, type="bonding" + ): + if remove: + cmd = "delete interfaces " + type + else: + cmd = "set interfaces " + type + cmd += " " + key + if attrib == "arp_monitor": + attrib = "arp-monitor" + elif attrib == "hash_policy": + attrib = "hash-policy" + cmd += " " + attrib + if value: + cmd += " '" + value + "'" + return cmd diff --git a/plugins/module_utils/network/vyos/facts/facts.py b/plugins/module_utils/network/vyos/facts/facts.py index eae9489..9c389c9 100644 --- a/plugins/module_utils/network/vyos/facts/facts.py +++ b/plugins/module_utils/network/vyos/facts/facts.py @@ -15,16 +15,16 @@ __metaclass__ = type from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) - from ansible.module_utils.network.common.facts.facts import FactsBase from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.interfaces.interfaces import ( InterfacesFacts, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.l3_interfaces.l3_interfaces import ( L3_interfacesFacts, ) - +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.lag_interfaces.lag_interfaces import ( + Lag_interfacesFacts, +) from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legacy.base import ( Default, Neighbors, @@ -34,7 +34,9 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.legac FACT_LEGACY_SUBSETS = dict(default=Default, neighbors=Neighbors, config=Config) FACT_RESOURCE_SUBSETS = dict( - interfaces=InterfacesFacts, l3_interfaces=L3_interfacesFacts + interfaces=InterfacesFacts, + l3_interfaces=L3_interfacesFacts, + lag_interfaces=Lag_interfacesFacts, ) diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/__init__.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/__init__.py diff --git a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py new file mode 100644 index 0000000..6ae780f --- /dev/null +++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py @@ -0,0 +1,150 @@ +# +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +""" +The vyos lag_interfaces fact class +It is in this file the configuration is collected from the device +for a given resource, parsed, and the facts tree is populated +based on the configuration. +""" +from __future__ import absolute_import, division, print_function + +__metaclass__ = type +from re import findall, search, M +from copy import deepcopy + +from ansible.module_utils.network.common import utils +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) + + +class Lag_interfacesFacts(object): + """ The vyos lag_interfaces fact class + """ + + def __init__(self, module, subspec="config", options="options"): + self._module = module + self.argument_spec = Lag_interfacesArgs.argument_spec + spec = deepcopy(self.argument_spec) + if subspec: + if options: + facts_argument_spec = spec[subspec][options] + else: + facts_argument_spec = spec[subspec] + else: + facts_argument_spec = spec + + self.generated_spec = utils.generate_dict(facts_argument_spec) + + def populate_facts(self, connection, ansible_facts, data=None): + """ Populate the facts for lag_interfaces + :param module: the module instance + :param connection: the device connection + :param data: previously collected conf + :rtype: dictionary + :returns: facts + """ + if not data: + data = connection.get_config() + + objs = [] + lag_names = findall(r"^set interfaces bonding (\S+)", data, M) + if lag_names: + for lag in set(lag_names): + lag_regex = r" %s .+$" % lag + cfg = findall(lag_regex, data, M) + obj = self.render_config(cfg) + + output = connection.run_commands( + ["show interfaces bonding " + lag + " slaves"] + ) + lines = output[0].splitlines() + members = [] + member = {} + if len(lines) > 1: + for line in lines[2:]: + splitted_line = line.split() + + if len(splitted_line) > 1: + member["member"] = splitted_line[0] + members.append(member) + else: + members = [] + member = {} + obj["name"] = lag.strip("'") + if members: + obj["members"] = members + + if obj: + objs.append(obj) + + facts = {} + if objs: + facts["lag_interfaces"] = [] + params = utils.validate_config( + self.argument_spec, {"config": objs} + ) + for cfg in params["config"]: + facts["lag_interfaces"].append(utils.remove_empties(cfg)) + + ansible_facts["ansible_network_resources"].update(facts) + return ansible_facts + + def render_config(self, conf): + """ + Render config as dictionary structure and delete keys + from spec for null values + + :param spec: The facts tree, generated from the argspec + :param conf: The configuration + :rtype: dictionary + :returns: The generated config + """ + arp_monitor_conf = "\n".join( + filter(lambda x: ("arp-monitor" in x), conf) + ) + hash_policy_conf = "\n".join( + filter(lambda x: ("hash-policy" in x), conf) + ) + lag_conf = "\n".join(filter(lambda x: ("bond" in x), conf)) + config = self.parse_attribs(["mode", "primary"], lag_conf) + config["arp_monitor"] = self.parse_arp_monitor(arp_monitor_conf) + config["hash_policy"] = self.parse_hash_policy(hash_policy_conf) + + return utils.remove_empties(config) + + def parse_attribs(self, attribs, conf): + config = {} + for item in attribs: + value = utils.parse_conf_arg(conf, item) + if value: + config[item] = value.strip("'") + else: + config[item] = None + return utils.remove_empties(config) + + def parse_arp_monitor(self, conf): + arp_monitor = None + if conf: + arp_monitor = {} + target_list = [] + interval = search(r"^.*arp-monitor interval (.+)", conf, M) + targets = findall(r"^.*arp-monitor target '(.+)'", conf, M) + if targets: + for target in targets: + target_list.append(target) + arp_monitor["target"] = target_list + if interval: + value = interval.group(1).strip("'") + arp_monitor["interval"] = int(value) + return arp_monitor + + def parse_hash_policy(self, conf): + hash_policy = None + if conf: + hash_policy = search(r"^.*hash-policy (.+)", conf, M) + hash_policy = hash_policy.group(1).strip("'") + return hash_policy diff --git a/plugins/module_utils/network/vyos/utils/utils.py b/plugins/module_utils/network/vyos/utils/utils.py index 5fd0da2..d6c11bd 100644 --- a/plugins/module_utils/network/vyos/utils/utils.py +++ b/plugins/module_utils/network/vyos/utils/utils.py @@ -4,8 +4,6 @@ # (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # utils - - from __future__ import absolute_import, division, print_function __metaclass__ = type @@ -67,3 +65,42 @@ def diff_list_of_dicts(want, have): diff.append(dict((x, y) for x, y in element)) return diff + + +def list_diff_have_only(want_list, have_list): + if have_list and not want_list: + diff = have_list + elif not have_list: + diff = None + else: + diff = [ + i + for i in have_list + want_list + if i in have_list and i not in want_list + ] + return diff + + +def list_diff_want_only(want_list, have_list): + if have_list and not want_list: + diff = None + elif not have_list: + diff = want_list + else: + diff = [ + i + for i in have_list + want_list + if i in want_list and i not in have_list + ] + return diff + + +def get_lst_diff_for_dicts(want, have, lst): + if not have: + diff = want.get(lst) or [] + + else: + want_elements = want.get(lst) or {} + have_elements = have.get(lst) or {} + diff = list_diff_want_only(want_elements, have_elements) + return diff diff --git a/plugins/modules/_vyos_interface.py b/plugins/modules/_vyos_interface.py index 5128574..71a98c5 100644 --- a/plugins/modules/_vyos_interface.py +++ b/plugins/modules/_vyos_interface.py @@ -183,7 +183,6 @@ 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, ) diff --git a/plugins/modules/_vyos_l3_interface.py b/plugins/modules/_vyos_l3_interface.py index a504e7c..054d810 100644 --- a/plugins/modules/_vyos_l3_interface.py +++ b/plugins/modules/_vyos_l3_interface.py @@ -110,7 +110,6 @@ 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, ) diff --git a/plugins/modules/_vyos_linkagg.py b/plugins/modules/_vyos_linkagg.py new file mode 100644 index 0000000..95fbae9 --- /dev/null +++ b/plugins/modules/_vyos_linkagg.py @@ -0,0 +1,317 @@ +#!/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 <http://www.gnu.org/licenses/>. +# + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["deprecated"], + "supported_by": "network", +} + + +DOCUMENTATION = """ +--- +module: vyos_linkagg +version_added: "2.4" +author: "Ricardo Carrillo Cruz (@rcarrillocruz)" +short_description: Manage link aggregation groups on VyOS network devices +description: + - This module provides declarative management of link aggregation groups + on VyOS network devices. +deprecated: + removed_in: '2.13' + alternative: vyos_lag_interfaces + why: Updated modules released with more functionality. +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 +""" + +EXAMPLES = """ +- name: configure link aggregation group + vyos_linkagg: + name: bond0 + members: + - eth0 + - eth1 + +- name: remove configuration + vyos_linkagg: + name: bond0 + state: absent + +- name: Create aggregate of linkagg definitions + vyos_linkagg: + aggregate: + - { name: bond0, members: [eth1] } + - { name: bond1, members: [eth2] } + +- name: Remove aggregate of linkagg definitions + 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.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_banner.py b/plugins/modules/vyos_banner.py index 447c174..39801b5 100644 --- a/plugins/modules/vyos_banner.py +++ b/plugins/modules/vyos_banner.py @@ -92,7 +92,6 @@ 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, ) diff --git a/plugins/modules/vyos_command.py b/plugins/modules/vyos_command.py index b812fae..892e853 100644 --- a/plugins/modules/vyos_command.py +++ b/plugins/modules/vyos_command.py @@ -150,7 +150,6 @@ from ansible.module_utils.network.common.utils import ( from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( run_commands, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, ) diff --git a/plugins/modules/vyos_config.py b/plugins/modules/vyos_config.py index 2956063..9ffc654 100644 --- a/plugins/modules/vyos_config.py +++ b/plugins/modules/vyos_config.py @@ -194,7 +194,6 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_config, run_commands, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, get_connection, diff --git a/plugins/modules/vyos_facts.py b/plugins/modules/vyos_facts.py index 1fa5214..6ceb234 100644 --- a/plugins/modules/vyos_facts.py +++ b/plugins/modules/vyos_facts.py @@ -29,6 +29,7 @@ description: author: - Nathaniel Case (@qalthos) - Nilashish Chakraborty (@Nilashishc) + - Rohit Thakur (@rohitthakur2590) extends_documentation_fragment: vyos notes: - Tested against VyOS 1.1.8 @@ -53,7 +54,7 @@ options: specific subset should not be collected. required: false version_added: "2.9" - choices: ['all', 'interfaces', '!interfaces', 'l3_interfaces', '!l3_interfaces'] + choices: ['all', 'interfaces', '!interfaces', 'l3_interfaces', '!l3_interfaces', 'lag_interfaces', '!lag_interfaces'] """ EXAMPLES = """ @@ -140,11 +141,9 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.facts.facts import ( FactsArgs, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.facts.facts import ( Facts, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, ) diff --git a/plugins/modules/vyos_interfaces.py b/plugins/modules/vyos_interfaces.py index ca61f4d..bc179ed 100644 --- a/plugins/modules/vyos_interfaces.py +++ b/plugins/modules/vyos_interfaces.py @@ -859,7 +859,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.interfaces.interfaces import ( InterfacesArgs, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.interfaces.interfaces import ( Interfaces, ) diff --git a/plugins/modules/vyos_l3_interfaces.py b/plugins/modules/vyos_l3_interfaces.py index 1a2e453..25a57d3 100644 --- a/plugins/modules/vyos_l3_interfaces.py +++ b/plugins/modules/vyos_l3_interfaces.py @@ -355,7 +355,6 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.l3_interfaces.l3_interfaces import ( L3_interfacesArgs, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.l3_interfaces.l3_interfaces import ( L3_interfaces, ) diff --git a/plugins/modules/vyos_lag_interfaces.py b/plugins/modules/vyos_lag_interfaces.py new file mode 100644 index 0000000..9707f2e --- /dev/null +++ b/plugins/modules/vyos_lag_interfaces.py @@ -0,0 +1,570 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright 2019 Red Hat +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +############################################# +# WARNING # +############################################# +# +# This file is auto generated by the resource +# module builder playbook. +# +# Do not edit this file manually. +# +# Changes to this file will be over written +# by the resource module builder. +# +# Changes should be made in the model used to +# generate this file or in the resource module +# builder template. +# +############################################# + +""" +The module file for vyos_lag_interfaces +""" + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "network", +} + +DOCUMENTATION = """ +--- +module: vyos_lag_interfaces +version_added: 2.9 +short_description: Manages attributes of link aggregation groups on VyOS network devices. +description: This module manages attributes of link aggregation groups on VyOS network devices. +notes: + - Tested against VyOS 1.1.8 (helium). + - This module works with connection C(network_cli). +author: Rohit Thakur (@rohitthakur2590) +options: + config: + description: A list of link aggregation group configurations. + type: list + suboptions: + name: + description: + - Name of the link aggregation group (LAG) or bond. + type: str + required: True + mode: + description: + - LAG or bond mode. + type: str + choices: + - 802.3ad + - active-backup + - broadcast + - round-robin + - transmit-load-balance + - adaptive-load-balance + - xor-hash + members: + description: + - List of member interfaces for the LAG (bond). + type: list + suboptions: + member: + description: + - Name of the member interface. + type: str + primary: + description: + - Primary device interfaces for the LAG (bond). + type: str + hash_policy: + description: + - LAG or bonding transmit hash policy. + type: str + choices: + - layer2 + - layer2+3 + - layer3+4 + arp_monitor: + description: + - ARP Link monitoring parameters. + type: dict + suboptions: + interval: + description: + - ARP link monitoring frequency in milliseconds. + type: int + target: + description: + - IP address to use for ARP monitoring. + type: list + state: + description: + - The state the configuration should be left in. + type: str + choices: + - merged + - replaced + - overridden + - deleted + default: merged + +""" +EXAMPLES = """ +# Using merged +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 +# set interfaces bonding bond3 +# +- name: Merge provided configuration with device configuration + vyos_lag_interfaces: + config: + - name: bond2 + mode: active-backup + members: + - member: eth2 + - member: eth1 + hash_policy: layer2 + primary: eth2 + + - name: 'bond3' + mode: 'active-backup' + hash_policy: 'layer2+3' + members: + - member: eth3 + primary: 'eth3' + state: merged +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "name": "bond2" +# }, +# { +# "name": "bond3" +# } +# ], +# +# "commands": [ +# "set interfaces bonding bond2 hash-policy 'layer2'", +# "set interfaces bonding bond2 mode 'active-backup'", +# "set interfaces ethernet eth2 bond-group bond2", +# "set interfaces ethernet eth1 bond-group bond2", +# "set interfaces bonding bond2 primary 'eth2'", +# "set interfaces bonding bond3 hash-policy 'layer2+3'", +# "set interfaces bonding bond3 mode 'active-backup'", +# "set interfaces ethernet eth3 bond-group bond3", +# "set interfaces bonding bond3 primary 'eth3'" +# ] +# +# "after": [ +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# } +# ], +# "mode": "active-backup", +# "name": "bond2", +# "primary": "eth2" +# }, +# { +# "hash_policy": "layer2+3", +# "members": [ +# { +# "member": "eth3" +# } +# ], +# "mode": "active-backup", +# "name": "bond3", +# "primary": "eth3" +# } +# ] +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 hash-policy 'layer2' +# set interfaces bonding bond2 mode 'active-backup' +# set interfaces bonding bond2 primary 'eth2' +# set interfaces bonding bond3 hash-policy 'layer2+3' +# set interfaces bonding bond3 mode 'active-backup' +# set interfaces bonding bond3 primary 'eth3' +# set interfaces ethernet eth1 bond-group 'bond2' +# set interfaces ethernet eth2 bond-group 'bond2' +# set interfaces ethernet eth3 bond-group 'bond3' + + +# Using replaced +# +# Before state: +# ------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 hash-policy 'layer2' +# set interfaces bonding bond2 mode 'active-backup' +# set interfaces bonding bond2 primary 'eth2' +# set interfaces bonding bond3 hash-policy 'layer2+3' +# set interfaces bonding bond3 mode 'active-backup' +# set interfaces bonding bond3 primary 'eth3' +# set interfaces ethernet eth1 bond-group 'bond2' +# set interfaces ethernet eth2 bond-group 'bond2' +# set interfaces ethernet eth3 bond-group 'bond3' +# +- name: Replace device configurations of listed LAGs with provided configurations + vyos_lag_interfaces: + config: + - name: bond3 + mode: '802.3ad' + hash_policy: 'layer2' + members: + - member: eth3 + state: replaced +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# } +# ], +# "mode": "active-backup", +# "name": "bond2", +# "primary": "eth2" +# }, +# { +# "hash_policy": "layer2+3", +# "members": [ +# { +# "member": "eth3" +# } +# ], +# "mode": "active-backup", +# "name": "bond3", +# "primary": "eth3" +# } +# ], +# +# "commands": [ +# "delete interfaces bonding bond3 primary", +# "set interfaces bonding bond3 hash-policy 'layer2'", +# "set interfaces bonding bond3 mode '802.3ad'" +# ], +# +# "after": [ +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# } +# ], +# "mode": "active-backup", +# "name": "bond2", +# "primary": "eth2" +# }, +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth3" +# } +# ], +# "mode": "802.3ad", +# "name": "bond3" +# } +# ], +# +# After state: +# ------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 hash-policy 'layer2' +# set interfaces bonding bond2 mode 'active-backup' +# set interfaces bonding bond2 primary 'eth2' +# set interfaces bonding bond3 hash-policy 'layer2' +# set interfaces bonding bond3 mode '802.3ad' +# set interfaces ethernet eth1 bond-group 'bond2' +# set interfaces ethernet eth2 bond-group 'bond2' +# set interfaces ethernet eth3 bond-group 'bond3' + + +# Using overridden +# +# Before state +# -------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 hash-policy 'layer2' +# set interfaces bonding bond2 mode 'active-backup' +# set interfaces bonding bond2 primary 'eth2' +# set interfaces bonding bond3 hash-policy 'layer2' +# set interfaces bonding bond3 mode '802.3ad' +# set interfaces ethernet eth1 bond-group 'bond2' +# set interfaces ethernet eth2 bond-group 'bond2' +# set interfaces ethernet eth3 bond-group 'bond3' +# +- name: Overrides all device configuration with provided configuration + vyos_lag_interfaces: + config: + - name: bond3 + mode: active-backup + members: + - member: eth1 + - member: eth2 + - member: eth3 + primary: eth3 + hash_policy: layer2 + state: overridden +# +# +# ------------------------- +# Module Execution Result +# ------------------------- +# +# "before": [ +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# } +# ], +# "mode": "active-backup", +# "name": "bond2", +# "primary": "eth2" +# }, +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth3" +# } +# ], +# "mode": "802.3ad", +# "name": "bond3" +# } +# ], +# +# "commands": [ +# "delete interfaces bonding bond2 hash-policy", +# "delete interfaces ethernet eth1 bond-group bond2", +# "delete interfaces ethernet eth2 bond-group bond2", +# "delete interfaces bonding bond2 mode", +# "delete interfaces bonding bond2 primary", +# "set interfaces bonding bond3 mode 'active-backup'", +# "set interfaces ethernet eth1 bond-group bond3", +# "set interfaces ethernet eth2 bond-group bond3", +# "set interfaces bonding bond3 primary 'eth3'" +# ], +# +# "after": [ +# { +# "name": "bond2" +# }, +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# }, +# { +# "member": "eth3" +# } +# ], +# "mode": "active-backup", +# "name": "bond3", +# "primary": "eth3" +# } +# ], +# +# +# After state +# ------------ +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 +# set interfaces bonding bond3 hash-policy 'layer2' +# set interfaces bonding bond3 mode 'active-backup' +# set interfaces bonding bond3 primary 'eth3' +# set interfaces ethernet eth1 bond-group 'bond3' +# set interfaces ethernet eth2 bond-group 'bond3' +# set interfaces ethernet eth3 bond-group 'bond3' + + +# Using deleted +# +# Before state +# ------------- +# +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 hash-policy 'layer2' +# set interfaces bonding bond2 mode 'active-backup' +# set interfaces bonding bond2 primary 'eth2' +# set interfaces bonding bond3 hash-policy 'layer2+3' +# set interfaces bonding bond3 mode 'active-backup' +# set interfaces bonding bond3 primary 'eth3' +# set interfaces ethernet eth1 bond-group 'bond2' +# set interfaces ethernet eth2 bond-group 'bond2' +# set interfaces ethernet eth3 bond-group 'bond3' +# +- name: Delete LAG attributes of given interfaces (Note This won't delete the interface itself) + vyos_lag_interfaces: + config: + - name: bond2 + - name: bond3 + state: deleted +# +# +# ------------------------ +# Module Execution Results +# ------------------------ +# +# "before": [ +# { +# "hash_policy": "layer2", +# "members": [ +# { +# "member": "eth1" +# }, +# { +# "member": "eth2" +# } +# ], +# "mode": "active-backup", +# "name": "bond2", +# "primary": "eth2" +# }, +# { +# "hash_policy": "layer2+3", +# "members": [ +# { +# "member": "eth3" +# } +# ], +# "mode": "active-backup", +# "name": "bond3", +# "primary": "eth3" +# } +# ], +# "commands": [ +# "delete interfaces bonding bond2 hash-policy", +# "delete interfaces ethernet eth1 bond-group bond2", +# "delete interfaces ethernet eth2 bond-group bond2", +# "delete interfaces bonding bond2 mode", +# "delete interfaces bonding bond2 primary", +# "delete interfaces bonding bond3 hash-policy", +# "delete interfaces ethernet eth3 bond-group bond3", +# "delete interfaces bonding bond3 mode", +# "delete interfaces bonding bond3 primary" +# ], +# +# "after": [ +# { +# "name": "bond2" +# }, +# { +# "name": "bond3" +# } +# ], +# +# After state +# ------------ +# vyos@vyos:~$ show configuration commands | grep bond +# set interfaces bonding bond2 +# set interfaces bonding bond3 + + +""" +RETURN = """ +before: + description: The configuration prior to the model invocation. + returned: always + type: list + 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 + sample: > + The configuration returned will always be in the same format + of the parameters above. +commands: + description: The set of commands pushed to the remote device. + returned: always + type: list + sample: + - 'set interfaces bonding bond2' + - 'set interfaces bonding bond2 hash-policy layer2' +""" + + +from ansible.module_utils.basic import AnsibleModule +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.argspec.lag_interfaces.lag_interfaces import ( + Lag_interfacesArgs, +) +from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.config.lag_interfaces.lag_interfaces import ( + Lag_interfaces, +) + + +def main(): + """ + Main entry point for module execution + + :returns: the result form module invocation + """ + required_if = [ + ("state", "merged", ("config",)), + ("state", "replaced", ("config",)), + ("state", "overridden", ("config",)), + ] + module = AnsibleModule( + argument_spec=Lag_interfacesArgs.argument_spec, + required_if=required_if, + supports_check_mode=True, + ) + + result = Lag_interfaces(module).execute_module() + module.exit_json(**result) + + +if __name__ == "__main__": + main() diff --git a/plugins/modules/vyos_linkagg.py b/plugins/modules/vyos_linkagg.py index 2fc8d66..294bec1 100644..120000 --- a/plugins/modules/vyos_linkagg.py +++ b/plugins/modules/vyos_linkagg.py @@ -1,309 +1 @@ -#!/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 <http://www.gnu.org/licenses/>. -# - -ANSIBLE_METADATA = { - "metadata_version": "1.1", - "status": ["preview"], - "supported_by": "network", -} - - -DOCUMENTATION = """ ---- -module: vyos_linkagg -version_added: "2.4" -author: "Ricardo Carrillo Cruz (@rcarrillocruz)" -short_description: Manage link aggregation groups on VyOS network devices -description: - - This module provides declarative management of link aggregation groups - on VyOS network devices. -notes: - - Tested against VYOS 1.1.7 -options: - name: - description: - - Name of the link aggregation group. - required: true - 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'] - members: - description: - - List of members of the link aggregation group. - aggregate: - description: List of link aggregation definitions. - state: - description: - - State of the link aggregation group. - default: present - choices: ['present', 'absent', 'up', 'down'] -extends_documentation_fragment: vyos -""" - -EXAMPLES = """ -- name: configure link aggregation group - vyos_linkagg: - name: bond0 - members: - - eth0 - - eth1 - -- name: remove configuration - vyos_linkagg: - name: bond0 - state: absent - -- name: Create aggregate of linkagg definitions - vyos_linkagg: - aggregate: - - { name: bond0, members: [eth1] } - - { name: bond1, members: [eth2] } - -- name: Remove aggregate of linkagg definitions - 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.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() +_vyos_linkagg.py
\ No newline at end of file diff --git a/plugins/modules/vyos_lldp.py b/plugins/modules/vyos_lldp.py index 18a013f..fdec814 100644 --- a/plugins/modules/vyos_lldp.py +++ b/plugins/modules/vyos_lldp.py @@ -69,7 +69,6 @@ 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, ) diff --git a/plugins/modules/vyos_lldp_interface.py b/plugins/modules/vyos_lldp_interface.py index 5d25ea3..494fac4 100644 --- a/plugins/modules/vyos_lldp_interface.py +++ b/plugins/modules/vyos_lldp_interface.py @@ -99,7 +99,6 @@ 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, ) diff --git a/plugins/modules/vyos_logging.py b/plugins/modules/vyos_logging.py index fa0d1cf..8eb5777 100644 --- a/plugins/modules/vyos_logging.py +++ b/plugins/modules/vyos_logging.py @@ -116,7 +116,6 @@ 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, ) diff --git a/plugins/modules/vyos_ping.py b/plugins/modules/vyos_ping.py index c770804..4b21927 100644 --- a/plugins/modules/vyos_ping.py +++ b/plugins/modules/vyos_ping.py @@ -136,11 +136,9 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( run_commands, ) - from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, ) - import re diff --git a/plugins/modules/vyos_static_route.py b/plugins/modules/vyos_static_route.py index 734a1b0..b7307ef 100644 --- a/plugins/modules/vyos_static_route.py +++ b/plugins/modules/vyos_static_route.py @@ -114,7 +114,6 @@ 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, ) diff --git a/plugins/modules/vyos_system.py b/plugins/modules/vyos_system.py index 3f306f8..9fee88f 100644 --- a/plugins/modules/vyos_system.py +++ b/plugins/modules/vyos_system.py @@ -98,7 +98,6 @@ 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, ) diff --git a/plugins/modules/vyos_user.py b/plugins/modules/vyos_user.py index 2bccd49..2a1181a 100644 --- a/plugins/modules/vyos_user.py +++ b/plugins/modules/vyos_user.py @@ -143,7 +143,6 @@ from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import get_config, load_config, ) - from ansible.module_utils.six import iteritems from ansible_collections.vyos.vyos.plugins.module_utils.network.vyos.vyos import ( vyos_argument_spec, diff --git a/plugins/modules/vyos_vlan.py b/plugins/modules/vyos_vlan.py index 6c0fad8..10eaecd 100644 --- a/plugins/modules/vyos_vlan.py +++ b/plugins/modules/vyos_vlan.py @@ -129,7 +129,6 @@ 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, ) diff --git a/test/integration/targets/prepare_vyos_tests/tasks/main.yaml b/test/integration/targets/prepare_vyos_tests/tasks/main.yaml index 494a1d2..a156ea4 100644 --- a/test/integration/targets/prepare_vyos_tests/tasks/main.yaml +++ b/test/integration/targets/prepare_vyos_tests/tasks/main.yaml @@ -1,10 +1,11 @@ --- - name: Ensure required interfaces are present in running-config - cli_config: + network.cli.cli_config: config: "{{ lines }}" vars: lines: | set interfaces ethernet eth0 address dhcp set interfaces ethernet eth1 set interfaces ethernet eth2 - set interfaces loopback lo + delete interfaces loopback lo + ignore_errors: yes diff --git a/test/integration/targets/vyos_command/tests/cli/cli_command.yaml b/test/integration/targets/vyos_command/tests/cli/cli_command.yaml index caeb202..08a7675 100644 --- a/test/integration/targets/vyos_command/tests/cli/cli_command.yaml +++ b/test/integration/targets/vyos_command/tests/cli/cli_command.yaml @@ -4,7 +4,7 @@ - block: - name: get output for single command - cli_command: + network.cli.cli_command: command: show version register: result @@ -14,7 +14,7 @@ - "result.stdout is defined" - name: send invalid command - cli_command: + network.cli.cli_command: command: 'show foo' register: result ignore_errors: yes @@ -27,7 +27,7 @@ - block: - name: test failure for local connection - cli_command: + network.cli.cli_command: command: show version register: result ignore_errors: yes diff --git a/test/integration/targets/vyos_config/tests/cli_config/cli_backup.yaml b/test/integration/targets/vyos_config/tests/cli_config/cli_backup.yaml index a431c01..8057705 100644 --- a/test/integration/targets/vyos_config/tests/cli_config/cli_backup.yaml +++ b/test/integration/targets/vyos_config/tests/cli_config/cli_backup.yaml @@ -22,7 +22,7 @@ with_items: "{{backup_files.files|default([])}}" - name: take config backup - cli_config: + network.cli.cli_config: backup: yes become: yes register: result @@ -43,7 +43,7 @@ - "backup_files.files is defined" - name: take configuration backup in custom filename and directory path - cli_config: + network.cli.cli_config: backup: yes backup_options: filename: backup.cfg @@ -66,7 +66,7 @@ - "backup_file.files is defined" - name: take configuration backup in custom filename - cli_config: + network.cli.cli_config: backup: yes backup_options: filename: backup.cfg @@ -88,7 +88,7 @@ - "backup_file.files is defined" - name: take configuration backup in custom path and default filename - cli_config: + network.cli.cli_config: backup: yes backup_options: dir_path: "{{ role_path }}/backup_test_dir/{{ inventory_hostname_short }}" diff --git a/test/integration/targets/vyos_config/tests/cli_config/cli_basic.yaml b/test/integration/targets/vyos_config/tests/cli_config/cli_basic.yaml index e83db1e..e5e3edb 100644 --- a/test/integration/targets/vyos_config/tests/cli_config/cli_basic.yaml +++ b/test/integration/targets/vyos_config/tests/cli_config/cli_basic.yaml @@ -2,11 +2,11 @@ - debug: msg="START cli_config/cli_basic.yaml on connection={{ ansible_connection }}" - name: setup - remove interface description - cli_config: &rm + network.cli.cli_config: &rm config: delete interfaces loopback lo description - name: configure device with config - cli_config: &conf + network.cli.cli_config: &conf config: set interfaces loopback lo description 'this is a test' register: result @@ -15,7 +15,7 @@ - "result.changed == true" - name: Idempotence - cli_config: *conf + network.cli.cli_config: *conf register: result - assert: @@ -23,6 +23,6 @@ - "result.changed == false" - name: teardown - cli_config: *rm + network.cli.cli_config: *rm - debug: msg="END cli_config/cli_basic.yaml on connection={{ ansible_connection }}" diff --git a/test/integration/targets/vyos_config/tests/cli_config/cli_comment.yaml b/test/integration/targets/vyos_config/tests/cli_config/cli_comment.yaml index 4f9a048..ecc9e8c 100644 --- a/test/integration/targets/vyos_config/tests/cli_config/cli_comment.yaml +++ b/test/integration/targets/vyos_config/tests/cli_config/cli_comment.yaml @@ -2,11 +2,11 @@ - debug: msg="START cli_config/cli_comment.yaml on connection={{ ansible_connection }}" - name: setup - cli_config: &rm + network.cli.cli_config: &rm config: set system host-name {{ inventory_hostname_short }} - name: configure using comment - cli_config: + network.cli.cli_config: config: set system host-name foo commit_comment: this is a test register: result @@ -25,6 +25,6 @@ - "'this is a test' in result.stdout_lines[0][1]" - name: teardown - cli_config: *rm + network.cli.cli_config: *rm - debug: msg="END cli_config/cli_comment.yaml on connection={{ ansible_connection }}" diff --git a/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml b/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml index b798bae..0a44fa4 100644 --- a/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml +++ b/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml @@ -1,6 +1,6 @@ --- - name: Setup - cli_config: + network.cli.cli_config: config: "{{ lines }}" vars: lines: | diff --git a/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml b/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml index 6d559c1..ce4723d 100644 --- a/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml +++ b/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml @@ -1,6 +1,6 @@ --- - name: Remove Config - cli_config: + network.cli.cli_config: config: "{{ lines }}" vars: lines: | diff --git a/test/integration/targets/vyos_interfaces/vars/main.yaml b/test/integration/targets/vyos_interfaces/vars/main.yaml index e3bd818..b973752 100644 --- a/test/integration/targets/vyos_interfaces/vars/main.yaml +++ b/test/integration/targets/vyos_interfaces/vars/main.yaml @@ -12,9 +12,6 @@ merged: - name: "eth2" enabled: true - - name: "lo" - enabled: true - commands: - "set interfaces ethernet eth1 description 'Configured by Ansible - Interface 1'" - "set interfaces ethernet eth1 mtu '1500'" @@ -28,9 +25,6 @@ merged: - "set interfaces ethernet eth2 disable" after: - - name: "lo" - enabled: true - - name: "eth0" enabled: true duplex: "auto" @@ -85,9 +79,6 @@ populate: duplex: "auto" speed: "auto" - - name: "lo" - enabled: true - replaced: commands: - "delete interfaces ethernet eth1 mtu" @@ -103,9 +94,6 @@ replaced: - "set interfaces ethernet eth2 mtu '1400'" after: - - name: "lo" - enabled: true - - name: "eth1" description: "Replaced by Ansible" enabled: true @@ -144,9 +132,6 @@ overridden: - "set interfaces ethernet eth2 mtu '1200'" after: - - name: "lo" - enabled: true - - name: "eth0" enabled: true speed: "auto" @@ -180,9 +165,6 @@ deleted: - "delete interfaces ethernet eth2 vif 200 description" after: - - name: "lo" - enabled: true - - name: "eth0" enabled: true speed: "auto" @@ -202,9 +184,6 @@ deleted: round_trip: after: - - name: "lo" - enabled: true - - name: "eth0" enabled: true speed: "auto" diff --git a/test/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml b/test/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml index 248a9bb..fc0bbb2 100644 --- a/test/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml +++ b/test/integration/targets/vyos_l3_interfaces/tests/cli/_populate.yaml @@ -1,6 +1,6 @@ --- - name: Setup - cli_config: + network.cli.cli_config: config: "{{ lines }}" vars: lines: | diff --git a/test/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml b/test/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml index 31af5ba..95b2b8c 100644 --- a/test/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml +++ b/test/integration/targets/vyos_l3_interfaces/tests/cli/_remove_config.yaml @@ -1,6 +1,6 @@ --- - name: Remove Config - cli_config: + network.cli.cli_config: config: "{{ lines }}" vars: lines: | diff --git a/test/integration/targets/vyos_lag_interfaces/defaults/main.yaml b/test/integration/targets/vyos_lag_interfaces/defaults/main.yaml new file mode 100644 index 0000000..164afea --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/defaults/main.yaml @@ -0,0 +1,3 @@ +--- +testcase: "[^_].*" +test_items: [] diff --git a/test/integration/targets/vyos_lag_interfaces/meta/main.yaml b/test/integration/targets/vyos_lag_interfaces/meta/main.yaml new file mode 100644 index 0000000..f88bce5 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/meta/main.yaml @@ -0,0 +1,2 @@ +dependencies: + - prepare_vyos_tests diff --git a/test/integration/targets/vyos_lag_interfaces/tasks/cli.yaml b/test/integration/targets/vyos_lag_interfaces/tasks/cli.yaml new file mode 100644 index 0000000..655e51e --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tasks/cli.yaml @@ -0,0 +1,19 @@ +--- +- name: Collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + use_regex: true + register: test_cases + delegate_to: localhost + +- name: Set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: Run test case (connection=network_cli) + include: "{{ test_case_to_run }}" + vars: + ansible_connection: network_cli + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/vyos_lag_interfaces/tasks/main.yaml b/test/integration/targets/vyos_lag_interfaces/tasks/main.yaml new file mode 100644 index 0000000..415c99d --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml new file mode 100644 index 0000000..c479f79 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml @@ -0,0 +1,8 @@ +--- +- name: Add Bond + network.cli.cli_config: + config: "{{ lines }}" + vars: + lines: | + set interfaces bonding bond0 + set interfaces bonding bond1 diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml new file mode 100644 index 0000000..6139508 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml @@ -0,0 +1,16 @@ +--- +- name: Setup + network.cli.cli_config: + config: "{{ lines }}" + vars: + lines: | + set interfaces bonding bond0 + set interfaces bonding bond0 hash-policy 'layer2' + set interfaces bonding bond0 mode 'active-backup' + set interfaces ethernet eth1 bond-group bond0 + set interfaces bonding bond1 + set interfaces bonding bond0 primary 'eth1' + set interfaces bonding bond1 hash-policy 'layer2+3' + set interfaces bonding bond1 mode 'active-backup' + set interfaces ethernet eth2 bond-group bond1 + set interfaces bonding bond1 primary 'eth2' diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml new file mode 100644 index 0000000..1d7ee69 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml @@ -0,0 +1,8 @@ +--- +- name: Remove Bond + network.cli.cli_config: + config: "{{ lines }}" + vars: + lines: | + delete interfaces bonding bond0 + delete interfaces bonding bond1 diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_config.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_config.yaml new file mode 100644 index 0000000..c5d3657 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/_remove_config.yaml @@ -0,0 +1,14 @@ +--- +- name: Remove Config + network.cli.cli_config: + config: "{{ lines }}" + vars: + lines: | + delete interfaces bonding bond0 hash-policy + delete interfaces ethernet eth1 bond-group bond0 + delete interfaces bonding bond0 mode + delete interfaces bonding bond0 primary + delete interfaces bonding bond1 hash-policy + delete interfaces ethernet eth2 bond-group bond1 + delete interfaces bonding bond1 mode + delete interfaces bonding bond1 primary diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml new file mode 100644 index 0000000..db6fb88 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml @@ -0,0 +1,46 @@ +--- +- debug: + msg: "Start vyos_lag_interfaces deleted integration tests ansible_connection={{ ansible_connection }}" + +- include_tasks: _populate.yaml + +- block: + - name: Delete attributes of given LAG interfaces. + vyos.vyos.vyos_lag_interfaces: &deleted + config: + - name: bond0 + - name: bond1 + state: deleted + register: result + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that the correct set of commands were generated + assert: + that: + - "{{ deleted['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that the after dicts were correctly generated + assert: + that: + - "{{ deleted['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Delete attributes of given interfaces (IDEMPOTENT) + vyos.vyos.vyos_lag_interfaces: *deleted + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result.changed == false" + + - name: Assert that the before dicts were correctly generated + assert: + that: + - "{{ deleted['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml new file mode 100644 index 0000000..78c9de1 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml @@ -0,0 +1,60 @@ +--- +- debug: + msg: "START vyos_lag_interfaces merged integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_bond.yaml + +- include_tasks: _add_bond.yaml + +- block: + - name: Merge the provided configuration with the exisiting running configuration + vyos.vyos.vyos_lag_interfaces: &merged + config: + - name: bond0 + hash_policy: "layer2" + mode: "active-backup" + members: + - member: eth1 + primary: eth1 + + - name: bond1 + hash_policy: "layer2+3" + mode: "active-backup" + members: + - member: eth2 + primary: eth2 + state: merged + register: result + + - name: Assert that before dicts were correctly generated + assert: + that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ merged['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dicts was correctly generated + assert: + that: + - "{{ merged['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) + vyos.vyos.vyos_lag_interfaces: *merged + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ merged['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml new file mode 100644 index 0000000..6139d9f --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml @@ -0,0 +1,54 @@ +--- +- debug: + msg: "START vyos_lag_interfaces overridden integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_bond.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Overrides all device configuration with provided configuration + vyos.vyos.vyos_lag_interfaces: &overridden + config: + - name: bond1 + mode: "active-backup" + members: + - member: eth2 + primary: eth2 + hash_policy: layer2 + state: overridden + register: result + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that correct commands were generated + assert: + that: + - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that after dicts were correctly generated + assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Overrides all device configuration with provided configurations (IDEMPOTENT) + vyos.vyos.vyos_lag_interfaces: *overridden + register: result + + - name: Assert that the previous task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dicts were correctly generated + assert: + that: + - "{{ overridden['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml new file mode 100644 index 0000000..ce469e0 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml @@ -0,0 +1,51 @@ +--- +- debug: + msg: "START vyos_lag_interfaces replaced integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _populate.yaml + +- block: + - name: Replace device configurations of listed LAG interfaces with provided configurations + vyos.vyos.vyos_lag_interfaces: &replaced + config: + - name: bond1 + mode: "802.3ad" + hash_policy: "layer2" + members: + - member: eth2 + state: replaced + register: result + + - name: Assert that correct set of commands were generated + assert: + that: + - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + + - name: Assert that before dicts are correctly generated + assert: + that: + - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + + - name: Assert that after dict is correctly generated + assert: + that: + - "{{ replaced['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Replace device configurations of listed LAG interfaces with provided configurarions (IDEMPOTENT) + vyos.vyos.vyos_lag_interfaces: *replaced + register: result + + - name: Assert that task was idempotent + assert: + that: + - "result['changed'] == false" + + - name: Assert that before dict is correctly generated + assert: + that: + - "{{ replaced['after'] | symmetric_difference(result['before']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml b/test/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml new file mode 100644 index 0000000..eb3814e --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml @@ -0,0 +1,69 @@ +--- +- debug: + msg: "START vyos_lag_interfaces round trip integration tests on connection={{ ansible_connection }}" + +- include_tasks: _remove_config.yaml + +- include_tasks: _remove_bond.yaml + +- block: + - name: Apply the provided configuration (base config) + vyos.vyos.vyos_lag_interfaces: + config: + - name: bond0 + hash_policy: "layer2" + mode: "active-backup" + members: + - member: eth1 + primary: eth1 + + - name: bond1 + hash_policy: "layer2+3" + mode: "active-backup" + members: + - member: eth2 + primary: eth2 + + state: merged + register: base_config + + - name: Gather lag_interfaces facts + vyos.vyos.vyos_facts: + gather_subset: + - default + gather_network_resources: + - lag_interfaces + + - name: Apply the provided configuration (config to be reverted) + vyos.vyos.vyos_lag_interfaces: + config: + - name: bond0 + hash_policy: "layer2+3" + mode: "802.3ad" + members: + - member: eth1 + + - name: bond1 + hash_policy: "layer2" + mode: "xor-hash" + members: + - member: eth2 + state: merged + register: result + + - name: Assert that changes were applied + assert: + that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}" + + - name: Revert back to base config using facts round trip + vyos.vyos.vyos_lag_interfaces: + config: "{{ ansible_facts['network_resources']['lag_interfaces'] }}" + state: overridden + register: revert + + - name: Assert that config was reverted + assert: + that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}" + + always: + - include_tasks: _remove_config.yaml diff --git a/test/integration/targets/vyos_lag_interfaces/vars/main.yaml b/test/integration/targets/vyos_lag_interfaces/vars/main.yaml new file mode 100644 index 0000000..8726e39 --- /dev/null +++ b/test/integration/targets/vyos_lag_interfaces/vars/main.yaml @@ -0,0 +1,115 @@ +--- +merged: + before: + - name: "bond0" + + - name: "bond1" + + commands: + - "set interfaces bonding bond0 hash-policy 'layer2'" + - "set interfaces bonding bond0 mode 'active-backup'" + - "set interfaces ethernet eth1 bond-group 'bond0'" + - "set interfaces bonding bond0 primary 'eth1'" + - "set interfaces bonding bond1 hash-policy 'layer2+3'" + - "set interfaces bonding bond1 mode 'active-backup'" + - "set interfaces ethernet eth2 bond-group 'bond1'" + - "set interfaces bonding bond1 primary 'eth2'" + + after: + - name: "bond0" + hash_policy: "layer2" + members: + - member: eth1 + mode: "active-backup" + primary: eth1 + + - name: "bond1" + hash_policy: "layer2+3" + members: + - member: eth2 + mode: "active-backup" + primary: eth2 + +populate: + - name: "bond0" + hash_policy: "layer2" + members: + - member: eth1 + mode: "active-backup" + primary: eth1 + + - name: "bond1" + hash_policy: "layer2+3" + members: + - member: eth2 + mode: "active-backup" + primary: eth2 + +replaced: + commands: + - "delete interfaces bonding bond1 primary" + - "set interfaces bonding bond1 hash-policy 'layer2'" + - "set interfaces bonding bond1 mode '802.3ad'" + + after: + - name: "bond0" + hash_policy: "layer2" + members: + - member: eth1 + mode: "active-backup" + primary: eth1 + + - name: "bond1" + hash_policy: "layer2" + members: + - member: eth2 + mode: "802.3ad" + +overridden: + commands: + - "delete interfaces bonding bond0 hash-policy" + - "delete interfaces ethernet eth1 bond-group 'bond0'" + - "delete interfaces bonding bond0 mode" + - "delete interfaces bonding bond0 primary" + - "set interfaces bonding bond1 hash-policy 'layer2'" + + after: + - name: "bond0" + - name: "bond1" + hash_policy: "layer2" + members: + - member: eth2 + mode: "active-backup" + primary: eth2 + +deleted: + commands: + - "delete interfaces bonding bond0 hash-policy" + - "delete interfaces ethernet eth1 bond-group 'bond0'" + - "delete interfaces bonding bond0 mode" + - "delete interfaces bonding bond0 primary" + - "delete interfaces bonding bond1 hash-policy" + - "delete interfaces ethernet eth2 bond-group 'bond1'" + - "delete interfaces bonding bond1 mode" + - "delete interfaces bonding bond1 primary" + + after: + - name: "bond0" + + - name: "bond1" + +round_trip: + after: + - name: "bond0" + hash_policy: "layer2+3" + members: + - member: eth1 + mode: "802.3ad" + primary: eth1 + + - name: "bond1" + hash_policy: "layer2" + members: + - member: eth2 + mode: "xor-hash" + primary: eth2 |