1#!/usr/bin/python 2# 3# Copyright: Ansible Project 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6from __future__ import absolute_import, division, print_function 7__metaclass__ = type 8 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'certified'} 13 14DOCUMENTATION = """ 15--- 16module: sros_command 17version_added: "2.2" 18author: "Peter Sprygada (@privateip)" 19short_description: Run commands on remote devices running Nokia SR OS 20description: 21 - Sends arbitrary commands to an SR OS node and returns the results 22 read from the device. This module includes an argument that will 23 cause the module to wait for a specific condition before returning 24 or timing out if the condition is not met. 25 - This module does not support running commands in configuration mode. 26 Please use M(sros_config) to configure SR OS devices. 27extends_documentation_fragment: sros 28options: 29 commands: 30 description: 31 - List of commands to send to the remote SR OS device over the 32 configured provider. The resulting output from the command 33 is returned. If the I(wait_for) argument is provided, the 34 module is not returned until the condition is satisfied or 35 the number of retries has expired. 36 required: true 37 wait_for: 38 description: 39 - List of conditions to evaluate against the output of the 40 command. The task will wait for each condition to be true 41 before moving forward. If the conditional is not true 42 within the configured number of retries, the task fails. 43 See examples. 44 aliases: ['waitfor'] 45 match: 46 description: 47 - The I(match) argument is used in conjunction with the 48 I(wait_for) argument to specify the match policy. Valid 49 values are C(all) or C(any). If the value is set to C(all) 50 then all conditionals in the wait_for must be satisfied. If 51 the value is set to C(any) then only one of the values must be 52 satisfied. 53 default: all 54 choices: ['any', 'all'] 55 retries: 56 description: 57 - Specifies the number of retries a command should by tried 58 before it is considered failed. The command is run on the 59 target device every retry and evaluated against the 60 I(wait_for) conditions. 61 default: 10 62 interval: 63 description: 64 - Configures the interval in seconds to wait between retries 65 of the command. If the command does not pass the specified 66 conditions, the interval indicates how long to wait before 67 trying the command again. 68 default: 1 69""" 70 71EXAMPLES = """ 72# Note: examples below use the following provider dict to handle 73# transport and authentication to the node. 74--- 75vars: 76 cli: 77 host: "{{ inventory_hostname }}" 78 username: admin 79 password: admin 80 transport: cli 81 82--- 83tasks: 84 - name: run show version on remote devices 85 sros_command: 86 commands: show version 87 provider: "{{ cli }}" 88 89 - name: run show version and check to see if output contains sros 90 sros_command: 91 commands: show version 92 wait_for: result[0] contains sros 93 provider: "{{ cli }}" 94 95 - name: run multiple commands on remote nodes 96 sros_command: 97 commands: 98 - show version 99 - show port detail 100 provider: "{{ cli }}" 101 102 - name: run multiple commands and evaluate the output 103 sros_command: 104 commands: 105 - show version 106 - show port detail 107 wait_for: 108 - result[0] contains TiMOS-B-14.0.R4 109 provider: "{{ cli }}" 110""" 111 112RETURN = """ 113stdout: 114 description: The set of responses from the commands 115 returned: always apart from low level errors (such as action plugin) 116 type: list 117 sample: ['...', '...'] 118 119stdout_lines: 120 description: The value of stdout split into a list 121 returned: always apart from low level errors (such as action plugin) 122 type: list 123 sample: [['...', '...'], ['...'], ['...']] 124 125failed_conditions: 126 description: The list of conditionals that have failed 127 returned: failed 128 type: list 129 sample: ['...', '...'] 130""" 131import time 132 133from ansible.module_utils.basic import AnsibleModule 134from ansible.module_utils.network.common.parsing import Conditional 135from ansible.module_utils.network.common.utils import ComplexList 136from ansible.module_utils.six import string_types 137from ansible.module_utils.network.sros.sros import run_commands, sros_argument_spec, check_args 138 139 140def to_lines(stdout): 141 for item in stdout: 142 if isinstance(item, string_types): 143 item = str(item).split('\n') 144 yield item 145 146 147def parse_commands(module, warnings): 148 command = ComplexList(dict( 149 command=dict(key=True), 150 prompt=dict(), 151 answer=dict() 152 ), module) 153 commands = command(module.params['commands']) 154 for index, item in enumerate(commands): 155 if module.check_mode and not item['command'].startswith('show'): 156 warnings.append( 157 'only show commands are supported when using check mode, not ' 158 'executing `%s`' % item['command'] 159 ) 160 elif item['command'].startswith('conf'): 161 module.fail_json( 162 msg='sros_command does not support running config mode ' 163 'commands. Please use sros_config instead' 164 ) 165 return commands 166 167 168def main(): 169 """main entry point for module execution 170 """ 171 argument_spec = dict( 172 commands=dict(type='list', required=True), 173 174 wait_for=dict(type='list', aliases=['waitfor']), 175 match=dict(default='all', choices=['all', 'any']), 176 177 retries=dict(default=10, type='int'), 178 interval=dict(default=1, type='int') 179 ) 180 181 argument_spec.update(sros_argument_spec) 182 183 module = AnsibleModule(argument_spec=argument_spec, 184 supports_check_mode=True) 185 186 result = {'changed': False} 187 188 warnings = list() 189 check_args(module, warnings) 190 commands = parse_commands(module, warnings) 191 result['warnings'] = warnings 192 193 wait_for = module.params['wait_for'] or list() 194 conditionals = [Conditional(c) for c in wait_for] 195 196 retries = module.params['retries'] 197 interval = module.params['interval'] 198 match = module.params['match'] 199 200 while retries > 0: 201 responses = run_commands(module, commands) 202 203 for item in list(conditionals): 204 if item(responses): 205 if match == 'any': 206 conditionals = list() 207 break 208 conditionals.remove(item) 209 210 if not conditionals: 211 break 212 213 time.sleep(interval) 214 retries -= 1 215 216 if conditionals: 217 failed_conditions = [item.raw for item in conditionals] 218 msg = 'One or more conditional statements have not been satisfied' 219 module.fail_json(msg=msg, failed_conditions=failed_conditions) 220 221 result = { 222 'changed': False, 223 'stdout': responses, 224 'stdout_lines': list(to_lines(responses)) 225 } 226 227 module.exit_json(**result) 228 229 230if __name__ == '__main__': 231 main() 232