1#!/usr/bin/python 2# 3# Copyright (c) 2019 Ericsson AB. 4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 5 6 7from __future__ import (absolute_import, division, print_function) 8__metaclass__ = type 9 10ANSIBLE_METADATA = {'metadata_version': '1.1', 11 'status': ['preview'], 12 'supported_by': 'community'} 13 14 15DOCUMENTATION = """ 16--- 17module: eric_eccli_command 18version_added: "2.9" 19author: Ericsson IPOS OAM team (@itercheng) 20short_description: Run commands on remote devices running ERICSSON ECCLI 21description: 22 - Sends arbitrary commands to an ERICSSON eccli node and returns the results 23 read from the device. This module includes an 24 argument that will cause the module to wait for a specific condition 25 before returning or timing out if the condition is not met. 26 - This module also support running commands in configuration mode 27 in raw command style. 28options: 29 commands: 30 description: 31 - List of commands to send to the remote ECCLI 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. If a command sent to the 36 device requires answering a prompt, it is possible to pass 37 a dict containing I(command), I(answer) and I(prompt). 38 Common answers are 'y' or "\\r" (carriage return, must be 39 double quotes). See examples. 40 type: list 41 required: true 42 wait_for: 43 description: 44 - List of conditions to evaluate against the output of the 45 command. The task will wait for each condition to be true 46 before moving forward. If the conditional is not true 47 within the configured number of retries, the task fails. 48 See examples. 49 type: list 50 aliases: ['waitfor'] 51 match: 52 description: 53 - The I(match) argument is used in conjunction with the 54 I(wait_for) argument to specify the match policy. Valid 55 values are C(all) or C(any). If the value is set to C(all) 56 then all conditionals in the wait_for must be satisfied. If 57 the value is set to C(any) then only one of the values must be 58 satisfied. 59 type: str 60 default: all 61 choices: ['any', 'all'] 62 retries: 63 description: 64 - Specifies the number of retries a command should by tried 65 before it is considered failed. The command is run on the 66 target device every retry and evaluated against the 67 I(wait_for) conditions. 68 type: int 69 default: 10 70 interval: 71 description: 72 - Configures the interval in seconds to wait between retries 73 of the command. If the command does not pass the specified 74 conditions, the interval indicates how long to wait before 75 trying the command again. 76 type: int 77 default: 1 78notes: 79 - Tested against IPOS 19.3 80 - For more information on using Ansible to manage network devices see the :ref:`Ansible Network Guide <network_guide>` 81 - For more information on using Ansible to manage Ericsson devices see the Ericsson documents. 82 - "Starting with Ansible 2.5 we recommend using C(connection: network_cli)." 83 - For more information please see the L(ERIC_ECCLI Platform Options guide,../network/user_guide/platform_eric_eccli.html). 84""" 85 86EXAMPLES = r""" 87tasks: 88 - name: run show version on remote devices 89 eric_eccli_command: 90 commands: show version 91 92 - name: run show version and check to see if output contains IPOS 93 eric_eccli_command: 94 commands: show version 95 wait_for: result[0] contains IPOS 96 97 - name: run multiple commands on remote nodes 98 eric_eccli_command: 99 commands: 100 - show version 101 - show running-config interfaces 102 103 - name: run multiple commands and evaluate the output 104 eric_eccli_command: 105 commands: 106 - show version 107 - show running-config interfaces 108 wait_for: 109 - result[0] contains IPOS 110 - result[1] contains management 111""" 112 113RETURN = """ 114stdout: 115 description: The set of responses from the commands 116 returned: always apart from low level errors (such as action plugin) 117 type: list 118 sample: ['...', '...'] 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: [['...', '...'], ['...'], ['...']] 124failed_conditions: 125 description: The list of conditionals that have failed 126 returned: failed 127 type: list 128 sample: ['...', '...'] 129""" 130import re 131import time 132 133from ansible.module_utils.network.eric_eccli.eric_eccli import run_commands 134from ansible.module_utils.basic import AnsibleModule 135from ansible.module_utils.network.common.utils import transform_commands 136from ansible.module_utils.network.common.parsing import Conditional 137from ansible.module_utils.six import string_types 138 139 140def parse_commands(module, warnings): 141 commands = transform_commands(module) 142 143 for item in list(commands): 144 if module.check_mode: 145 if item['command'].startswith('conf'): 146 warnings.append( 147 'only non-config commands are supported when using check mode, not ' 148 'executing %s' % item['command'] 149 ) 150 commands.remove(item) 151 return commands 152 153 154def main(): 155 """main entry point for module execution 156 """ 157 argument_spec = dict( 158 commands=dict(type='list', required=True), 159 160 wait_for=dict(type='list', aliases=['waitfor']), 161 match=dict(default='all', choices=['all', 'any']), 162 163 retries=dict(default=10, type='int'), 164 interval=dict(default=1, type='int') 165 ) 166 167 module = AnsibleModule(argument_spec=argument_spec, 168 supports_check_mode=True) 169 170 result = {'changed': False} 171 172 warnings = list() 173 commands = parse_commands(module, warnings) 174 result['warnings'] = warnings 175 176 wait_for = module.params['wait_for'] or list() 177 conditionals = [Conditional(c) for c in wait_for] 178 179 retries = module.params['retries'] 180 interval = module.params['interval'] 181 match = module.params['match'] 182 183 while retries > 0: 184 responses = run_commands(module, commands) 185 186 for item in list(conditionals): 187 if item(responses): 188 if match == 'any': 189 conditionals = list() 190 break 191 conditionals.remove(item) 192 193 if not conditionals: 194 break 195 196 time.sleep(interval) 197 retries -= 1 198 199 if conditionals: 200 failed_conditions = [item.raw for item in conditionals] 201 msg = 'One or more conditional statements have not been satisfied' 202 module.fail_json(msg=msg, failed_conditions=failed_conditions) 203 204 result.update({ 205 'changed': False, 206 'stdout': responses, 207 'stdout_lines': list() 208 }) 209 210 module.exit_json(**result) 211 212 213if __name__ == '__main__': 214 main() 215