Merge pull request #3429 from privateip/junos_config

add support for netconf to junos_config
This commit is contained in:
Peter Sprygada 2016-04-24 14:15:43 -04:00
commit 76f17c31e9

View file

@ -20,9 +20,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 Juniper JUNOS configuration sections short_description: Manage configuration on remote devices running Junos
description: description:
- The M(junos_config) module provides an abstraction for working
with the configuration running on remote devices. It can perform
operations that influence the confiugration state.
- This module provides an implementation for configuring Juniper - This module provides an implementation for configuring Juniper
JUNOS devices. The configuration statements must start with either JUNOS devices. The configuration statements must start with either
`set` or `delete` and are compared against the current device `set` or `delete` and are compared against the current device
@ -31,148 +34,208 @@ extends_documentation_fragment: junos
options: options:
lines: lines:
description: description:
- The ordered set of commands that should be configured in the - The path to the config source. The source can be either a
section. The commands must be the exact same commands as found file with config or a template that will be merged during
in the device config. Be sure to note the configuration runtime. By default the task will search for the source
command syntanx as some commands are automatically modified by the file in role or playbook root folder in templates directory.
device config parser.
required: true
before:
description:
- The ordered set of commands to push on to the command stack if
a change needs to be made. This allows the playbook designer
the opportunity to perform configuration commands prior to pushing
any changes without affecting how the set of commands are matched
against the system
required: false required: false
default: null default: null
after: backup:
description: description:
- The ordered set of commands to append to the end of the command - When this argument is configured true, the module will backup
stack if a changed needs to be made. Just like with I(before) this the configuration from the node prior to making any changes.
allows the playbook designer to append a set of commands to be The backup file will be written to backup_{{ hostname }} in
executed after the command set. the root of the playbook directory.
required: false
default: null
force:
description:
- The force argument instructs the module to not consider the
current device 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.
required: false required: false
default: false default: false
choices: [ "true", "false" ] choices: ["true", "false"]
config: rollback:
description: description:
- The module, by default, will connect to the remote device and - The C(rollback) argument instructs the module to rollback the
retrieve the current config to use as a base for comparing current configuration to the identifier specified in the
against the contents of source. There are times when it is not argument. If the specified rollback identifier does not
desirable to have the task get the current running-config for exist on the remote device, the module will fail. To rollback
every task in a playbook. The I(config) argument allows the to the most recent commit, set the C(rollback) argument to 0
implementer to pass in the configuruation to use as the base
config for comparision.
required: false required: false
default: null default: null
zeroize:
description:
- The C(zeroize) argument is used to completely ssantaize the
remote device configuration back to initial defaults. This
argument will effectively remove all current configuration
statements on the remote device
required: false
default: null
confirm:
description:
- The C(confirm) argument will configure a time out value for
the commit to be confirmed before it is automatically
rolled back. If the C(confirm) argument is set to False, this
argument is silently ignored. If the value for this argument
is set to 0, the commit is confirmed immediately.
required: false
default: 0
comment:
description:
- The C(comment) argument specifies a text string to be used
when committing the configuration. If the C(confirm) argument
is set to False, this argument is silently ignored.
required: false
default: configured by junos_config
action:
description:
- The C(action) argument instructs the module how to load the
configuration into the remote device. When action is set to
I(merge) the C(src) template file is merged with the current
device configuration. When the I(replace) action is used,
the current device configuration is replaced by the C(src).
Finally when C(overwrite) is used, the device configuration will
be overwritten.
required: true
default: merge
choices: ['merge', 'replace', 'overwrite']
replace:
description:
- The C(replace) argument will instruct the remote device to
replace the current configuration hierarchy with the one specified
in the corresponding hierarchy of the source configuraiton loaded
from this module.
required: true
default: false
overwrite:
description:
format:
description:
- The C(format) argument specifies the format of the configuration
template specified in C(src). If the format argument is not
specified, the module will attempt to infer the configuration
format based of file extension. Files that end in I(xml) will set
the format to xml. Files that end in I(set) will set the format
to set and all other files will default the format to text.
required: false
default: text
choices: ['text', 'xml', 'set']
requirements:
- junos-eznc
notes:
- This module requires the netconf system service be enabled on
the remote device being managed
""" """
EXAMPLES = """ EXAMPLES = """
- junos_config: - name: load configuration lines in device
lines: ['set system host-name {{ inventory_hostname }}'] junos_config:
lines:
- set system host-name {{ inventory_hostname }}
- delete interfaces ge-0/0/0 description
comment: update config
- name: rollback the configuration to id 10
junos_config:
rollback: 10
- name: zero out the current configuration
junos_config:
zeroize: yes
- name: confirm a candidate configuration
junos_config:
""" """
RETURN = """
updates:
description: The set of commands that will be pushed to the remote device
returned: always
type: list
sample: ['...', '...']
responses:
description: The set of responses from issuing the commands on the device
returned: always
type: list
sample: ['...', '...']
"""
import re import re
import itertools
def get_config(module): DEFAULT_COMMENT = 'configured by junos_config'
config = module.params['config'] or dict()
if not config and not module.params['force']:
config = module.config
return config
def to_lines(config): def diff_config(candidate, config):
lines = list()
for item in config:
if item.raw.endswith(';'):
line = [p.text for p in item.parents]
line.append(item.text)
lines.append(' '.join(line))
return lines
def main(): updates = set()
argument_spec = dict( for line in candidate:
lines=dict(aliases=['commands'], required=True, type='list'),
before=dict(type='list'),
after=dict(type='list'),
force=dict(default=False, type='bool'),
config=dict()
)
module = get_module(argument_spec=argument_spec,
supports_check_mode=True)
lines = module.params['lines']
before = module.params['before']
after = module.params['after']
contents = get_config(module)
parsed = module.parse_config(contents)
config = to_lines(parsed)
result = dict(changed=False)
candidate = list()
for line in lines:
parts = line.split() parts = line.split()
action = parts[0] action = parts[0]
cfgline = ' '.join(parts[1:]) cfgline = ' '.join(parts[1:])
if action not in ['set', 'delete']: if action not in ['set', 'delete']:
module.fail_json(msg='line must start with either `set` or `delete`') module.fail_json(msg='line must start with either `set` or `delete`')
elif action == 'set' and cfgline not in config: elif action == 'set' and cfgline not in config:
candidate.append(line) updates.add(line)
elif action == 'delete' and not config: elif action == 'delete' and not config:
candidate.append(line) updates.add(line)
elif action == 'delete': elif action == 'delete':
regexp = re.compile(r'^%s$' % cfgline)
for cfg in config: for cfg in config:
if regexp.match(cfg): if cfg.startswith(cfgline):
candidate.append(line) updates.add(cfgline)
break
if candidate: return list(updates)
if before:
candidate[:0] = before
if after: def main():
candidate.extend(after)
argument_spec = dict(
lines=dict(type='list'),
rollback=dict(type='int'),
zeroize=dict(default=False, type='bool'),
confirm=dict(default=0, type='int'),
comment=dict(default=DEFAULT_COMMENT),
replace=dict(default=False, type='bool'),
transport=dict(default='netconf', choices=['netconf'])
)
mutually_exclusive = [('lines', 'rollback'), ('lines', 'zeroize'),
('rollback', 'zeroize')]
module = get_module(argument_spec=argument_spec,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
rollback = module.params['rollback']
zeroize = module.params['zeroize']
comment = module.params['comment']
confirm = module.params['confirm']
if module.params['replace']:
action = 'replace'
else:
action = 'merge'
lines = module.params['lines']
commit = not module.check_mode
results = dict(changed=False)
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: if not module.check_mode:
response = module.configure(candidate) module.run_commands('request system zeroize')
result['responses'] = response results['changed'] = True
result['changed'] = True
module.exit_json(**results)
result['updates'] = candidate
return module.exit_json(**result)
from ansible.module_utils.basic import * from ansible.module_utils.basic import *
from ansible.module_utils.shell import *
from ansible.module_utils.netcfg import *
from ansible.module_utils.junos import * from ansible.module_utils.junos import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()