roll up of fixes for sros modules (#22972)

* fixes action handlers for sros
* fixes sros_config module execution to use AnsibleModule
* fixes sros_command module to use socket connection
* adds sros to constants
This commit is contained in:
Peter Sprygada 2017-03-25 10:35:15 -04:00 committed by GitHub
commit 3169cbd493
9 changed files with 434 additions and 214 deletions

View file

@ -16,10 +16,11 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = """
---
@ -143,34 +144,45 @@ failed_conditions:
type: list
sample: ['...', '...']
"""
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcli import CommandRunner
from ansible.module_utils.netcli import AddCommandError, FailedConditionsError
from ansible.module_utils.sros import NetworkModule, NetworkError
import time
VALID_KEYS = ['command', 'output', 'prompt', 'response']
from ansible.module_utils.sros import run_commands
from ansible.module_utils.sros import sros_argument_spec, check_args
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.network_common import ComplexList
from ansible.module_utils.netcli import Conditional
from ansible.module_utils.six import string_types
def to_lines(stdout):
for item in stdout:
if isinstance(item, basestring):
if isinstance(item, string_types):
item = str(item).split('\n')
yield item
def parse_commands(module):
for cmd in module.params['commands']:
if isinstance(cmd, basestring):
cmd = dict(command=cmd, output=None)
elif 'command' not in cmd:
module.fail_json(msg='command keyword argument is required')
elif cmd.get('output') not in [None, 'text']:
module.fail_json(msg='invalid output specified for command')
elif not set(cmd.keys()).issubset(VALID_KEYS):
module.fail_json(msg='unknown keyword specified')
yield cmd
def parse_commands(module, warnings):
command = ComplexList(dict(
command=dict(key=True),
prompt=dict(),
answer=dict()
), module)
commands = command(module.params['commands'])
for index, item in enumerate(commands):
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']
)
elif item['command'].startswith('conf'):
module.fail_json(
msg='sros_command does not support running config mode '
'commands. Please use sros_config instead'
)
return commands
def main():
spec = dict(
# { command: <str>, output: <str>, prompt: <str>, response: <str> }
"""main entry point for module execution
"""
argument_spec = dict(
commands=dict(type='list', required=True),
wait_for=dict(type='list', aliases=['waitfor']),
@ -180,59 +192,52 @@ def main():
interval=dict(default=1, type='int')
)
module = NetworkModule(argument_spec=spec,
connect_on_load=False,
argument_spec.update(sros_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
commands = list(parse_commands(module))
conditionals = module.params['wait_for'] or list()
result = {'changed': False}
warnings = list()
runner = CommandRunner(module)
for cmd in commands:
if module.check_mode and not cmd['command'].startswith('show'):
warnings.append('only show commands are supported when using '
'check mode, not executing `%s`' % cmd['command'])
else:
if cmd['command'].startswith('conf'):
module.fail_json(msg='sros_command does not support running '
'config mode commands. Please use '
'sros_config instead')
try:
runner.add_command(**cmd)
except AddCommandError:
exc = get_exception()
warnings.append('duplicate command detected: %s' % cmd)
for item in conditionals:
runner.add_conditional(item)
runner.retries = module.params['retries']
runner.interval = module.params['interval']
runner.match = module.params['match']
try:
runner.run()
except FailedConditionsError:
exc = get_exception()
module.fail_json(msg=str(exc), failed_conditions=exc.failed_conditions)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc))
result = dict(changed=False, stdout=list())
for cmd in commands:
try:
output = runner.get_command(cmd['command'])
except ValueError:
output = 'command not executed due to check_mode, see warnings'
result['stdout'].append(output)
check_args(module, warnings)
commands = parse_commands(module, warnings)
result['warnings'] = warnings
result['stdout_lines'] = list(to_lines(result['stdout']))
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 = {
'changed': False,
'stdout': responses,
'stdout_lines': list(to_lines(responses))
}
module.exit_json(**result)

View file

@ -16,9 +16,11 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = """
@ -203,16 +205,22 @@ updates:
description: The set of commands that will be pushed to the remote device
returned: always
type: list
sample: ['...', '...']
sample: ['config system name "sros01"']
commands:
description: The set of commands that will be pushed to the remote device
returned: always
type: list
sample: ['config system name "sros01"']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: path
sample: /playbooks/ansible/backup/sros_config.2016-07-16@22:28:34
"""
from ansible.module_utils.basic import get_exception
from ansible.module_utils.sros import NetworkModule, NetworkError
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.sros import sros_argument_spec, check_args
from ansible.module_utils.sros import load_config, run_commands, get_config
def sanitize_config(lines):
commands = list()
@ -224,15 +232,17 @@ def sanitize_config(lines):
commands.append(line)
return commands
def get_config(module, result):
def get_active_config(module):
contents = module.params['config']
if not contents:
defaults = module.params['defaults']
contents = module.config.get_config(detail=defaults)
return NetworkConfig(device_os='sros', contents=contents)
flags = []
if module.params['defaults']:
flags = ['detail']
return get_config(module, flags)
return contents
def get_candidate(module):
candidate = NetworkConfig(device_os='sros')
candidate = NetworkConfig(indent=4)
if module.params['src']:
candidate.load(module.params['src'])
elif module.params['lines']:
@ -246,33 +256,23 @@ def run(module, result):
candidate = get_candidate(module)
if match != 'none':
config = get_config(module, result)
config_text = get_active_config(module)
config = NetworkConfig(indent=4, contents=config_text)
configobjs = candidate.difference(config)
else:
configobjs = candidate.items
if configobjs:
commands = dumps(configobjs, 'lines')
commands = dumps(configobjs, 'commands')
commands = sanitize_config(commands.split('\n'))
result['commands'] = commands
result['updates'] = commands
# check if creating checkpoints is possible
if not module.connection.rollback_enabled:
warn = 'Cannot create checkpoint. Please enable this feature ' \
'using the sros_rollback module. Automatic rollback ' \
'will be disabled'
result['warnings'].append(warn)
# send the configuration commands to the device and merge
# them with the current running config
if not module.check_mode:
module.config.load_config(commands)
result['changed'] = True
if module.params['save']:
if not module.check_mode:
module.config.save_config()
load_config(module, commands)
result['changed'] = True
def main():
@ -293,23 +293,30 @@ def main():
save=dict(type='bool', default=False),
)
argument_spec.update(sros_argument_spec)
mutually_exclusive = [('lines', 'src')]
module = NetworkModule(argument_spec=argument_spec,
connect_on_load=False,
module = AnsibleModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
result = dict(changed=False, warnings=list())
if module.params['backup']:
result['__backup__'] = module.config.get_config()
warnings = list()
check_args(module, warnings)
if warnings:
result['warnings'] = warnings
try:
run(module, result)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
if module.params['backup']:
result['__backup__'] = get_config(module)
run(module, result)
if module.params['save']:
if not module.check_mode:
run_commands(module, ['admin save'])
result['changed'] = True
module.exit_json(**result)

View file

@ -16,9 +16,11 @@
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
#
ANSIBLE_METADATA = {'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'}
ANSIBLE_METADATA = {
'metadata_version': '1.0',
'status': ['preview'],
'supported_by': 'community'
}
DOCUMENTATION = """
@ -107,10 +109,12 @@ updates:
type: list
sample: ['...', '...']
"""
from ansible.module_utils.basic import get_exception
from ansible.module_utils.sros import NetworkModule, NetworkError
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.sros import load_config, get_config
from ansible.module_utils.sros import sros_argument_spec, check_args
from ansible.module_utils.netcfg import NetworkConfig, dumps
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
@ -136,7 +140,7 @@ def present(module, commands):
invoke(setter, module, commands)
def absent(module, commands):
config = module.config.get_config()
config = get_config(module)
if 'rollback-location' in config:
commands.append('configure system rollback no rollback-location')
if 'rescue-location' in config:
@ -166,27 +170,9 @@ def set_rescue_location(module, commands):
value = module.params['rescue_location']
commands.append('configure system rollback rescue-location "%s"' % value)
def get_config(module):
contents = module.config.get_config()
return NetworkConfig(device_os='sros', contents=contents)
def load_config(module, commands, result):
candidate = NetworkConfig(device_os='sros', contents='\n'.join(commands))
config = get_config(module)
configobjs = candidate.difference(config)
if configobjs:
commands = dumps(configobjs, 'lines')
commands = sanitize_config(commands.split('\n'))
result['updates'] = commands
# send the configuration commands to the device and merge
# them with the current running config
if not module.check_mode:
module.config(commands)
result['changed'] = True
def get_device_config(module):
contents = get_config(module)
return NetworkConfig(indent=4, contents=contents)
def main():
""" main entry point for module execution
@ -202,8 +188,9 @@ def main():
state=dict(default='present', choices=['present', 'absent'])
)
module = NetworkModule(argument_spec=argument_spec,
connect_on_load=False,
argument_spec.update(sros_argument_spec)
module = AnsibleModule(argument_spec=argument_spec,
supports_check_mode=True)
state = module.params['state']
@ -213,11 +200,24 @@ def main():
commands = list()
invoke(state, module, commands)
try:
load_config(module, commands, result)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
candidate = NetworkConfig(indent=4, contents='\n'.join(commands))
config = get_device_config(module)
configobjs = candidate.difference(config)
if configobjs:
#commands = dumps(configobjs, 'lines')
commands = dumps(configobjs, 'commands')
commands = sanitize_config(commands.split('\n'))
result['updates'] = commands
result['commands'] = commands
# send the configuration commands to the device and merge
# them with the current running config
if not module.check_mode:
load_config(module, commands)
result['changed'] = True
module.exit_json(**result)