merge functions from ios into ios_config

* merge changes from ios shared module functions into ios_config.
* 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 or check
* add backup argument to backup current running config to control host
* add defaults argument to control collection of config with or withoutdefaults
* 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
pull/18777/head
Peter Sprygada 8 years ago committed by Matt Clay
parent aa79cdb7d9
commit 525cd8b947

@ -57,30 +57,6 @@ options:
required: false required: false
default: null default: null
version_added: "2.2" version_added: "2.2"
dest:
description:
- Configures a destination file to 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 directory. This will, by default, overwrite any
previously created file. See I(append) to change the behavior.
- When the I(dest) argument is used, the output from processing the
configuration lines is written to a file and not to the actual
device. If theI(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 default if I(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: no
choices: ['yes', 'no']
version_added: "2.2"
before: before:
description: description:
- The ordered set of commands to push on to the command stack if - The ordered set of commands to push on to the command stack if
@ -122,17 +98,44 @@ options:
required: false required: false
default: line default: line
choices: ['line', 'block'] choices: ['line', 'block']
update_config: force:
description: description:
- This arugment will either cause or prevent the changed commands - The force argument instructs the module to not consider the
from being sent to the remote device. The set to true, the current devices running-config. When set to true, this will
remote IOS device will be configured with the updated commands cause the module to push the contents of I(src) into the device
and when set to false, the remote device will not be updated. 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 required: false
default: yes default: false
choices: ['yes', 'no'] choices: [ "true", "false" ]
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(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', 'check']
version_added: "2.2" version_added: "2.2"
backup_config: commit:
description:
- This argument specifies the update method to use when applying the
configuration changes to the remote node. If the value is set to
I(merge) the configuration updates are merged with the running-
config. If the value is set to I(check), no changes are made to
the remote host.
required: false
default: merge
choices: ['merge', 'check']
version_added: "2.2"
backup:
description: description:
- This argument will cause the module to create a full backup of - This argument will cause the module to create a full backup of
the current C(running-config) from the remote device before any the current C(running-config) from the remote device before any
@ -143,39 +146,80 @@ options:
default: no default: no
choices: ['yes', 'no'] choices: ['yes', 'no']
version_added: "2.2" version_added: "2.2"
config:
description:
- The C(config) argument allows the playbook desginer to supply
the base configuration to be used to validate configuration
changes necessary. If this argument is provided, the module
will not download the running-config from the remote node.
required: false
default: null
version_added: "2.2"
default:
description:
- This argument specifies whether or not to collect all defaults
when getting the remote device running config. When enabled,
the module will get the current config by issuing the command
C(show running-config all).
required: false
default: no
choices: ['yes', 'no']
version_added: "2.2"
save:
description:
- The C(save) argument instructs the module to save the running-
config to the startup-config at the conclusion of the module
running. If check mode is specified, this argument is ignored.
required: false
default: no
choices: ['yes', 'no']
version_added: "2.2"
state:
description:
- This argument specifies whether or not the running-config is
present on the remote device. When set to I(absent) the
running-config on the remote device is erased.
required: false
default: no
choices: ['yes', 'no']
version_added: "2.2"
""" """
EXAMPLES = """ EXAMPLES = """
- ios_config: # Note: examples below use the following provider dict to handle
lines: ['hostname {{ inventory_hostname }}'] # transport and authentication to the node.
force: yes vars:
cli:
host: "{{ inventory_hostname }}"
username: cisco
password: cisco
transport: cli
- name: configure top level configuration
ios_config:
lines: hostname {{ inventory_hostname }}
provider: "{{ cli }}"
- name: configure interface settings
ios_config:
lines:
- description test interface
- ip address 172.31.1.1 255.255.255.0
parents: interface Ethernet1
provider: "{{ cli }}"
- ios_config: - name: load new acl into device
ios_config:
lines: lines:
- 10 permit ip host 1.1.1.1 any log - 10 permit ip host 1.1.1.1 any log
- 20 permit ip host 2.2.2.2 any log - 20 permit ip host 2.2.2.2 any log
- 30 permit ip host 3.3.3.3 any log - 30 permit ip host 3.3.3.3 any log
- 40 permit ip host 4.4.4.4 any log - 40 permit ip host 4.4.4.4 any log
- 50 permit ip host 5.5.5.5 any log - 50 permit ip host 5.5.5.5 any log
parents: ['ip access-list extended test'] parents: ip access-list extended test
before: ['no ip access-list extended test'] before: no ip access-list extended test
match: exact match: exact
provider: "{{ cli }}"
- ios_config:
lines:
- 10 permit ip host 1.1.1.1 any log
- 20 permit ip host 2.2.2.2 any log
- 30 permit ip host 3.3.3.3 any log
- 40 permit ip host 4.4.4.4 any log
parents: ['ip access-list extended test']
before: ['no ip access-list extended test']
replace: block
- ios_config:
commands: "{{lookup('file', 'datcenter1.txt')}}"
parents: ['ip access-list test']
before: ['no ip access-list test']
replace: block
""" """
@ -185,16 +229,23 @@ updates:
returned: always returned: always
type: list type: list
sample: ['...', '...'] sample: ['...', '...']
backup_path:
description: The full path to the backup file
returned: when backup is yes
type: path
sample: /playbooks/ansible/backup/ios_config.2016-07-16@22:28:34
responses: responses:
description: The set of responses from issuing the commands on the device description: The set of responses from issuing the commands on the device
returned: when not check_mode returned: when not check_mode
type: list type: list
sample: ['...', '...'] sample: ['...', '...']
""" """
import re
from ansible.module_utils.basic import get_exception
from ansible.module_utils.ios import NetworkModule, NetworkError
from ansible.module_utils.netcfg import NetworkConfig, dumps from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.network import NetworkModule from ansible.module_utils.netcli import Command
from ansible.module_utils.ios import load_config, get_config, ios_argument_spec
def invoke(name, *args, **kwargs): def invoke(name, *args, **kwargs):
func = globals().get(name) func = globals().get(name)
@ -204,11 +255,28 @@ def invoke(name, *args, **kwargs):
def check_args(module, warnings): def check_args(module, warnings):
if module.params['parents']: if module.params['parents']:
if not module.params['lines'] or module.params['src']: if not module.params['lines'] or module.params['src']:
warnings.append('ignoring unneeded argument parents') warnings.append('ignoring unnecessary argument parents')
if module.params['match'] == 'none' and module.params['replace']: if module.params['match'] == 'none' and module.params['replace']:
warnings.append('ignorning unneeded argument replace') warnings.append('ignorning unnecessary argument replace')
if module.params['dest'] and module.params['save_config'] is True: if module.params['force']:
warnings.append('config will not be saved with dest argument used') warnings.append('The force argument is deprecated, please use '
'match=none instead. This argument will be '
'removed in the future')
def get_config(module, result):
defaults = module.params['default']
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=1, contents=contents)
def get_candidate(module): def get_candidate(module):
candidate = NetworkConfig(indent=1) candidate = NetworkConfig(indent=1)
@ -219,6 +287,65 @@ def get_candidate(module):
candidate.add(module.params['lines'], parents=parents) candidate.add(module.params['lines'], parents=parents)
return candidate return candidate
def load_backup(module):
try:
module.cli(['exit', 'config replace flash:/ansible-rollback force'])
except NetworkError:
module.fail_json(msg='unable to rollback configuration')
def backup_config(module):
cmd = 'copy running-config flash:/ansible-rollback'
cmd = Command(cmd, prompt=re.compile('\? $'), response='\n')
module.cli(cmd)
def load_config(module, commands, result):
if not module.check_mode and module.params['update'] != 'check':
module.config(commands)
result['changed'] = module.params['update'] != 'check'
result['updates'] = commands.split('\n')
def present(module, result):
match = module.params['match']
replace = module.params['replace']
candidate = get_candidate(module)
if match != 'none':
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')
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']:
commands.extend(module.params['after'])
# create a backup copy of the current running-config on
# device flash drive
backup_config(module)
# send the configuration commands to the device and merge
# them with the current running config
load_config(module, commands, result)
# remove the backup copy of the running-config since its
# no longer needed
module.cli('delete /force flash:/ansible-rollback')
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(): def main():
argument_spec = dict( argument_spec = dict(
@ -233,10 +360,20 @@ def main():
match=dict(default='line', choices=['line', 'strict', 'exact', 'none']), match=dict(default='line', choices=['line', 'strict', 'exact', 'none']),
replace=dict(default='line', choices=['line', 'block']), replace=dict(default='line', choices=['line', 'block']),
update_config=dict(type='bool', default=False), # this argument is deprecated in favor of setting match: none
backup_config=dict(type='bool', default=False) # it will be removed in a future version
force=dict(default=False, type='bool'),
update=dict(choices=['merge', 'check'], default='merge'),
backup=dict(type='bool', default=False),
config=dict(),
default=dict(type='bool', default=False),
save=dict(type='bool', default=False),
state=dict(choices=['present', 'absent'], default='present')
) )
argument_spec.update(ios_argument_spec)
mutually_exclusive = [('lines', 'src')] mutually_exclusive = [('lines', 'src')]
@ -245,48 +382,25 @@ def main():
mutually_exclusive=mutually_exclusive, mutually_exclusive=mutually_exclusive,
supports_check_mode=True) supports_check_mode=True)
module.check_mode = not module.params['update_config'] state = module.params['state']
parents = module.params['parents'] or list() if module.params['force'] is True:
module.params['match'] = 'none'
match = module.params['match']
replace = module.params['replace']
warnings = list() warnings = list()
invoke('check_args', module, warnings) check_args(module, warnings)
result = dict(changed=False, saved=False)
candidate = get_candidate(module)
if module.params['match'] != 'none':
config = get_config(module)
configobjs = candidate.difference(config, match=match, replace=replace)
else:
configobjs = candidate.items
if module.params['backup_config']:
result['__backup__'] = module.cli('show running-config')[0]
commands = list() result = dict(changed=False, warnings=warnings)
if configobjs:
commands = dumps(configobjs, 'commands')
commands = commands.split('\n')
if module.params['before']:
commands[:0] = module.params['before']
if module.params['after']: if module.params['backup']:
commands.extend(module.params['after']) result['__backup__'] = module.config.get_config()
if not module.check_mode:
response = load_config(module, commands, nodiff=True)
result.update(**response)
result['changed'] = True
result['updates'] = commands try:
result['connected'] = module.connected invoke(state, module, result)
except NetworkError:
load_backup(module)
exc = get_exception()
module.fail_json(msg=str(exc))
module.exit_json(**result) module.exit_json(**result)

Loading…
Cancel
Save