@ -16,212 +16,113 @@ DOCUMENTATION = """
module : cli_command
version_added : " 2.7 "
author : " Nathaniel Case (@qalthos) "
short_description : Run a rbitrary commands on cli - based network devices
short_description : Run a cli command on cli - based network devices
description :
- Sends an arbitrary set of commands to a network 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 .
notes :
- Tested against EOS 4.15
- Sends a command to a network device and returns the result read from the device .
options :
command s :
command :
description :
- The commands to send to the remote EOS 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 I ( retries ) has been exceeded .
- The command to send to the remote network device . The resulting output
from the command is returned , unless I ( sendonly ) is set .
required : true
wait_for :
prompt :
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 .
Note - With I ( wait_for ) the value in C ( result [ ' stdout ' ] ) can be accessed
using C ( result ) , that is to access C ( result [ ' stdout ' ] [ 0 ] ) use C ( result [ 0 ] ) See examples .
aliases : [ ' waitfor ' ]
version_added : " 2.2 "
match :
- A single regex pattern or a sequence of patterns to evaluate the expected
prompt from I ( command ) .
required : false
answer :
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 I ( 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 ' ]
version_added : " 2.2 "
retries :
- The answer to reply with if I ( prompt ) is matched .
required : false
sendonly :
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 I ( wait_for )
conditionals .
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 .
default : 1
- The boolean value , that when set to true will send I ( command ) to the
device but not wait for a result .
type : bool
default : false
required : false
"""
EXAMPLES = """
- name : run show version on remote devices
cli_command :
commands : show version
- name : run show version and check to see if output contains Arista
cli_command :
commands : show version
wait_for : result [ 0 ] contains Arista
command : show version
- name : run multiple commands on remote nodes
- name : run command with json formatted output
cli_command :
commands :
- show version
- show interfaces
command : show version | json
- name : run multiple commands and evaluate the output
- name : run command expecting user confirmation
cli_command :
commands :
- show version
- show interfaces
wait_for :
- result [ 0 ] contains Arista
- result [ 1 ] contains Loopback0
- name : run commands and specify the output format
cli_command :
commands :
- command : show version
output : json
command : commit replace
prompt : This commit will replace or remove the entire running configuration
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 : [ ' ... ' , ' ... ' ]
description : The response from the command
returned : when sendonly is false
type : string
sample : ' Version: VyOS 1.1.7[...] '
json :
description : A dictionary representing a JSON - formatted response
returned : when the device response is valid JSON
type : dict
sample : |
{
" architecture " : " i386 " ,
" bootupTimestamp " : 1532649700.56 ,
" modelName " : " vEOS " ,
" version " : " 4.15.9M "
[ . . . ]
}
"""
import time
from ansible . module_utils . _text import to_text
from ansible . module_utils . six import string_types
from ansible . module_utils . basic import AnsibleModule
from ansible . module_utils . connection import Connection , ConnectionError
from ansible . module_utils . network . common . parsing import Conditional
from ansible . module_utils . network . common . utils import ComplexList
VALID_KEYS = [ ' command ' , ' output ' , ' prompt ' , ' response ' ]
def to_lines ( output ) :
lines = [ ]
for item in output :
if isinstance ( item , string_types ) :
item = to_text ( item ) . split ( ' \n ' )
lines . append ( item )
return lines
def parse_commands ( module , warnings ) :
transform = ComplexList ( dict (
command = dict ( key = True ) ,
output = dict ( ) ,
prompt = dict ( ) ,
answer = dict ( )
) , module )
commands = transform ( module . params [ ' commands ' ] )
if module . check_mode :
for item in list ( commands ) :
if not item [ ' command ' ] . startswith ( ' show ' ) :
warnings . append (
' Only show commands are supported when using check_mode, not '
' executing %s ' % item [ ' command ' ]
)
commands . remove ( item )
return commands
def 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 ' )
command = dict ( type = ' str ' , required = True ) ,
prompt = dict ( type = ' list ' , required = False ) ,
answer = dict ( type = ' str ' , required = False ) ,
sendonly = dict ( type = ' bool ' , default = False , required = False ) ,
)
module = AnsibleModule ( argument_spec = argument_spec ,
required_together = [ [ ' prompt ' , ' response ' ] ]
module = AnsibleModule ( argument_spec = argument_spec , required_together = required_together ,
supports_check_mode = True )
if module . check_mode and not module . params [ ' command ' ] . startswith ( ' show ' ) :
module . fail_json (
msg = ' Only show commands are supported when using check_mode, not '
' executing %s ' % module . params [ ' command ' ]
)
warnings = list ( )
result = { ' changed ' : False , ' warnings ' : warnings }
wait_for = module . params [ ' wait_for ' ] or list ( )
connection = Connection ( module . _socket_path )
response = ' '
try :
conditionals = [ Conditional ( c ) for c in wait_for ]
except AttributeError as exc :
module . fail_json ( msg = to_text ( exc ) )
commands = parse_commands ( module , warnings )
retries = module . params [ ' retries ' ]
interval = module . params [ ' interval ' ]
match = module . params [ ' match ' ]
response = connection . get ( * * module . params )
except ConnectionError as exc :
module . fail_json ( msg = to_text ( exc , errors = ' surrogate_then_replace ' ) )
connection = Connection ( module . _socket_path )
for attempt in range ( retries ) :
responses = [ ]
if not module . params [ ' sendonly ' ] :
try :
for command in commands :
responses . append ( connection . get ( * * command ) )
except ConnectionError as exc :
module . fail_json ( msg = to_text ( exc , errors = ' surrogate_then_replace ' ) )
for item in list ( conditionals ) :
if item ( responses ) :
if match == ' any ' :
conditionals = list ( )
break
conditionals . remove ( item )
if not conditionals :
break
time . sleep ( interval )
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 ( {
' stdout ' : responses ,
' stdout_lines ' : to_lines ( responses )
} )
result [ ' json ' ] = module . from_json ( response )
except ValueError :
pass
result . update ( {
' stdout ' : response ,
} )
module . exit_json ( * * result )