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

@ -21,219 +21,227 @@ 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:
description:
- Set to started or stopped. A state of started will
enable NXAPI access, and a state of stopped will
disable or shutdown all NXAPI access.
choices:
- started
- stopped
required: false
default: started
http_port: http_port:
description: description:
- Port on which the HTTP server will listen. - Configure the port with which the HTTP server will listen on
for requests. By default, NXAPI will bind the HTTP service
to the standard HTTP port 80. This argument accepts valid
port values in the range of 1 to 65535.
required: false required: false
default: 80 default: 80
https_port:
description:
- Port on which the HTTPS server will listen.
required: false
default: 443
http: http:
description: description:
- Enable/disable HTTP server. - Controls the operating state of the HTTP protocol as one of the
underlying transports for NXAPI. By default, NXAPI will enable
the HTTP transport when the feature is first configured. To
disable the use of the HTTP transport, set the value of this
argument to False.
required: false required: false
default: true default: yes
choices: choices: ['yes', 'no']
- true aliases: ['enable_http']
- false https_port:
aliases: description:
- enable_http - Configure the port with which the HTTPS server will listen on
for requests. By default, NXAPI will bind the HTTPS service
to the standard HTTPS port 443. This argument accepts valid
port values in the range of 1 to 65535.
required: false
default: 443
https: https:
description: description:
- Enable/disable HTTPS server. - Controls the operating state of the HTTPS protocol as one of the
underlying transports for NXAPI. By default, NXAPI will disable
the HTTPS transport when the feature is first configured. To
enable the use of the HTTPS transport, set the value of this
argument to True.
required: false required: false
choices: default: yes
- true choices: ['yes', 'no']
- false aliases: ['enable_https']
default: true
aliases:
- enable_https
sandbox: sandbox:
description: description:
- Enable/disable NXAPI web based UI for entering commands. - The NXAPI feature provides a web base UI for developers for
entering commands. This feature is initially disabled when
the NXAPI feature is configured for the first time. When the
C(sandbox) argument is set to True, the developer sandbox URL
will accept requests and when the value is set to False, the
sandbox URL is unavailable.
required: false required: false
default: true default: no
choices: choices: ['yes', 'no']
- true aliases: ['enable_sandbox']
- false config:
aliases: description:
- enable_sandbox - 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:
provider: {{ cli }}
- name: Enable NXAPI with no HTTP, HTTPS at port 9443 and sandbox disabled
nxos_nxapi: nxos_nxapi:
enable_http: false enable_http: false
https_port: 9443 https_port: 9443
https: yes
enable_sandbox: no enable_sandbox: no
provider: {{ provider }} provider: {{ cli }}
- name: shutdown NXAPI access - name: remove NXAPI configuration
nxos_nxapi: nxos_nxapi:
state: stopped state: absent
provider: {{ provider }} 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
sample: false
commands:
description:
- Set of commands to be executed on remote device. If run in check mode,
commands will not be executed.
returned: always returned: always
type: list type: list
sample: [ sample: ['no feature nxapi']
'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'):
module.configure(commands)
def get_nxapi_state(module):
features = module.execute(['show feature | grep nxapi'])[0]
if re.search('disabled', features) is None:
return 'started'
return 'stopped'
def config_server(module):
nxapi_state = get_nxapi_state(module)
config = dict()
if nxapi_state == 'started':
config = module.from_json(module.execute(['show nxapi | json'])[0])
state = module.params.get('state')
result = dict(changed=False, _config=config, commands=[])
commands = []
if config.get('nxapi_status', 'Disabled') == 'Disabled':
if state == 'started':
# enable nxapi and get the new default config
commands.append('feature nxapi') commands.append('feature nxapi')
result['_config'] = dict() setters = set()
result['changed'] = True for key, value in module.argument_spec.iteritems():
if module.params.get('check_mode'): setter = value.get('setter') or 'set_%s' % key
# make an assumption about default state if setter not in setters:
config['http_port'] = 80 setters.add(setter)
config['sandbox_status'] = 'Disabled' if module.params[key] is not None:
else: invoke(setter, module, commands)
# get the default config
execute_commands(module, commands) def absent(module, commands):
config = module.from_json(module.execute(['show nxapi | json'])[0])
else:
# nxapi already disabled
return result
elif config.get('nxapi_status', 'Disabled') == 'Enabled' and state == 'stopped':
# disable nxapi and exit
commands.append('no feature nxapi') commands.append('no feature nxapi')
result['changed'] = True
result['commands'] = commands
execute_commands(module, commands)
return result
# configure http and https def set_http(module, commands):
for protocol in ['http', 'https']: port = module.params['http_port']
cmds, chg = http_commands(protocol, module.params['{0}_port'.format(protocol)], if not 0 <= port <= 65535:
module.params[protocol], config) module.fail_json(msg='http_port must be between 1 and 65535')
if chg: elif module.params['http'] is True:
commands += cmds commands.append('nxapi http port %s' % port)
result['changed'] = True elif module.params['http'] is False:
commands.append('no nxapi http')
# configure sandbox def set_https(module, commands):
config_sandbox = config.get('sandbox_status', None) port = module.params['https_port']
enable_sandbox = module.params.get('sandbox') 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')
if config_sandbox is None: def set_sandbox(module, commands):
# there is no prior state, so we must set one if module.params['sandbox'] is True:
result['changed'] = True
if enable_sandbox:
commands.append('nxapi sandbox') commands.append('nxapi sandbox')
else: elif module.params['sandbox'] is False:
commands.append('no nxapi sandbox') commands.append('no nxapi sandbox')
else:
# there is a prior state, so be idempotent def get_config(module):
if config_sandbox == 'Enabled' and not enable_sandbox: contents = module.params['config']
# turn off sandbox if not contents:
commands.append('no nxapi sandbox') try:
result['changed'] = True contents = module.cli(['show running-config nxapi all'])[0]
elif config_sandbox == 'Disabled' and enable_sandbox: except NetworkError:
# turn on sandbox contents = None
commands.append('nxapi sandbox') config = NetworkConfig(indent=2)
if contents:
config.load(contents)
return config
def load_checkpoint(module, result):
try:
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)
def load_config(module, commands, result):
# create a config checkpoint
checkpoint = 'ansible_%s' % int(time.time())
module.cli(['checkpoint %s' % checkpoint], output='text')
result['__checkpoint__'] = checkpoint
# load the config into the device
module.config.load_config(commands)
# load was successfully, remove the config checkpoint
module.cli(['no checkpoint %s' % checkpoint])
def load(module, commands, result):
candidate = NetworkConfig(indent=2, contents='\n'.join(commands))
config = get_config(module)
configobjs = candidate.difference(config)
if configobjs:
commands = dumps(configobjs, 'commands').split('\n')
result['updates'] = commands
if not module.check_mode:
load_config(module, commands, result)
result['changed'] = True result['changed'] = True
if len(commands) > 0: def clean_result(result):
# something requires change # strip out any keys that have two leading and two trailing
result['commands'] = commands # underscore characters
execute_commands(module, commands) for key in result.keys():
if PRIVATE_KEYS_RE.match(key):
return result del result[key]
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,
connect_on_load=False,
supports_check_mode=True) 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()