diff --git a/lib/ansible/modules/network/cnos/cnos_lldp.py b/lib/ansible/modules/network/cnos/cnos_lldp.py new file mode 100644 index 00000000000..f98ac40d434 --- /dev/null +++ b/lib/ansible/modules/network/cnos/cnos_lldp.py @@ -0,0 +1,140 @@ +#!/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 Link Aggregation with Lenovo Switches +# Lenovo Networking +# +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + +DOCUMENTATION = """ +--- +module: cnos_lldp +version_added: "2.8" +author: "Anil Kumar Muraleedharan (@amuraleedhar)" +short_description: Manage LLDP configuration on Lenovo CNOS network devices. +description: + - This module provides declarative management of LLDP service + on Lenovc CNOS network devices. +notes: + - Tested against CNOS 10.9.1 +options: + state: + description: + - State of the LLDP configuration. If value is I(present) lldp will be + enabled else if it is I(absent) it will be disabled. + default: present + choices: ['present', 'absent'] +""" + +EXAMPLES = """ +- name: Enable LLDP service + cnos_lldp: + state: present + +- name: Disable LLDP service + cnos_lldp: + state: absent +""" + +RETURN = """ +commands: + description: The list of configuration mode commands to send to the device + returned: always, except for the platforms that use Netconf transport to + manage the device. + type: list + sample: + - lldp timer 1024 + - lldp trap-interval 330 +""" +from ansible.module_utils.basic import AnsibleModule +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 debugOutput, run_commands +from ansible.module_utils.connection import exec_command + + +def get_ethernet_range(module): + output = run_commands(module, ['show interface brief'])[0].split('\n') + maxport = None + last_interface = None + for line in output: + if line.startswith('Ethernet1/'): + last_interface = line.split(' ')[0] + if last_interface is not None: + eths = last_interface.split('/') + maxport = eths[1] + return maxport + + +def main(): + """ main entry point for module execution + """ + argument_spec = dict( + state=dict(default='present', + choices=['present', 'absent']) + ) + + module = AnsibleModule(argument_spec=argument_spec, + supports_check_mode=True) + warnings = list() + result = {'changed': False} + if warnings: + result['warnings'] = warnings + + maxport = get_ethernet_range(module) + commands = [] + prime_cmd = 'interface ethernet 1/1-' + maxport + + if module.params['state'] == 'absent': + commands.append(prime_cmd) + commands.append('no lldp receive') + commands.append('no lldp transmit') + commands.append('exit') + commands.append('interface mgmt 0') + commands.append('no lldp receive') + commands.append('no lldp transmit') + commands.append('exit') + elif module.params['state'] == 'present': + commands.append(prime_cmd) + commands.append('lldp receive') + commands.append('lldp transmit') + commands.append('exit') + commands.append('interface mgmt 0') + commands.append('lldp receive') + commands.append('lldp transmit') + commands.append('exit') + + 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_lldp/aliases b/test/integration/targets/cnos_lldp/aliases new file mode 100644 index 00000000000..be010d923f4 --- /dev/null +++ b/test/integration/targets/cnos_lldp/aliases @@ -0,0 +1,2 @@ +# No Lenovo Switch simulator yet, so not enabled +unsupported diff --git a/test/integration/targets/cnos_lldp/defaults/main.yaml b/test/integration/targets/cnos_lldp/defaults/main.yaml new file mode 100644 index 00000000000..5f709c5aac1 --- /dev/null +++ b/test/integration/targets/cnos_lldp/defaults/main.yaml @@ -0,0 +1,2 @@ +--- +testcase: "*" diff --git a/test/integration/targets/cnos_lldp/tasks/cli.yaml b/test/integration/targets/cnos_lldp/tasks/cli.yaml new file mode 100644 index 00000000000..303af407622 --- /dev/null +++ b/test/integration/targets/cnos_lldp/tasks/cli.yaml @@ -0,0 +1,22 @@ +--- +- 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 cases (connection=network_cli) + include: "{{ test_case_to_run }}" + 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_lldp/tasks/main.yaml b/test/integration/targets/cnos_lldp/tasks/main.yaml new file mode 100644 index 00000000000..415c99d8b12 --- /dev/null +++ b/test/integration/targets/cnos_lldp/tasks/main.yaml @@ -0,0 +1,2 @@ +--- +- { include: cli.yaml, tags: ['cli'] } diff --git a/test/integration/targets/cnos_lldp/tests/cli/basic.yaml b/test/integration/targets/cnos_lldp/tests/cli/basic.yaml new file mode 100644 index 00000000000..f499ff43ac9 --- /dev/null +++ b/test/integration/targets/cnos_lldp/tests/cli/basic.yaml @@ -0,0 +1,44 @@ +--- +- debug: msg="START cnos_lldp cli/basic.yaml on connection={{ ansible_connection }}" + +- name: Enable LLDP service + cnos_lldp: + state: present + register: result + +- assert: + that: + - 'result.changed == true' + - '"lldp receive" in result.commands' + - '"lldp transmit" in result.commands' + +- name: Enable LLDP service again (idempotent) + cnos_lldp: + state: present + register: result + +- assert: + that: + - 'result.changed == true' + +- name: Disable LLDP service + cnos_lldp: + state: absent + register: result + +- assert: + that: + - 'result.changed == true' + - '"no lldp receive" in result.commands' + - '"no lldp transmit" in result.commands' + +- name: Disable LLDP service (idempotent) + cnos_lldp: + state: absent + register: result + +- assert: + that: + - 'result.changed == true' + +- debug: msg="END cnos_lldp cli/basic.yaml on connection={{ ansible_connection }}" diff --git a/test/units/modules/network/cnos/test_cnos_lldp.py b/test/units/modules/network/cnos/test_cnos_lldp.py new file mode 100644 index 00000000000..dd8f2dd5354 --- /dev/null +++ b/test/units/modules/network/cnos/test_cnos_lldp.py @@ -0,0 +1,104 @@ +# +# (c) 2019 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 . +# +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + +import re +import json + +from units.compat.mock import patch +from ansible.modules.network.cnos import cnos_lldp +from units.modules.utils import set_module_args +from .cnos_module import TestCnosModule, load_fixture + + +class TestCnosLldpModule(TestCnosModule): + module = cnos_lldp + + def setUp(self): + super(TestCnosLldpModule, self).setUp() + self._patch_get_config = patch( + 'ansible.modules.network.cnos.cnos_lldp.get_config' + ) + self._patch_load_config = patch( + 'ansible.modules.network.cnos.cnos_lldp.load_config' + ) + self._patch_get_ethernet_range = patch( + 'ansible.modules.network.cnos.cnos_lldp.get_ethernet_range' + ) + + self._get_config = self._patch_get_config.start() + self._load_config = self._patch_load_config.start() + self._get_ethernet_range = self._patch_get_ethernet_range.start() + + def tearDown(self): + super(TestCnosLldpModule, self).tearDown() + self._patch_get_config.stop() + self._patch_load_config.stop() + self._patch_get_ethernet_range.stop() + + def load_fixtures(self, commands=None): + config_file = 'cnos_config_config.cfg' + self._get_config.return_value = load_fixture(config_file) + self._load_config.return_value = None + self._get_ethernet_range.return_value = '54' + + def test_cnos_lldp_present(self, *args, **kwargs): + set_module_args(dict( + state='present' + )) + result = self.execute_module(changed=True) + self.assertEqual( + result, + { + 'commands': [ + 'interface ethernet 1/1-54', + 'lldp receive', + 'lldp transmit', + 'exit', + 'interface mgmt 0', + 'lldp receive', + 'lldp transmit', + 'exit' + ], + 'changed': True + } + ) + + def test_cnos_lldp_absent(self, *args, **kwargs): + set_module_args(dict( + state='absent' + )) + result = self.execute_module(changed=True) + self.assertEqual( + result, + { + 'commands': [ + 'interface ethernet 1/1-54', + 'no lldp receive', + 'no lldp transmit', + 'exit', + 'interface mgmt 0', + 'no lldp receive', + 'no lldp transmit', + 'exit' + ], + 'changed': True + } + )