update eos_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 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-07-16 21:42:24 -04:00 committed by Matt Clay
parent 12fc073f91
commit 717af59c96

View file

@ -36,7 +36,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
@ -45,6 +46,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
@ -72,7 +84,7 @@ options:
must be an equal match.
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
@ -90,9 +102,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: ['yes', 'no']
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) I(replace) 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:
description:
- The module, by default, will connect to the remote device and
@ -104,12 +132,52 @@ 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 = """
# 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
- eos_config:
lines: ['hostname {{ inventory_hostname }}']
force: yes
lines: hostname {{ inventory_hostname }}
provider: "{{ cli }}"
- eos_config:
lines:
@ -118,9 +186,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 }}"
- eos_config:
lines:
@ -128,9 +197,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
provider: "{{ cli }}"
"""
RETURN = """
@ -139,82 +209,173 @@ updates:
returned: always
type: list
sample: ['...', '...']
responses:
description: The set of responses from issuing the commands on the device
retured: always
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.eos 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 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=3)
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 get_config(module, result, defaults=False):
defaults = module.params['defaults']
if defaults is True:
key = '__configall__'
else:
key = '__config__'
contents = module.params['config'] or result.get(key)
if not contents:
contents = module.config.get_config(include_defaults=defaults)
result[key] = contents
return NetworkConfig(indent=3, 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):
session = 'ansible_%s' % int(time.time())
# save the sesion name in case we need later
result['__session__'] = session
replace = module.params['update'] == 'replace'
commit = not module.check_mode
diff = module.config.load_config(commands, session=session,
replace=replace, commit=commit)
# once the configuration is done, remove the config session and
# remove the session name from the result
module.cli(['no configure session %s' % session])
del result['__session__']
result['diff'] = dict(prepared=diff)
result['changed'] = not diff
def present(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').split('\n')
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
result['updates'] = commands
if update != 'check':
load_config(module, commands, result)
if module.params['save'] and not module.check_mode:
module.config.save_config()
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'], required=True, type='list'),
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']),
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'),
config=dict()
update=dict(choices=['merge', 'replace', 'check'], default='merge'),
backup=dict(type='bool', default=False),
config=dict(),
defaults=dict(type='bool', default=False),
save=dict(default=False),
state=dict(default='present', choices=['absent', 'present'])
)
module = get_module(argument_spec=argument_spec,
mutually_exclusive = [('lines', 'src')]
module = NetworkModule(argument_spec=argument_spec,
connect_on_load=False,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
lines = module.params['lines']
parents = module.params['parents'] or list()
state = module.params['state']
before = module.params['before']
after = module.params['after']
if module.params['force'] is True:
module.params['match'] = 'none'
match = module.params['match']
replace = module.params['replace']
warnings = list()
check_args(module, warnings)
if not module.params['force']:
contents = get_config(module)
config = NetworkConfig(contents=contents, indent=3)
result = dict(changed=False, warnings=warnings)
candidate = NetworkConfig(indent=3)
candidate.add(lines, parents=parents)
if module.params['backup']:
result['__backup__'] = backup_config(module, result)
commands = candidate.difference(config, path=parents, match=match, replace=replace)
else:
commands = parents
commands.extend(lines)
try:
invoke(state, module, result)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc))
result = dict(changed=False)
if commands:
if before:
commands[:0] = before
if after:
commands.extend(after)
if not module.check_mode:
commands = [str(c).strip() for c in commands]
response = module.configure(commands)
result['responses'] = response
result['changed'] = True
result['updates'] = commands
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.eos import *
if __name__ == '__main__':
main()