moves Cli and Command class from network to netcmd module
This restructure moves the Cli object to netcmd and includes a roll up of inor bugfix updates to CommandRunner * CommandRunner now only allows one instance of a command in the stack and raise an exception if a duplidate command is detected * CommandRunner now caches returns based on command and output * CommandRunner is not responsible for creating Command instances
This commit is contained in:
parent
fa395bee7b
commit
7c928e878b
1 changed files with 143 additions and 59 deletions
|
@ -33,6 +33,149 @@ import shlex
|
||||||
|
|
||||||
from ansible.module_utils.basic import BOOLEANS_TRUE, BOOLEANS_FALSE
|
from ansible.module_utils.basic import BOOLEANS_TRUE, BOOLEANS_FALSE
|
||||||
|
|
||||||
|
def to_list(val):
|
||||||
|
if isinstance(val, (list, tuple)):
|
||||||
|
return list(val)
|
||||||
|
elif val is not None:
|
||||||
|
return [val]
|
||||||
|
else:
|
||||||
|
return list()
|
||||||
|
|
||||||
|
class FailedConditionsError(Exception):
|
||||||
|
def __init__(self, msg, failed_conditions):
|
||||||
|
super(FailedConditionsError, self).__init__(msg)
|
||||||
|
self.failed_conditions = failed_conditions
|
||||||
|
|
||||||
|
class AddCommandError(Exception):
|
||||||
|
def __init__(self, msg, command):
|
||||||
|
super(AddCommandError, self).__init__(msg)
|
||||||
|
self.command = command
|
||||||
|
|
||||||
|
class Cli(object):
|
||||||
|
|
||||||
|
def __init__(self, connection):
|
||||||
|
self.connection = connection
|
||||||
|
self.default_output = connection.default_output or 'text'
|
||||||
|
self._commands = list()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def commands(self):
|
||||||
|
return [str(c) for c in self._commands]
|
||||||
|
|
||||||
|
def __call__(self, commands, output=None):
|
||||||
|
objects = list()
|
||||||
|
for cmd in commands:
|
||||||
|
objects.append(self.to_command(cmd, output))
|
||||||
|
return self.connection.run_commands(objects)
|
||||||
|
|
||||||
|
def to_command(self, command, output=None, prompt=None, response=None):
|
||||||
|
output = output or self.default_output
|
||||||
|
if isinstance(command, Command):
|
||||||
|
return command
|
||||||
|
elif isinstance(command, dict):
|
||||||
|
output = cmd.get('output') or output
|
||||||
|
cmd = cmd['command']
|
||||||
|
if isinstance(prompt, basestring):
|
||||||
|
prompt = re.compile(re.escape(prompt))
|
||||||
|
return Command(command, output, prompt=prompt, response=response)
|
||||||
|
|
||||||
|
def add_commands(self, commands, output=None, **kwargs):
|
||||||
|
for cmd in commands:
|
||||||
|
self._commands.append(self.to_command(cmd, output, **kwargs))
|
||||||
|
|
||||||
|
def run_commands(self):
|
||||||
|
responses = self.connection.run_commands(self._commands)
|
||||||
|
for resp, cmd in itertools.izip(responses, self._commands):
|
||||||
|
cmd.response = resp
|
||||||
|
|
||||||
|
# wipe out the commands list to avoid issues if additional
|
||||||
|
# commands are executed later
|
||||||
|
self._commands = list()
|
||||||
|
|
||||||
|
return responses
|
||||||
|
|
||||||
|
class Command(object):
|
||||||
|
|
||||||
|
def __init__(self, command, output=None, prompt=None, is_reboot=False,
|
||||||
|
response=None, delay=0):
|
||||||
|
|
||||||
|
self.command = command
|
||||||
|
self.output = output
|
||||||
|
|
||||||
|
self.prompt = prompt
|
||||||
|
self.response = response
|
||||||
|
|
||||||
|
self.is_reboot = is_reboot
|
||||||
|
self.delay = delay
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.command
|
||||||
|
|
||||||
|
class CommandRunner(object):
|
||||||
|
|
||||||
|
def __init__(self, module):
|
||||||
|
self.module = module
|
||||||
|
|
||||||
|
self.items = list()
|
||||||
|
self.conditionals = set()
|
||||||
|
|
||||||
|
self.commands = list()
|
||||||
|
|
||||||
|
self.retries = 10
|
||||||
|
self.interval = 1
|
||||||
|
|
||||||
|
self.match = 'all'
|
||||||
|
|
||||||
|
self._cache = dict()
|
||||||
|
self._default_output = module.connection.default_output
|
||||||
|
|
||||||
|
|
||||||
|
def add_command(self, command, output=None, prompt=None, response=None):
|
||||||
|
if command in [str(c) for c in self.commands]:
|
||||||
|
raise AddCommandError('duplicated command detected', command=command)
|
||||||
|
cmd = self.module.cli.to_command(command, output=output, prompt=prompt,
|
||||||
|
response=response)
|
||||||
|
self.commands.append(cmd)
|
||||||
|
|
||||||
|
def get_command(self, command, output=None):
|
||||||
|
output = output or self._default_output
|
||||||
|
try:
|
||||||
|
cmdobj = self._cache[(command, output)]
|
||||||
|
return cmdobj.response
|
||||||
|
except KeyError:
|
||||||
|
for cmd in self.commands:
|
||||||
|
if str(cmd) == command and cmd.output == output:
|
||||||
|
self._cache[(command, output)] = cmd
|
||||||
|
return cmd.response
|
||||||
|
raise ValueError("command '%s' not found" % command)
|
||||||
|
|
||||||
|
def get_responses(self):
|
||||||
|
return [cmd.response for cmd in self.commands]
|
||||||
|
|
||||||
|
def add_conditional(self, condition):
|
||||||
|
self.conditionals.add(Conditional(condition))
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while self.retries > 0:
|
||||||
|
self.module.cli.add_commands(self.commands)
|
||||||
|
responses = self.module.cli.run_commands()
|
||||||
|
|
||||||
|
for item in list(self.conditionals):
|
||||||
|
if item(responses):
|
||||||
|
if self.match == 'any':
|
||||||
|
return item
|
||||||
|
self.conditionals.remove(item)
|
||||||
|
|
||||||
|
if not self.conditionals:
|
||||||
|
break
|
||||||
|
|
||||||
|
time.sleep(self.interval)
|
||||||
|
self.retries -= 1
|
||||||
|
else:
|
||||||
|
failed_conditions = [item.raw for item in self.conditionals]
|
||||||
|
errmsg = 'One or more conditional statements have not be satisfied'
|
||||||
|
raise FailedConditionsError(errmsg, failed_conditions)
|
||||||
|
|
||||||
class Conditional(object):
|
class Conditional(object):
|
||||||
"""Used in command modules to evaluate waitfor conditions
|
"""Used in command modules to evaluate waitfor conditions
|
||||||
"""
|
"""
|
||||||
|
@ -154,62 +297,3 @@ class Conditional(object):
|
||||||
match = re.search(value, self.value, re.M)
|
match = re.search(value, self.value, re.M)
|
||||||
return match is not None
|
return match is not None
|
||||||
|
|
||||||
|
|
||||||
class FailedConditionsError(Exception):
|
|
||||||
|
|
||||||
def __init__(self, msg, failed_conditions):
|
|
||||||
super(FailedConditionsError, self).__init__(msg)
|
|
||||||
self.failed_conditions = failed_conditions
|
|
||||||
|
|
||||||
class CommandRunner(object):
|
|
||||||
|
|
||||||
def __init__(self, module):
|
|
||||||
self.module = module
|
|
||||||
|
|
||||||
self.items = list()
|
|
||||||
self.conditionals = set()
|
|
||||||
|
|
||||||
self.retries = 10
|
|
||||||
self.interval = 1
|
|
||||||
|
|
||||||
self._cache = dict()
|
|
||||||
|
|
||||||
def add_command(self, command, output=None):
|
|
||||||
self.module.cli.add_commands(command, output=output)
|
|
||||||
|
|
||||||
def get_command(self, command):
|
|
||||||
try:
|
|
||||||
cmdobj = self._cache[command]
|
|
||||||
return cmdobj.response
|
|
||||||
except KeyError:
|
|
||||||
for cmd in self.module.cli.commands:
|
|
||||||
if str(cmd) == command:
|
|
||||||
self._cache[command] = cmd
|
|
||||||
return cmd.response
|
|
||||||
raise ValueError("command '%s' not found" % command)
|
|
||||||
|
|
||||||
|
|
||||||
def add_conditional(self, condition):
|
|
||||||
self.conditionals.add(Conditional(condition))
|
|
||||||
|
|
||||||
def run_commands(self):
|
|
||||||
responses = self.module.cli.run_commands()
|
|
||||||
self.items = responses
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
while self.retries > 0:
|
|
||||||
self.run_commands()
|
|
||||||
|
|
||||||
for item in list(self.conditionals):
|
|
||||||
if item(self.items):
|
|
||||||
self.conditionals.remove(item)
|
|
||||||
|
|
||||||
if not self.conditionals:
|
|
||||||
break
|
|
||||||
|
|
||||||
time.sleep(self.interval)
|
|
||||||
self.retries -= 1
|
|
||||||
else:
|
|
||||||
failed_conditions = [item.raw for item in self.conditionals]
|
|
||||||
raise FailedConditionsError('timeout waiting for value', failed_conditions)
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue