icx: new module icx_command (#59903)

* new module

* new cliconf

* cliconf

* icx cliconf

* icx test units module

* icx units module

* icx banner unit test

* added notes

* new changes

* icx .rst

* modified platform_index.rst

* modified platform_index.rst

* changes resolved

* PR comments resolved

* Update platform_index.rst

PR comment resolved

* new module

* new cliconf

* cliconf

* icx cliconf

* icx test units module

* icx units module

* icx banner unit test

* added notes

* new changes

* icx .rst

* modified platform_index.rst

* modified platform_index.rst

* changes resolved

* PR comments resolved

* Update platform_index.rst

PR comment resolved

* new module icx_command

* PR issue

* new PR fix
This commit is contained in:
sushma-alethea 2019-08-07 13:27:24 +05:30 committed by Ganesh Nalawade
parent fb87bafaae
commit 92187ae53e
5 changed files with 372 additions and 1 deletions

View file

@ -56,7 +56,6 @@ options:
by specifying it as module parameter. by specifying it as module parameter.
type: bool type: bool
default: yes default: yes
""" """
EXAMPLES = """ EXAMPLES = """

View file

@ -0,0 +1,233 @@
#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = """
---
module: icx_command
version_added: "2.9"
author: "Ruckus Wireless (@Commscope)"
short_description: Run arbitrary commands on remote Ruckus ICX 7000 series switches
description:
- Sends arbitrary commands to an ICX node and returns the results
read from the device. This module includes an
argument that will cause the module to wait for a specific condition
before returning or timing out if the condition is not met.
notes:
- Tested against ICX 10.1
options:
commands:
description:
- List of commands to send to the remote ICX device over the
configured provider. The resulting output from the command
is returned. If the I(wait_for) argument is provided, the
module is not returned until the condition is satisfied or
the number of retries has expired. If a command sent to the
device requires answering a prompt, checkall and newline if
multiple prompts, it is possible to pass
a dict containing I(command), I(answer), I(prompt), I(check_all)
and I(newline).Common answers are 'y' or "\\r" (carriage return,
must be double quotes). See examples.
type: list
required: true
wait_for:
description:
- List of conditions to evaluate against the output of the
command. The task will wait for each condition to be true
before moving forward. If the conditional is not true
within the configured number of retries, the task fails.
See examples.
type: list
aliases: ['waitfor']
match:
description:
- The I(match) argument is used in conjunction with the
I(wait_for) argument to specify the match policy. Valid
values are C(all) or C(any). If the value is set to C(all)
then all conditionals in the wait_for must be satisfied. If
the value is set to C(any) then only one of the values must be
satisfied.
type: str
default: all
choices: ['any', 'all']
retries:
description:
- Specifies the number of times a command should by tried
before it is considered failed. The command is run on the
target device every retry and evaluated against the
I(wait_for) conditions.
type: int
default: 10
interval:
description:
- Configures the interval in seconds to wait between retries
of the command. If the command does not pass the specified
conditions, the interval indicates how long to wait before
trying the command again.
type: int
default: 1
"""
EXAMPLES = """
tasks:
- name: run show version on remote devices
icx_command:
commands: show version
- name: run show version and check to see if output contains ICX
icx_command:
commands: show version
wait_for: result[0] contains ICX
- name: run multiple commands on remote nodes
icx_command:
commands:
- show version
- show interfaces
- name: run multiple commands and evaluate the output
icx_command:
commands:
- show version
- show interfaces
wait_for:
- result[0] contains ICX
- result[1] contains GigabitEthernet1/1/1
- name: run commands that require answering a prompt
icx_command:
commands:
- command: 'service password-encryption sha1'
prompt: 'Warning: Moving to higher password-encryption type,.*'
answer: 'y'
- name: run commands that require answering multiple prompt
icx_command:
commands:
- command: 'username qqq password qqq'
prompt:
- 'User already exists. Do you want to modify:.*'
- 'To modify or remove user, enter current password:'
answer:
- 'y'
- 'qqq\\\r'
check_all: True
newline: False
"""
RETURN = """
stdout:
description: The set of responses from the commands
returned: always apart from low level errors
type: list
sample: ['...', '...']
stdout_lines:
description: The value of stdout split into a list
returned: always apart from low level errors
type: list
sample: [['...', '...'], ['...'], ['...']]
failed_conditions:
description: The list of conditionals that have failed
returned: failed
type: list
sample: ['...', '...']
"""
import re
import time
from ansible.module_utils.network.icx.icx import run_commands
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network.common.utils import ComplexList, to_lines
from ansible.module_utils.network.common.parsing import Conditional
from ansible.module_utils.six import string_types
def parse_commands(module, warnings):
command = ComplexList(dict(
command=dict(key=True),
prompt=dict(),
answer=dict(),
check_all=dict(type='bool', default='False'),
newline=dict(type='bool', default='True')
), module)
commands = command(module.params['commands'])
for item in list(commands):
if module.check_mode:
if not item['command'].startswith('show'):
warnings.append(
'Only show commands are supported when using check mode, not executing configure terminal')
commands.remove(item)
return commands
def main():
"""main entry point for module execution
"""
argument_spec = dict(
commands=dict(type='list', required=True),
wait_for=dict(type='list', aliases=['waitfor']),
match=dict(default='all', choices=['all', 'any']),
retries=dict(default=10, type='int'),
interval=dict(default=1, type='int')
)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
result = {'changed': False}
warnings = list()
run_commands(module, ['skip'])
commands = parse_commands(module, warnings)
result['warnings'] = warnings
wait_for = module.params['wait_for'] or list()
conditionals = [Conditional(c) for c in wait_for]
retries = module.params['retries']
interval = module.params['interval']
match = module.params['match']
while retries > 0:
responses = run_commands(module, commands)
for item in list(conditionals):
if item(responses):
if match == 'any':
conditionals = list()
break
conditionals.remove(item)
if not conditionals:
break
time.sleep(interval)
retries -= 1
if conditionals:
failed_conditions = [item.raw for item in conditionals]
msg = 'One or more conditional statements have not been satisfied'
module.fail_json(msg=msg, failed_conditions=failed_conditions)
result.update({
'changed': False,
'stdout': responses,
'stdout_lines': list(to_lines(responses))
})
module.exit_json(**result)
if __name__ == '__main__':
main()

View file

@ -0,0 +1,26 @@
Copyright (c) 1996-2017 Brocade Communications Systems, Inc. All rights reserved.
UNIT 1: compiled on Feb 17 2017 at 16:03:13 labeled as SPS08060
(23946048 bytes) from Secondary SPS08060.bin
SW: Version 08.0.60T211
Compressed Boot-Monitor Image size = 786944, Version:10.1.09T225 (mnz10109)
Compiled on Sat Feb 18 00:15:43 2017
HW: Stackable ICX7150-48-POE
==========================================================================
UNIT 1: SL 1: ICX7150-48P-4X1G POE 48-port Management Module
Serial #:FEC3220N00C
Current License: 4X1G
P-ASIC 0: type B160, rev 11 Chip BCM56160_B0
==========================================================================
UNIT 1: SL 2: ICX7150-2X1GC 2-port 2G Module
==========================================================================
UNIT 1: SL 3: ICX7150-4X10GF 4-port 40G Module
==========================================================================
1000 MHz ARM processor ARMv7 88 MHz bus
8192 KB boot flash memory
2048 MB code flash memory
1024 MB DRAM
STACKID 1 system uptime is 4 day(s) 19 hour(s) 53 minute(s) 5 second(s)
The system started at 00:01:49 GMT+00 Sat Jan 01 2000
The system : started=cold start

View file

@ -0,0 +1,113 @@
# Copyright: (c) 2019, Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
import json
from units.compat.mock import patch
from ansible.modules.network.icx import icx_command
from units.modules.utils import set_module_args
from .icx_module import TestICXModule, load_fixture
class TestICXCommandModule(TestICXModule):
module = icx_command
def setUp(self):
super(TestICXCommandModule, self).setUp()
self.mock_run_commands = patch('ansible.modules.network.icx.icx_command.run_commands')
self.run_commands = self.mock_run_commands.start()
def tearDown(self):
super(TestICXCommandModule, self).tearDown()
self.mock_run_commands.stop()
def load_fixtures(self, commands=None):
def load_from_file(*args, **kwargs):
module, commands = args
output = list()
for item in commands:
try:
if item == 'skip':
continue
obj = json.loads(item['command'])
command = obj['command']
except ValueError:
command = item['command']
filename = str(command).replace(' ', '_')
output.append(load_fixture(filename))
return output
self.run_commands.side_effect = load_from_file
def test_icx_command_simple(self):
set_module_args(dict(commands=['show version']))
result = self.execute_module()
self.assertEqual(len(result['stdout']), 1)
self.assertTrue(result['stdout'][0].startswith('Copyright (c) 1996-2017 Brocade Communications Systems'))
def test_icx_command_multiple(self):
set_module_args(dict(commands=['show version', 'show version']))
result = self.execute_module()
self.assertEqual(len(result['stdout']), 2)
self.assertTrue(result['stdout'][0].startswith('Copyright (c) 1996-2017 Brocade Communications Systems'))
def test_icx_command_wait_for(self):
wait_for = 'result[0] contains "ICX"'
set_module_args(dict(commands=['show version'], wait_for=wait_for))
self.execute_module()
def test_icx_command_wait_for_fails(self):
wait_for = 'result[0] contains "test string"'
set_module_args(dict(commands=['show version'], wait_for=wait_for))
self.execute_module(failed=True)
# run_commands call count is 1(skip) + 10(current)
self.assertEqual(self.run_commands.call_count, 11)
def test_icx_command_retries(self):
wait_for = 'result[0] contains "test string"'
set_module_args(dict(commands=['show version'], wait_for=wait_for, retries=2))
self.execute_module(failed=True)
self.assertEqual(self.run_commands.call_count, 3)
def test_icx_command_match_any(self):
wait_for = ['result[0] contains "ICX"',
'result[0] contains "test string"']
set_module_args(dict(commands=['show version'], wait_for=wait_for, match='any'))
self.execute_module()
def test_icx_command_match_all(self):
wait_for = ['result[0] contains "ICX"',
'result[0] contains "Version:10.1.09T225"']
set_module_args(dict(commands=['show version'], wait_for=wait_for, match='all'))
self.execute_module()
def test_icx_command_match_all_failure(self):
wait_for = ['result[0] contains "ICX"',
'result[0] contains "test string"']
commands = ['show version', 'show version']
set_module_args(dict(commands=commands, wait_for=wait_for, match='all'))
self.execute_module(failed=True)
def test_icx_command_configure_check_warning(self):
commands = ['configure terminal']
set_module_args({
'commands': commands,
'_ansible_check_mode': True,
})
result = self.execute_module()
self.assertEqual(
result['warnings'],
['Only show commands are supported when using check mode, not executing configure terminal'],
)
def test_icx_command_configure_not_warning(self):
commands = ['configure terminal']
set_module_args(dict(commands=commands))
result = self.execute_module()
self.assertEqual(result['warnings'], [])