mirror of
				https://github.com/ansible-collections/community.general.git
				synced 2025-10-26 05:50:36 -07:00 
			
		
		
		
	Various modules document the default 'null' value, but it causes None to be shown in the documentation explicitly.
		
			
				
	
	
		
			221 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			221 lines
		
	
	
	
		
			7.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/python
 | |
| 
 | |
| # 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: exos_command
 | |
| version_added: "2.6"
 | |
| author: "Rafael D. Vencioneck (@rdvencioneck)"
 | |
| short_description: Run commands on remote devices running Extreme EXOS
 | |
| description:
 | |
|   - Sends arbitrary commands to an Extreme EXOS device 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.
 | |
|   - This module does not support running configuration commands.
 | |
|     Please use M(exos_config) to configure EXOS devices.
 | |
| notes:
 | |
|   - If a command sent to the device requires answering a prompt, it is possible
 | |
|     to pass a dict containing I(command), I(answer) and I(prompt). See examples.
 | |
| options:
 | |
|   commands:
 | |
|     description:
 | |
|       - List of commands to send to the remote EXOS 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.
 | |
|     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.
 | |
|   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.
 | |
|     default: all
 | |
|     choices: ['any', 'all']
 | |
|   retries:
 | |
|     description:
 | |
|       - Specifies the number of retries 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.
 | |
|     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.
 | |
|     default: 1
 | |
| """
 | |
| 
 | |
| EXAMPLES = """
 | |
| tasks:
 | |
|   - name: run show version on remote devices
 | |
|     exos_command:
 | |
|       commands: show version
 | |
|   - name: run show version and check to see if output contains ExtremeXOS
 | |
|     exos_command:
 | |
|       commands: show version
 | |
|       wait_for: result[0] contains ExtremeXOS
 | |
|   - name: run multiple commands on remote nodes
 | |
|     exos_command:
 | |
|       commands:
 | |
|         - show version
 | |
|         - show ports no-refresh
 | |
|   - name: run multiple commands and evaluate the output
 | |
|     exos_command:
 | |
|       commands:
 | |
|         - show version
 | |
|         - show ports no-refresh
 | |
|       wait_for:
 | |
|         - result[0] contains ExtremeXOS
 | |
|         - result[1] contains 20
 | |
|   - name: run command that requires answering a prompt
 | |
|     exos_command:
 | |
|       commands:
 | |
|         - command: 'clear license-info'
 | |
|           prompt: 'Are you sure.*'
 | |
|           answer: 'Yes'
 | |
| """
 | |
| 
 | |
| RETURN = """
 | |
| stdout:
 | |
|   description: The set of responses from the commands
 | |
|   returned: always apart from low level errors (such as action plugin)
 | |
|   type: list
 | |
|   sample: ['...', '...']
 | |
| stdout_lines:
 | |
|   description: The value of stdout split into a list
 | |
|   returned: always apart from low level errors (such as action plugin)
 | |
|   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.exos.exos import run_commands
 | |
| from ansible.module_utils.basic import AnsibleModule
 | |
| from ansible.module_utils.network.common.utils import ComplexList
 | |
| from ansible.module_utils.network.common.parsing import Conditional
 | |
| from ansible.module_utils.six import string_types
 | |
| 
 | |
| 
 | |
| def to_lines(stdout):
 | |
|     for item in stdout:
 | |
|         if isinstance(item, string_types):
 | |
|             item = str(item).split('\n')
 | |
|         yield item
 | |
| 
 | |
| 
 | |
| def parse_commands(module, warnings):
 | |
|     command = ComplexList(dict(
 | |
|         command=dict(key=True),
 | |
|         prompt=dict(),
 | |
|         answer=dict()
 | |
|     ), module)
 | |
|     commands = command(module.params['commands'])
 | |
|     for item in list(commands):
 | |
|         command_split = re.match(r'^(\w*)(.*)$', item['command'])
 | |
|         if module.check_mode and not item['command'].startswith('show'):
 | |
|             warnings.append(
 | |
|                 'only show commands are supported when using check mode, not '
 | |
|                 'executing `%s`' % item['command']
 | |
|             )
 | |
|             commands.remove(item)
 | |
|         elif command_split and command_split.group(1) not in ('check', 'clear', 'debug', 'history',
 | |
|                                                               'ls', 'mrinfo', 'mtrace', 'nslookup',
 | |
|                                                               'ping', 'rtlookup', 'show', 'traceroute'):
 | |
|             module.fail_json(
 | |
|                 msg='some commands were not recognized. exos_command can only run read-only'
 | |
|                     'commands. For configuration commands, please use exos_config instead'
 | |
|             )
 | |
|     return commands
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     """main entry point for module execution
 | |
|     """
 | |
|     argument_spec = dict(
 | |
|         commands=dict(type='list', required=True),
 | |
| 
 | |
|         wait_for=dict(type='list'),
 | |
|         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()
 | |
|     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 be 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()
 |