From 92ac734e136328e85c1bf20d5ec305211bcf710d Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Mon, 4 Jul 2016 23:02:28 -0400 Subject: [PATCH] initial commit of vyos_command module This adds a new module, vyos_command for sending CLI commands to remote devices running VyOS operating system. --- lib/ansible/modules/network/vyos/__init__.py | 0 .../modules/network/vyos/vyos_command.py | 195 ++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 lib/ansible/modules/network/vyos/__init__.py create mode 100644 lib/ansible/modules/network/vyos/vyos_command.py diff --git a/lib/ansible/modules/network/vyos/__init__.py b/lib/ansible/modules/network/vyos/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/lib/ansible/modules/network/vyos/vyos_command.py b/lib/ansible/modules/network/vyos/vyos_command.py new file mode 100644 index 00000000000..8771613c06b --- /dev/null +++ b/lib/ansible/modules/network/vyos/vyos_command.py @@ -0,0 +1,195 @@ +#!/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 . +# + +DOCUMENTATION = """ +--- +module: vyos_command +version_added: "2.2" +author: "Peter Sprygada (@privateip)" +short_description: Run one or more commands on VyOS devices +description: + - The command module allows running one or more commands on remote + devices running VyOS. The mdoule commands can also be introspected + to validate key parameters before returning successfully. If the + conditional statements are not met in the wait period, the task + fails. +extends_documentation_fragment: vyos +options: + commands: + description: + - The ordered set of commands to execute on the remote device + running VyOS. The output from the command execution is + returned to the playbook. If the I(wait_for) argument is + provided, the module is not returned until the condition is + satistifed or the number of retries has been exceeded. + required: true + wait_for: + description: + - Specifies what to evaluate from the output of the command + and what conditionals to apply. This argument will cause + the task to wait for a particular conditional to be true + before moving forward. If the conditional is not true + by the configured retries, the task fails. See examples. + required: false + default: null + aliases: ['waitfor'] + retries: + description: + - Specifies the number of retries a command should be tried + before it is considered failed. The command is run on the + target device every retry and evaluated against the waitfor + conditionals + required: false + default: 10 + interval: + description: + - Configures the interval in seconds to wait between retries + of the command. If the command does not pass the specified + conditional, the interval indicates how to long to wait before + trying the command again. + required: false + default: 1 +""" + +EXAMPLES = """ +# Note: examples below use the following provider dict to handle +# transport and authentication to the node. +vars: + cli: + host: "{{ inventory_hostname }}" + username: vyos + password: vyos + transport: cli + +- vyos_command: + commands: + - show interfaces ethernet {{ item }} + provider: "{{ cli }}" + with_items: + - eth0 + - eth1 + +- vyos_command: + commands: + - show version + - show hardware cpu + wait_for: + - "result[0] contains 'VyOS 1.1.7'" + provider: "{{ cli }}" +""" + +RETURN = """ +stdout: + description: The set of responses from the commands + returned: always + type: list + sample: ['...', '...'] +stdout_lines: + description: The value of stdout split into a list + returned: always + type: list + sample: [['...', '...'], ['...'], ['...']] +failed_conditions: + description: The conditionals that failed + retured: failed + type: list + sample: ['...', '...'] +warnings: + description: The list of warnings (if any) generated by module based on arguments + returned: always + type: list + sample: ['...', '...'] +""" +from ansible.module_utils.netcmd import CommandRunner, FailedConditionsError +from ansible.module_utils.vyos import NetworkModule, NetworkError +from ansible.module_utils.vyos import vyos_argument_spec, get_exception + +def to_lines(stdout): + for item in stdout: + if isinstance(item, basestring): + item = str(item).split('\n') + yield item + +def main(): + """Main entry point for Ansible module execution + """ + argument_spec = dict( + commands=dict(type='list'), + wait_for=dict(type='list', aliases=['waitfor']), + + retries=dict(default=10, type='int'), + interval=dict(default=1, type='int') + ) + + module = NetworkModule(argument_spec=argument_spec, + connect_on_load=False, + supports_check_mode=True) + + + commands = module.params['commands'] + conditionals = module.params['wait_for'] or list() + + warnings = list() + + runner = CommandRunner(module) + + for cmd in commands: + if module.check_mode and not cmd.startswith('show'): + warnings.append('only show commands are supported when using ' + 'check mode, not executing `%s`' % cmd) + else: + if cmd.startswith('conf'): + module.fail_json(msg='vyos_command does not support running ' + 'config mode commands. Please use ' + 'vyos_config instead') + runner.add_command(cmd) + + for item in conditionals: + runner.add_conditional(item) + + runner.retries = module.params['retries'] + runner.interval = module.params['interval'] + + try: + runner.run() + except FailedConditionsError: + exc = get_exception() + module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions) + except NetworkError: + exc = get_exception() + module.fail_json(msg=str(exc)) + + result = dict(changed=False, warnings=warnings) + + result['stdout'] = list() + for cmd in commands: + try: + output = runner.get_command(cmd) + except ValueError: + output = 'command not executed due to check_mode, see warnings' + result['stdout'].append(output) + + result['warnings'] = warnings + result['stdout_lines'] = list(to_lines(result['stdout'])) + + module.exit_json(**result) + + +if __name__ == '__main__': + main() +