From 64add286572523f8a7bc7fda1a2f8aa9b04c8d3e Mon Sep 17 00:00:00 2001 From: Ricardo Carrillo Cruz Date: Fri, 2 Jun 2017 14:06:38 +0200 Subject: [PATCH] Platform agnostic net_system module (#24953) * Platform agnostic net_system module Also refactor the action network plugins for better code re-use Still more refactoring to do once the connection plugin work is complete * Replace importlib for imp importlib is not available on 2.6, so we need to stick to imp * Load action plugin via module metadata * Better error message if no implementation is found Now the plugin will show the module name and the network OS in the error message * Fix typo on documentation author line * Fix pep8 issues * Add missing options key on doc string and stringify version * Return None in case module has no metadata * Read module metadata only if it's a python module Check for module suffix, if it's .py then read metadata. Otherwise this fails on non-python modules, like Windows PS for example. * Read metadata variable only if it's a python module Fix referencing a variable before assignment * Add action_handler to validate_modules metadata schema * Pull metadata with plugin_docs get_docstring Using load_source from PluginLoader is troublesome, it is not guaranteed a module may be importable at the controller, e.g. if a module depends on module_utils functions it won't work, because module_utils is not in the sys path. Rather than putting that module dependencies introspection, just use plain parsing like plugin_docs get_docstring does as we only care about reading ANSIBLE_METADATA. * Add platform agnostic group of groups for integration tests This will be the target for platform agnostic integration tests. * Add integration tests for net_system * Switch to action plugin inheritance from metadata driven action handler As the metadata action driven action handler work is being worked on on its standalone proposal+PR, let's just go back to have one action handler per platform agnostic module. Those action plugins will inherit from net_base. * Add blank line to fix pep8 * Add aliases file to net_system integration test This will avoid CI failure * Fix integration tests for net_system * Give more precedence to task network_os over inventory network_os --- lib/ansible/module_utils/eos.py | 4 + lib/ansible/module_utils/ios.py | 4 + lib/ansible/module_utils/iosxr.py | 4 + lib/ansible/module_utils/junos.py | 4 + lib/ansible/module_utils/nxos.py | 4 + lib/ansible/module_utils/vyos.py | 4 + lib/ansible/modules/network/net_system.py | 110 ++++++++++ lib/ansible/plugins/action/net_base.py | 195 ++++++++++++++++++ lib/ansible/plugins/action/net_system.py | 26 +++ test/integration/inventory.network | 20 +- test/integration/platform_agnostic.yaml | 11 + test/integration/targets/net_system/aliases | 2 + .../targets/net_system/defaults/main.yaml | 2 + .../targets/net_system/tasks/cli.yaml | 16 ++ .../targets/net_system/tasks/main.yaml | 2 + .../tests/cli/set_name_servers.yaml | 16 ++ .../tests/eos/set_name_servers.yaml | 64 ++++++ .../tests/ios/set_name_servers.yaml | 67 ++++++ .../tests/iosxr/set_name_servers.yaml | 59 ++++++ .../tests/nxos/set_name_servers.yaml | 66 ++++++ 20 files changed, 674 insertions(+), 6 deletions(-) create mode 100644 lib/ansible/modules/network/net_system.py create mode 100644 lib/ansible/plugins/action/net_base.py create mode 100644 lib/ansible/plugins/action/net_system.py create mode 100644 test/integration/platform_agnostic.yaml create mode 100644 test/integration/targets/net_system/aliases create mode 100644 test/integration/targets/net_system/defaults/main.yaml create mode 100644 test/integration/targets/net_system/tasks/cli.yaml create mode 100644 test/integration/targets/net_system/tasks/main.yaml create mode 100644 test/integration/targets/net_system/tests/cli/set_name_servers.yaml create mode 100644 test/integration/targets/net_system/tests/eos/set_name_servers.yaml create mode 100644 test/integration/targets/net_system/tests/ios/set_name_servers.yaml create mode 100644 test/integration/targets/net_system/tests/iosxr/set_name_servers.yaml create mode 100644 test/integration/targets/net_system/tests/nxos/set_name_servers.yaml diff --git a/lib/ansible/module_utils/eos.py b/lib/ansible/module_utils/eos.py index e3fbf415fa..28bcfb5a6c 100644 --- a/lib/ansible/module_utils/eos.py +++ b/lib/ansible/module_utils/eos.py @@ -66,6 +66,10 @@ ARGS_DEFAULT_VALUE = { } +def get_argspec(): + return eos_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in eos_argument_spec: diff --git a/lib/ansible/module_utils/ios.py b/lib/ansible/module_utils/ios.py index c574b4c091..6ff369c484 100644 --- a/lib/ansible/module_utils/ios.py +++ b/lib/ansible/module_utils/ios.py @@ -45,6 +45,10 @@ ios_argument_spec = { } +def get_argspec(): + return ios_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in ios_argument_spec: diff --git a/lib/ansible/module_utils/iosxr.py b/lib/ansible/module_utils/iosxr.py index 769f487f52..07ffe10738 100644 --- a/lib/ansible/module_utils/iosxr.py +++ b/lib/ansible/module_utils/iosxr.py @@ -44,6 +44,10 @@ iosxr_argument_spec = { } +def get_argspec(): + return iosxr_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in iosxr_argument_spec: diff --git a/lib/ansible/module_utils/junos.py b/lib/ansible/module_utils/junos.py index b45acfb640..83813ace48 100644 --- a/lib/ansible/module_utils/junos.py +++ b/lib/ansible/module_utils/junos.py @@ -48,6 +48,10 @@ ARGS_DEFAULT_VALUE = { } +def get_argspec(): + return junos_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in junos_argument_spec: diff --git a/lib/ansible/module_utils/nxos.py b/lib/ansible/module_utils/nxos.py index fce1e8eba9..2c0db76757 100644 --- a/lib/ansible/module_utils/nxos.py +++ b/lib/ansible/module_utils/nxos.py @@ -62,6 +62,10 @@ ARGS_DEFAULT_VALUE = { } +def get_argspec(): + return nxos_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in nxos_argument_spec: diff --git a/lib/ansible/module_utils/vyos.py b/lib/ansible/module_utils/vyos.py index 9d4ef682b3..881bfb8b87 100644 --- a/lib/ansible/module_utils/vyos.py +++ b/lib/ansible/module_utils/vyos.py @@ -45,6 +45,10 @@ vyos_argument_spec = { } +def get_argspec(): + return vyos_argument_spec + + def check_args(module, warnings): provider = module.params['provider'] or {} for key in vyos_argument_spec: diff --git a/lib/ansible/modules/network/net_system.py b/lib/ansible/modules/network/net_system.py new file mode 100644 index 0000000000..5776f6967c --- /dev/null +++ b/lib/ansible/modules/network/net_system.py @@ -0,0 +1,110 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2017, Ansible by Red Hat, inc +# +# This file is part of Ansible by Red Hat +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# + +ANSIBLE_METADATA = {'metadata_version': '1.0', + 'status': ['preview'], + 'supported_by': 'core'} + + +DOCUMENTATION = """ +--- +module: net_system +version_added: "2.4" +author: "Ricardo Carrillo Cruz (@rcarrillocruz)" +short_description: Manage the system attributes on network devices +description: + - This module provides declarative management of node system attributes + on network devices. It provides an option to configure host system + parameters or remove those parameters from the device active + configuration. +options: + hostname: + description: + - Configure the device hostname parameter. This option takes an ASCII string value. + domain_name: + description: + - Configure the IP domain name + on the remote device to the provided value. Value + should be in the dotted name form and will be + appended to the C(hostname) to create a fully-qualified + domain name. + domain_search: + description: + - Provides the list of domain suffixes to + append to the hostname for the purpose of doing name resolution. + This argument accepts a list of names and will be reconciled + with the current active configuration on the running node. + lookup_source: + description: + - Provides one or more source + interfaces to use for performing DNS lookups. The interface + provided in C(lookup_source) must be a valid interface configured + on the device. + name_servers: + description: + - List of DNS name servers by IP address to use to perform name resolution + lookups. This argument accepts either a list of DNS servers See + examples. + state: + description: + - State of the configuration + values in the device's current active configuration. When set + to I(present), the values should be configured in the device active + configuration and when set to I(absent) the values should not be + in the device active configuration + default: present + choices: ['present', 'absent'] +""" + +EXAMPLES = """ +- name: configure hostname and domain name + net_system: + hostname: ios01 + domain_name: test.example.com + domain-search: + - ansible.com + - redhat.com + - cisco.com + +- name: remove configuration + net_system: + state: absent + +- name: configure DNS lookup sources + net_system: + lookup_source: MgmtEth0/0/CPU0/0 + +- name: configure name servers + net_system: + name_servers: + - 8.8.8.8 + - 8.8.4.4 +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - hostname ios01 + - ip domain name test.example.com +""" diff --git a/lib/ansible/plugins/action/net_base.py b/lib/ansible/plugins/action/net_base.py new file mode 100644 index 0000000000..6df0bdc09c --- /dev/null +++ b/lib/ansible/plugins/action/net_base.py @@ -0,0 +1,195 @@ +# (c) 2015, Ansible Inc, +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import os +import sys +import copy + +from ansible.plugins.action import ActionBase +from ansible.utils.path import unfrackpath +from ansible.plugins import connection_loader +from ansible.module_utils.basic import AnsibleFallbackNotFound +from ansible.module_utils.six import iteritems +from ansible.module_utils._text import to_bytes + +from imp import find_module, load_module + +try: + from __main__ import display +except ImportError: + from ansible.utils.display import Display + display = Display() + + +class ActionModule(ActionBase): + + def run(self, tmp=None, task_vars=None): + if self._play_context.connection != 'local': + return dict( + failed=True, + msg='invalid connection specified, expected connection=local, ' + 'got %s' % self._play_context.connection + ) + + play_context = copy.deepcopy(self._play_context) + play_context.network_os = self._get_network_os(task_vars) + # TODO this can be netconf + play_context.connection = 'network_cli' + + self.provider = self._load_provider(play_context.network_os) + + play_context.remote_addr = self.provider['host'] or self._play_context.remote_addr + play_context.port = self.provider['port'] or self._play_context.port or 22 + play_context.remote_user = self.provider['username'] or self._play_context.connection_user + play_context.password = self.provider['password'] or self._play_context.password + play_context.private_key_file = self.provider['ssh_keyfile'] or self._play_context.private_key_file + play_context.timeout = self.provider['timeout'] or self._play_context.timeout + if 'authorize' in self.provider.keys(): + play_context.become = self.provider['authorize'] or False + play_context.become_pass = self.provider['auth_pass'] + + socket_path = self._start_connection(play_context) + task_vars['ansible_socket'] = socket_path + + result = super(ActionModule, self).run(tmp, task_vars) + + module = self._get_implementation_module(play_context.network_os, self._task.action) + + if not module: + result['failed'] = True + result['msg'] = ('Could not find implementation module %s for %s' % + (self._task.action, play_context.network_os)) + else: + new_module_args = self._task.args.copy() + # perhaps delete the provider argument here as well since the + # module code doesn't need the information, the connection is + # already started + if 'network_os' in new_module_args: + del new_module_args['network_os'] + + display.vvvv('Running implementation module %s' % module) + result.update(self._execute_module(module_name=module, + module_args=new_module_args, task_vars=task_vars, + wrap_async=self._task.async)) + + display.vvvv('Caching network OS %s in facts' % play_context.network_os) + result['ansible_facts'] = {'network_os': play_context.network_os} + + return result + + def _start_connection(self, play_context): + + display.vvv('using connection plugin %s' % play_context.connection, play_context.remote_addr) + connection = self._shared_loader_obj.connection_loader.get('persistent', + play_context, sys.stdin) + + socket_path = self._get_socket_path(play_context) + display.vvvv('socket_path: %s' % socket_path, play_context.remote_addr) + + if not os.path.exists(socket_path): + # start the connection if it isn't started + rc, out, err = connection.exec_command('open_shell()') + display.vvvv('open_shell() returned %s %s %s' % (rc, out, err)) + if not rc == 0: + return {'failed': True, + 'msg': 'unable to open shell. Please see: ' + + 'https://docs.ansible.com/ansible/network_debug_troubleshooting.html#unable-to-open-shell', + 'rc': rc} + else: + # make sure we are in the right cli context which should be + # enable mode and not config module + rc, out, err = connection.exec_command('prompt()') + if str(out).strip().endswith(')#'): + display.vvvv('wrong context, sending exit to device', self._play_context.remote_addr) + connection.exec_command('exit') + + if self._play_context.become_method == 'enable': + self._play_context.become = False + self._play_context.become_method = None + + return socket_path + + def _get_network_os(self, task_vars): + if ('network_os' in self._task.args and self._task.args['network_os']): + display.vvvv('Getting network OS from task argument') + network_os = self._task.args['network_os'] + elif (self._play_context.network_os): + display.vvvv('Getting network OS from inventory') + network_os = self._play_context.network_os + elif ('network_os' in task_vars['ansible_facts'] and + task_vars['ansible_facts']['network_os']): + display.vvvv('Getting network OS from fact') + network_os = task_vars['ansible_facts']['network_os'] + else: + # this will be replaced by the call to get_capabilities() on the + # connection + display.vvvv('Getting network OS from net discovery') + network_os = None + + return network_os + + def _get_implementation_module(self, network_os, platform_agnostic_module): + implementation_module = network_os + '_' + platform_agnostic_module.partition('_')[2] + if implementation_module not in self._shared_loader_obj.module_loader: + implementation_module = None + + return implementation_module + + # this will be removed once the new connection work is done + def _get_socket_path(self, play_context): + ssh = connection_loader.get('ssh', class_only=True) + cp = ssh._create_control_path(play_context.remote_addr, play_context.port, play_context.remote_user) + path = unfrackpath("$HOME/.ansible/pc") + return cp % dict(directory=path) + + def _load_provider(self, network_os): + # we should be able to stream line this a bit by creating a common + # provider argument spec in module_utils/network_common.py or another + # option is that there isn't a need to push provider into the module + # since the connection is started in the action handler. + f, p, d = find_module('ansible') + f2, p2, d2 = find_module('module_utils', [p]) + f3, p3, d3 = find_module(network_os, [p2]) + module = load_module('ansible.module_utils.' + network_os, f3, p3, d3) + + provider = self._task.args.get('provider', {}) + for key, value in iteritems(module.get_argspec()): + if key != 'provider' and key not in provider: + if key in self._task.args: + provider[key] = self._task.args[key] + elif 'fallback' in value: + provider[key] = self._fallback(value['fallback']) + elif key not in provider: + provider[key] = None + return provider + + def _fallback(self, fallback): + strategy = fallback[0] + args = [] + kwargs = {} + + for item in fallback[1:]: + if isinstance(item, dict): + kwargs = item + else: + args = item + try: + return strategy(*args, **kwargs) + except AnsibleFallbackNotFound: + pass diff --git a/lib/ansible/plugins/action/net_system.py b/lib/ansible/plugins/action/net_system.py new file mode 100644 index 0000000000..a4ee4db0b6 --- /dev/null +++ b/lib/ansible/plugins/action/net_system.py @@ -0,0 +1,26 @@ +# (c) 2017, Ansible Inc, +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from ansible.plugins.action.net_base import ActionModule as _ActionModule + + +class ActionModule(_ActionModule): + def run(self, tmp=None, task_vars=None): + result = super(ActionModule, self).run(tmp, task_vars) + return result diff --git a/test/integration/inventory.network b/test/integration/inventory.network index c959e33613..d90e1aff93 100644 --- a/test/integration/inventory.network +++ b/test/integration/inventory.network @@ -5,27 +5,27 @@ ansible_python_interpreter=python [eos] #veos-dut-01 -veos01 +veos01 ansible_network_os=eos [nxos] -nxos01 +nxos01 ansible_network_os=nxos [iosxr] -iosxr01 +iosxr01 ansible_network_os=iosxr [ios] -ios01 +ios01 ansible_network_os=ios #csr01 [junos] -vsrx01 +vsrx01 ansible_network_os=junos [cumulus] clvx01 [vyos] -vyos-dut-01 +vyos01 ansible_network_os=vyos [ops] ops01 @@ -33,4 +33,12 @@ ops01 [asa] asa01 +[platform_agnostic:children] +ios +iosxr +eos +junos +vyos +nxos + # vim: nospell filetype=dosini diff --git a/test/integration/platform_agnostic.yaml b/test/integration/platform_agnostic.yaml new file mode 100644 index 0000000000..ee9c55c2a5 --- /dev/null +++ b/test/integration/platform_agnostic.yaml @@ -0,0 +1,11 @@ +--- +- hosts: platform_agnostic + gather_facts: no + connection: local + + vars: + limit_to: "*" + debug: false + + roles: + - { role: net_system, when: "limit_to in ['*', 'net_system']" } diff --git a/test/integration/targets/net_system/aliases b/test/integration/targets/net_system/aliases new file mode 100644 index 0000000000..5c43f4b8f8 --- /dev/null +++ b/test/integration/targets/net_system/aliases @@ -0,0 +1,2 @@ +network/ci +skip/python3 diff --git a/test/integration/targets/net_system/defaults/main.yaml b/test/integration/targets/net_system/defaults/main.yaml new file mode 100644 index 0000000000..5f709c5aac --- /dev/null +++ b/test/integration/targets/net_system/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/test/integration/targets/net_system/tasks/cli.yaml b/test/integration/targets/net_system/tasks/cli.yaml new file mode 100644 index 0000000000..46d86dd698 --- /dev/null +++ b/test/integration/targets/net_system/tasks/cli.yaml @@ -0,0 +1,16 @@ +--- +- name: collect all cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + 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 + include: "{{ test_case_to_run }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/net_system/tasks/main.yaml b/test/integration/targets/net_system/tasks/main.yaml new file mode 100644 index 0000000000..415c99d8b1 --- /dev/null +++ b/test/integration/targets/net_system/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/net_system/tests/cli/set_name_servers.yaml b/test/integration/targets/net_system/tests/cli/set_name_servers.yaml new file mode 100644 index 0000000000..e803e60e48 --- /dev/null +++ b/test/integration/targets/net_system/tests/cli/set_name_servers.yaml @@ -0,0 +1,16 @@ +--- +- debug: msg="START cli/set_name_servers.yaml" + +- include: "{{ role_path }}/tests/ios/set_name_servers.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'ios' + +- include: "{{ role_path }}/tests/iosxr/set_name_servers.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'iosxr' + +- include: "{{ role_path }}/tests/nxos/set_name_servers.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'nxos' + +- include: "{{ role_path }}/tests/eos/set_name_servers.yaml" + when: hostvars[inventory_hostname]['ansible_network_os'] == 'eos' + +- debug: msg="END cli/set_name_servers.yaml" diff --git a/test/integration/targets/net_system/tests/eos/set_name_servers.yaml b/test/integration/targets/net_system/tests/eos/set_name_servers.yaml new file mode 100644 index 0000000000..4cb1b51602 --- /dev/null +++ b/test/integration/targets/net_system/tests/eos/set_name_servers.yaml @@ -0,0 +1,64 @@ +--- +- debug: msg="START eos/set_name_servers.yaml" + +- name: setup + eos_config: + lines: + - no ip name-server + - vrf definition ansible + match: none + provider: "{{ cli }}" + +- name: configure name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 3 + - "'ip name-server 1.1.1.1' in result.commands" + - "'ip name-server 2.2.2.2' in result.commands" + - "'ip name-server 3.3.3.3' in result.commands" + +- name: verify name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == false + +- name: remove one + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 1 + - "'no ip name-server 3.3.3.3' in result.commands" + +- name: teardown + eos_config: + lines: + - no ip domain lookup source-interface + - no vrf definition ansible + match: none + provider: "{{ cli }}" + +- debug: msg="END eos/set_name_servers.yaml" diff --git a/test/integration/targets/net_system/tests/ios/set_name_servers.yaml b/test/integration/targets/net_system/tests/ios/set_name_servers.yaml new file mode 100644 index 0000000000..c355a1e233 --- /dev/null +++ b/test/integration/targets/net_system/tests/ios/set_name_servers.yaml @@ -0,0 +1,67 @@ +--- +- debug: msg="START ios/set_name_servers.yaml" + +- name: setup + ios_config: + lines: + - no ip name-server + match: none + authorize: yes + provider: "{{ cli }}" + +- name: configure name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 3 + - "'ip name-server 1.1.1.1' in result.commands" + - "'ip name-server 2.2.2.2' in result.commands" + - "'ip name-server 3.3.3.3' in result.commands" + +- name: verify name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == false + +- name: remove one + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + authorize: yes + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 1 + - "'no ip name-server 3.3.3.3' in result.commands" + +- name: teardown + ios_config: + lines: + - no ip domain lookup source-interface + match: none + authorize: yes + provider: "{{ cli }}" + +- debug: msg="END ios/set_name_servers.yaml" diff --git a/test/integration/targets/net_system/tests/iosxr/set_name_servers.yaml b/test/integration/targets/net_system/tests/iosxr/set_name_servers.yaml new file mode 100644 index 0000000000..c8103f4bb1 --- /dev/null +++ b/test/integration/targets/net_system/tests/iosxr/set_name_servers.yaml @@ -0,0 +1,59 @@ +--- +- debug: msg="START iosxr/set_name_servers.yaml" + +- name: setup + iosxr_config: + lines: + - no ip name-server 1.1.1.1 + - no ip name-server 2.2.2.2 + - no ip name-server 3.3.3.3 + match: none + provider: "{{ cli }}" + +- name: configure name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 3 + - "'domain name-server 1.1.1.1' in result.commands" + - "'domain name-server 2.2.2.2' in result.commands" + - "'domain name-server 3.3.3.3' in result.commands" + +- name: verify name_servers + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == false + +- name: remove one + net_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 1 + - "'no domain name-server 3.3.3.3' in result.commands" + +# FIXME: No teardown +# +- debug: msg="END iosxr/set_name_servers.yaml" diff --git a/test/integration/targets/net_system/tests/nxos/set_name_servers.yaml b/test/integration/targets/net_system/tests/nxos/set_name_servers.yaml new file mode 100644 index 0000000000..1e0e9e6ebf --- /dev/null +++ b/test/integration/targets/net_system/tests/nxos/set_name_servers.yaml @@ -0,0 +1,66 @@ +--- +- debug: msg="START nxos/set_name_servers.yaml" + +- name: setup + nxos_config: + lines: + - no ip name-server 1.1.1.1 + - no ip name-server 2.2.2.2 + - no ip name-server 3.3.3.3 + match: none + provider: "{{ cli }}" + +- name: configure name_servers + nxos_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 3 + - "'ip name-server 1.1.1.1' in result.commands" + - "'ip name-server 2.2.2.2' in result.commands" + - "'ip name-server 3.3.3.3' in result.commands" + +- name: verify name_servers + nxos_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + - 3.3.3.3 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == false + +- name: remove one + nxos_system: + name_servers: + - 1.1.1.1 + - 2.2.2.2 + provider: "{{ cli }}" + register: result + +- assert: + that: + - result.changed == true + - result.commands|length == 1 + - "'no ip name-server 3.3.3.3' in result.commands" + +- name: teardown + nxos_config: + lines: + - no ip lookup source-interface + match: none + provider: "{{ cli }}" + ignore_errors: yes + # FIXME Copied from iosxr, not sure what we need here + +- debug: msg="END nxos/set_name_servers.yaml"