From 5f08277eb4c6015f18e737cf3ea8b79fab710db8 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Fri, 10 Jun 2016 15:45:30 -0400 Subject: [PATCH] refactor ios_config for network module This refactors the ios_config module to use the network module added in 2.2 to simplify common network functions new features * add src, dest arguments for working with config * results now return flag if the config was saved or not * adds append argument for updating the dest file (when dest is used) --- network/ios/ios_config.py | 177 ++++++++++++++++++++++++++------------ 1 file changed, 120 insertions(+), 57 deletions(-) diff --git a/network/ios/ios_config.py b/network/ios/ios_config.py index 915bec36168..aaa2671a5e2 100644 --- a/network/ios/ios_config.py +++ b/network/ios/ios_config.py @@ -36,7 +36,9 @@ 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 + aliases: ['commands'] parents: description: - The ordered set of parents that uniquely identify the section @@ -45,6 +47,40 @@ options: level or global commands. required: false 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 dir. This argument is mutually + exclusive with O(lines). + required: false + default: null + version_added: "2.2" + dest: + description: + - Configures a destination file write the source template or config + updates to. The path to the destination file can either be a full + path on the Ansible control host or a relative path from the + playbook or role root dir. This will, by default, overwrite any + previously created file. See O(append) to change the behavior. + - When the O(dest) argument is used, the output from processing the + configuration lines is written to a file and not to the actual + device. If the O(dest) argument is omitted, then the configuration + is written to the device. + required: false + default: null + version_added: "2.2" + append: + description: + - Changes the default behavior when writing the configuration out + to a remote file on disk. By defaul if O(dest) is specified, the + file is overridden. By setting this argument to true, the remote + file (if it exists) is appended to. + required: false + default: false + choices: ['yes', 'no'] + version_added: "2.2" before: description: - The ordered set of commands to push on to the command stack if @@ -68,11 +104,13 @@ options: 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(strict), command lines are matched with respect - to position. Finally if match is set to I(exact), command lines - must be an equal match. + to position. If match is set to I(exact), command lines + 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 default: line - choices: ['line', 'strict', 'exact'] + choices: ['line', 'strict', 'exact', 'none'] replace: description: - Instructs the module on the way to perform the configuration @@ -84,26 +122,16 @@ options: required: false default: line choices: ['line', 'block'] - force: + backup_config: description: - - The force argument instructs the module to not consider the - 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. + - 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: false - choices: ['yes', 'no'] - config: - description: - - The module, by default, will connect to the remote device and - retrieve the current running-config to use as a base for comparing - against the contents of source. There are times when it is not - desirable to have the task get the current running-config for - every task in a playbook. The I(config) argument allows the - implementer to pass in the configuruation to use as the base - config for comparision. - required: false - default: null + version_added: "2.2" """ EXAMPLES = """ @@ -153,72 +181,107 @@ responses: type: list sample: ['...', '...'] """ +from ansible.module_utils.netcfg import NetworkConfig, dumps +from ansible.module_utils.ios import NetworkModule +from ansible.module_utils.ios import load_config, get_config, ios_argument_spec -def get_config(module): - config = module.params['config'] or dict() - if not config and not module.params['force']: - config = module.config - return config +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 unneeded argument parents') + if module.params['match'] == 'none' and module.params['replace']: + warnings.append('ignorning unneeded argument replace') + if module.params['dest'] and module.params['save_config'] is True: + warnings.append('config will not be saved with dest argument used') + +def get_candidate(module): + candidate = NetworkConfig(indent=1) + if module.params['src']: + candidate = module.params['src'] + elif module.params['lines']: + parents = module.params['parents'] or list() + candidate.add(module.params['lines'], parents=parents) + return candidate def main(): 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'), + dest=dict(type='path'), + append=dict(type='bool', default=False), + 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']), - force=dict(default=False, type='bool'), - config=dict() + + backup_config=dict(type='bool', default=False) ) + argument_spec.update(ios_argument_spec) - module = get_module(argument_spec=argument_spec, - supports_check_mode=True) + 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() - before = module.params['before'] - after = module.params['after'] - match = module.params['match'] replace = module.params['replace'] - if not module.params['force']: - contents = get_config(module) - config = NetworkConfig(contents=contents, indent=1) + warnings = list() + invoke('check_args', module, warnings) - candidate = NetworkConfig(indent=1) - candidate.add(lines, parents=parents) + result = dict(changed=False, saved=False) - commands = candidate.difference(config, path=parents, match=match, replace=replace) + candidate = get_candidate(module) + + if module.params['match'] != 'none': + config = get_config(module) + configobjs = candidate.difference(config, match=match, replace=replace) else: - commands = parents - commands.extend(lines) + configobjs = candidate.items - result = dict(changed=False) + if module.params['backup_config']: + result['__backup__'] = module.cli('show running-config')[0] - if commands: - if before: + commands = list() + if configobjs: + commands = dumps(configobjs, 'commands') + + if module.params['before']: commands[:0] = before - if after: - commands.extend(after) + if module.params['after']: + commands.extend(module.params['after']) + + if not module.params['dest']: + response = load_config(module, commands, nodiff=True) + result.update(**response) + else: + result['__config__'] = dumps(configobjs, 'block') - if not module.check_mode: - commands = [str(c).strip() for c in commands] - response = module.configure(commands) - result['responses'] = response result['changed'] = True + if commands: + commands = commands.split('\n') + result['updates'] = commands + result['connected'] = module.connected + 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.ios import * + if __name__ == '__main__': main()