update junos_config module

* adds src argument to load configuration from disk
* adds src_format to set the source file format
* adds update argument with choices merge or replace
* deprecates the replace argument in favor of update=replace
This commit is contained in:
Peter Sprygada 2016-09-02 21:31:06 -04:00 committed by Matt Clay
parent 0303c813c8
commit 213c8e5a81

View file

@ -21,15 +21,12 @@ DOCUMENTATION = """
module: junos_config module: junos_config
version_added: "2.1" version_added: "2.1"
author: "Peter Sprygada (@privateip)" author: "Peter Sprygada (@privateip)"
short_description: Manage configuration on remote devices running Junos short_description: Manage configuration on devices running Juniper JUNOS
description: description:
- Provides an abstraction for working - This module provides an implementation for working with the active
with the configuration running on remote Junos devices. It can perform configuration running on Juniper JUNOS devices. It provides a set
operations that influence the configuration state. of arguments for loading configuration, performing rollback operations
- This module provides an implementation for configuring Juniper and zeroing the active configuration on the device.
JUNOS devices. The configuration statements must start with either
`set` or `delete` and are compared against the current device
configuration and only changes are pushed to the device.
extends_documentation_fragment: junos extends_documentation_fragment: junos
options: options:
lines: lines:
@ -40,6 +37,38 @@ options:
file in role or playbook root folder in templates directory. file in role or playbook root folder in templates directory.
required: false required: false
default: null 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"
src_format:
description:
- The I(src_format) argument specifies the format of the configuration
found int I(src). If the I(src_format) argument is not provided,
the module will attempt to determine the format of the configuration
file specified in I(src).
required: false
default: null
choices: ['xml', 'set', 'text', 'json']
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(replace). When the argument is set to
I(merge), the configuration changes are merged with the current
device active configuration.
required: false
default: merge
choices: ['merge', 'replace']
version_added: "2.2"
rollback: rollback:
description: description:
- The C(rollback) argument instructs the module to rollback the - The C(rollback) argument instructs the module to rollback the
@ -82,11 +111,24 @@ options:
replace the current configuration hierarchy with the one specified replace the current configuration hierarchy with the one specified
in the corresponding hierarchy of the source configuration loaded in the corresponding hierarchy of the source configuration loaded
from this module. from this module.
- Note this argument should be considered deprecated. To achieve
the equivalent, set the I(update) argument to C(replace). This argument
will be removed in a future release.
required: false
required: true required: true
choices: choices: ['yes', 'no']
- yes
- no
default: false default: false
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"
requirements: requirements:
- junos-eznc - junos-eznc
notes: notes:
@ -95,118 +137,181 @@ notes:
""" """
EXAMPLES = """ EXAMPLES = """
- name: load configuration lines in device # Note: examples below use the following provider dict to handle
# transport and authentication to the node.
vars:
netconf:
host: "{{ inventory_hostname }}"
username: ansible
password: Ansible
- name: load configure file into device
junos_config: junos_config:
lines: src: srx.cfg
- set system host-name {{ inventory_hostname }}
- delete interfaces ge-0/0/0 description
comment: update config comment: update config
provider: "{{ netconf }}"
- name: rollback the configuration to id 10 - name: rollback the configuration to id 10
junos_config: junos_config:
rollback: 10 rollback: 10
provider: "{{ netconf }}"
- name: zero out the current configuration - name: zero out the current configuration
junos_config: junos_config:
zeroize: yes zeroize: yes
provider: "{{ netconf }}"
- name: confirm a candidate configuration
junos_config:
""" """
RETURN = """
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: path
sample: /playbooks/ansible/backup/config.2016-07-16@22:28:34
"""
import re import re
import json
from lxml import etree
from ansible.module_utils.junos import NetworkModule, NetworkError
DEFAULT_COMMENT = 'configured by junos_config' DEFAULT_COMMENT = 'configured by junos_config'
def diff_config(candidate, config):
updates = set() def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
for line in candidate: def run(module, result):
parts = line.split() if module.params['rollback']:
action = parts[0] action = 'rollback_config'
cfgline = ' '.join(parts[1:]) elif module.params['zeroize']:
action = 'zeroize_config'
else:
action = 'load_config'
if action not in ['set', 'delete']: return invoke(action, module, result)
module.fail_json(msg='line must start with either `set` or `delete`')
elif action == 'set' and cfgline not in config:
updates.add(line)
elif action == 'delete' and not config: def check_args(module, warnings):
updates.add(line) if module.params['replace'] is True:
warnings.append('The replace argument is deprecated, please use '
'update=replace instead. This argument will be '
'removed in the future')
if module.params['lines'] and module.params['update'] == 'replace':
module.fail_json(msg='config replace is only allowed when src is specified')
elif action == 'delete': def guess_format(config):
for cfg in config: try:
if cfg[4:].startswith(cfgline): json.loads(config)
updates.add(line) return 'json'
except ValueError:
pass
try:
etree.fromstring(config)
return 'xml'
except etree.XMLSyntaxError:
pass
if config.startswith('set') or config.startswith('delete'):
return 'set'
return 'text'
def backup_config(module, result):
result['__backup__'] = module.config.get_config()
def load_config(module, result):
comment = module.params['comment']
confirm = module.params['confirm']
update = module.params['update']
candidate = module.params['lines'] or module.params['src']
commit = not module.check_mode
config_format = module.params['src_format'] or guess_format(candidate)
diff = module.config.load_config(candidate, update=update, comment=comment,
format=config_format, commit=commit,
confirm=confirm)
if diff:
result['changed'] = True
result['diff'] = dict(prepared=diff)
def rollback_config(module, result):
rollback = module.params['rollback']
comment = module.params['comment']
commit = not module.check_mode
diff = module.connection.rollback_config(rollback, commit=commit,
comment=comment)
if diff:
result['changed'] = True
result['diff'] = dict(prepared=diff)
def zeroize_config(module, result):
if not module.check_mode:
module.cli.run_commands('request system zeroize')
result['changed'] = True
return list(updates)
def main(): def main():
argument_spec = dict( argument_spec = dict(
lines=dict(type='list'), lines=dict(type='list'),
rollback=dict(type='int'),
zeroize=dict(default=False, type='bool'), src=dict(type='path'),
src_format=dict(choices=['xml', 'text', 'set', 'json']),
# update operations
update=dict(default='merge', choices=['merge', 'replace']),
confirm=dict(default=0, type='int'), confirm=dict(default=0, type='int'),
comment=dict(default=DEFAULT_COMMENT), comment=dict(default=DEFAULT_COMMENT),
# config operations
backup=dict(type='bool', default=False),
rollback=dict(type='int'),
zeroize=dict(default=False, type='bool'),
# this argument is deprecated in favor of setting update=replace
# and will be removed in a future version
replace=dict(default=False, type='bool'), replace=dict(default=False, type='bool'),
transport=dict(default='netconf', choices=['netconf']) transport=dict(default='netconf', choices=['netconf'])
) )
mutually_exclusive = [('lines', 'rollback'), ('lines', 'zeroize'), mutually_exclusive = [('lines', 'rollback'), ('lines', 'zeroize'),
('rollback', 'zeroize')] ('rollback', 'zeroize'), ('lines', 'src'),
('src', 'zeroize'), ('src', 'rollback')]
module = get_module(argument_spec=argument_spec, module = NetworkModule(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive, mutually_exclusive=mutually_exclusive,
supports_check_mode=True) supports_check_mode=True)
rollback = module.params['rollback'] warnings = list()
zeroize = module.params['zeroize'] check_args(module, warnings)
comment = module.params['comment'] if module.params['replace'] is True:
confirm = module.params['confirm'] module.params['update'] = 'replace'
if module.params['replace']: result = dict(changed=False, warnings=warnings)
action = 'replace'
else:
action = 'merge'
lines = module.params['lines'] try:
commit = not module.check_mode run(module, result)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
results = dict(changed=False) module.exit_json(**result)
if lines:
config = str(module.get_config(config_format='set')).split('\n')
updates = diff_config(lines, config)
if updates:
updates = '\n'.join(updates)
diff = module.load_config(updates, action=action, comment=comment,
format='set', commit=commit, confirm=confirm)
if diff:
results['changed'] = True
results['diff'] = dict(prepared=diff)
elif rollback is not None:
diff = module.rollback_config(rollback, commit=commit)
if diff:
results['changed'] = True
results['diff'] = dict(prepared=diff)
elif zeroize:
if not module.check_mode:
module.run_commands('request system zeroize')
results['changed'] = True
module.exit_json(**results)
from ansible.module_utils.basic import *
from ansible.module_utils.junos import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()