updates to vyos_config module arguments

* arguments for vyos_config for 2.2 are now complete
* adds loading config file from disk (src argument)
* removes unsupported rollback argument
* changes update_config to update with options merge or check
* changes backup_config to backup
* add state argument for state of configuration file
* adds backup argument to backup current configuration
* adds save argument to control if active config is saved to disk
* adds comment argument for setting commit comment
* adds match argument to control configuraiton match

Tested with VyOS 1.7
This commit is contained in:
Peter Sprygada 2016-07-17 16:51:52 -04:00 committed by Matt Clay
parent e2d55d86e4
commit df972feb23

View file

@ -39,46 +39,89 @@ options:
default: null
src:
description:
- Path to the configuration file to load.
- The C(src) argument specifies the path to the source config
file to load. The source config file can either be in
bracket format or set format. The source file can include
Jinja2 template variables.
required: no
default: null
rollback:
match:
description:
- Rollback the device configuration to the
revision specified. If the specified rollback revision does
not exist, then the module will produce an error and fail.
- NOTE THIS WILL CAUSE THE DEVICE TO REBOOT AUTOMATICALLY.
- The C(match) argument controls the method used to match
against the current active configuration. By default, the
desired config is matched against the active config and the
deltas are loaded. If the C(match) argument is set to C(none)
the active configuration is ignored and the configuration is
always loaded.
required: false
default: null
update_config:
default: line
choices: ['line', 'none']
update:
description:
- Should the configuration on the remote device be updated with the
calculated changes. When set to true, the configuration will be updated
and when set to false, the configuration will not be updated.
- The C(update) argument controls the method used to update the
remote device configuration. This argument accepts two valid
options, C(merge) or C(check). When C(merge) is specified, the
configuration is merged into the current active config. When
C(check) is specified, the module returns the set of updates
that would be applied to the active configuration.
required: false
default: true
choices: ['yes', 'no']
backup_config:
default: merge
choices: ['merge', 'check']
backup:
description:
- Create a local backup copy of the current running configuration
prior to making any changes. The configuration file will be
stored in the backups folder in the root of the playbook or role.
- The C(backup) argument will backup the current devices active
configuration to the Ansible control host prior to making any
changes. The backup file will be located in the backup folder
in the root of the playbook
required: false
default: false
choices: ['yes', 'no']
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 vyos_config'
config:
description:
- The C(config) argument specifies the base configuration to use
to compare against the desired configuration. If this value
is not specified, the module will automatically retrieve the
current active configuration from the remote device.
required: false
default: null
save:
description:
- The C(save) argument controls whether or not changes made
to the active configuration are saved to disk. This is
independent of committing the config. When set to True, the
active configuration is saved.
required: false
default: false
choices: ['yes', 'no']
state:
description:
- The C(state) argument controls the existing state of the config
file on disk. When set to C(present), the configuration should
exist on disk and when set to C(absent) the configuration file
is removed. This only applies to the startup configuration.
required: false
default: present
choices: ['present', 'absent']
"""
RETURN = """
connected:
description: Boolean that specifies if the module connected to the device
returned: always
type: bool
sample: true
updates:
description: The list of configuration commands sent to the device
returned: always
type: list
sample: ['...', '...']
removed:
description: The list of configuration commands removed to avoid a load failure
returned: always
type: list
sample: ['...', '...']
"""
EXAMPLES = """
@ -99,16 +142,24 @@ vars:
- delete service dhcp-server
provider: "{{ cli }}"
- name: rollback config to revision 3
- name: backup and load from file
vyos_config:
rollback: 3
src: vyos.cfg
backup: yes
provider: "{{ cli }}"
"""
import re
from ansible.module_utils.network import Command, get_exception
from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.vyos import NetworkModule, NetworkError
from ansible.module_utils.vyos import vyos_argument_spec
from ansible.module_utils.vyos import load_config, load_candidate
DEFAULT_COMMENT = 'configured by vyos_config'
CONFIG_FILTERS = [
re.compile(r'set system login user \S+ authentication encrypted-password')
]
def invoke(name, *args, **kwargs):
@ -116,6 +167,10 @@ def invoke(name, *args, **kwargs):
if func:
return func(*args, **kwargs)
def check_args(module, warnings):
if module.params['save'] and module.params['update'] == 'check':
warnings.append('The configuration will not be saved when update '
'is set to check')
def config_to_commands(config):
set_format = config.startswith('set') or config.startswith('delete')
@ -123,84 +178,158 @@ def config_to_commands(config):
if not set_format:
candidate = [c.line for c in candidate.items]
commands = list()
# this filters out less specific lines
for item in candidate:
for index, entry in enumerate(commands):
if item.startswith(entry):
del commands[index]
break
commands.append(item)
else:
commands = str(candidate).split('\n')
return commands
def get_config(module, result):
contents = module.params['config']
if not contents:
contents = module.config.get_config(output='set').split('\n')
def do_lines(module, result):
commands = module.params['lines']
result.update(load_config(module, commands))
else:
contents = config_to_commands(contents)
return contents
def get_candidate(module):
contents = module.params['src'] or module.params['lines']
if module.params['lines']:
contents = '\n'.join(contents)
return config_to_commands(contents)
def diff_config(commands, config):
config = [str(c).replace("'", '') for c in config]
updates = list()
visited = set()
for line in commands:
item = str(line).replace("'", '')
if not item.startswith('set') and not item.startswith('delete'):
raise ValueError('line must start with either `set` or `delete`')
elif item.startswith('set') and item not in config:
updates.append(line)
elif item.startswith('delete'):
if not config:
updates.append(line)
else:
item = re.sub(r'delete', 'set', item)
for entry in config:
if entry.startswith(item) and line not in visited:
updates.append(line)
visited.add(line)
return list(updates)
def sanitize_config(config, result):
result['removed'] = list()
for regex in CONFIG_FILTERS:
for index, line in enumerate(list(config)):
if regex.search(line):
result['removed'].append(line)
del config[index]
def load_config(module, commands, result):
comment = module.params['comment']
commit = not module.check_mode
save = module.params['save']
# sanitize loadable config to remove items that will fail
# remove items will be returned in the sanitized keyword
# in the result.
sanitize_config(commands, result)
diff = module.config.load_config(commands, commit=commit, comment=comment,
save=save)
if diff:
result['diff'] = dict(prepared=diff)
result['changed'] = True
def do_src(module, result):
contents = module.params['src']
commands = config_to_commands(contents)
result.update(load_config(module, commands))
def present(module, result):
# get the current active config from the node or passed in via
# the config param
config = get_config(module, result)
# create the candidate config object from the arguments
candidate = get_candidate(module)
# create loadable config that includes only the configuration updates
updates = diff_config(candidate, config)
result['updates'] = updates
if module.params['update'] != 'check':
load_config(module, updates, result)
if result.get('removed'):
result['warnings'].append('Some configuration commands where '
'removed, please see the removed key')
def do_rollback(module, result):
rollback = 'rollback %s' % module.params['rollback']
prompt = re.compile('\[confirm\]')
cmd = Command(rollback, prompt=prompt, response='y', is_reboot=True, delay=1)
try:
module.cli(['configure', cmd])
except NetworkError:
exc = get_exception()
cmds = [str(c) for c in exc.kwargs.get('commands', list())]
module.fail_json(msg=str(exc), commands=cmds)
def absent(module, result):
if not module.check_mode:
module.cli('rm /config/config.boot')
result['changed'] = True
def main():
argument_spec = dict(
lines=dict(type='list'),
src=dict(type='path'),
rollback=dict(type='int'),
update_config=dict(type='bool', default=False),
backup_config=dict(type='bool', default=False)
match=dict(default='line', choices=['line', 'none']),
update=dict(default='merge', choices=['merge', 'check']),
backup=dict(default=False, type='bool'),
comment=dict(default=DEFAULT_COMMENT),
config=dict(),
save=dict(default=False, type='bool'),
state=dict(choices=['present', 'absent'], default='present')
)
argument_spec.update(vyos_argument_spec)
mutually_exclusive = [('lines', 'rollback'), ('lines', 'src'),
('src', 'rollback')]
mutually_exclusive = [('lines', 'src')]
module = NetworkModule(argument_spec=argument_spec,
connect_on_load=False,
mutually_exclusive=mutually_exclusive,
supports_check_mode=True)
module.check_mode = not module.params['update_config']
state = module.params['state']
result = dict(changed=False, warnings=list())
warnings = list()
check_args(module, warnings)
if module.params['backup_config']:
result['__backup__'] = module.cli('show configuration')[0]
result = dict(changed=False, warnings=warnings)
if module.params['lines']:
do_lines(module, result)
if module.params['backup']:
result['__backup__'] = module.config.get_config()
elif module.params['src']:
do_src(module, result)
elif module.params['rollback'] and not module.check_mode:
do_rollback(module, result)
if 'filtered' in result:
result['warnings'].append('Some configuration commands where '
'filtered, please see the filtered key')
result['connected'] = module.connected
try:
invoke(state, module, result)
except NetworkError:
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
module.exit_json(**result)