update nxos_nxapi module with minor enhancements (#4573)

* added new config argument
* added states present and absent
* update to use network shared modules

Tested on NXOS 7.3(0)D1(1)
This commit is contained in:
Peter Sprygada 2016-08-30 15:05:18 -04:00 committed by Matt Clay
parent 5b00b40c22
commit 7280b2642a

View file

@ -19,221 +19,229 @@
DOCUMENTATION = """ DOCUMENTATION = """
--- ---
module: nxos_nxapi module: nxos_nxapi
version_added: "2.1" version_added: "2.1"
author: "Chris Houseknecht (@chouseknecht)" author: "Peter Sprygada (@privateip)"
short_description: Manage NXAPI configuration on an NXOS device. short_description: Manage NXAPI configuration on an NXOS device.
description: description:
- Use to enable or disable NXAPI access, set the port and state - Configures the NXAPI feature on devices running Cisco NXOS. The
of http and https servers, and enable or disable the sandbox. NXAPI feature is absent from the configuration by default. Since
- When enabling NXAPI access the default is to enable HTTP on port this module manages the NXAPI feature it only supports the use
80, enable HTTPS on port 443, and enable the web based UI sandbox. of the C(Cli) transport.
Use the options below to override the default configuration.
extends_documentation_fragment: nxos extends_documentation_fragment: nxos
options: options:
state: http_port:
description: description:
- Set to started or stopped. A state of started will - Configure the port with which the HTTP server will listen on
enable NXAPI access, and a state of stopped will for requests. By default, NXAPI will bind the HTTP service
disable or shutdown all NXAPI access. to the standard HTTP port 80. This argument accepts valid
choices: port values in the range of 1 to 65535.
- started required: false
- stopped default: 80
required: false http:
default: started description:
http_port: - Controls the operating state of the HTTP protocol as one of the
description: underlying transports for NXAPI. By default, NXAPI will enable
- Port on which the HTTP server will listen. the HTTP transport when the feature is first configured. To
required: false disable the use of the HTTP transport, set the value of this
default: 80 argument to False.
https_port: required: false
description: default: yes
- Port on which the HTTPS server will listen. choices: ['yes', 'no']
required: false aliases: ['enable_http']
default: 443 https_port:
http: description:
description: - Configure the port with which the HTTPS server will listen on
- Enable/disable HTTP server. for requests. By default, NXAPI will bind the HTTPS service
required: false to the standard HTTPS port 443. This argument accepts valid
default: true port values in the range of 1 to 65535.
choices: required: false
- true default: 443
- false https:
aliases: description:
- enable_http - Controls the operating state of the HTTPS protocol as one of the
https: underlying transports for NXAPI. By default, NXAPI will disable
description: the HTTPS transport when the feature is first configured. To
- Enable/disable HTTPS server. enable the use of the HTTPS transport, set the value of this
required: false argument to True.
choices: required: false
- true default: yes
- false choices: ['yes', 'no']
default: true aliases: ['enable_https']
aliases: sandbox:
- enable_https description:
sandbox: - The NXAPI feature provides a web base UI for developers for
description: entering commands. This feature is initially disabled when
- Enable/disable NXAPI web based UI for entering commands. the NXAPI feature is configured for the first time. When the
required: false C(sandbox) argument is set to True, the developer sandbox URL
default: true will accept requests and when the value is set to False, the
choices: sandbox URL is unavailable.
- true required: false
- false default: no
aliases: choices: ['yes', 'no']
- enable_sandbox aliases: ['enable_sandbox']
config:
description:
- The C(config) argument provides an optional argument to
specify the device running-config to used as the basis for
configuring the remote system. The C(config) argument accepts
a string value that represents the device configuration.
required: false
default: null
version_added: "2.2"
state:
description:
- The C(state) argument controls whether or not the NXAPI
feature is configured on the remote device. When the value
is C(present) the NXAPI feature configuration is present in
the device running-config. When the values is C(absent) the
feature configuration is removed from the running-config.
choices: ['present', 'absent']
required: false
default: present
""" """
EXAMPLES = """ EXAMPLES = """
- name: Enable NXAPI access with default configuration # Note: examples below use the following provider dict to handle
nxos_nxapi: # transport and authentication to the node.
provider: {{ provider }} vars:
cli:
host: "{{ inventory_hostname }}"
username: admin
password: admin
- name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled - name: Enable NXAPI access with default configuration
nxos_nxapi: nxos_nxapi:
enable_http: false provider: {{ cli }}
https_port: 9443
enable_sandbox: no
provider: {{ provider }}
- name: shutdown NXAPI access - name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled
nxos_nxapi: nxos_nxapi:
state: stopped enable_http: false
provider: {{ provider }} https_port: 9443
https: yes
enable_sandbox: no
provider: {{ cli }}
- name: remove NXAPI configuration
nxos_nxapi:
state: absent
provider: {{ cli }}
""" """
RETURN = """ RETURN = """
changed: updates:
description: description:
- Indicates if commands were sent to the device. - Returns the list of commands that need to be pushed into the remote
returned: always device to satisfy the arguments
type: boolean returned: always
sample: false type: list
sample: ['no feature nxapi']
commands:
description:
- Set of commands to be executed on remote device. If run in check mode,
commands will not be executed.
returned: always
type: list
sample: [
'nxapi feature',
'nxapi http port 8080'
]
_config:
description:
- Configuration found on the device prior ro any commands being executed.
returned: always
type: object
sample: {...}
""" """
import re
import time
from ansible.module_utils.netcfg import NetworkConfig, dumps
from ansible.module_utils.nxos import NetworkModule, NetworkError
from ansible.module_utils.basic import get_exception
def http_commands(protocol, port, enable, config): PRIVATE_KEYS_RE = re.compile('__.+__')
port_config = config.get('{0}_port'.format(protocol), None)
changed = False
commands = []
if port_config is None and enable:
# enable
changed = True
commands.append('nxapi {0} port {1}'.format(protocol, port))
elif port_config is not None:
if not enable:
# disable
commands.append('no nxapi {0}'.format(protocol))
changed = True
elif port_config != port:
# update port
commands.append('nxapi {0} port {1}'.format(protocol, port))
changed = True
return commands, changed
def invoke(name, *args, **kwargs):
func = globals().get(name)
if func:
return func(*args, **kwargs)
def execute_commands(module, commands): def present(module, commands):
if not module.params.get('check_mode'): commands.append('feature nxapi')
module.configure(commands) setters = set()
for key, value in module.argument_spec.iteritems():
setter = value.get('setter') or 'set_%s' % key
if setter not in setters:
setters.add(setter)
if module.params[key] is not None:
invoke(setter, module, commands)
def absent(module, commands):
commands.append('no feature nxapi')
def get_nxapi_state(module): def set_http(module, commands):
features = module.execute(['show feature | grep nxapi'])[0] port = module.params['http_port']
if re.search('disabled', features) is None: if not 0 <= port <= 65535:
return 'started' module.fail_json(msg='http_port must be between 1 and 65535')
return 'stopped' elif module.params['http'] is True:
commands.append('nxapi http port %s' % port)
elif module.params['http'] is False:
commands.append('no nxapi http')
def set_https(module, commands):
port = module.params['https_port']
if not 0 <= port <= 65535:
module.fail_json(msg='https_port must be between 1 and 65535')
elif module.params['https'] is True:
commands.append('nxapi https port %s' % port)
elif module.params['https'] is False:
commands.append('no nxapi https')
def config_server(module): def set_sandbox(module, commands):
if module.params['sandbox'] is True:
commands.append('nxapi sandbox')
elif module.params['sandbox'] is False:
commands.append('no nxapi sandbox')
nxapi_state = get_nxapi_state(module) def get_config(module):
contents = module.params['config']
if not contents:
try:
contents = module.cli(['show running-config nxapi all'])[0]
except NetworkError:
contents = None
config = NetworkConfig(indent=2)
if contents:
config.load(contents)
return config
config = dict() def load_checkpoint(module, result):
if nxapi_state == 'started': try:
config = module.from_json(module.execute(['show nxapi | json'])[0]) checkpoint = result['__checkpoint__']
module.cli(['rollback running-config checkpoint %s' % checkpoint,
'no checkpoint %s' % checkpoint], output='text')
except KeyError:
module.fail_json(msg='unable to rollback, checkpoint not found')
except NetworkError:
exc = get_exception()
msg = 'unable to rollback configuration'
module.fail_json(msg=msg, checkpoint=checkpoint, **exc.kwargs)
state = module.params.get('state') def load_config(module, commands, result):
result = dict(changed=False, _config=config, commands=[]) # create a config checkpoint
commands = [] checkpoint = 'ansible_%s' % int(time.time())
module.cli(['checkpoint %s' % checkpoint], output='text')
result['__checkpoint__'] = checkpoint
if config.get('nxapi_status', 'Disabled') == 'Disabled': # load the config into the device
if state == 'started': module.config.load_config(commands)
# enable nxapi and get the new default config
commands.append('feature nxapi') # load was successfully, remove the config checkpoint
result['_config'] = dict() module.cli(['no checkpoint %s' % checkpoint])
result['changed'] = True
if module.params.get('check_mode'): def load(module, commands, result):
# make an assumption about default state candidate = NetworkConfig(indent=2, contents='\n'.join(commands))
config['http_port'] = 80 config = get_config(module)
config['sandbox_status'] = 'Disabled' configobjs = candidate.difference(config)
else:
# get the default config if configobjs:
execute_commands(module, commands) commands = dumps(configobjs, 'commands').split('\n')
config = module.from_json(module.execute(['show nxapi | json'])[0]) result['updates'] = commands
else: if not module.check_mode:
# nxapi already disabled load_config(module, commands, result)
return result
elif config.get('nxapi_status', 'Disabled') == 'Enabled' and state == 'stopped':
# disable nxapi and exit
commands.append('no feature nxapi')
result['changed'] = True result['changed'] = True
result['commands'] = commands
execute_commands(module, commands)
return result
# configure http and https def clean_result(result):
for protocol in ['http', 'https']: # strip out any keys that have two leading and two trailing
cmds, chg = http_commands(protocol, module.params['{0}_port'.format(protocol)], # underscore characters
module.params[protocol], config) for key in result.keys():
if chg: if PRIVATE_KEYS_RE.match(key):
commands += cmds del result[key]
result['changed'] = True
# configure sandbox
config_sandbox = config.get('sandbox_status', None)
enable_sandbox = module.params.get('sandbox')
if config_sandbox is None:
# there is no prior state, so we must set one
result['changed'] = True
if enable_sandbox:
commands.append('nxapi sandbox')
else:
commands.append('no nxapi sandbox')
else:
# there is a prior state, so be idempotent
if config_sandbox == 'Enabled' and not enable_sandbox:
# turn off sandbox
commands.append('no nxapi sandbox')
result['changed'] = True
elif config_sandbox == 'Disabled' and enable_sandbox:
# turn on sandbox
commands.append('nxapi sandbox')
result['changed'] = True
if len(commands) > 0:
# something requires change
result['commands'] = commands
execute_commands(module, commands)
return result
def main(): def main():
@ -241,28 +249,56 @@ def main():
""" """
argument_spec = dict( argument_spec = dict(
state=dict(default='started', choices=['started', 'stopped']), http=dict(aliases=['enable_http'], default=True, type='bool', setter='set_http'),
http_port=dict(default=80, type='int'), http_port=dict(default=80, type='int', setter='set_http'),
https_port=dict(default=443, type='int'),
http=dict(aliases=['enable_http'], default=True, type='bool'), https=dict(aliases=['enable_https'], default=False, type='bool', setter='set_https'),
https=dict(aliases=['enable_https'], default=True, type='bool'), https_port=dict(default=443, type='int', setter='set_https'),
sandbox=dict(aliases=['enable_sandbox'], default=True, type='bool'),
sandbox=dict(aliases=['enable_sandbox'], default=False, type='bool'),
# Only allow configuration of NXAPI using cli transpsort # Only allow configuration of NXAPI using cli transpsort
transport=dict(required=True, choices=['cli']) transport=dict(required=True, choices=['cli']),
config=dict(),
# Support for started and stopped is for backwards capability only and
# will be removed in a future version
state=dict(default='present', choices=['started', 'stopped', 'present', 'absent'])
) )
module = get_module(argument_spec=argument_spec, module = NetworkModule(argument_spec=argument_spec,
supports_check_mode=True) connect_on_load=False,
supports_check_mode=True)
result = config_server(module) state = module.params['state']
return module.exit_json(**result) warnings = list()
result = dict(changed=False, warnings=warnings)
if state == 'started':
state = 'present'
warnings.append('state=started is deprecated and will be removed in a '
'a future release. Please use state=present instead')
elif state == 'stopped':
state = 'absent'
warnings.append('state=stopped is deprecated and will be removed in a '
'a future release. Please use state=absent instead')
commands = list()
invoke(state, module, commands)
try:
load(module, commands, result)
except (ValueError, NetworkError):
load_checkpoint(module, result)
exc = get_exception()
module.fail_json(msg=str(exc), **exc.kwargs)
clean_result(result)
module.exit_json(**result)
from ansible.module_utils.basic import *
from ansible.module_utils.shell import *
from ansible.module_utils.nxos import *
if __name__ == '__main__': if __name__ == '__main__':
main() main()