diff --git a/changelogs/fragments/T7010-lag-interfaces-integration-tests.yaml b/changelogs/fragments/T7010-lag-interfaces-integration-tests.yaml new file mode 100644 index 00000000..8a236f5b --- /dev/null +++ b/changelogs/fragments/T7010-lag-interfaces-integration-tests.yaml @@ -0,0 +1,4 @@ +--- +minor_changes: + - fix integration tests for `lag_interfaces` + - add unit tests for `lag_interfaces` 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 index 835615b9..6890fe0c 100644 --- a/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/config/lag_interfaces/lag_interfaces.py @@ -1,435 +1,433 @@ # 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.six import iteritems from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base import ( ConfigBase, ) from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import ( dict_diff, to_list, ) 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 ( get_lst_diff_for_dicts, list_diff_have_only, list_diff_want_only, search_obj_in_list, ) 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, data=None): """Get the 'facts' (the current configuration) :rtype: A dictionary :returns: The current configuration as a dictionary """ facts, _warnings = Facts(self._module).get_facts( self.gather_subset, self.gather_network_resources, data=data, ) 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} warnings = list() commands = list() if self.state in self.ACTION_STATES: existing_lag_interfaces_facts = self.get_lag_interfaces_facts() else: existing_lag_interfaces_facts = [] if self.state in self.ACTION_STATES or self.state == "rendered": commands.extend(self.set_config(existing_lag_interfaces_facts)) if commands and self.state in self.ACTION_STATES: if not self._module.check_mode: self._connection.edit_config(commands) result["changed"] = True if self.state in self.ACTION_STATES: result["commands"] = commands if self.state in self.ACTION_STATES or self.state == "gathered": changed_lag_interfaces_facts = self.get_lag_interfaces_facts() elif self.state == "rendered": result["rendered"] = commands elif self.state == "parsed": running_config = self._module.params["running_config"] if not running_config: self._module.fail_json( msg="value of running_config parameter must not be empty for state parsed", ) result["parsed"] = self.get_lag_interfaces_facts(data=running_config) else: changed_lag_interfaces_facts = [] if self.state in self.ACTION_STATES: result["before"] = existing_lag_interfaces_facts if result["changed"]: result["after"] = changed_lag_interfaces_facts elif self.state == "gathered": result["gathered"] = 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 = [] if self.state in ("merged", "replaced", "overridden", "rendered") and not want: self._module.fail_json( msg="value of config parameter must not be empty for state {0}".format(self.state), ) if self.state == "overridden": commands.extend(self._state_overridden(want, have)) elif self.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 self.state in ("merged", "rendered"): commands.extend(self._state_merged(want_item, obj_in_have)) elif self.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", + "member interface", + key["member"], ), ) 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=want["name"] + " arp-monitor", attrib="interval", value=str(arp_monitor["interval"]), ), ) if diff_targets: for target in diff_targets: commands.append( self._compute_command( key=want["name"] + " arp-monitor", attrib="target", value=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"], + "member interface", + member["member"], 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 {} 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( self._compute_command( key=have["name"] + " arp-monitor", attrib="interval", remove=True, ), ) 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( self._compute_command( key=have["name"] + " arp-monitor", attrib="target", value=target, remove=True, ), ) 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["member"], "bond-group", have["name"], True, "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/lag_interfaces/lag_interfaces.py b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py index 78638bb7..8e1c8624 100644 --- a/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py +++ b/plugins/module_utils/network/vyos/facts/lag_interfaces/lag_interfaces.py @@ -1,137 +1,145 @@ # # -*- 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 copy import deepcopy from re import M, findall, search from ansible_collections.ansible.netcommon.plugins.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 get_config(self, connection): + """Get the configuration from the device + :param connection: the device connection + :rtype: string + :returns: The configuration + """ + return connection.get_config() + 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() + data = self.get_config(connection) 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) members = [] member = {} - group_regex = r".*eth.* '%s'" % lag + group_regex = r"%s member interface .*eth.*" % lag g_cfg = findall(group_regex, data, M) for item in g_cfg: - output = search("^set interfaces ethernet (\\S+)", item, M) + output = search("member interface '(\\S+)'", item, M) if output: member["member"] = output.group(1).strip("'") - members.append(member) + members.append(deepcopy(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/tests/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml index fa191ed3..3c6743c2 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_add_bond.yaml @@ -1,8 +1,8 @@ --- - ansible.builtin.include_tasks: _remove_bond.yaml - name: Add Bond - vars: - lines: "set interfaces bonding bond0\nset interfaces bonding bond1\n" ansible.netcommon.cli_config: - config: "{{ lines }}" + config: |- + set interfaces bonding bond0 + set interfaces bonding bond1 diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_parsed_config.cfg b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_parsed_config.cfg index 1a275bdd..d9f18551 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_parsed_config.cfg +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_parsed_config.cfg @@ -1,8 +1,8 @@ set interfaces bonding bond0 hash-policy 'layer2' set interfaces bonding bond0 mode 'active-backup' set interfaces bonding bond0 primary 'eth1' set interfaces bonding bond1 hash-policy 'layer2+3' set interfaces bonding bond1 mode 'active-backup' set interfaces bonding bond1 primary 'eth2' -set interfaces ethernet eth1 bond-group 'bond0' -set interfaces ethernet eth2 bond-group 'bond1' +set interfaces bonding bond0 member interface 'eth1' +set interfaces bonding bond1 member interface 'eth2' diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml index be0d6351..728a8cf0 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_populate.yaml @@ -1,18 +1,18 @@ --- - ansible.builtin.include_tasks: _add_bond.yaml - name: Setup 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 bond0 member interface 'eth1' 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 member interface 'eth2' set interfaces bonding bond1 primary 'eth2' ansible.netcommon.cli_config: config: "{{ lines }}" diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml index e22a6ca7..3643ae32 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/_remove_bond.yaml @@ -1,21 +1,21 @@ - name: Remove Config vars: lines: |- delete interfaces bonding bond0 hash-policy - delete interfaces ethernet eth1 bond-group bond0 + delete interfaces bonding bond0 member interface 'eth1' 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 member interface 'eth2' delete interfaces bonding bond1 mode delete interfaces bonding bond1 primary ansible.netcommon.cli_config: config: "{{ lines }}" - name: Remove Bond vars: lines: | delete interfaces bonding bond0 delete interfaces bonding bond1 ansible.netcommon.cli_config: config: "{{ lines }}" diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml index e30be078..d273f040 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/deleted.yaml @@ -1,46 +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. register: result vyos.vyos.vyos_lag_interfaces: &id001 config: - name: bond0 - name: bond1 state: deleted - name: Assert that the before dicts were correctly generated assert: that: - - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + - 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 }}" + - 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 }}" + - deleted['after'] | symmetric_difference(result['after']) |length == 0 - name: Delete attributes of given interfaces (IDEMPOTENT) register: result vyos.vyos.vyos_lag_interfaces: *id001 - 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 }}" + - deleted['after'] | symmetric_difference(result['before']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/gathered.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/gathered.yaml index f6c281f5..ff7f9c7d 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/gathered.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/gathered.yaml @@ -1,20 +1,20 @@ --- - debug: msg: START vyos_lag_interfaces gathered integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Gather the provided configuration with the existing running configuration register: result vyos.vyos.vyos_lag_interfaces: config: state: gathered - name: Assert that gathered dicts was correctly generated assert: that: - - "{{ populate | symmetric_difference(result['gathered']) |length == 0 }}" + - populate | symmetric_difference(result['gathered']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml index 64ac9a4a..2274f301 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/merged.yaml @@ -1,55 +1,55 @@ --- - debug: msg: START vyos_lag_interfaces merged integration tests on connection={{ ansible_connection }} - include_tasks: _add_bond.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.vyos_lag_interfaces: &id001 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 - name: Assert that before dicts were correctly generated assert: - that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}" + 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 }}" + - 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 }}" + - merged['after'] | symmetric_difference(result['after']) |length == 0 - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) register: result vyos.vyos.vyos_lag_interfaces: *id001 - 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 }}" + - merged['after'] | symmetric_difference(result['before']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml index 482d54d1..b1f0065c 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/overridden.yaml @@ -1,49 +1,49 @@ --- - debug: msg: START vyos_lag_interfaces overridden integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Overrides all device configuration with provided configuration register: result vyos.vyos.vyos_lag_interfaces: &id001 config: - name: bond1 mode: active-backup members: - member: eth2 primary: eth2 hash_policy: layer2 state: overridden - name: Assert that before dicts were correctly generated assert: that: - - "{{ populate | symmetric_difference(result['before']) |length == 0 }}" + - populate | symmetric_difference(result['before']) |length == 0 - name: Assert that correct commands were generated assert: that: - - "{{ overridden['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - 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 }}" + - overridden['after'] | symmetric_difference(result['after']) |length == 0 - name: Overrides all device configuration with provided configurations (IDEMPOTENT) register: result vyos.vyos.vyos_lag_interfaces: *id001 - 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 }}" + - overridden['after'] | symmetric_difference(result['before']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/parsed.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/parsed.yaml index 81a7b46d..c3dd4ff2 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/parsed.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/parsed.yaml @@ -1,27 +1,27 @@ --- - debug: msg: START vyos_lag_interfaces parsed integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Gather lag_interfaces facts register: lag_interfaces_facts vyos.vyos.vyos_facts: gather_subset: - default gather_network_resources: - lag_interfaces - name: Provide the running configuration for parsing (config to be parsed) register: result vyos.vyos.vyos_lag_interfaces: running_config: "{{ lookup('file', '_parsed_config.cfg') }}" state: parsed - name: Assert that correct parsing done assert: - that: "{{ ansible_facts['network_resources']['lag_interfaces'] | symmetric_difference(result['parsed']) |length == 0 }}" + that: ansible_facts['network_resources']['lag_interfaces'] | symmetric_difference(result['parsed']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/rendered.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/rendered.yaml index 7d86a3ae..0241b7c4 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/rendered.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/rendered.yaml @@ -1,32 +1,32 @@ --- - debug: msg: START vyos_lag_interfaces rendered integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Structure provided configuration into device specific commands register: result vyos.vyos.vyos_lag_interfaces: config: - 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 state: rendered - name: Assert that correct set of commands were generated assert: that: - - "{{ rendered['commands'] | symmetric_difference(result['rendered']) |length == 0 }}" + - rendered['commands'] | symmetric_difference(result['rendered']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml index 66e55df7..caf3f1d1 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/replaced.yaml @@ -1,48 +1,48 @@ --- - debug: msg: START vyos_lag_interfaces replaced integration tests on connection={{ ansible_connection }} - include_tasks: _populate.yaml - block: - name: Replace device configurations of listed LAG interfaces with provided configurations register: result vyos.vyos.vyos_lag_interfaces: &id001 config: - name: bond1 mode: 802.3ad hash_policy: layer2 members: - member: eth2 state: replaced - name: Assert that correct set of commands were generated assert: that: - - "{{ replaced['commands'] | symmetric_difference(result['commands']) |length == 0 }}" + - 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 }}" + - 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 }}" + - replaced['after'] | symmetric_difference(result['after']) |length == 0 - name: Replace device configurations of listed LAG interfaces with provided configurarions (IDEMPOTENT) register: result vyos.vyos.vyos_lag_interfaces: *id001 - 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 }}" + - replaced['after'] | symmetric_difference(result['before']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml index 5e9dc752..b4c07abe 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/cli/rtt.yaml @@ -1,65 +1,65 @@ --- - debug: msg: START vyos_lag_interfaces round trip integration tests on connection={{ ansible_connection }} - include_tasks: _remove_bond.yaml - block: - name: Apply the provided configuration (base config) register: 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 - 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) register: result vyos.vyos.vyos_lag_interfaces: config: - name: bond0 hash_policy: layer2+3 - mode: 802.3ad + mode: transmit-load-balance members: - member: eth1 - name: bond1 hash_policy: layer2 - mode: xor-hash + mode: adaptive-load-balance members: - member: eth2 state: merged - name: Assert that changes were applied assert: - that: "{{ round_trip['after'] | symmetric_difference(result['after']) |length == 0 }}" + that: round_trip['after'] | symmetric_difference(result['after']) |length == 0 - name: Revert back to base config using facts round trip register: revert vyos.vyos.vyos_lag_interfaces: config: "{{ ansible_facts['network_resources']['lag_interfaces'] }}" state: overridden - name: Assert that config was reverted assert: - that: "{{ base_config['after'] | symmetric_difference(revert['after']) |length == 0 }}" + that: base_config['after'] | symmetric_difference(revert['after']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/tests/redirection/cli/shortname.yaml b/tests/integration/targets/vyos_lag_interfaces/tests/redirection/cli/shortname.yaml index 40c5fe2b..fd2c10a5 100644 --- a/tests/integration/targets/vyos_lag_interfaces/tests/redirection/cli/shortname.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/tests/redirection/cli/shortname.yaml @@ -1,55 +1,55 @@ --- - debug: msg: START lag_interfaces merged on connection={{ ansible_connection }} - include_tasks: _add_bond.yaml - block: - name: Merge the provided configuration with the existing running configuration register: result vyos.vyos.lag_interfaces: &id001 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 - name: Assert that before dicts were correctly generated assert: - that: "{{ merged['before'] | symmetric_difference(result['before']) |length == 0 }}" + 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 }}" + - 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 }}" + - merged['after'] | symmetric_difference(result['after']) |length == 0 - name: Merge the provided configuration with the existing running configuration (IDEMPOTENT) register: result vyos.vyos.lag_interfaces: *id001 - 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 }}" + - merged['after'] | symmetric_difference(result['before']) |length == 0 always: - include_tasks: _remove_bond.yaml diff --git a/tests/integration/targets/vyos_lag_interfaces/vars/main.yaml b/tests/integration/targets/vyos_lag_interfaces/vars/main.yaml index 9784fb97..40b6a9d8 100644 --- a/tests/integration/targets/vyos_lag_interfaces/vars/main.yaml +++ b/tests/integration/targets/vyos_lag_interfaces/vars/main.yaml @@ -1,109 +1,109 @@ --- 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 member interface 'eth1' - 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 member interface 'eth2' - 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 member interface 'eth1' - 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 member interface 'eth1' - 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 member interface 'eth2' - delete interfaces bonding bond1 mode - delete interfaces bonding bond1 primary after: - name: bond0 - name: bond1 rendered: commands: - set interfaces bonding bond0 hash-policy 'layer2' - - set interfaces ethernet eth1 bond-group 'bond0' + - set interfaces bonding bond0 member interface 'eth1' - set interfaces bonding bond0 mode 'active-backup' - set interfaces bonding bond0 primary 'eth1' - set interfaces bonding bond1 hash-policy 'layer2+3' - - set interfaces ethernet eth2 bond-group 'bond1' + - set interfaces bonding bond1 member interface 'eth2' - set interfaces bonding bond1 mode 'active-backup' - set interfaces bonding bond1 primary 'eth2' round_trip: after: - name: bond0 hash_policy: layer2+3 members: - member: eth1 - mode: 802.3ad + mode: transmit-load-balance primary: eth1 - name: bond1 hash_policy: layer2 members: - member: eth2 - mode: xor-hash + mode: adaptive-load-balance primary: eth2 diff --git a/tests/unit/modules/network/vyos/fixtures/vyos_lag_interfaces_config.cfg b/tests/unit/modules/network/vyos/fixtures/vyos_lag_interfaces_config.cfg new file mode 100644 index 00000000..645cbdb1 --- /dev/null +++ b/tests/unit/modules/network/vyos/fixtures/vyos_lag_interfaces_config.cfg @@ -0,0 +1,5 @@ +set interfaces bonding bond0 member interface 'eth0' +set interfaces bonding bond0 member interface 'eth1' +set interfaces bonding bond0 primary 'eth0' +set interfaces bonding bond0 mode '802.3ad' +set interfaces bonding bond0 hash-policy 'layer2+3' diff --git a/tests/unit/modules/network/vyos/test_vyos_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_interfaces.py index 8549d768..f4a5d2a5 100644 --- a/tests/unit/modules/network/vyos/test_vyos_interfaces.py +++ b/tests/unit/modules/network/vyos/test_vyos_interfaces.py @@ -1,452 +1,452 @@ # (c) 2016 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 <http://www.gnu.org/licenses/>. # Make coding more python3-ish from __future__ import absolute_import, division, print_function __metaclass__ = type from unittest.mock import patch from ansible_collections.vyos.vyos.plugins.modules import vyos_interfaces from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args from .vyos_module import TestVyosModule, load_fixture -class TestVyosFirewallInterfacesModule(TestVyosModule): +class TestVyosInterfacesModule(TestVyosModule): module = vyos_interfaces def setUp(self): - super(TestVyosFirewallInterfacesModule, self).setUp() + super(TestVyosInterfacesModule, self).setUp() self.mock_get_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config", ) self.get_config = self.mock_get_config.start() self.mock_load_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config", ) self.load_config = self.mock_load_config.start() self.mock_get_resource_connection_config = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection", ) self.get_resource_connection_config = self.mock_get_resource_connection_config.start() self.mock_get_resource_connection_facts = patch( "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", ) self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() self.mock_execute_show_command = patch( "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos." "facts.interfaces.interfaces.InterfacesFacts.get_device_data", ) self.execute_show_command = self.mock_execute_show_command.start() # define the default fixture for the vyos_interfaces module self.fixture_path = "vyos_interfaces_config.cfg" def tearDown(self): - super(TestVyosFirewallInterfacesModule, self).tearDown() + super(TestVyosInterfacesModule, self).tearDown() self.mock_get_resource_connection_config.stop() self.mock_get_resource_connection_facts.stop() self.mock_get_config.stop() self.mock_load_config.stop() self.mock_execute_show_command.stop() def load_fixtures(self, commands=None, filename=None): def load_from_file(*args, **kwargs): return load_fixture(self.fixture_path) self.execute_show_command.side_effect = load_from_file def test_vyos_interfaces_merged(self): set_module_args( dict( config=[ dict(name="bond1", description="Bond - 1", enabled=True), dict(name="vtun1", description="vtun - 1", enabled=True), dict(name="wg01", description="wg - 1", enabled=True), ], state="merged", ), ) commands = [ "set interfaces bonding bond1 description 'Bond - 1'", "set interfaces openvpn vtun1 description 'vtun - 1'", "set interfaces wireguard wg01 description 'wg - 1'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_merged_retain_vif(self): # we have a vif in eth1 at this point, so that should be retained self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict(name="bond1", description="Bond - 1", enabled=True), dict(name="vtun1", description="vtun - 1", enabled=True), dict(name="wg01", description="wg - 1", enabled=True), ], state="merged", ), ) commands = [ "set interfaces bonding bond1 description 'Bond - 1'", "set interfaces openvpn vtun1 description 'vtun - 1'", "set interfaces wireguard wg01 description 'wg - 1'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_merged_additional_vif(self): # we have a vif in eth1 at this point, so that should be retained self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict( name="eth1", vifs=[ dict( vlan_id=105, description="vlan 105", ), ], ), ], state="merged", ), ) commands = [ "set interfaces ethernet eth1 vif 105 description 'vlan 105'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_merged_idempotent(self): set_module_args( dict( config=[ dict( name="wg02", description="wire guard int 2", enabled=True, ), ], state="merged", ), ) self.execute_module(changed=False, commands=[]) def test_vyos_interfaces_merged_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="merged", ), ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_replaced_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="replaced", ), ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_replaced_remove_vif(self): # we have a vif in eth1 at this point, so that should be removed self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="replaced", ), ) commands = [ "delete interfaces ethernet eth1 vif 200", "delete interfaces ethernet eth1 vif 201", "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_merged_enable_vif(self): # merge in enabling vif self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict( name="eth1", vifs=[ dict( vlan_id=201, enabled=True, ), ], ), ], state="merged", ), ) commands = [ "delete interfaces ethernet eth1 vif 201 disable", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_replaced_retain_vif(self): # we have a vif in eth1 at this point, so that should be removed self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict( name="eth1", description="Configured by Ansible", vifs=[ dict( vlan_id=200, ), dict( vlan_id=201, description="VIF eth1.201", enabled=True, ), ], ), ], state="replaced", ), ) commands = [ "delete interfaces ethernet eth1 vif 200 description", "delete interfaces ethernet eth1 vif 201 disable", "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_overridden_newinterface(self): set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="overridden", ), ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", "delete interfaces wireguard wg02 description", "delete interfaces ethernet eth3 description", "delete interfaces ethernet eth3 disable", ] self.execute_module(changed=True, commands=commands) def test_vyos_overridden_remove_vif(self): # we have a vif in eth1 at this point, so that should be removed self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict( name="eth4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), dict(name="eth1", description="Configured by Ansible"), ], state="overridden", ), ) commands = [ "set interfaces ethernet eth1 description 'Configured by Ansible'", "set interfaces ethernet eth4 description 'Ethernet 4'", "set interfaces ethernet eth4 duplex 'auto'", "set interfaces ethernet eth4 speed 'auto'", "delete interfaces wireguard wg02 description", "delete interfaces ethernet eth3 description", "delete interfaces ethernet eth3 disable", "delete interfaces ethernet eth1 vif 200", "delete interfaces ethernet eth1 vif 201", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_idempotent_disable(self): set_module_args( dict( config=[ dict( name="eth3", description="Ethernet 3", enabled=False, ), ], state="merged", ), ) commands = [] self.execute_module(changed=False, commands=commands) def test_vyos_interfaces_idempotent_disable_replace(self): set_module_args( dict( config=[ dict( name="eth3", description="Ethernet 3", enabled=False, ), ], state="replaced", ), ) commands = [] self.execute_module(changed=False, commands=commands) def test_vyos_interfaces_deleted_remove_vif(self): # we have a vif in eth1 at this point, so that should be removed self.fixture_path = "vyos_interfaces_config_vif.cfg" set_module_args( dict( config=[ dict(name="eth1"), ], state="deleted", ), ) commands = [ "delete interfaces ethernet eth1 vif 200", "delete interfaces ethernet eth1 vif 201", "delete interfaces ethernet eth1 description", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_deleted_remove_all(self): # we have a vif in eth1 at this point, so that should be removed set_module_args( dict( config=[], state="deleted", ), ) commands = [ "delete interfaces ethernet eth1 description", "delete interfaces ethernet eth3 description", "delete interfaces ethernet eth3 disable", "delete interfaces wireguard wg02 description", ] self.execute_module(changed=True, commands=commands) def test_vyos_interfaces_replaced_bad_name(self): set_module_args( dict( config=[ dict( name="int4", description="Ethernet 4", enabled=True, speed="auto", duplex="auto", ), ], state="replaced", ), ) self.execute_module(failed=True) diff --git a/tests/unit/modules/network/vyos/test_vyos_lag_interfaces.py b/tests/unit/modules/network/vyos/test_vyos_lag_interfaces.py new file mode 100644 index 00000000..406ca598 --- /dev/null +++ b/tests/unit/modules/network/vyos/test_vyos_lag_interfaces.py @@ -0,0 +1,157 @@ +# (c) 2016 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 <http://www.gnu.org/licenses/>. + +# Make coding more python3-ish +from __future__ import absolute_import, division, print_function + + +__metaclass__ = type + +from unittest.mock import patch + +from ansible_collections.vyos.vyos.plugins.modules import vyos_lag_interfaces +from ansible_collections.vyos.vyos.tests.unit.modules.utils import set_module_args + +from .vyos_module import TestVyosModule, load_fixture + + +class TestVyosLagInterfacesModule(TestVyosModule): + module = vyos_lag_interfaces + + def setUp(self): + super(TestVyosLagInterfacesModule, self).setUp() + self.mock_get_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.get_config", + ) + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.network.Config.load_config", + ) + self.load_config = self.mock_load_config.start() + + self.mock_get_resource_connection_config = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.cfg.base.get_resource_connection", + ) + self.get_resource_connection_config = self.mock_get_resource_connection_config.start() + + self.mock_get_resource_connection_facts = patch( + "ansible_collections.ansible.netcommon.plugins.module_utils.network.common.facts.facts.get_resource_connection", + ) + self.get_resource_connection_facts = self.mock_get_resource_connection_facts.start() + + self.mock_execute_show_command = patch( + "ansible_collections.vyos.vyos.plugins.module_utils.network.vyos." + "facts.lag_interfaces.lag_interfaces.Lag_interfacesFacts.get_config", + ) + self.execute_show_command = self.mock_execute_show_command.start() + # define the default fixture for the vyos_interfaces module + self.fixture_path = "vyos_lag_interfaces_config.cfg" + + def tearDown(self): + super(TestVyosLagInterfacesModule, self).tearDown() + self.mock_get_resource_connection_config.stop() + self.mock_get_resource_connection_facts.stop() + self.mock_get_config.stop() + self.mock_load_config.stop() + self.mock_execute_show_command.stop() + + def load_fixtures(self, commands=None, filename=None): + def load_from_file(*args, **kwargs): + return load_fixture(self.fixture_path) + + self.execute_show_command.side_effect = load_from_file + + def test_vyos_interfaces_merged(self): + set_module_args( + dict( + config=[ + dict( + name="bond1", + members=[{"member": "eth2"}, {"member": "eth3"}], + mode="802.3ad", + hash_policy="layer2+3", + primary="eth2", + ), + ], + state="merged", + ), + ) + + commands = [ + "set interfaces bonding bond1 member interface 'eth2'", + "set interfaces bonding bond1 member interface 'eth3'", + "set interfaces bonding bond1 primary 'eth2'", + "set interfaces bonding bond1 mode '802.3ad'", + "set interfaces bonding bond1 hash-policy 'layer2+3'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_interfaces_replaced(self): + set_module_args( + dict( + config=[ + dict( + name="bond1", + members=[{"member": "eth2"}, {"member": "eth3"}], + mode="802.3ad", + hash_policy="layer2+3", + primary="eth2", + ), + ], + state="replaced", + ), + ) + + commands = [ + "set interfaces bonding bond1 member interface 'eth2'", + "set interfaces bonding bond1 member interface 'eth3'", + "set interfaces bonding bond1 primary 'eth2'", + "set interfaces bonding bond1 mode '802.3ad'", + "set interfaces bonding bond1 hash-policy 'layer2+3'", + ] + self.execute_module(changed=True, commands=commands) + + def test_vyos_interfaces_overridden(self): + set_module_args( + dict( + config=[ + dict( + name="bond1", + members=[{"member": "eth2"}, {"member": "eth3"}], + mode="802.3ad", + hash_policy="layer2+3", + primary="eth2", + ), + ], + state="overridden", + ), + ) + + commands = [ + "delete interfaces bonding bond0 member interface 'eth0'", + "delete interfaces bonding bond0 member interface 'eth1'", + "delete interfaces bonding bond0 primary", + "delete interfaces bonding bond0 mode", + "delete interfaces bonding bond0 hash-policy", + "set interfaces bonding bond1 member interface 'eth2'", + "set interfaces bonding bond1 member interface 'eth3'", + "set interfaces bonding bond1 primary 'eth2'", + "set interfaces bonding bond1 mode '802.3ad'", + "set interfaces bonding bond1 hash-policy 'layer2+3'", + ] + self.execute_module(changed=True, commands=commands)