summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNilashish Chakraborty <nilashishchakraborty8@gmail.com>2019-07-31 23:06:53 +0530
committerGitHub <noreply@github.com>2019-07-31 23:06:53 +0530
commit730fa78ca42d9347fe3482f53bc62be22d50a9e7 (patch)
treea5597ae5605093b5b6c8a1124fb5ce6e12fbb8c4
parent288d74ca482ea8dbf182213b39d7e2e3da1ce915 (diff)
downloadansible-730fa78ca42d9347fe3482f53bc62be22d50a9e7.tar.gz
Add vyos_interfaces resource module (#58589)
Signed-off-by: NilashishC <nilashishchakraborty8@gmail.com>
-rw-r--r--docs/docsite/rst/porting_guides/porting_guide_2.9.rst2
-rw-r--r--lib/ansible/module_utils/network/vyos/argspec/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/argspec/facts/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/argspec/facts/facts.py29
-rw-r--r--lib/ansible/module_utils/network/vyos/argspec/interfaces/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/argspec/interfaces/interfaces.py67
-rw-r--r--lib/ansible/module_utils/network/vyos/config/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/config/interfaces/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/config/interfaces/interfaces.py280
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/facts.py59
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/interfaces/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/interfaces/interfaces.py123
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/legacy/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/facts/legacy/base.py161
-rw-r--r--lib/ansible/module_utils/network/vyos/utils/__init__.py0
-rw-r--r--lib/ansible/module_utils/network/vyos/utils/utils.py55
-rw-r--r--lib/ansible/modules/network/vyos/_vyos_interface.py (renamed from lib/ansible/modules/network/vyos/vyos_interface.py)6
-rw-r--r--lib/ansible/modules/network/vyos/vyos_facts.py317
-rw-r--r--lib/ansible/modules/network/vyos/vyos_interfaces.py876
-rw-r--r--test/integration/targets/prepare_vyos_tests/tasks/main.yaml9
-rw-r--r--test/integration/targets/vyos_interfaces/defaults/main.yaml3
-rw-r--r--test/integration/targets/vyos_interfaces/meta/main.yaml2
-rw-r--r--test/integration/targets/vyos_interfaces/tasks/cli.yaml19
-rw-r--r--test/integration/targets/vyos_interfaces/tasks/main.yaml2
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml17
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml17
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/deleted.yaml46
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/merged.yaml59
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/overridden.yaml52
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/replaced.yaml55
-rw-r--r--test/integration/targets/vyos_interfaces/tests/cli/rtt.yaml78
-rw-r--r--test/integration/targets/vyos_interfaces/vars/main.yaml230
-rw-r--r--test/sanity/ignore.txt16
-rw-r--r--test/units/modules/network/vyos/test_vyos_facts.py14
35 files changed, 2339 insertions, 255 deletions
diff --git a/docs/docsite/rst/porting_guides/porting_guide_2.9.rst b/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
index 8490ac51a5..5e3ef2efe2 100644
--- a/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
+++ b/docs/docsite/rst/porting_guides/porting_guide_2.9.rst
@@ -64,6 +64,8 @@ The following modules will be removed in Ansible 2.13. Please update update your
* nxos_linkagg use :ref:`nxos_lag_interfaces <nxos_lag_interfaces_module>` instead.
+* vyos_interface use :ref:`vyos_interfaces <vyos_interfaces_module>` instead.
+
Noteworthy module changes
-------------------------
diff --git a/lib/ansible/module_utils/network/vyos/argspec/__init__.py b/lib/ansible/module_utils/network/vyos/argspec/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/argspec/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/argspec/facts/__init__.py b/lib/ansible/module_utils/network/vyos/argspec/facts/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/argspec/facts/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/argspec/facts/facts.py b/lib/ansible/module_utils/network/vyos/argspec/facts/facts.py
new file mode 100644
index 0000000000..0ad41a094c
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/argspec/facts/facts.py
@@ -0,0 +1,29 @@
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The arg spec for the vyos facts module.
+"""
+
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class FactsArgs(object): # pylint: disable=R0903
+ """ The arg spec for the vyos facts module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ choices = [
+ 'all',
+ 'interfaces',
+ '!interfaces'
+ ]
+
+ argument_spec = {
+ 'gather_subset': dict(default=['!config'], type='list'),
+ 'gather_network_resources': dict(choices=choices, type='list'),
+ }
diff --git a/lib/ansible/module_utils/network/vyos/argspec/interfaces/__init__.py b/lib/ansible/module_utils/network/vyos/argspec/interfaces/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/argspec/interfaces/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/argspec/interfaces/interfaces.py b/lib/ansible/module_utils/network/vyos/argspec/interfaces/interfaces.py
new file mode 100644
index 0000000000..d6ab446558
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/argspec/interfaces/interfaces.py
@@ -0,0 +1,67 @@
+# 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_interfaces module
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+class InterfacesArgs(object): # pylint: disable=R0903
+ """The arg spec for the vyos_interfaces module
+ """
+
+ def __init__(self, **kwargs):
+ pass
+
+ argument_spec = \
+ {
+ 'config': {
+ 'elements': 'dict',
+ 'options': {
+ 'description': {'type': 'str'},
+ 'duplex': {'choices': ['full', 'half', 'auto']},
+ 'enabled': {'default': True, 'type': 'bool'},
+ 'mtu': {'type': 'int'},
+ 'name': {'required': True, 'type': 'str'},
+ 'speed': {'choices': ['auto', '10', '100', '1000', '2500',
+ '10000'],
+ 'type': 'str'},
+ 'vifs': {
+ 'elements': 'dict',
+ 'options': {
+ 'vlan_id': {'type': 'int'},
+ 'description': {'type': 'str'},
+ 'enabled': {'default': True, 'type': 'bool'},
+ 'mtu': {'type': 'int'}
+ },
+ 'type': 'list'
+ },
+ },
+ 'type': 'list'
+ },
+ 'state': {'choices': ['merged', 'replaced',
+ 'overridden', 'deleted'],
+ 'default': 'merged',
+ 'type': 'str'}
+ } # pylint: disable=C0301
diff --git a/lib/ansible/module_utils/network/vyos/config/__init__.py b/lib/ansible/module_utils/network/vyos/config/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/config/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/config/interfaces/__init__.py b/lib/ansible/module_utils/network/vyos/config/interfaces/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/config/interfaces/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/config/interfaces/interfaces.py b/lib/ansible/module_utils/network/vyos/config/interfaces/interfaces.py
new file mode 100644
index 0000000000..791a3194a7
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/config/interfaces/interfaces.py
@@ -0,0 +1,280 @@
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The vyos_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 copy import deepcopy
+from ansible.module_utils.network.common.cfg.base import ConfigBase
+from ansible.module_utils.network.common.utils import to_list, dict_diff, remove_empties
+from ansible.module_utils.six import iteritems
+from ansible.module_utils.network.vyos.facts.facts import Facts
+from ansible.module_utils.network. \
+ vyos.utils.utils import search_obj_in_list, get_interface_type, dict_delete
+
+
+class Interfaces(ConfigBase):
+ """
+ The vyos_interfaces class
+ """
+
+ gather_subset = [
+ '!all',
+ '!min',
+ ]
+
+ gather_network_resources = [
+ 'interfaces'
+ ]
+
+ def __init__(self, module):
+ super(Interfaces, self).__init__(module)
+
+ def get_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)
+ interfaces_facts = facts['ansible_network_resources'].get('interfaces')
+ if not interfaces_facts:
+ return []
+ return 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_interfaces_facts = self.get_interfaces_facts()
+ commands.extend(self.set_config(existing_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_interfaces_facts = self.get_interfaces_facts()
+
+ result['before'] = existing_interfaces_facts
+ if result['changed']:
+ result['after'] = changed_interfaces_facts
+
+ result['warnings'] = warnings
+ return result
+
+ def set_config(self, existing_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_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=want, have=have))
+
+ elif state == 'deleted':
+ if not want:
+ for intf in have:
+ commands.extend(
+ self._state_deleted(
+ {'name': intf['name']},
+ intf
+ )
+ )
+ else:
+ for item in want:
+ obj_in_have = search_obj_in_list(item['name'], have)
+ commands.extend(
+ self._state_deleted(
+ item, obj_in_have
+ )
+ )
+ else:
+ for item in want:
+ name = item['name']
+ obj_in_have = search_obj_in_list(name, have)
+
+ if not obj_in_have:
+ obj_in_have = {'name': item['name']}
+
+ elif state == 'merged':
+ commands.extend(
+ self._state_merged(
+ item, obj_in_have
+ )
+ )
+
+ elif state == 'replaced':
+ commands.extend(
+ self._state_replaced(
+ 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._state_deleted(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 intf in have:
+ intf_in_want = search_obj_in_list(intf['name'], want)
+ if not intf_in_want:
+ commands.extend(self._state_deleted({'name': intf['name']}, intf))
+
+ for intf in want:
+ intf_in_have = search_obj_in_list(intf['name'], have)
+ commands.extend(self._state_replaced(intf, intf_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 = []
+ want_copy = deepcopy(remove_empties(want))
+ have_copy = deepcopy(have)
+
+ want_vifs = want_copy.pop('vifs', [])
+ have_vifs = have_copy.pop('vifs', [])
+
+ updates = dict_diff(have_copy, want_copy)
+
+ if updates:
+ for key, value in iteritems(updates):
+ commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name']))
+
+ if want_vifs:
+ for want_vif in want_vifs:
+ have_vif = search_obj_in_list(want_vif['vlan_id'], have_vifs, key='vlan_id')
+ if not have_vif:
+ have_vif = {'vlan_id': want_vif['vlan_id'], 'enabled': True}
+
+ vif_updates = dict_diff(have_vif, want_vif)
+ if vif_updates:
+ for key, value in iteritems(vif_updates):
+ commands.append(self._compute_commands(key=key, value=value, interface=want_copy['name'], vif=want_vif['vlan_id']))
+
+ return commands
+
+ def _state_deleted(self, want, 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 = []
+
+ want_copy = deepcopy(remove_empties(want))
+ have_copy = deepcopy(have)
+
+ want_vifs = want_copy.pop('vifs', [])
+ have_vifs = have_copy.pop('vifs', [])
+
+ for key in dict_delete(have_copy, want_copy).keys():
+ if key == 'enabled':
+ continue
+ commands.append(self._compute_commands(key=key, interface=want_copy['name'], remove=True))
+ if have_copy['enabled'] is False:
+ commands.append(self._compute_commands(key='enabled', value=True, interface=want_copy['name']))
+
+ if have_vifs:
+ for have_vif in have_vifs:
+ want_vif = search_obj_in_list(have_vif['vlan_id'], want_vifs, key='vlan_id')
+ if not want_vif:
+ want_vif = {'vlan_id': have_vif['vlan_id'], 'enabled': True}
+
+ for key in dict_delete(have_vif, want_vif).keys():
+ if key == 'enabled':
+ continue
+ commands.append(self._compute_commands(key=key, interface=want_copy['name'], vif=want_vif['vlan_id'], remove=True))
+ if have_vif['enabled'] is False:
+ commands.append(self._compute_commands(key='enabled', value=True, interface=want_copy['name'], vif=want_vif['vlan_id']))
+
+ return commands
+
+ def _compute_commands(self, interface, key, vif=None, value=None, remove=False):
+ intf_context = 'interfaces {0} {1}'.format(get_interface_type(interface), interface)
+ set_cmd = 'set {0}'.format(intf_context)
+ del_cmd = 'delete {0}'.format(intf_context)
+
+ if vif:
+ set_cmd = set_cmd + (' vif {0}'.format(vif))
+ del_cmd = del_cmd + (' vif {0}'.format(vif))
+
+ if key == 'enabled':
+ if not value:
+ command = "{0} disable".format(set_cmd)
+ else:
+ command = "{0} disable".format(del_cmd)
+ else:
+ if not remove:
+ command = "{0} {1} '{2}'".format(set_cmd, key, value)
+ else:
+ command = "{0} {1}".format(del_cmd, key)
+
+ return command
diff --git a/lib/ansible/module_utils/network/vyos/facts/__init__.py b/lib/ansible/module_utils/network/vyos/facts/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/facts/facts.py b/lib/ansible/module_utils/network/vyos/facts/facts.py
new file mode 100644
index 0000000000..080b5cca90
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/facts.py
@@ -0,0 +1,59 @@
+# Copyright 2019 Red Hat
+# GNU General Public License v3.0+
+# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+"""
+The facts class for vyos
+this file validates each subset of facts and selectively
+calls the appropriate facts gathering function
+"""
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+from ansible.module_utils.network.vyos.argspec.facts.facts import FactsArgs
+from ansible.module_utils.network.common.facts.facts import FactsBase
+from ansible.module_utils.network.vyos.facts.interfaces.interfaces import InterfacesFacts
+from ansible.module_utils.network.vyos.facts.legacy.base import Default, Neighbors, Config
+from ansible.module_utils. \
+ network.vyos.vyos import run_commands, get_capabilities
+
+
+FACT_LEGACY_SUBSETS = dict(
+ default=Default,
+ neighbors=Neighbors,
+ config=Config
+)
+FACT_RESOURCE_SUBSETS = dict(
+ interfaces=InterfacesFacts,
+)
+
+
+class Facts(FactsBase):
+ """ The fact class for vyos
+ """
+
+ VALID_LEGACY_GATHER_SUBSETS = frozenset(FACT_LEGACY_SUBSETS.keys())
+ VALID_RESOURCE_SUBSETS = frozenset(FACT_RESOURCE_SUBSETS.keys())
+
+ def __init__(self, module):
+ super(Facts, self).__init__(module)
+
+ def get_facts(self, legacy_facts_type=None, resource_facts_type=None, data=None):
+ """ Collect the facts for vyos
+
+ :param legacy_facts_type: List of legacy facts types
+ :param resource_facts_type: List of resource fact types
+ :param data: previously collected conf
+ :rtype: dict
+ :return: the facts gathered
+ """
+ netres_choices = FactsArgs.argument_spec['gather_network_resources'].get('choices', [])
+ if self.VALID_RESOURCE_SUBSETS:
+ self.get_network_resources_facts(netres_choices, FACT_RESOURCE_SUBSETS,
+ resource_facts_type, data)
+
+ if self.VALID_LEGACY_GATHER_SUBSETS:
+ self.get_network_legacy_facts(FACT_LEGACY_SUBSETS, legacy_facts_type)
+
+ return self.ansible_facts, self._warnings
diff --git a/lib/ansible/module_utils/network/vyos/facts/interfaces/__init__.py b/lib/ansible/module_utils/network/vyos/facts/interfaces/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/interfaces/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/facts/interfaces/interfaces.py b/lib/ansible/module_utils/network/vyos/facts/interfaces/interfaces.py
new file mode 100644
index 0000000000..325319e7bf
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/interfaces/interfaces.py
@@ -0,0 +1,123 @@
+#
+# -*- 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 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, M
+from copy import deepcopy
+from ansible.module_utils.network.common import utils
+from ansible.module_utils.network.vyos.argspec.interfaces.interfaces import InterfacesArgs
+
+
+class InterfacesFacts(object):
+ """ The vyos interfaces fact class
+ """
+
+ def __init__(self, module, subspec='config', options='options'):
+ self._module = module
+ self.argument_spec = 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 interfaces
+ :param connection: the device connection
+ :param ansible_facts: Facts dictionary
+ :param data: previously collected conf
+ :rtype: dictionary
+ :returns: facts
+ """
+ if not data:
+ data = connection.get_config(flags=['| grep interfaces'])
+
+ objs = []
+ interface_names = findall(r'^set interfaces (?:ethernet|bonding|vti|loopback|vxlan) (?:\'*)(\S+)(?:\'*)',
+ data, M)
+ if interface_names:
+ for interface in set(interface_names):
+ intf_regex = r' %s .+$' % interface.strip("'")
+ cfg = findall(intf_regex, data, M)
+ obj = self.render_config(cfg)
+ obj['name'] = interface.strip("'")
+ if obj:
+ objs.append(obj)
+ facts = {}
+ if objs:
+ facts['interfaces'] = []
+ params = utils.validate_config(self.argument_spec, {'config': objs})
+ for cfg in params['config']:
+ facts['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
+ """
+ vif_conf = '\n'.join(filter(lambda x: ('vif' in x), conf))
+ eth_conf = '\n'.join(filter(lambda x: ('vif' not in x), conf))
+ config = self.parse_attribs(
+ ['description', 'speed', 'mtu', 'duplex'], eth_conf)
+ config['vifs'] = self.parse_vifs(vif_conf)
+
+ return utils.remove_empties(config)
+
+ def parse_vifs(self, conf):
+ vif_names = findall(r'vif (?:\'*)(\d+)(?:\'*)', conf, M)
+ vifs_list = None
+
+ if vif_names:
+ vifs_list = []
+ for vif in set(vif_names):
+ vif_regex = r' %s .+$' % vif
+ cfg = '\n'.join(findall(vif_regex, conf, M))
+ obj = self.parse_attribs(['description', 'mtu'], cfg)
+ obj['vlan_id'] = int(vif)
+ if obj:
+ vifs_list.append(obj)
+ vifs_list = sorted(vifs_list, key=lambda i: i['vlan_id'])
+
+ return vifs_list
+
+ def parse_attribs(self, attribs, conf):
+ config = {}
+ for item in attribs:
+ value = utils.parse_conf_arg(conf, item)
+ if value and item == 'mtu':
+ config[item] = int(value.strip("'"))
+ elif value:
+ config[item] = value.strip("'")
+ else:
+ config[item] = None
+ if 'disable' in conf:
+ config['enabled'] = False
+ else:
+ config['enabled'] = True
+
+ return utils.remove_empties(config)
diff --git a/lib/ansible/module_utils/network/vyos/facts/legacy/__init__.py b/lib/ansible/module_utils/network/vyos/facts/legacy/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/legacy/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/facts/legacy/base.py b/lib/ansible/module_utils/network/vyos/facts/legacy/base.py
new file mode 100644
index 0000000000..cdf0cceb5b
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/facts/legacy/base.py
@@ -0,0 +1,161 @@
+# -*- 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 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
+
+
+import platform
+import re
+from ansible.module_utils. \
+ network.vyos.vyos import run_commands, get_capabilities
+
+
+class LegacyFactsBase(object):
+
+ COMMANDS = frozenset()
+
+ def __init__(self, module):
+ self.module = module
+ self.facts = dict()
+ self.warnings = list()
+ self.responses = None
+
+ def populate(self):
+ self.responses = run_commands(self.module, list(self.COMMANDS))
+
+
+class Default(LegacyFactsBase):
+
+ COMMANDS = [
+ 'show version',
+ ]
+
+ def populate(self):
+ super(Default, self).populate()
+ data = self.responses[0]
+ self.facts['serialnum'] = self.parse_serialnum(data)
+ self.facts.update(self.platform_facts())
+
+ def parse_serialnum(self, data):
+ match = re.search(r'HW S/N:\s+(\S+)', data)
+ if match:
+ return match.group(1)
+
+ def platform_facts(self):
+ platform_facts = {}
+
+ resp = get_capabilities(self.module)
+ device_info = resp['device_info']
+
+ platform_facts['system'] = device_info['network_os']
+
+ for item in ('model', 'image', 'version', 'platform', 'hostname'):
+ val = device_info.get('network_os_%s' % item)
+ if val:
+ platform_facts[item] = val
+
+ platform_facts['api'] = resp['network_api']
+ platform_facts['python_version'] = platform.python_version()
+
+ return platform_facts
+
+
+class Config(LegacyFactsBase):
+
+ COMMANDS = [
+ 'show configuration commands',
+ 'show system commit',
+ ]
+
+ def populate(self):
+ super(Config, self).populate()
+
+ self.facts['config'] = self.responses
+
+ commits = self.responses[1]
+ entries = list()
+ entry = None
+
+ for line in commits.split('\n'):
+ match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line)
+ if match:
+ if entry:
+ entries.append(entry)
+
+ entry = dict(revision=match.group(1),
+ datetime=match.group(2),
+ by=str(match.group(3)).strip(),
+ via=str(match.group(4)).strip(),
+ comment=None)
+ else:
+ entry['comment'] = line.strip()
+
+ self.facts['commits'] = entries
+
+
+class Neighbors(LegacyFactsBase):
+
+ COMMANDS = [
+ 'show lldp neighbors',
+ 'show lldp neighbors detail',
+ ]
+
+ def populate(self):
+ super(Neighbors, self).populate()
+
+ all_neighbors = self.responses[0]
+ if 'LLDP not configured' not in all_neighbors:
+ neighbors = self.parse(
+ self.responses[1]
+ )
+ self.facts['neighbors'] = self.parse_neighbors(neighbors)
+
+ def parse(self, data):
+ parsed = list()
+ values = None
+ for line in data.split('\n'):
+ if not line:
+ continue
+ elif line[0] == ' ':
+ values += '\n%s' % line
+ elif line.startswith('Interface'):
+ if values:
+ parsed.append(values)
+ values = line
+ if values:
+ parsed.append(values)
+ return parsed
+
+ def parse_neighbors(self, data):
+ facts = dict()
+ for item in data:
+ interface = self.parse_interface(item)
+ host = self.parse_host(item)
+ port = self.parse_port(item)
+ if interface not in facts:
+ facts[interface] = list()
+ facts[interface].append(dict(host=host, port=port))
+ return facts
+
+ def parse_interface(self, data):
+ match = re.search(r'^Interface:\s+(\S+),', data)
+ return match.group(1)
+
+ def parse_host(self, data):
+ match = re.search(r'SysName:\s+(.+)$', data, re.M)
+ if match:
+ return match.group(1)
+
+ def parse_port(self, data):
+ match = re.search(r'PortDescr:\s+(.+)$', data, re.M)
+ if match:
+ return match.group(1)
diff --git a/lib/ansible/module_utils/network/vyos/utils/__init__.py b/lib/ansible/module_utils/network/vyos/utils/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/utils/__init__.py
diff --git a/lib/ansible/module_utils/network/vyos/utils/utils.py b/lib/ansible/module_utils/network/vyos/utils/utils.py
new file mode 100644
index 0000000000..dbfe4227a0
--- /dev/null
+++ b/lib/ansible/module_utils/network/vyos/utils/utils.py
@@ -0,0 +1,55 @@
+# -*- 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)
+
+# utils
+
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+def search_obj_in_list(name, lst, key='name'):
+ for item in lst:
+ if item[key] == name:
+ return item
+ return None
+
+
+def get_interface_type(interface):
+ """Gets the type of interface
+ """
+ if interface.startswith('eth'):
+ return 'ethernet'
+ elif interface.startswith('bond'):
+ return 'bonding'
+ elif interface.startswith('vti'):
+ return 'vti'
+ elif interface.startswith('lo'):
+ return 'loopback'
+
+
+def dict_delete(base, comparable):
+ """
+ This function generates a dict containing key, value pairs for keys
+ that are present in the `base` dict but not present in the `comparable`
+ dict.
+
+ :param base: dict object to base the diff on
+ :param comparable: dict object to compare against base
+ :returns: new dict object with key, value pairs that needs to be deleted.
+
+ """
+ to_delete = dict()
+
+ for key in base:
+ if isinstance(base[key], dict):
+ sub_diff = dict_delete(base[key], comparable.get(key, {}))
+ if sub_diff:
+ to_delete[key] = sub_diff
+ else:
+ if key not in comparable:
+ to_delete[key] = base[key]
+
+ return to_delete
diff --git a/lib/ansible/modules/network/vyos/vyos_interface.py b/lib/ansible/modules/network/vyos/_vyos_interface.py
index 4b97ea55df..f6652b1fd3 100644
--- a/lib/ansible/modules/network/vyos/vyos_interface.py
+++ b/lib/ansible/modules/network/vyos/_vyos_interface.py
@@ -20,7 +20,7 @@
#
ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
+ 'status': ['deprecated'],
'supported_by': 'network'}
@@ -33,6 +33,10 @@ short_description: Manage Interface on VyOS network devices
description:
- This module provides declarative management of Interfaces
on VyOS network devices.
+deprecated:
+ removed_in: '2.13'
+ alternative: vyos_interfaces
+ why: Updated modules released with more functionality.
notes:
- Tested against VYOS 1.1.7
options:
diff --git a/lib/ansible/modules/network/vyos/vyos_facts.py b/lib/ansible/modules/network/vyos/vyos_facts.py
index c4a6974567..ef2682bec3 100644
--- a/lib/ansible/modules/network/vyos/vyos_facts.py
+++ b/lib/ansible/modules/network/vyos/vyos_facts.py
@@ -1,65 +1,90 @@
#!/usr/bin/python
-#
-# 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/>.
-#
+# -*- 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 module file for vyos_facts
+"""
+
+
ANSIBLE_METADATA = {'metadata_version': '1.1',
- 'status': ['preview'],
+ 'status': [u'preview'],
'supported_by': 'network'}
DOCUMENTATION = """
---
module: vyos_facts
-version_added: "2.2"
-author: "Nathaniel Case (@Qalthos)"
-short_description: Collect facts from remote devices running VyOS
+version_added: 2.2
+short_description: Get facts about vyos devices.
description:
- - Collects a base set of device facts from a remote device that
- is running VyOS. This module prepends all of the
- base network fact keys with U(ansible_net_<fact>). The facts
- module will always collect a base set of facts from the device
- and can enable or disable collection of additional facts.
+ - Collects facts from network devices running the vyos operating
+ system. This module places the facts gathered in the fact tree keyed by the
+ respective resource name. The facts module will always collect a
+ base set of facts from the device and can enable or disable
+ collection of additional facts.
+author:
+ - Nathaniel Case (@qalthos)
+ - Nilashish Chakraborty (@Nilashishc)
extends_documentation_fragment: vyos
notes:
- - Tested against VYOS 1.1.7
+ - Tested against VyOS 1.1.8
options:
gather_subset:
description:
- When supplied, this argument will restrict the facts collected
to a given subset. Possible values for this argument include
- all, default, config, and neighbors. Can specify a list of
- values to include a larger subset. Values can also be used
+ all, default, config, and neighbors. Can specify a list of
+ values to include a larger subset. Values can also be used
with an initial C(M(!)) to specify that a specific subset should
not be collected.
required: false
default: "!config"
+ gather_network_resources:
+ description:
+ - When supplied, this argument will restrict the facts collected
+ to a given subset. Possible values for this argument include
+ all and the resources like interfaces.
+ Can specify a list of values to include a larger subset. Values
+ can also be used with an initial C(M(!)) to specify that a
+ specific subset should not be collected.
+ required: false
+ version_added: "2.9"
+ choices: ['all', 'interfaces', '!interfaces']
"""
EXAMPLES = """
-- name: collect all facts from the device
- vyos_facts:
+# Gather all facts
+- vyos_facts:
gather_subset: all
+ gather_network_resources: all
-- name: collect only the config and default facts
- vyos_facts:
+# collect only the config and default facts
+- vyos_facts:
gather_subset: config
-- name: collect everything exception the config
- vyos_facts:
+# collect everything exception the config
+- vyos_facts:
gather_subset: "!config"
+
+# Collect only the interfaces facts
+- vyos_facts:
+ gather_subset:
+ - '!all'
+ - '!min'
+ gather_network_resources:
+ - interfaces
+
+# Do not collect interfaces facts
+- vyos_facts:
+ gather_network_resources:
+ - "!interfaces"
+
+# Collect interfaces and minimal default facts
+- vyos_facts:
+ gather_subset: min
+ gather_network_resources: interfaces
"""
RETURN = """
@@ -103,228 +128,38 @@ ansible_net_python_version:
description: The Python version Ansible controller is using
returned: always
type: str
+ansible_net_gather_network_resources:
+ description: The list of fact resource subsets collected from the device
+ returned: always
+ type: list
"""
-import platform
-import re
-
from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.six import iteritems
-from ansible.module_utils.network.vyos.vyos import run_commands, get_capabilities
+from ansible.module_utils.network.vyos.argspec.facts.facts import FactsArgs
+from ansible.module_utils.network.vyos.facts.facts import Facts
from ansible.module_utils.network.vyos.vyos import vyos_argument_spec
-class FactsBase(object):
-
- COMMANDS = frozenset()
-
- def __init__(self, module):
- self.module = module
- self.facts = dict()
- self.responses = None
-
- def populate(self):
- self.responses = run_commands(self.module, list(self.COMMANDS))
-
-
-class Default(FactsBase):
-
- COMMANDS = [
- 'show version',
- ]
-
- def populate(self):
- super(Default, self).populate()
- data = self.responses[0]
- self.facts['serialnum'] = self.parse_serialnum(data)
- self.facts.update(self.platform_facts())
-
- def parse_serialnum(self, data):
- match = re.search(r'HW S/N:\s+(\S+)', data)
- if match:
- return match.group(1)
-
- def platform_facts(self):
- platform_facts = {}
-
- resp = get_capabilities(self.module)
- device_info = resp['device_info']
-
- platform_facts['system'] = device_info['network_os']
-
- for item in ('model', 'image', 'version', 'platform', 'hostname'):
- val = device_info.get('network_os_%s' % item)
- if val:
- platform_facts[item] = val
-
- platform_facts['api'] = resp['network_api']
- platform_facts['python_version'] = platform.python_version()
-
- return platform_facts
-
-
-class Config(FactsBase):
-
- COMMANDS = [
- 'show configuration commands',
- 'show system commit',
- ]
-
- def populate(self):
- super(Config, self).populate()
-
- self.facts['config'] = self.responses
-
- commits = self.responses[1]
- entries = list()
- entry = None
-
- for line in commits.split('\n'):
- match = re.match(r'(\d+)\s+(.+)by(.+)via(.+)', line)
- if match:
- if entry:
- entries.append(entry)
-
- entry = dict(revision=match.group(1),
- datetime=match.group(2),
- by=str(match.group(3)).strip(),
- via=str(match.group(4)).strip(),
- comment=None)
- else:
- entry['comment'] = line.strip()
-
- self.facts['commits'] = entries
-
-
-class Neighbors(FactsBase):
-
- COMMANDS = [
- 'show lldp neighbors',
- 'show lldp neighbors detail',
- ]
-
- def populate(self):
- super(Neighbors, self).populate()
-
- all_neighbors = self.responses[0]
- if 'LLDP not configured' not in all_neighbors:
- neighbors = self.parse(
- self.responses[1]
- )
- self.facts['neighbors'] = self.parse_neighbors(neighbors)
-
- def parse(self, data):
- parsed = list()
- values = None
- for line in data.split('\n'):
- if not line:
- continue
- elif line[0] == ' ':
- values += '\n%s' % line
- elif line.startswith('Interface'):
- if values:
- parsed.append(values)
- values = line
- if values:
- parsed.append(values)
- return parsed
-
- def parse_neighbors(self, data):
- facts = dict()
- for item in data:
- interface = self.parse_interface(item)
- host = self.parse_host(item)
- port = self.parse_port(item)
- if interface not in facts:
- facts[interface] = list()
- facts[interface].append(dict(host=host, port=port))
- return facts
-
- def parse_interface(self, data):
- match = re.search(r'^Interface:\s+(\S+),', data)
- return match.group(1)
-
- def parse_host(self, data):
- match = re.search(r'SysName:\s+(.+)$', data, re.M)
- if match:
- return match.group(1)
-
- def parse_port(self, data):
- match = re.search(r'PortDescr:\s+(.+)$', data, re.M)
- if match:
- return match.group(1)
-
-
-FACT_SUBSETS = dict(
- default=Default,
- neighbors=Neighbors,
- config=Config
-)
-
-VALID_SUBSETS = frozenset(FACT_SUBSETS.keys())
-
-
def main():
- argument_spec = dict(
- gather_subset=dict(default=['!config'], type='list')
- )
+ """
+ Main entry point for module execution
+
+ :returns: ansible_facts
+ """
+ argument_spec = FactsArgs.argument_spec
argument_spec.update(vyos_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
- warnings = list()
-
- gather_subset = module.params['gather_subset']
-
- runable_subsets = set()
- exclude_subsets = set()
-
- for subset in gather_subset:
- if subset == 'all':
- runable_subsets.update(VALID_SUBSETS)
- continue
-
- if subset.startswith('!'):
- subset = subset[1:]
- if subset == 'all':
- exclude_subsets.update(VALID_SUBSETS)
- continue
- exclude = True
- else:
- exclude = False
-
- if subset not in VALID_SUBSETS:
- module.fail_json(msg='Subset must be one of [%s], got %s' %
- (', '.join(VALID_SUBSETS), subset))
-
- if exclude:
- exclude_subsets.add(subset)
- else:
- runable_subsets.add(subset)
-
- if not runable_subsets:
- runable_subsets.update(VALID_SUBSETS)
-
- runable_subsets.difference_update(exclude_subsets)
- runable_subsets.add('default')
-
- facts = dict()
- facts['gather_subset'] = list(runable_subsets)
-
- instances = list()
- for key in runable_subsets:
- instances.append(FACT_SUBSETS[key](module))
+ warnings = ['default value for `gather_subset` '
+ 'will be changed to `min` from `!config` v2.11 onwards']
- for inst in instances:
- inst.populate()
- facts.update(inst.facts)
+ result = Facts(module).get_facts()
- ansible_facts = dict()
- for key, value in iteritems(facts):
- key = 'ansible_net_%s' % key
- ansible_facts[key] = value
+ ansible_facts, additional_warnings = result
+ warnings.extend(additional_warnings)
module.exit_json(ansible_facts=ansible_facts, warnings=warnings)
diff --git a/lib/ansible/modules/network/vyos/vyos_interfaces.py b/lib/ansible/modules/network/vyos/vyos_interfaces.py
new file mode 100644
index 0000000000..c8733fdaf9
--- /dev/null
+++ b/lib/ansible/modules/network/vyos/vyos_interfaces.py
@@ -0,0 +1,876 @@
+#!/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_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_interfaces
+version_added: 2.9
+short_description: Manages interface attributes of VyOS network devices.
+description:
+ - This module manages the interface attributes on VyOS network devices.
+ - This module supports managing base attributes of Ethernet, Bonding,
+ VXLAN, Loopback and Virtual Tunnel Interfaces.
+author: Nilashish Chakraborty (@nilashishc)
+options:
+ config:
+ description: The provided interfaces configuration.
+ type: list
+ suboptions:
+ name:
+ description:
+ - Full name of the interface, e.g. eth0, eth1, bond0, vti1, vxlan2.
+ type: str
+ required: True
+ description:
+ description:
+ - Interface description.
+ type: str
+ duplex:
+ description:
+ - Interface duplex mode.
+ - Applicable for Ethernet interfaces only.
+ choices: ['full', 'half', 'auto']
+ type: str
+ enabled:
+ default: True
+ description:
+ - Administrative state of the interface.
+ - Set the value to C(true) to administratively enable
+ the interface or C(false) to disable it.
+ type: bool
+ mtu:
+ description:
+ - MTU for a specific interface. Refer to vendor documentation for valid values.
+ - Applicable for Ethernet, Bonding, VXLAN and Virtual Tunnel interfaces.
+ type: int
+ speed:
+ description:
+ - Interface link speed.
+ - Applicable for Ethernet interfaces only.
+ type: str
+ choices: ['auto', '10', '100', '1000', '2500', '10000']
+ vifs:
+ description:
+ - Virtual sub-interfaces related configuration.
+ - 802.1Q VLAN interfaces are represented as virtual sub-interfaces in VyOS.
+ type: list
+ suboptions:
+ vlan_id:
+ description:
+ - Identifier for the virtual sub-interface.
+ type: int
+ description:
+ description:
+ - Virtual sub-interface description.
+ type: str
+ enabled:
+ description:
+ - Administrative state of the virtual sub-interface.
+ - Set the value to C(true) to administratively enable
+ the interface or C(false) to disable it.
+ type: bool
+ default: True
+ mtu:
+ description:
+ - MTU for the virtual sub-interface.
+ - Refer to vendor documentation for valid values.
+ type: int
+ 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 interfaces
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces loopback lo
+
+- name: Merge provided configuration with device configuration
+ vyos_interfaces:
+ config:
+ - name: eth2
+ description: 'Configured by Ansible'
+ enabled: True
+ vifs:
+ - vlan_id: 200
+ description: "VIF 200 - ETH2"
+
+ - name: eth3
+ description: 'Configured by Ansible'
+ mtu: 1500
+
+ - name: bond1
+ description: 'Bond - 1'
+ mtu: 1200
+
+ - name: vti2
+ description: 'VTI - 2'
+ enabled: false
+ state: merged
+#
+#
+# -------------------------
+# Module Execution Result
+# -------------------------
+#
+# "before": [
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "enabled": true,
+# "name": "eth3"
+# },
+# {
+# "enabled": true,
+# "name": "eth2"
+# },
+# {
+# "enabled": true,
+# "name": "eth1"
+# },
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+# "commands": [
+# "set interfaces ethernet eth2 description 'Configured by Ansible'",
+# "set interfaces ethernet eth2 vif 200",
+# "set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'",
+# "set interfaces ethernet eth3 description 'Configured by Ansible'",
+# "set interfaces ethernet eth3 mtu '1500'",
+# "set interfaces bonding bond1",
+# "set interfaces bonding bond1 description 'Bond - 1'",
+# "set interfaces bonding bond1 mtu '1200'",
+# "set interfaces vti vti2",
+# "set interfaces vti vti2 description 'VTI - 2'",
+# "set interfaces vti vti2 disable"
+# ]
+#
+# "after": [
+# {
+# "description": "Bond - 1",
+# "enabled": true,
+# "mtu": 1200,
+# "name": "bond1"
+# },
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "description": "VTI - 2",
+# "enabled": false,
+# "name": "vti2"
+# },
+# {
+# "description": "Configured by Ansible",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth3"
+# },
+# {
+# "description": "Configured by Ansible",
+# "enabled": true,
+# "name": "eth2",
+# "vifs": [
+# {
+# "description": "VIF 200 - ETH2",
+# "enabled": true,
+# "vlan_id": "200"
+# }
+# ]
+# },
+# {
+# "enabled": true,
+# "name": "eth1"
+# },
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+#
+# -------------
+# After state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces bonding bond1 description 'Bond - 1'
+# set interfaces bonding bond1 mtu '1200'
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth2 description 'Configured by Ansible'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth2 vif 200 description 'VIF 200 - ETH2'
+# set interfaces ethernet eth3 description 'Configured by Ansible'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces ethernet eth3 mtu '1500'
+# set interfaces loopback lo
+# set interfaces vti vti2 description 'VTI - 2'
+# set interfaces vti vti2 disable
+#
+
+
+# Using replaced
+#
+# -------------
+# Before state:
+# -------------
+#
+# vyos:~$ show configuration commands | grep eth
+# set interfaces bonding bond1 description 'Bond - 1'
+# set interfaces bonding bond1 mtu '1400'
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:f3:6c:b5'
+# set interfaces ethernet eth0 smp_affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
+# set interfaces ethernet eth1 duplex 'full'
+# set interfaces ethernet eth1 hw-id '08:00:27:ad:ef:65'
+# set interfaces ethernet eth1 smp_affinity 'auto'
+# set interfaces ethernet eth1 speed '100'
+# set interfaces ethernet eth2 description 'Configured by Ansible'
+# set interfaces ethernet eth2 duplex 'full'
+# set interfaces ethernet eth2 hw-id '08:00:27:ab:4e:79'
+# set interfaces ethernet eth2 mtu '500'
+# set interfaces ethernet eth2 smp_affinity 'auto'
+# set interfaces ethernet eth2 speed '100'
+# set interfaces ethernet eth2 vif 200 description 'Configured by Ansible'
+# set interfaces ethernet eth3 description 'Configured by Ansible'
+# set interfaces ethernet eth3 duplex 'full'
+# set interfaces ethernet eth3 hw-id '08:00:27:17:3c:85'
+# set interfaces ethernet eth3 mtu '1500'
+# set interfaces ethernet eth3 smp_affinity 'auto'
+# set interfaces ethernet eth3 speed '100'
+# set interfaces loopback lo
+#
+#
+- name: Replace device configurations of listed interfaces with provided configurations
+ vyos_interfaces:
+ config:
+ - name: eth2
+ description: "Replaced by Ansible"
+
+ - name: eth3
+ description: "Replaced by Ansible"
+
+ - name: eth1
+ description: "Replaced by Ansible"
+ state: replaced
+#
+#
+# -----------------------
+# Module Execution Result
+# -----------------------
+#
+# "before": [
+# {
+# "description": "Bond - 1",
+# "enabled": true,
+# "mtu": 1400,
+# "name": "bond1"
+# },
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "description": "Configured by Ansible",
+# "duplex": "full",
+# "enabled": true,
+# "mtu": 1500,
+# "name": "eth3",
+# "speed": "100"
+# },
+# {
+# "description": "Configured by Ansible",
+# "duplex": "full",
+# "enabled": true,
+# "mtu": 500,
+# "name": "eth2",
+# "speed": "100",
+# "vifs": [
+# {
+# "description": "VIF 200 - ETH2",
+# "enabled": true,
+# "vlan_id": "200"
+# }
+# ]
+# },
+# {
+# "description": "Configured by Ansible Eng Team",
+# "duplex": "full",
+# "enabled": true,
+# "name": "eth1",
+# "speed": "100"
+# },
+# {
+# "description": "Management Interface for the Appliance",
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+# "commands": [
+# "delete interfaces ethernet eth2 speed",
+# "delete interfaces ethernet eth2 duplex",
+# "delete interfaces ethernet eth2 mtu",
+# "delete interfaces ethernet eth2 vif 200 description",
+# "set interfaces ethernet eth2 description 'Replaced by Ansible'",
+# "delete interfaces ethernet eth3 speed",
+# "delete interfaces ethernet eth3 duplex",
+# "delete interfaces ethernet eth3 mtu",
+# "set interfaces ethernet eth3 description 'Replaced by Ansible'",
+# "delete interfaces ethernet eth1 speed",
+# "delete interfaces ethernet eth1 duplex",
+# "set interfaces ethernet eth1 description 'Replaced by Ansible'"
+# ]
+#
+# "after": [
+# {
+# "description": "Bond - 1",
+# "enabled": true,
+# "mtu": 1400,
+# "name": "bond1"
+# },
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "description": "Replaced by Ansible",
+# "enabled": true,
+# "name": "eth3"
+# },
+# {
+# "description": "Replaced by Ansible",
+# "enabled": true,
+# "name": "eth2",
+# "vifs": [
+# {
+# "enabled": true,
+# "vlan_id": "200"
+# }
+# ]
+# },
+# {
+# "description": "Replaced by Ansible",
+# "enabled": true,
+# "name": "eth1"
+# },
+# {
+# "description": "Management Interface for the Appliance",
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+#
+# -------------
+# After state:
+# -------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces bonding bond1 description 'Bond - 1'
+# set interfaces bonding bond1 mtu '1400'
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 description 'Management Interface for the Appliance'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 description 'Replaced by Ansible'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth2 description 'Replaced by Ansible'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth2 vif 200
+# set interfaces ethernet eth3 description 'Replaced by Ansible'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces loopback lo
+#
+#
+# Using overridden
+#
+#
+# --------------
+# Before state
+# --------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 description 'Ethernet Interface - 0'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 mtu '1200'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 description 'Configured by Ansible Eng Team'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 mtu '100'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth1 vif 100 description 'VIF 100 - ETH1'
+# set interfaces ethernet eth1 vif 100 disable
+# set interfaces ethernet eth2 description 'Configured by Ansible Team (Admin Down)'
+# set interfaces ethernet eth2 disable
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 mtu '600'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth3 description 'Configured by Ansible Network'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces loopback lo
+# set interfaces vti vti1 description 'Virtual Tunnel Interface - 1'
+# set interfaces vti vti1 mtu '68'
+#
+#
+- name: Overrides all device configuration with provided configuration
+ vyos_interfaces:
+ config:
+ - name: eth0
+ description: Outbound Interface For The Appliance
+ speed: auto
+ duplex: auto
+
+ - name: eth2
+ speed: auto
+ duplex: auto
+
+ - name: eth3
+ mtu: 1200
+ state: overridden
+#
+#
+# ------------------------
+# Module Execution Result
+# ------------------------
+#
+# "before": [
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "description": "Virtual Tunnel Interface - 1",
+# "enabled": true,
+# "mtu": 68,
+# "name": "vti1"
+# },
+# {
+# "description": "Configured by Ansible Network",
+# "enabled": true,
+# "name": "eth3"
+# },
+# {
+# "description": "Configured by Ansible Team (Admin Down)",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2"
+# },
+# {
+# "description": "Configured by Ansible Eng Team",
+# "enabled": true,
+# "mtu": 100,
+# "name": "eth1",
+# "vifs": [
+# {
+# "description": "VIF 100 - ETH1",
+# "enabled": false,
+# "vlan_id": "100"
+# }
+# ]
+# },
+# {
+# "description": "Ethernet Interface - 0",
+# "duplex": "auto",
+# "enabled": true,
+# "mtu": 1200,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+# "commands": [
+# "delete interfaces vti vti1 description",
+# "delete interfaces vti vti1 mtu",
+# "delete interfaces ethernet eth1 description",
+# "delete interfaces ethernet eth1 mtu",
+# "delete interfaces ethernet eth1 vif 100 description",
+# "delete interfaces ethernet eth1 vif 100 disable",
+# "delete interfaces ethernet eth0 mtu",
+# "set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'",
+# "delete interfaces ethernet eth2 description",
+# "delete interfaces ethernet eth2 mtu",
+# "set interfaces ethernet eth2 duplex 'auto'",
+# "delete interfaces ethernet eth2 disable",
+# "set interfaces ethernet eth2 speed 'auto'",
+# "delete interfaces ethernet eth3 description",
+# "set interfaces ethernet eth3 mtu '1200'"
+# ],
+#
+# "after": [
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "enabled": true,
+# "name": "vti1"
+# },
+# {
+# "enabled": true,
+# "mtu": 1200,
+# "name": "eth3"
+# },
+# {
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth2",
+# "speed": "auto"
+# },
+# {
+# "enabled": true,
+# "name": "eth1",
+# "vifs": [
+# {
+# "enabled": true,
+# "vlan_id": "100"
+# }
+# ]
+# },
+# {
+# "description": "Outbound Interface For The Appliance",
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+#
+# ------------
+# After state
+# ------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 description 'Outbound Interface For The Appliance'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth1 vif 100
+# set interfaces ethernet eth2 duplex 'auto'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth2 speed 'auto'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces ethernet eth3 mtu '1200'
+# set interfaces loopback lo
+# set interfaces vti vti1
+#
+#
+# Using deleted
+#
+#
+# -------------
+# Before state
+# -------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces bonding bond0 mtu '1300'
+# set interfaces bonding bond1 description 'LAG - 1'
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 description 'Configured by Ansible Network'
+# set interfaces ethernet eth1 duplex 'full'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth1 speed '100'
+# set interfaces ethernet eth2 description 'Configured by Ansible'
+# set interfaces ethernet eth2 disable
+# set interfaces ethernet eth2 duplex 'full'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 mtu '600'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth2 speed '100'
+# set interfaces ethernet eth3 description 'Configured by Ansible Network'
+# set interfaces ethernet eth3 duplex 'full'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces ethernet eth3 speed '100'
+# set interfaces loopback lo
+#
+#
+- name: Delete attributes of given interfaces (Note - This won't delete the interfaces themselves)
+ vyos_interfaces:
+ config:
+ - name: bond1
+
+ - name: eth1
+
+ - name: eth2
+
+ - name: eth3
+ state: deleted
+#
+#
+# ------------------------
+# Module Execution Results
+# ------------------------
+#
+# "before": [
+# {
+# "enabled": true,
+# "mtu": 1300,
+# "name": "bond0"
+# },
+# {
+# "description": "LAG - 1",
+# "enabled": true,
+# "name": "bond1"
+# },
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "description": "Configured by Ansible Network",
+# "duplex": "full",
+# "enabled": true,
+# "name": "eth3",
+# "speed": "100"
+# },
+# {
+# "description": "Configured by Ansible",
+# "duplex": "full",
+# "enabled": false,
+# "mtu": 600,
+# "name": "eth2",
+# "speed": "100"
+# },
+# {
+# "description": "Configured by Ansible Network",
+# "duplex": "full",
+# "enabled": true,
+# "name": "eth1",
+# "speed": "100"
+# },
+# {
+# "description": "Outbound Interface for this appliance",
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+# "commands": [
+# "delete interfaces bonding bond1 description",
+# "delete interfaces ethernet eth1 speed",
+# "delete interfaces ethernet eth1 duplex",
+# "delete interfaces ethernet eth1 description",
+# "delete interfaces ethernet eth2 speed",
+# "delete interfaces ethernet eth2 disable",
+# "delete interfaces ethernet eth2 duplex",
+# "delete interfaces ethernet eth2 disable",
+# "delete interfaces ethernet eth2 description",
+# "delete interfaces ethernet eth2 disable",
+# "delete interfaces ethernet eth2 mtu",
+# "delete interfaces ethernet eth2 disable",
+# "delete interfaces ethernet eth3 speed",
+# "delete interfaces ethernet eth3 duplex",
+# "delete interfaces ethernet eth3 description"
+# ]
+#
+# "after": [
+# {
+# "enabled": true,
+# "mtu": 1300,
+# "name": "bond0"
+# },
+# {
+# "enabled": true,
+# "name": "bond1"
+# },
+# {
+# "enabled": true,
+# "name": "lo"
+# },
+# {
+# "enabled": true,
+# "name": "eth3"
+# },
+# {
+# "enabled": true,
+# "name": "eth2"
+# },
+# {
+# "enabled": true,
+# "name": "eth1"
+# },
+# {
+# "description": "Outbound Interface for this appliance",
+# "duplex": "auto",
+# "enabled": true,
+# "name": "eth0",
+# "speed": "auto"
+# }
+# ]
+#
+#
+# ------------
+# After state
+# ------------
+#
+# vyos@vyos:~$ show configuration commands | grep interfaces
+# set interfaces bonding bond0 mtu '1300'
+# set interfaces bonding bond1
+# set interfaces ethernet eth0 address 'dhcp'
+# set interfaces ethernet eth0 address 'dhcpv6'
+# set interfaces ethernet eth0 description 'Outbound Interface for this appliance'
+# set interfaces ethernet eth0 duplex 'auto'
+# set interfaces ethernet eth0 hw-id '08:00:27:30:f0:22'
+# set interfaces ethernet eth0 smp-affinity 'auto'
+# set interfaces ethernet eth0 speed 'auto'
+# set interfaces ethernet eth1 hw-id '08:00:27:ea:0f:b9'
+# set interfaces ethernet eth1 smp-affinity 'auto'
+# set interfaces ethernet eth2 hw-id '08:00:27:c2:98:23'
+# set interfaces ethernet eth2 smp-affinity 'auto'
+# set interfaces ethernet eth3 hw-id '08:00:27:43:70:8c'
+# set interfaces loopback lo
+#
+#
+"""
+RETURN = """
+before:
+ description: The configuration prior to the model invocation.
+ returned: always
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+ type: list
+after:
+ description: The resulting configuration model invocation.
+ returned: when changed
+ sample: >
+ The configuration returned will always be in the same format
+ of the parameters above.
+ type: list
+commands:
+ description: The set of commands pushed to the remote device.
+ returned: always
+ type: list
+ sample:
+ - 'set interfaces ethernet eth1 mtu 1200'
+ - 'set interfaces ethernet eth2 vif 100 description VIF 100'
+"""
+
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible.module_utils.network.vyos.argspec.interfaces.interfaces import InterfacesArgs
+from ansible.module_utils.network.vyos.config.interfaces.interfaces import Interfaces
+
+
+def main():
+ """
+ Main entry point for module execution
+
+ :returns: the result form module invocation
+ """
+ module = AnsibleModule(argument_spec=InterfacesArgs.argument_spec,
+ supports_check_mode=True)
+
+ result = Interfaces(module).execute_module()
+ module.exit_json(**result)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/test/integration/targets/prepare_vyos_tests/tasks/main.yaml b/test/integration/targets/prepare_vyos_tests/tasks/main.yaml
new file mode 100644
index 0000000000..9f2fbf8d63
--- /dev/null
+++ b/test/integration/targets/prepare_vyos_tests/tasks/main.yaml
@@ -0,0 +1,9 @@
+---
+- name: Ensure required interfaces are present in running-config
+ cli_config:
+ config: "{{ lines }}"
+ vars:
+ lines: |
+ set interfaces ethernet eth1
+ set interfaces ethernet eth2
+ set interfaces loopback lo
diff --git a/test/integration/targets/vyos_interfaces/defaults/main.yaml b/test/integration/targets/vyos_interfaces/defaults/main.yaml
new file mode 100644
index 0000000000..164afead28
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/defaults/main.yaml
@@ -0,0 +1,3 @@
+---
+testcase: "[^_].*"
+test_items: []
diff --git a/test/integration/targets/vyos_interfaces/meta/main.yaml b/test/integration/targets/vyos_interfaces/meta/main.yaml
new file mode 100644
index 0000000000..e380a13e01
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/meta/main.yaml
@@ -0,0 +1,2 @@
+dependencies:
+ - prepare_vyos_tests \ No newline at end of file
diff --git a/test/integration/targets/vyos_interfaces/tasks/cli.yaml b/test/integration/targets/vyos_interfaces/tasks/cli.yaml
new file mode 100644
index 0000000000..655e51ee63
--- /dev/null
+++ b/test/integration/targets/vyos_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_interfaces/tasks/main.yaml b/test/integration/targets/vyos_interfaces/tasks/main.yaml
new file mode 100644
index 0000000000..415c99d8b1
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tasks/main.yaml
@@ -0,0 +1,2 @@
+---
+- { include: cli.yaml, tags: ['cli'] }
diff --git a/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml b/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml
new file mode 100644
index 0000000000..b798bae015
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/_populate.yaml
@@ -0,0 +1,17 @@
+---
+- name: Setup
+ cli_config:
+ config: "{{ lines }}"
+ vars:
+ lines: |
+ set interfaces ethernet "{{ intf }}" description 'Configured by Ansible'
+ set interfaces ethernet "{{ intf }}" speed 'auto'
+ set interfaces ethernet "{{ intf }}" duplex 'auto'
+ set interfaces ethernet "{{ intf }}" mtu '1500'
+ set interfaces ethernet "{{ intf }}" vif 200
+ set interfaces ethernet "{{ intf }}" vif 200 description 'VIF - 200'
+ loop:
+ - eth1
+ - eth2
+ loop_control:
+ loop_var: intf \ No newline at end of file
diff --git a/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml b/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml
new file mode 100644
index 0000000000..6d559c1512
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/_remove_config.yaml
@@ -0,0 +1,17 @@
+---
+- name: Remove Config
+ cli_config:
+ config: "{{ lines }}"
+ vars:
+ lines: |
+ delete interfaces ethernet "{{ intf }}" description
+ delete interfaces ethernet "{{ intf }}" speed
+ delete interfaces ethernet "{{ intf }}" duplex
+ delete interfaces ethernet "{{ intf }}" mtu
+ delete interfaces ethernet "{{ intf }}" disable
+ delete interfaces ethernet "{{ intf }}" vif
+ loop:
+ - eth1
+ - eth2
+ loop_control:
+ loop_var: intf \ No newline at end of file
diff --git a/test/integration/targets/vyos_interfaces/tests/cli/deleted.yaml b/test/integration/targets/vyos_interfaces/tests/cli/deleted.yaml
new file mode 100644
index 0000000000..5b08ea95f8
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/deleted.yaml
@@ -0,0 +1,46 @@
+---
+- debug:
+ msg: "Start vyos_interfaces deleted integration tests ansible_connection={{ ansible_connection }}"
+
+- include_tasks: _populate.yaml
+
+- block:
+ - name: Delete attributes of given interfaces
+ vyos_interfaces: &deleted
+ config:
+ - name: eth1
+ - name: eth2
+ 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_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_interfaces/tests/cli/merged.yaml b/test/integration/targets/vyos_interfaces/tests/cli/merged.yaml
new file mode 100644
index 0000000000..62371913da
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/merged.yaml
@@ -0,0 +1,59 @@
+---
+- debug:
+ msg: "START vyos_interfaces merged integration tests on connection={{ ansible_connection }}"
+
+- include_tasks: _remove_config.yaml
+
+- block:
+ - name: Merge the provided configuration with the exisiting running configuration
+ vyos_interfaces: &merged
+ config:
+ - name: eth1
+ description: "Configured by Ansible - Interface 1"
+ mtu: 1500
+ speed: auto
+ duplex: auto
+ vifs:
+ - vlan_id: 100
+ description: "Eth1 - VIF 100"
+ mtu: 400
+
+ - vlan_id: 101
+ description: "Eth1 - VIF 101"
+
+ - name: eth2
+ description: "Configured by Ansible - Interface 2 (ADMIN DOWN)"
+ mtu: 600
+ enabled: false
+ 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_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_interfaces/tests/cli/overridden.yaml b/test/integration/targets/vyos_interfaces/tests/cli/overridden.yaml
new file mode 100644
index 0000000000..43040c1e67
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/overridden.yaml
@@ -0,0 +1,52 @@
+---
+- debug:
+ msg: "START vyos_interfaces overridden integration tests on connection={{ ansible_connection }}"
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+ - name: Overrides all device configuration with provided configuration
+ vyos_interfaces: &overridden
+ config:
+ - name: eth0
+ speed: "auto"
+ duplex: "auto"
+ - name: eth2
+ description: "Overridden by Ansible"
+ mtu: 1200
+ 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_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_interfaces/tests/cli/replaced.yaml b/test/integration/targets/vyos_interfaces/tests/cli/replaced.yaml
new file mode 100644
index 0000000000..6acc5830a6
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/replaced.yaml
@@ -0,0 +1,55 @@
+---
+- debug:
+ msg: "START vyos_interfaces replaced integration tests on connection={{ ansible_connection }}"
+
+- include_tasks: _remove_config.yaml
+
+- include_tasks: _populate.yaml
+
+- block:
+ - name: Replace device configurations of listed interfaces with provided configurations
+ vyos_interfaces: &replaced
+ config:
+ - name: eth1
+ description: "Replaced by Ansible"
+ vifs:
+ - vlan_id: 100
+ description: "VIF 100 - Replaced by Ansible"
+
+ - name: eth2
+ mtu: 1400
+ description: "Replaced by Ansible"
+ 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 interfaces with provided configurarions (IDEMPOTENT)
+ vyos_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 \ No newline at end of file
diff --git a/test/integration/targets/vyos_interfaces/tests/cli/rtt.yaml b/test/integration/targets/vyos_interfaces/tests/cli/rtt.yaml
new file mode 100644
index 0000000000..c246fa3591
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/tests/cli/rtt.yaml
@@ -0,0 +1,78 @@
+---
+- debug:
+ msg: "START vyos_interfaces round trip integration tests on connection={{ ansible_connection }}"
+
+- include_tasks: _remove_config.yaml
+
+- block:
+ - name: Apply the provided configuration (base config)
+ vyos_interfaces:
+ config:
+ - name: eth0
+ enabled: true
+ duplex: "auto"
+ speed: "auto"
+
+ - name: eth1
+ description: "Interface - 1"
+ mtu: 1500
+ vifs:
+ - vlan_id: 100
+ description: "Eth1 - VIF 100"
+ mtu: 200
+
+ - vlan_id: 101
+ enabled: false
+
+ - name: eth2
+ description: "Interface - 2"
+ enabled: true
+ mtu: 900
+ state: merged
+ register: base_config
+
+ - name: Gather interfaces facts
+ vyos_facts:
+ gather_subset:
+ - default
+ gather_network_resources:
+ - interfaces
+
+ - name: Apply the provided configuration (config to be reverted)
+ vyos_interfaces:
+ config:
+ - name: eth1
+ description: "Interface 1 - Description (WILL BE REVERTED)"
+ mtu: 1200
+ vifs:
+ - vlan_id: 100
+ description: "Eth1 - VIF 100 (WILL BE REVERTED)"
+ mtu: 400
+
+ - vlan_id: 101
+ description: "Eth1 - VIF 101 (WILL BE REMOVED)"
+ enabled: true
+
+ - name: eth2
+ description: "Interface 2 (ADMIN DOWN) (WILL BE REVERTED)"
+ mtu: 600
+ enabled: false
+ 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_interfaces:
+ config: "{{ ansible_facts['network_resources']['interfaces'] }}"
+ state: replaced
+ 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_interfaces/vars/main.yaml b/test/integration/targets/vyos_interfaces/vars/main.yaml
new file mode 100644
index 0000000000..e3bd8188d7
--- /dev/null
+++ b/test/integration/targets/vyos_interfaces/vars/main.yaml
@@ -0,0 +1,230 @@
+---
+merged:
+ before:
+ - name: "eth0"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+
+ - name: "eth1"
+ enabled: true
+
+ - 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'"
+ - "set interfaces ethernet eth1 duplex 'auto'"
+ - "set interfaces ethernet eth1 speed 'auto'"
+ - "set interfaces ethernet eth1 vif 100 description 'Eth1 - VIF 100'"
+ - "set interfaces ethernet eth1 vif 100 mtu '400'"
+ - "set interfaces ethernet eth1 vif 101 description 'Eth1 - VIF 101'"
+ - "set interfaces ethernet eth2 description 'Configured by Ansible - Interface 2 (ADMIN DOWN)'"
+ - "set interfaces ethernet eth2 mtu '600'"
+ - "set interfaces ethernet eth2 disable"
+
+ after:
+ - name: "lo"
+ enabled: true
+
+ - name: "eth0"
+ enabled: true
+ duplex: "auto"
+ speed: "auto"
+
+ - name: "eth1"
+ description: "Configured by Ansible - Interface 1"
+ mtu: 1500
+ speed: "auto"
+ duplex: "auto"
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ description: "Eth1 - VIF 100"
+ mtu: 400
+ enabled: true
+
+ - vlan_id: 101
+ description: "Eth1 - VIF 101"
+ enabled: true
+
+ - name: "eth2"
+ description: "Configured by Ansible - Interface 2 (ADMIN DOWN)"
+ mtu: 600
+ enabled: false
+
+populate:
+ - name: "eth1"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+ description: "Configured by Ansible"
+ mtu: 1500
+ vifs:
+ - vlan_id: 200
+ enabled: true
+ description: "VIF - 200"
+
+ - name: "eth2"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+ description: "Configured by Ansible"
+ mtu: 1500
+ vifs:
+ - vlan_id: 200
+ enabled: true
+ description: "VIF - 200"
+
+ - name: "eth0"
+ enabled: true
+ duplex: "auto"
+ speed: "auto"
+
+ - name: "lo"
+ enabled: true
+
+replaced:
+ commands:
+ - "delete interfaces ethernet eth1 mtu"
+ - "delete interfaces ethernet eth1 speed"
+ - "delete interfaces ethernet eth1 duplex"
+ - "delete interfaces ethernet eth1 vif 200 description"
+ - "set interfaces ethernet eth1 description 'Replaced by Ansible'"
+ - "set interfaces ethernet eth1 vif 100 description 'VIF 100 - Replaced by Ansible'"
+ - "delete interfaces ethernet eth2 speed"
+ - "delete interfaces ethernet eth2 duplex"
+ - "delete interfaces ethernet eth2 vif 200 description"
+ - "set interfaces ethernet eth2 description 'Replaced by Ansible'"
+ - "set interfaces ethernet eth2 mtu '1400'"
+
+ after:
+ - name: "lo"
+ enabled: true
+
+ - name: "eth1"
+ description: "Replaced by Ansible"
+ enabled: true
+ vifs:
+ - vlan_id: 100
+ enabled: true
+ description: "VIF 100 - Replaced by Ansible"
+
+ - vlan_id: 200
+ enabled: true
+
+ - name: "eth2"
+ mtu: 1400
+ description: "Replaced by Ansible"
+ enabled: true
+ vifs:
+ - vlan_id: 200
+ enabled: true
+
+ - name: "eth0"
+ enabled: true
+ duplex: "auto"
+ speed: "auto"
+
+overridden:
+ commands:
+ - "delete interfaces ethernet eth1 description"
+ - "delete interfaces ethernet eth1 speed"
+ - "delete interfaces ethernet eth1 duplex"
+ - "delete interfaces ethernet eth1 mtu"
+ - "delete interfaces ethernet eth1 vif 200 description"
+ - "delete interfaces ethernet eth2 speed"
+ - "delete interfaces ethernet eth2 duplex"
+ - "delete interfaces ethernet eth2 vif 200 description"
+ - "set interfaces ethernet eth2 description 'Overridden by Ansible'"
+ - "set interfaces ethernet eth2 mtu '1200'"
+
+ after:
+ - name: "lo"
+ enabled: true
+
+ - name: "eth0"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+
+ - name: "eth1"
+ enabled: true
+ vifs:
+ - vlan_id: 200
+ enabled: true
+
+ - name: "eth2"
+ enabled: true
+ description: "Overridden by Ansible"
+ mtu: 1200
+ vifs:
+ - vlan_id: 200
+ enabled: true
+
+deleted:
+ commands:
+ - "delete interfaces ethernet eth1 description"
+ - "delete interfaces ethernet eth1 speed"
+ - "delete interfaces ethernet eth1 duplex"
+ - "delete interfaces ethernet eth1 mtu"
+ - "delete interfaces ethernet eth1 vif 200 description"
+ - "delete interfaces ethernet eth2 description"
+ - "delete interfaces ethernet eth2 speed"
+ - "delete interfaces ethernet eth2 duplex"
+ - "delete interfaces ethernet eth2 mtu"
+ - "delete interfaces ethernet eth2 vif 200 description"
+
+ after:
+ - name: "lo"
+ enabled: true
+
+ - name: "eth0"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+
+ - name: "eth1"
+ enabled: true
+ vifs:
+ - vlan_id: 200
+ enabled: true
+
+ - name: "eth2"
+ enabled: true
+ vifs:
+ - vlan_id: 200
+ enabled: true
+
+round_trip:
+ after:
+ - name: "lo"
+ enabled: true
+
+ - name: "eth0"
+ enabled: true
+ speed: "auto"
+ duplex: "auto"
+
+ - name: "eth1"
+ description: "Interface 1 - Description (WILL BE REVERTED)"
+ enabled: true
+ mtu: 1200
+ vifs:
+ - vlan_id: 100
+ description: "Eth1 - VIF 100 (WILL BE REVERTED)"
+ mtu: 400
+ enabled: true
+
+ - vlan_id: 101
+ description: "Eth1 - VIF 101 (WILL BE REMOVED)"
+ enabled: true
+
+ - name: "eth2"
+ description: "Interface 2 (ADMIN DOWN) (WILL BE REVERTED)"
+ mtu: 600
+ enabled: false
diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt
index e5b08324cb..58dfa8fb16 100644
--- a/test/sanity/ignore.txt
+++ b/test/sanity/ignore.txt
@@ -5767,14 +5767,14 @@ lib/ansible/modules/network/vyos/vyos_facts.py future-import-boilerplate
lib/ansible/modules/network/vyos/vyos_facts.py metaclass-boilerplate
lib/ansible/modules/network/vyos/vyos_facts.py validate-modules:E324
lib/ansible/modules/network/vyos/vyos_facts.py validate-modules:E337
-lib/ansible/modules/network/vyos/vyos_interface.py future-import-boilerplate
-lib/ansible/modules/network/vyos/vyos_interface.py metaclass-boilerplate
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E322
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E324
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E326
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E337
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E338
-lib/ansible/modules/network/vyos/vyos_interface.py validate-modules:E340
+lib/ansible/modules/network/vyos/_vyos_interface.py future-import-boilerplate
+lib/ansible/modules/network/vyos/_vyos_interface.py metaclass-boilerplate
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E322
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E324
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E326
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E337
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E338
+lib/ansible/modules/network/vyos/_vyos_interface.py validate-modules:E340
lib/ansible/modules/network/vyos/vyos_l3_interface.py future-import-boilerplate
lib/ansible/modules/network/vyos/vyos_l3_interface.py metaclass-boilerplate
lib/ansible/modules/network/vyos/vyos_l3_interface.py validate-modules:E322
diff --git a/test/units/modules/network/vyos/test_vyos_facts.py b/test/units/modules/network/vyos/test_vyos_facts.py
index abc68ad855..91ad81a80e 100644
--- a/test/units/modules/network/vyos/test_vyos_facts.py
+++ b/test/units/modules/network/vyos/test_vyos_facts.py
@@ -33,10 +33,13 @@ class TestVyosFactsModule(TestVyosModule):
def setUp(self):
super(TestVyosFactsModule, self).setUp()
- self.mock_run_commands = patch('ansible.modules.network.vyos.vyos_facts.run_commands')
+ self.mock_run_commands = patch('ansible.module_utils.network.vyos.facts.legacy.base.run_commands')
self.run_commands = self.mock_run_commands.start()
- self.mock_get_capabilities = patch('ansible.modules.network.vyos.vyos_facts.get_capabilities')
+ self.mock_get_resource_connection = patch('ansible.module_utils.network.common.facts.facts.get_resource_connection')
+ self.get_resource_connection = self.mock_get_resource_connection.start()
+
+ self.mock_get_capabilities = patch('ansible.module_utils.network.vyos.facts.legacy.base.get_capabilities')
self.get_capabilities = self.mock_get_capabilities.start()
self.get_capabilities.return_value = {
'device_info': {
@@ -52,6 +55,7 @@ class TestVyosFactsModule(TestVyosModule):
super(TestVyosFactsModule, self).tearDown()
self.mock_run_commands.stop()
self.mock_get_capabilities.stop()
+ self.mock_get_resource_connection.stop()
def load_fixtures(self, commands=None):
def load_from_file(*args, **kwargs):
@@ -74,7 +78,7 @@ class TestVyosFactsModule(TestVyosModule):
set_module_args(dict(gather_subset='default'))
result = self.execute_module()
facts = result.get('ansible_facts')
- self.assertEqual(len(facts), 8)
+ self.assertEqual(len(facts), 10)
self.assertEqual(facts['ansible_net_hostname'].strip(), 'vyos01')
self.assertEqual(facts['ansible_net_version'], 'VyOS 1.1.7')
@@ -82,7 +86,7 @@ class TestVyosFactsModule(TestVyosModule):
set_module_args(dict(gather_subset='!all'))
result = self.execute_module()
facts = result.get('ansible_facts')
- self.assertEqual(len(facts), 8)
+ self.assertEqual(len(facts), 10)
self.assertEqual(facts['ansible_net_hostname'].strip(), 'vyos01')
self.assertEqual(facts['ansible_net_version'], 'VyOS 1.1.7')
@@ -90,7 +94,7 @@ class TestVyosFactsModule(TestVyosModule):
set_module_args(dict(gather_subset=['!neighbors', '!config']))
result = self.execute_module()
facts = result.get('ansible_facts')
- self.assertEqual(len(facts), 8)
+ self.assertEqual(len(facts), 10)
self.assertEqual(facts['ansible_net_hostname'].strip(), 'vyos01')
self.assertEqual(facts['ansible_net_version'], 'VyOS 1.1.7')