update iosxr_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, replace or check
* add backup argument to backup current running config to control host
* add comment argument to provide comment to commit
* deprecated force argument, use match=none instead
pull/18777/head
Peter Sprygada 8 years ago committed by Matt Clay
parent c4988262b5
commit d138b94c70

@ -26,9 +26,8 @@ description:
- Cisco IOS XR configurations use a simple block indent file syntax - Cisco IOS XR configurations use a simple block indent file syntax
for segmenting configuration into sections. This module provides for segmenting configuration into sections. This module provides
an implementation for working with IOS XR configuration sections in an implementation for working with IOS XR configuration sections in
a deterministic way. This module works with either CLI or NXAPI a deterministic way.
transports. extends_documentation_fragment: iosxr
extends_documentation_fragment: ios
options: options:
lines: lines:
description: description:
@ -37,7 +36,9 @@ options:
in the device running-config. Be sure to note the configuration in the device running-config. Be sure to note the configuration
command syntax as some commands are automatically modified by the command syntax as some commands are automatically modified by the
device config parser. device config parser.
required: true required: false
default: null
aliases: ['commands']
parents: parents:
description: description:
- The ordered set of parents that uniquely identify the section - The ordered set of parents that uniquely identify the section
@ -46,6 +47,16 @@ options:
level or global commands. level or global commands.
required: false required: false
default: null default: null
src:
description:
- Specifies the source path to the file that contains the configuration
or configuration template to load. The path to the source file can
either be the full path on the Ansible control host or a relative
path from the playbook or role root directory. This argument is mutually
exclusive with I(lines).
required: false
default: null
version_added: "2.2"
before: before:
description: description:
- The ordered set of commands to push on to the command stack if - The ordered set of commands to push on to the command stack if
@ -69,11 +80,13 @@ options:
the set of commands against the current device config. If the set of commands against the current device config. If
match is set to I(line), commands are matched line by line. If match is set to I(line), commands are matched line by line. If
match is set to I(strict), command lines are matched with respect match is set to I(strict), command lines are matched with respect
to position. Finally if match is set to I(exact), command lines to position. If match is set to I(exact), command lines
must be an equal match. must be an equal match. Finally, if match is set to I(none), the
module will not attempt to compare the source configuration with
the running configuration on the remote device.
required: false required: false
default: line default: line
choices: ['line', 'strict', 'exact'] choices: ['line', 'strict', 'exact', 'none']
replace: replace:
description: description:
- Instructs the module on the way to perform the configuration - Instructs the module on the way to perform the configuration
@ -91,9 +104,26 @@ options:
current devices running-config. When set to true, this will current devices running-config. When set to true, this will
cause the module to push the contents of I(src) into the device cause the module to push the contents of I(src) into the device
without first checking if already configured. without first checking if already configured.
- Note this argument should be considered deprecated. To achieve
the equivalent, set the match argument to none. This argument
will be removed in a future release.
required: false required: false
default: false default: false
choices: [ "true", "false" ] choices: [ "yes", "no" ]
version_added: "2.2"
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', 'replace', 'check']
version_added: "2.2"
config: config:
description: description:
- The module, by default, will connect to the remote device and - The module, by default, will connect to the remote device and
@ -105,25 +135,56 @@ options:
config for comparison. config for comparison.
required: false required: false
default: null default: null
backup:
description:
- This argument will cause the module to create a full backup of
the current C(running-config) from the remote device before any
changes are made. The backup file is written to the C(backup)
folder in the playbook root directory. If the directory does not
exist, it is created.
required: false
default: no
choices: ['yes', 'no']
version_added: "2.2"
comment:
description:
- Allows a commit description to be specified to be included
when the configuration is committed. If the configuration is
not changed or committed, this argument is ignored.
required: false
default: 'configured by iosxr_config'
version_added: "2.2"
""" """
EXAMPLES = """ EXAMPLES = """
- iosxr_config: # Note: examples below use the following provider dict to handle
lines: ['hostname {{ inventory_hostname }}'] # transport and authentication to the node.
force: yes vars:
cli:
host: "{{ inventory_hostname }}"
username: cisco
password: cisco
transport: cli
- iosxr_config: - name: configure top level configuration
lines: iosxr_config:
- description configured by ansible lines: hostname {{ inventory_hostname }}
- ipv4 address 10.0.0.25 255.255.255.0 provider: "{{ cli }}"
parents: ['interface GigabitEthernet0/0/0/0']
- iosxr_config: - name: configure interface settings
commands: "{{lookup('file', 'datcenter1.txt')}}" iosxr_config:
parents: ['ipv4 access-list test'] lines:
before: ['no ip access-listv4 test'] - description test interface
replace: block - ip address 172.31.1.1 255.255.255.0
parents: interface GigabitEthernet0/0/0/0
provider: "{{ cli }}"
- name: load a config from disk and replace the current config
iosxr_config:
src: config.cfg
update: replace
backup: yes
provider: "{{ cli }}"
""" """
RETURN = """ RETURN = """
@ -132,82 +193,144 @@ updates:
returned: always returned: always
type: list type: list
sample: ['...', '...'] sample: ['...', '...']
backup_path:
responses: description: The full path to the backup file
description: The set of responses from issuing the commands on the device returned: when backup is yes
retured: always type: path
type: list sample: /playbooks/ansible/backup/iosxr01.2016-07-16@22:28:34
sample: ['...', '...']
""" """
from ansible.module_utils.basic import get_exception
from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.iosxr import NetworkModule, NetworkError
def get_config(module): DEFAULT_COMMIT_COMMENT = 'configured by iosxr_config'
config = module.params['config'] or dict()
if not config and not module.params['force']:
config = module.config
return config
def main():
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
def check_args(module, warnings):
if module.params['parents']:
if not module.params['lines'] or module.params['src']:
warnings.append('ignoring unnecessary argument parents')
if module.params['match'] == 'none' and module.params['replace']:
warnings.append('ignoring unnecessary argument replace')
if module.params['update'] == 'replace' and not module.params['src']:
module.fail_json(msg='Must specify src when update is `replace`')
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_config(module, result):
contents = module.params['config'] or result.get('__config__')
if not contents:
contents = module.config.get_config()
return NetworkConfig(indent=1, contents=contents)
def get_candidate(module):
candidate = NetworkConfig(indent=1)
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
def load_config(module, commands, result):
replace = module.params['update'] == 'replace'
comment = module.params['comment']
commit = not module.check_mode
diff = module.config.load_config(commands, replace=replace, commit=commit,
comment=comment)
result['diff'] = dict(prepared=diff)
result['changed'] = True
def run(module, result):
match = module.params['match']
replace = module.params['replace']
update = module.params['update']
candidate = get_candidate(module)
if match != 'none' and update != 'replace':
config = get_config(module, result)
configobjs = candidate.difference(config, match=match, replace=replace)
else:
config = None
configobjs = candidate.items
if configobjs:
commands = dumps(configobjs, 'commands')
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
result['updates'] = commands.split('\n')
if update != 'check':
load_config(module, commands, result)
def main():
"""main entry point for module execution
"""
argument_spec = dict( argument_spec = dict(
lines=dict(aliases=['commands'], required=True, type='list'), lines=dict(aliases=['commands'], type='list'),
parents=dict(type='list'), parents=dict(type='list'),
src=dict(type='path'),
before=dict(type='list'), before=dict(type='list'),
after=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'] match=dict(default='line', choices=['line', 'strict', 'exact', 'none']),
parents = module.params['parents'] or list() replace=dict(default='line', choices=['line', 'block']),
before = module.params['before'] update=dict(choices=['merge', 'replace', 'check'], default='merge'),
after = module.params['after'] backup=dict(type='bool', default=False),
comment=dict(default=DEFAULT_COMMIT_COMMENT),
match = module.params['match'] # this argument is deprecated in favor of setting match: none
replace = module.params['replace'] # it will be removed in a future version
force=dict(default=False, type='bool'),
contents = get_config(module) config=dict(),
config = module.parse_config(contents) )
if not module.params['force']: mutually_exclusive = [('lines', 'src')]
contents = get_config(module)
config = NetworkConfig(contents=contents, indent=1)
candidate = NetworkConfig(indent=1) module = NetworkModule(argument_spec=argument_spec,
candidate.add(lines, parents=parents) connect_on_load=False,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
commands = candidate.difference(config, path=parents, match=match, replace=replace) if module.params['force'] is True:
else: module.params['match'] = 'none'
commands = parents
commands.extend(lines)
result = dict(changed=False) warnings = list()
check_args(module, warnings)
if commands: result = dict(changed=False, warnings=warnings)
if before:
commands[:0] = before
if after: if module.params['backup']:
commands.extend(after) config = module.config.get_config()
result['__config__'] = config
result['__backup__'] = config
if not module.check_mode: try:
commands = [str(c).strip() for c in commands] run(module, result)
response = module.configure(commands) except NetworkError:
result['responses'] = response exc = get_exception()
result['changed'] = True module.fail_json(msg=str(exc), **exc.kwargs)
result['updates'] = commands
module.exit_json(**result) module.exit_json(**result)
from ansible.module_utils.basic import *
from ansible.module_utils.shell import *
from ansible.module_utils.netcfg import *
from ansible.module_utils.iosxr import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()

Loading…
Cancel
Save