update nxos_config with new arguments

* add src argument to provide path to config file
* add new choice to match used to ignore current running config
* add update argument with choices merge or check
* add backup argument to backup current running config to control host
* add defaults argument to control collection of config with or without defaults
* add save argument to save current running config to startup config
* add state argument to control state of config file
* deprecated force argument, use match=none instead
This commit is contained in:
Peter Sprygada 2016-08-10 15:59:05 -04:00 committed by Matt Clay
parent 525cd8b947
commit 543ec0f14e

View file

@ -37,7 +37,8 @@ options:
in the device running-config. Be sure to note the configuration
command syntax as some commands are automatically modified by the
device config parser.
required: true
required: false
default: null
parents:
description:
- The ordered set of parents that uniquely identify the section
@ -46,6 +47,17 @@ options:
level or global commands.
required: false
default: null
src:
description:
- The I(src) argument provides a path to the configuration file
to load into the remote system. The path can either be a full
system path to the configuration file if the value starts with /
or relative to the root of the implemented role or playbook.
This arugment is mutually exclusive with the I(lines) and
I(parents) arguments.
required: false
default: null
version_added: "2.2"
before:
description:
- The ordered set of commands to push on to the command stack if
@ -71,9 +83,12 @@ options:
match is set to I(strict), command lines are matched with respect
to position. Finally if match is set to I(exact), command lines
must be an equal match.
- Version 2.2 added a new choice I(none). When match is set to
none, the configure is loaded into the remote device without
consulting the configuration.
required: false
default: line
choices: ['line', 'strict', 'exact']
choices: ['line', 'strict', 'exact', 'none']
replace:
description:
- Instructs the module on the way to perform the configuration
@ -91,9 +106,25 @@ options:
current devices running-config. When set to true, this will
cause the module to push the contents of I(src) into the device
without first checking if already configured.
- Note this argument should be considered deprecated. To achieve
the equivalient, set the match argument to none. This argument
will be removed in a future release.
required: false
default: false
choices: [ "true", "false" ]
update:
description:
- The I(update) argument controls how the configuration statements
are processed on the remote device. Valid choices for the I(update)
argument are I(merge) and I(check). When the argument is set to
I(merge), the configuration changes are merged with the current
device running configuration. When the argument is set to I(check)
the configuration updates are determined but not actually configured
on the remote device.
required: false
default: merge
choices: ['merge', 'check']
version_added: "2.2"
config:
description:
- The module, by default, will connect to the remote device and
@ -105,12 +136,55 @@ options:
config for comparison.
required: false
default: null
defaults:
description:
- The I(defaults) argument will influence how the running-config
is collected from the device. When the value is set to true,
the command used to collect the running-config is append with
the all keyword. When the value is set to false, the command
is issued without the all keyword
required: false
default: false
version_added: "2.2"
save:
description:
- The I(save) argument will instruct the module to save the
running-config to startup-config. This operation is performed
after any changes are made to the current running config. If
no changes are made, the configuration is still saved to the
startup config. This option will always cause the module to
return changed.
required: false
default: false
version_added: "2.2"
state:
description:
- The I(state) argument specifies the state of the config
file on the device. When set to present, the configuration
is updated based on the values of the module. When the value
is set to absent, the device startup config is erased.
required: true
default: present
choices: ['present', 'absent']
version_added: "2.2"
"""
EXAMPLES = """
- nxos_config:
lines: ['hostname {{ inventory_hostname }}']
force: yes
# Note: examples below use the following provider dict to handle
# transport and authentication to the node.
vars:
cli:
host: "{{ inventory_hostname }}"
username: admin
password: admin
transport: cli
- name: configure top level configuration and save it
nxos_config:
lines: hostname {{ inventory_hostname }}
save: yes
provider: "{{ cli }}"
- nxos_config:
lines:
@ -119,9 +193,10 @@ EXAMPLES = """
- 30 permit ip 3.3.3.3/32 any log
- 40 permit ip 4.4.4.4/32 any log
- 50 permit ip 5.5.5.5/32 any log
parents: ['ip access-list test']
before: ['no ip access-list test']
parents: ip access-list test
before: no ip access-list test
match: exact
provider: "{{ cli }}"
- nxos_config:
lines:
@ -129,16 +204,10 @@ EXAMPLES = """
- 20 permit ip 2.2.2.2/32 any log
- 30 permit ip 3.3.3.3/32 any log
- 40 permit ip 4.4.4.4/32 any log
parents: ['ip access-list test']
before: ['no ip access-list test']
parents: ip access-list test
before: no ip access-list test
replace: block
- nxos_config:
lines: "{{lookup('file', 'datcenter1.txt')}}"
parents: ['ip access-list test']
before: ['no ip access-list test']
replace: block
provider: "{{ cli }}"
"""
RETURN = """
@ -147,83 +216,179 @@ updates:
returned: always
type: list
sample: ['...', '...']
responses:
description: The set of responses from issuing the commands on the device
retured: when not check_mode
type: list
sample: ['...', '...']
"""
import time
def get_config(module):
config = module.params['config'] or dict()
if not config and not module.params['force']:
config = module.config
return config
from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.nxos import NetworkModule, NetworkError
from ansible.module_utils.basic import get_exception
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
def main():
argument_spec = dict(
lines=dict(aliases=['commands'], required=True, type='list'),
parents=dict(type='list'),
before=dict(type='list'),
after=dict(type='list'),
match=dict(default='line', choices=['line', 'strict', 'exact']),
replace=dict(default='line', choices=['line', 'block']),
force=dict(default=False, type='bool'),
config=dict()
)
module = get_module(argument_spec=argument_spec,
supports_check_mode=True)
lines = module.params['lines']
parents = module.params['parents'] or list()
before = module.params['before']
after = module.params['after']
match = module.params['match']
replace = module.params['replace']
contents = get_config(module)
config = module.parse_config(contents)
if not module.params['force']:
contents = get_config(module)
config = NetworkConfig(contents=contents, indent=2)
def check_args(module, warnings):
if module.params['save'] and module.check_mode:
warnings.append('will not save configuration due to checkmode')
if module.params['parents'] and module.params['src']:
warnings.append('ignoring parents argument when src specified')
if module.params['force']:
warnings.append('The force argument is deprecated, please use '
'match=none instead. This argument will be '
'removed in the future')
def get_candidate(module):
candidate = NetworkConfig(indent=2)
candidate.add(lines, parents=parents)
if module.params['src']:
candidate.load(module.params['src'])
elif module.params['lines']:
parents = module.params['parents'] or list()
candidate.add(module.params['lines'], parents=parents)
return candidate
commands = candidate.difference(config, path=parents, match=match, replace=replace)
def get_config(module, result):
defaults = module.params['defaults']
if defaults is True:
key = '__configall__'
else:
commands = parents
commands.extend(lines)
key = '__config__'
result = dict(changed=False)
contents = module.params['config'] or result.get(key)
if commands:
if before:
commands[:0] = before
if not contents:
contents = module.config.get_config(include_defaults=defaults)
result[key] = contents
if after:
commands.extend(after)
return NetworkConfig(indent=1, contents=contents)
def backup_config(module, result):
if '__config__' not in result:
result['__config__'] = module.config.get_config()
result['__backup__'] = result['__config__']
def load_config(module, commands, result):
if not module.check_mode:
commands = [str(c).strip() for c in commands]
response = module.configure(commands)
result['responses'] = response
checkpoint = 'ansible_%s' % int(time.time())
module.cli(['checkpoint %s' % checkpoint], output='text')
result['__checkpoint__'] = checkpoint
module.config.load_config(commands)
result['changed'] = True
def load_checkpoint(module, result):
try:
checkpoint = result['__checkpoint__']
module.cli(['rollback running-config checkpoint %s' % checkpoint,
'no checkpoint %s' % checkpoint], output='text')
except KeyError:
module.fail_json(msg='unable to rollback, checkpoint not found')
except NetworkError:
exc = get_exception()
msg = 'unable to rollback configuration'
module.fail_json(msg=msg, checkpoint=checkpoint, **exc.kwargs)
def present(module, result):
match = module.params['match']
replace = module.params['replace']
update = module.params['update']
candidate = get_candidate(module)
if match != 'none':
config = get_config(module, result)
configobjs = candidate.difference(config, match=match, replace=replace)
else:
config = None
configobjs = candidate.items
if module.params['backup']:
backup_config(module, result)
if configobjs:
commands = dumps(configobjs, 'commands').split('\n')
result['updates'] = commands
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
# if the update mode is set to check just return
# and do not try to load into the system
if update != 'check':
load_config(module, commands, result)
# remove the checkpoint file used to restore the config
# in case of an error
if not module.check_mode:
module.cli('no checkpoint %s' % result['__checkpoint__'])
if module.params['save'] and not module.check_mode:
module.config.save_config()
result['changed'] = True
def absent(module, result):
if not module.check_mode:
module.cli('write erase')
result['changed'] = True
def main():
""" main entry point for module execution
"""
argument_spec = dict(
lines=dict(aliases=['commands'], type='list'),
parents=dict(type='list'),
src=dict(type='path'),
before=dict(type='list'),
after=dict(type='list'),
match=dict(default='line', choices=['line', 'strict', 'exact', 'none']),
replace=dict(default='line', choices=['line', 'block']),
# this argument is deprecated in favor of setting match: none
# it will be removed in a future version
force=dict(default=False, type='bool'),
update=dict(choices=['merge', 'check'], default='merge'),
backup=dict(type='bool', default=False),
config=dict(),
defaults=dict(type='bool', default=False),
save=dict(type='bool', default=False),
state=dict(default='present', choices=['absent', 'present'])
)
mutually_exclusive = [('lines', 'src')]
module = NetworkModule(argument_spec=argument_spec,
connect_on_load=False,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
state = module.params['state']
if module.params['force'] is True:
module.params['match'] = 'none'
warnings = list()
check_args(module, warnings)
result = dict(changed=False, warnings=warnings)
try:
invoke(state, module, result)
except NetworkError:
load_checkpoint(module, result)
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
module.exit_json(**result)
from ansible.module_utils.basic import *
from ansible.module_utils.urls import *
from ansible.module_utils.shell import *
from ansible.module_utils.netcfg import *
from ansible.module_utils.nxos import *
if __name__ == '__main__':
main()