From 7a24ecde86bb499c531b5525f4dd1af674767fa5 Mon Sep 17 00:00:00 2001 From: Anil Kumar Muraleedharan Date: Wed, 20 Mar 2019 18:20:45 +0530 Subject: [PATCH] Lenovo cnos system module (#53764) * Adding cnos_system module to Ansible. * Adding UT, Functional test required for cnos_system. Bugs came up are fixed * Adding more files to the cnos_system suit. --- .../modules/network/cnos/cnos_system.py | 388 ++++++++++++++++++ .../cnos_linkagg/cnos_linkagg_sample_hosts | 4 +- test/integration/targets/cnos_system/aliases | 2 + .../cnos_system/cnos_system_sample_hosts | 14 + .../targets/cnos_system/defaults/main.yaml | 2 + .../targets/cnos_system/tasks/cli.yaml | 33 ++ .../targets/cnos_system/tasks/main.yaml | 2 + .../cnos_system/tests/cli/net_system.yaml | 34 ++ .../tests/cli/set_domain_list.yaml | 111 +++++ .../tests/cli/set_domain_name.yaml | 32 ++ .../tests/cli/set_name_servers.yaml | 73 ++++ .../cnos_system/tests/common/sanity.yaml | 122 ++++++ .../tests/common/set_hostname.yaml | 36 ++ .../cnos/fixtures/cnos_system_config.cfg | 11 + .../modules/network/cnos/test_cnos_system.py | 103 +++++ 15 files changed, 965 insertions(+), 2 deletions(-) create mode 100644 lib/ansible/modules/network/cnos/cnos_system.py create mode 100644 test/integration/targets/cnos_system/aliases create mode 100644 test/integration/targets/cnos_system/cnos_system_sample_hosts create mode 100644 test/integration/targets/cnos_system/defaults/main.yaml create mode 100644 test/integration/targets/cnos_system/tasks/cli.yaml create mode 100644 test/integration/targets/cnos_system/tasks/main.yaml create mode 100644 test/integration/targets/cnos_system/tests/cli/net_system.yaml create mode 100644 test/integration/targets/cnos_system/tests/cli/set_domain_list.yaml create mode 100644 test/integration/targets/cnos_system/tests/cli/set_domain_name.yaml create mode 100644 test/integration/targets/cnos_system/tests/cli/set_name_servers.yaml create mode 100644 test/integration/targets/cnos_system/tests/common/sanity.yaml create mode 100644 test/integration/targets/cnos_system/tests/common/set_hostname.yaml create mode 100644 test/units/modules/network/cnos/fixtures/cnos_system_config.cfg create mode 100644 test/units/modules/network/cnos/test_cnos_system.py diff --git a/lib/ansible/modules/network/cnos/cnos_system.py b/lib/ansible/modules/network/cnos/cnos_system.py new file mode 100644 index 00000000000..d48a046526f --- /dev/null +++ b/lib/ansible/modules/network/cnos/cnos_system.py @@ -0,0 +1,388 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type +# +# Copyright (C) 2019 Lenovo. +# (c) 2017, Ansible by Red Hat, inc +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# +# Module to work on System Configuration with Lenovo Switches +# Lenovo Networking +# +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +--- +module: cnos_system +version_added: "2.8" +author: "Anil Kumar Muraleedharan (@amuraleedhar)" +short_description: Manage the system attributes on Lenovo CNOS devices +description: + - This module provides declarative management of node system attributes + on Lenovo CNOS 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 or keyword 'default' + domain_name: + description: + - Configures the default domain + name suffix to be used when referencing this node by its + FQDN. This argument accepts either a list of domain names or + a list of dicts that configure the domain name and VRF name or + keyword 'default'. See examples. + lookup_enabled: + description: + - Administrative control for enabling or disabling DNS lookups. + When this argument is set to True, lookups are performed and + when it is set to False, lookups are not performed. + type: bool + domain_search: + description: + - Configures a list of domain + name suffixes to search when performing DNS name resolution. + This argument accepts either a list of domain names or + a list of dicts that configure the domain name and VRF name or + keyword 'default'. See examples. + 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 or + a list of hashes that configure the name server and VRF name or + keyword 'default'. See examples. + lookup_source: + description: + - Provides one or more source interfaces to use for performing DNS + lookups. The interface must be a valid interface configured. + on the device. + 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 + cnos_system: + hostname: cnos01 + domain_name: test.example.com + +- name: remove configuration + cnos_system: + state: absent + +- name: configure name servers + cnos_system: + name_servers: + - 8.8.8.8 + - 8.8.4.4 + +- name: configure DNS Lookup sources + cnos_system: + lookup_source: MgmtEth0/0/CPU0/0 + lookup_enabled: yes + +- name: configure name servers with VRF support + nxos_system: + name_servers: + - { server: 8.8.8.8, vrf: mgmt } + - { server: 8.8.4.4, vrf: mgmt } +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always + type: list + sample: + - hostname cnos01 + - ip domain-name test.example.com vrf default +""" +import re + +from ansible.module_utils.network.cnos.cnos import get_config, load_config +from ansible.module_utils.network.cnos.cnos import cnos_argument_spec +from ansible.module_utils.network.cnos.cnos import check_args, debugOutput +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.six import iteritems +from ansible.module_utils.network.common.config import NetworkConfig +from ansible.module_utils.network.common.utils import ComplexList + +_CONFIGURED_VRFS = None + + +def map_obj_to_commands(want, have, module): + commands = list() + state = module.params['state'] + + def needs_update(x): + return want.get(x) and (want.get(x) != have.get(x)) + + def difference(x, y, z): + return [item for item in x[z] if item not in y[z]] + + if state == 'absent': + if have['hostname']: + commands.append('no hostname') + + for item in have['domain_name']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + + for item in have['domain_search']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + + for item in have['name_servers']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) + commands.append(cmd) + + if state == 'present': + if needs_update('hostname'): + if want['hostname'] == 'default': + if have['hostname']: + commands.append('no hostname') + else: + commands.append('hostname %s' % want['hostname']) + + if want.get('lookup_enabled') is not None: + if have.get('lookup_enabled') != want.get('lookup_enabled'): + cmd = 'ip domain-lookup' + if want['lookup_enabled'] is False: + cmd = 'no %s' % cmd + commands.append(cmd) + + if want['domain_name']: + if want.get('domain_name')[0]['name'] == 'default': + if have['domain_name']: + for item in have['domain_name']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + else: + for item in difference(have, want, 'domain_name'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + for item in difference(want, have, 'domain_name'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'ip domain-name {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + + if want['domain_search']: + if want.get('domain_search')[0]['name'] == 'default': + if have['domain_search']: + for item in have['domain_search']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + else: + for item in difference(have, want, 'domain_search'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + for item in difference(want, have, 'domain_search'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'ip domain-list {0} vrf {1}'.format(item['name'], my_vrf) + commands.append(cmd) + + if want['name_servers']: + if want.get('name_servers')[0]['server'] == 'default': + if have['name_servers']: + for item in have['name_servers']: + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) + commands.append(cmd) + else: + for item in difference(have, want, 'name_servers'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'no ip name-server {0} vrf {1}'.format(item['server'], my_vrf) + commands.append(cmd) + for item in difference(want, have, 'name_servers'): + my_vrf = 'default' + if item['vrf'] is not None: + my_vrf = item['vrf'] + cmd = 'ip name-server {0} vrf {1}'.format(item['server'], my_vrf) + commands.append(cmd) + + return commands + + +def parse_hostname(config): + match = re.search(r'^hostname (\S+)', config, re.M) + if match: + return match.group(1) + + +def parse_domain_name(config): + objects = list() + myconf = config.splitlines() + for line in myconf: + if 'ip domain-name' in line: + datas = line.split() + objects.append({'name': datas[2], 'vrf': datas[4]}) + + return objects + + +def parse_domain_search(config): + objects = list() + myconf = config.splitlines() + for line in myconf: + if 'ip domain-list' in line: + datas = line.split() + objects.append({'name': datas[2], 'vrf': datas[4]}) + + return objects + + +def parse_name_servers(config): + objects = list() + myconf = config.splitlines() + for line in myconf: + if 'ip name-server' in line: + datas = line.split() + objects.append({'server': datas[2], 'vrf': datas[4]}) + + return objects + + +def map_config_to_obj(module): + config = get_config(module) + configobj = NetworkConfig(indent=2, contents=config) + + return { + 'hostname': parse_hostname(config), + 'lookup_enabled': 'no ip domain-lookup' not in config, + 'domain_name': parse_domain_name(config), + 'domain_search': parse_domain_search(config), + 'name_servers': parse_name_servers(config), + } + + +def map_params_to_obj(module): + obj = { + 'hostname': module.params['hostname'], + 'lookup_enabled': module.params['lookup_enabled'], + } + + domain_name = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + domain_search = ComplexList(dict( + name=dict(key=True), + vrf=dict() + ), module) + + name_servers = ComplexList(dict( + server=dict(key=True), + vrf=dict() + ), module) + + for arg, cast in [('domain_name', domain_name), + ('domain_search', domain_search), + ('name_servers', name_servers)]: + if module.params[arg] is not None: + obj[arg] = cast(module.params[arg]) + else: + obj[arg] = None + + return obj + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + hostname=dict(), + lookup_enabled=dict(type='bool'), + + # { name: , vrf: } + domain_name=dict(type='list'), + + # {name: , vrf: } + domain_search=dict(type='list'), + + # { server: ; vrf: } + name_servers=dict(type='list'), + + lookup_source=dict(type='str'), + state=dict(default='present', choices=['present', 'absent']) + ) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + + warnings = list() + check_args(module, warnings) + + result = {'changed': False} + if warnings: + result['warnings'] = warnings + + want = map_params_to_obj(module) + have = map_config_to_obj(module) + + commands = map_obj_to_commands(want, have, module) + result['commands'] = commands + if commands: + if not module.check_mode: + load_config(module, commands) + result['changed'] = True + + module.exit_json(**result) + + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/cnos_linkagg/cnos_linkagg_sample_hosts b/test/integration/targets/cnos_linkagg/cnos_linkagg_sample_hosts index 7243e2b75be..8c816da7e76 100644 --- a/test/integration/targets/cnos_linkagg/cnos_linkagg_sample_hosts +++ b/test/integration/targets/cnos_linkagg/cnos_linkagg_sample_hosts @@ -6,9 +6,9 @@ # - You can enter hostnames or ip Addresses # - A hostname/ip can be a member of multiple groups # -# In the /etc/ansible/hosts file u have to enter [cnos_portchannel_sample] tag +# In the /etc/ansible/hosts file u have to enter [cnos_linkagg_sample] tag # Following you should specify IP Addresses details # Please change and with appropriate value for your switch. [cnos_linkagg_sample] -10.241.107.39 ansible_network_os=cnos ansible_ssh_user= ansible_ssh_pass= +10.241.107.39 ansible_network_os=cnos ansible_ssh_user= ansible_ssh_pass= diff --git a/test/integration/targets/cnos_system/aliases b/test/integration/targets/cnos_system/aliases new file mode 100644 index 00000000000..cdb50333531 --- /dev/null +++ b/test/integration/targets/cnos_system/aliases @@ -0,0 +1,2 @@ +# No Lenovo Switch simulator yet, so not enabled +unsupported \ No newline at end of file diff --git a/test/integration/targets/cnos_system/cnos_system_sample_hosts b/test/integration/targets/cnos_system/cnos_system_sample_hosts new file mode 100644 index 00000000000..a37a287015b --- /dev/null +++ b/test/integration/targets/cnos_system/cnos_system_sample_hosts @@ -0,0 +1,14 @@ +# You have to paste this dummy information in /etc/ansible/hosts +# Notes: +# - Comments begin with the '#' character +# - Blank lines are ignored +# - Groups of hosts are delimited by [header] elements +# - You can enter hostnames or ip Addresses +# - A hostname/ip can be a member of multiple groups +# +# In the /etc/ansible/hosts file u have to enter [cnos_system_sample] tag +# Following you should specify IP Addresses details +# Please change and with appropriate value for your switch. + +[cnos_system_sample] + ansible_network_os=cnos ansible_ssh_user= ansible_ssh_pass= diff --git a/test/integration/targets/cnos_system/defaults/main.yaml b/test/integration/targets/cnos_system/defaults/main.yaml new file mode 100644 index 00000000000..5f709c5aac1 --- /dev/null +++ b/test/integration/targets/cnos_system/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/test/integration/targets/cnos_system/tasks/cli.yaml b/test/integration/targets/cnos_system/tasks/cli.yaml new file mode 100644 index 00000000000..b952553f225 --- /dev/null +++ b/test/integration/targets/cnos_system/tasks/cli.yaml @@ -0,0 +1,33 @@ +--- +- name: collect common test cases + find: + paths: "{{ role_path }}/tests/common" + patterns: "{{ testcase }}.yaml" + connection: local + register: test_cases + +- name: collect cli test cases + find: + paths: "{{ role_path }}/tests/cli" + patterns: "{{ testcase }}.yaml" + connection: local + register: cli_cases + +- set_fact: + test_cases: + files: "{{ test_cases.files }} + {{ cli_cases.files }}" + +- name: set test_items + set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}" + +- name: run test cases (connection=network_cli) + include: "{{ test_case_to_run }} ansible_connection=network_cli connection={{ cli }}" + with_items: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run + +- name: run test case (connection=local) + include: "{{ test_case_to_run }} ansible_connection=local" + with_first_found: "{{ test_items }}" + loop_control: + loop_var: test_case_to_run diff --git a/test/integration/targets/cnos_system/tasks/main.yaml b/test/integration/targets/cnos_system/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/cnos_system/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/cnos_system/tests/cli/net_system.yaml b/test/integration/targets/cnos_system/tests/cli/net_system.yaml new file mode 100644 index 00000000000..9b1db3e0404 --- /dev/null +++ b/test/integration/targets/cnos_system/tests/cli/net_system.yaml @@ -0,0 +1,34 @@ +--- +- debug: msg="START cnos cli/net_system.yaml on connection={{ ansible_connection }}" + +# Add minimal testcase to check args are passed correctly to +# implementation module and module run is successful. + +- name: setup + cnos_config: + lines: + - no ip domain-list ansible.com vrf default + - no ip domain-list redhat.com vrf default + match: none + +- name: configure domain_list using platform agnostic module + net_system: + domain_search: + - ansible.com + - redhat.com + register: result + +- assert: + that: + - result.changed == true + - "'ip domain-list ansible.com vrf default' in result.commands" + - "'ip domain-list redhat.com vrf default' in result.commands" + +- name: setup + cnos_config: + lines: + - no ip domain-list ansible.com vrf default + - no ip domain-list redhat.com vrf default + match: none + +- debug: msg="END cnos cli/net_system.yaml on connection={{ ansible_connection }}" diff --git a/test/integration/targets/cnos_system/tests/cli/set_domain_list.yaml b/test/integration/targets/cnos_system/tests/cli/set_domain_list.yaml new file mode 100644 index 00000000000..b35ae6b4fa7 --- /dev/null +++ b/test/integration/targets/cnos_system/tests/cli/set_domain_list.yaml @@ -0,0 +1,111 @@ +--- +- debug: msg="START cli/set_domain_list.yaml" + +- name: setup + cnos_config: + lines: + - no ip domain-list ansible.com vrf default + - no ip domain-list redhat.com vrf default + match: none + +- name: configure domain_list + cnos_system: + domain_search: + - ansible.com + - redhat.com + register: result + +- assert: + that: + - result.changed == true + - "'ip domain-list ansible.com vrf default' in result.commands" + - "'ip domain-list redhat.com vrf default' in result.commands" + +- name: verify domain_list + cnos_system: + domain_search: + - ansible.com + - redhat.com + register: result + +- assert: + that: + - result.changed == true + +- name: remove one entry + cnos_system: + domain_search: + - ansible.com + register: result + +- assert: + that: + - result.changed == true + - "'no ip domain-list redhat.com vrf default' in result.commands" + +- name: verify remove one entry + cnos_system: + domain_search: + - ansible.com + register: result + +- assert: + that: + - result.changed == true + +- name: add one entry + cnos_system: + domain_search: + - ansible.com + - redhat.com + register: result + +- assert: + that: + - result.changed == true + - "'ip domain-list redhat.com vrf default' in result.commands" + +- name: verify add one entry + cnos_system: + domain_search: + - ansible.com + - redhat.com + register: result + +- assert: + that: + - result.changed == true + +- name: add and remove one entry + cnos_system: + domain_search: + - ansible.com + - eng.ansible.com + register: result + +- assert: + that: + - result.changed == true + - "'no ip domain-list redhat.com vrf default' in result.commands" + - "'ip domain-list eng.ansible.com vrf default' in result.commands" + +- name: verify add and remove one entry + cnos_system: + domain_search: + - ansible.com + - eng.ansible.com + register: result + +- assert: + that: + - result.changed == true + +- name: teardown + cnos_config: + lines: + - no ip domain-list ansible.com vrf default + - no ip domain-list redhat.com vrf default + - no ip domain-list eng.ansible.com vrf default + match: none + +- debug: msg="END cli/set_domain_search.yaml" diff --git a/test/integration/targets/cnos_system/tests/cli/set_domain_name.yaml b/test/integration/targets/cnos_system/tests/cli/set_domain_name.yaml new file mode 100644 index 00000000000..d64dca527ad --- /dev/null +++ b/test/integration/targets/cnos_system/tests/cli/set_domain_name.yaml @@ -0,0 +1,32 @@ +--- +- debug: msg="START cli/set_domain_name.yaml" + +- name: setup + cnos_config: + lines: no ip domain-name eng.ansible.com vrf default + match: none + +- name: configure domain_name + cnos_system: + domain_name: eng.ansible.com + register: result + +- assert: + that: + - "result.changed == true" + +- name: verify domain_name + cnos_system: + domain_name: eng.ansible.com + register: result + +- assert: + that: + - "result.changed == true" + +- name: teardown + cnos_config: + lines: no ip domain-name eng.ansible.com vrf default + match: none + +- debug: msg="END cli/set_domain_name.yaml" diff --git a/test/integration/targets/cnos_system/tests/cli/set_name_servers.yaml b/test/integration/targets/cnos_system/tests/cli/set_name_servers.yaml new file mode 100644 index 00000000000..1f29a51a88e --- /dev/null +++ b/test/integration/targets/cnos_system/tests/cli/set_name_servers.yaml @@ -0,0 +1,73 @@ +--- +- debug: msg="START cli/set_name_servers.yaml" + +- name: setup + cnos_config: &reset + lines: + - no ip name-server 10.241.107.1 vrf default + - no ip name-server 10.241.107.2 vrf default + - no ip name-server 10.241.107.3 vrf default + match: none + +- name: configure name_servers + cnos_system: + name_servers: + - 10.241.107.1 + - 10.241.107.2 + - 10.241.107.3 + register: result + +- assert: + that: + - result.changed == true + - "'ip name-server 10.241.107.1 vrf default' in result.commands" + - "'ip name-server 10.241.107.2 vrf default' in result.commands" + - "'ip name-server 10.241.107.3 vrf default' in result.commands" + +- name: verify name_servers + cnos_system: + name_servers: + - 10.241.107.1 + - 10.241.107.2 + - 10.241.107.3 + register: result + +- assert: + that: + - result.changed == true + +- name: remove one + cnos_system: + name_servers: + - 10.241.107.1 + - 10.241.107.2 + register: result + +- assert: + that: + - result.changed == true +# - result.commands|length == 1 + - "'no ip name-server 10.241.107.3 vrf default' in result.commands" + +- name: default name server + cnos_system: &defns + name_servers: default + register: result + +- assert: + that: + - result.changed == true + +- name: Idempotent check + cnos_system: *defns + register: result + +- assert: + that: + - result.changed == false + +- name: teardown + cnos_config: *reset + ignore_errors: yes + +- debug: msg="END cli/set_name_servers.yaml" diff --git a/test/integration/targets/cnos_system/tests/common/sanity.yaml b/test/integration/targets/cnos_system/tests/common/sanity.yaml new file mode 100644 index 00000000000..a97fb8bc1e3 --- /dev/null +++ b/test/integration/targets/cnos_system/tests/common/sanity.yaml @@ -0,0 +1,122 @@ +--- +- debug: msg="START connection={{ ansible_connection }}/sanity.yaml" +- debug: msg="Using provider={{ connection.transport }}" + when: ansible_connection == "local" + +- block: + - name: remove configuration + cnos_system: &remove + state: absent + register: result + ignore_errors: yes + + - name: configure lookup_enabled + cnos_system: &dlo + lookup_enabled: true + state: present + register: result + + - name: configure hostname and domain-name + cnos_system: &hostname + hostname: switch + domain_name: test.example.com + register: result + + - assert: &true + that: + - "result.changed == true" + + - name: Idempotence check + cnos_system: *hostname + register: result + + - assert: &false + that: + - "result.changed == true" + + - name: configure name servers + cnos_system: &ns + name_servers: + - 8.8.8.8 + - 8.8.4.4 + register: result + + - assert: *true + + - name: Idempotence check + cnos_system: *ns + register: result + + - assert: *false + + - name: configure name servers with VRF support + cnos_system: &nsv + name_servers: + - { server: 8.8.8.8, vrf: management } + - { server: 8.8.4.4, vrf: management } + register: result + + - assert: *true + + - name: Idempotence check + cnos_system: *nsv + register: result + + - assert: + that: + - "result.changed == false" + + - name: configure lookup_enabled1 + cnos_system: &ndlo + lookup_enabled: false + register: result + + - assert: *true + + - name: Idempotence check + cnos_system: *ndlo + register: result + + - assert: + that: + - "result.changed == false" + + - name: configure lookup_enabled2 + cnos_system: *dlo + register: result + + - assert: *true + + - name: Idempotence check + cnos_system: *dlo + register: result + + - assert: + that: + - "result.changed == false" + + - name: default configuration + cnos_system: &default + hostname: default + domain_name: default + name_servers: default + register: result + + - assert: *true + + - name: Idempotence check + cnos_system: *default + register: result + + - assert: + that: + - "result.changed == false" + + always: + - name: remove configuration + cnos_system: *remove + + - name: Re-configure hostname + cnos_system: *hostname + +- debug: msg="END connection={{ ansible_connection }}/sanity.yaml" diff --git a/test/integration/targets/cnos_system/tests/common/set_hostname.yaml b/test/integration/targets/cnos_system/tests/common/set_hostname.yaml new file mode 100644 index 00000000000..67f52bf3abe --- /dev/null +++ b/test/integration/targets/cnos_system/tests/common/set_hostname.yaml @@ -0,0 +1,36 @@ +--- +- debug: msg="START connection={{ ansible_connection }}/set_hostname.yaml" +- debug: msg="Using provider={{ connection.transport }}" + when: ansible_connection == "local" + +- block: + - name: setup + cnos_config: + lines: "hostname switch" + match: none + + - name: configure hostname + cnos_system: + hostname: foo + register: result + + - assert: + that: + - "result.changed == true" + + - name: verify hostname + cnos_system: + hostname: foo + register: result + + - assert: + that: + - "result.changed == false" + + always: + - name: teardown + cnos_config: + lines: "hostname switch" + match: none + +- debug: msg="END connection={{ ansible_connection }}/set_hostname.yaml" diff --git a/test/units/modules/network/cnos/fixtures/cnos_system_config.cfg b/test/units/modules/network/cnos/fixtures/cnos_system_config.cfg new file mode 100644 index 00000000000..b1e07b7c124 --- /dev/null +++ b/test/units/modules/network/cnos/fixtures/cnos_system_config.cfg @@ -0,0 +1,11 @@ +hostname lenovo + +ip route 1.2.0.0/24 Null0 255 +ip route 1.2.3.4/31 Ethernet1/44 1.2.3.1 +ip route 1.2.3.4/32 1.2.34.5 +ip route 10.241.106.0/24 Ethernet1/13 10.241.107.1 113 tag 1013 description anil +ip route 10.241.106.4/32 1.2.3.5 tag 333 description anillll +ip route 10.241.106.4/32 1.3.56.7 +ip route 10.241.107.0/24 10.241.107.1 +ip route 10.241.107.1/32 Ethernet1/33 10.241.107.2 100 tag 111 description anil + diff --git a/test/units/modules/network/cnos/test_cnos_system.py b/test/units/modules/network/cnos/test_cnos_system.py new file mode 100644 index 00000000000..569d54eefab --- /dev/null +++ b/test/units/modules/network/cnos/test_cnos_system.py @@ -0,0 +1,103 @@ +# +# (c) 2016 Red Hat Inc. +# Copyright (C) 2017 Lenovo. +# +# 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 . + +# Make coding more python3-ish +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +from units.compat.mock import patch +from ansible.modules.network.cnos import cnos_system +from units.modules.utils import set_module_args +from .cnos_module import TestCnosModule, load_fixture + + +class TestCnosSystemModule(TestCnosModule): + + module = cnos_system + + def setUp(self): + super(TestCnosSystemModule, self).setUp() + + self.mock_get_config = patch('ansible.modules.network.cnos.cnos_system.get_config') + self.get_config = self.mock_get_config.start() + + self.mock_load_config = patch('ansible.modules.network.cnos.cnos_system.load_config') + self.load_config = self.mock_load_config.start() + + def tearDown(self): + super(TestCnosSystemModule, self).tearDown() + self.mock_get_config.stop() + self.mock_load_config.stop() + + def load_fixtures(self, commands=None, device=''): + self.get_config.return_value = load_fixture('cnos_system_config.cfg') + self.load_config.return_value = None + + def test_cnos_system_hostname_changed(self): + set_module_args(dict(hostname='foo')) + commands = ['hostname foo'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_domain_lookup(self): + set_module_args(dict(lookup_enabled=False)) + commands = ['no ip domain-lookup'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_missing_vrf(self): + domain_name = dict(name='example.com', vrf='example') + set_module_args(dict(domain_name=domain_name)) + self.execute_module(failed=True) + + def test_cnos_system_domain_name(self): + set_module_args(dict(domain_name=['example.net'])) + commands = ['ip domain-name example.net vrf default'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_domain_name_complex(self): + domain_name = dict(name='example.net', vrf='management') + set_module_args(dict(domain_name=[domain_name])) + commands = ['ip domain-name example.net vrf management'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_domain_search(self): + set_module_args(dict(domain_search=['example.net'])) + commands = ['ip domain-list example.net vrf default'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_domain_search_complex(self): + domain_search = dict(name='example.net', vrf='management') + set_module_args(dict(domain_search=[domain_search])) + commands = ['ip domain-list example.net vrf management'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_name_servers(self): + set_module_args(dict(name_servers=['1.2.3.4', '8.8.8.8'])) + commands = ['ip name-server 1.2.3.4 vrf default', 'ip name-server 8.8.8.8 vrf default'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_name_servers_complex(self): + name_servers = dict(server='1.2.3.4', vrf='management') + set_module_args(dict(name_servers=[name_servers])) + commands = ['ip name-server 1.2.3.4 vrf management'] + self.execute_module(changed=True, commands=commands) + + def test_cnos_system_state_absent(self): + set_module_args(dict(state='absent')) + commands = ['no hostname'] + self.execute_module(changed=True, commands=commands)