From 64e139adb1476354be520d695fa99e50b58375fe Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Mon, 9 Nov 2015 09:18:38 -0500 Subject: [PATCH 1/3] add initial support for Cisco NXAPI This commit adds the shared module support for Cisco NXAPI. The shared module builds on top of the urls shared module. The urls module provides the http/s transport. This module only supports the JSON request message format. --- lib/ansible/module_utils/nxapi.py | 127 ++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 lib/ansible/module_utils/nxapi.py diff --git a/lib/ansible/module_utils/nxapi.py b/lib/ansible/module_utils/nxapi.py new file mode 100644 index 00000000000..150620d65ff --- /dev/null +++ b/lib/ansible/module_utils/nxapi.py @@ -0,0 +1,127 @@ +# +# (c) 2015 Peter Sprygada, +# +# This file is part of Ansible +# +# Ansible is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Ansible is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Ansible. If not, see . +# +""" +This module adds support for Cisco NXAPI to Ansible shared +module_utils. It builds on module_utils/urls.py to provide +NXAPI support over HTTP/S which is required for proper operation. + +In order to use this module, include it as part of a custom +module as shown below. + +** Note: The order of the import statements does matter. ** + +from ansible.module_utils.basic import * +from ansible.module_utils.urls import * +from ansible.module_utils.nxapi import * + +The nxapi module provides the following common argument spec: + + * host (str) - [Required] The IPv4 address or FQDN of the network device + + * port (str) - Overrides the default port to use for the HTTP/S + connection. The default values are 80 for HTTP and + 443 for HTTPS + + * url_username (str) - [Required] The username to use to authenticate + the HTTP/S connection. Aliases: username + + * url_password (str) - [Required] The password to use to authenticate + the HTTP/S connection. Aliases: password + + * use_ssl (bool) - Specifies whether or not to use an encrypted (HTTPS) + connection or not. The default value is False. + + * command_type (str) - The type of command to send to the remote + device. Valid values in `cli_show`, `cli_show_ascii`, 'cli_conf` + and `bash`. The default value is `cli_show_ascii` + +In order to communicate with Cisco NXOS devices, the NXAPI feature +must be enabled and configured on the device. + +""" + +NXAPI_COMMAND_TYPES = ['cli_show', 'cli_show_ascii', 'cli_conf', 'bash'] + +def nxapi_argument_spec(spec=None): + """Creates an argument spec for working with NXAPI + """ + arg_spec = url_argument_spec() + arg_spec.update(dict( + host=dict(required=True), + port=dict(), + url_username=dict(required=True, aliases=['username']), + url_password=dict(required=False, aliases=['password']), + use_ssl=dict(default=False, type='bool'), + command_type=dict(default='cli_show_ascii', choices=NXAPI_COMMAND_TYPES) + )) + if spec: + arg_spec.update(spec) + return arg_spec + +def nxapi_url(module): + """Constructs a valid NXAPI url + """ + proto = 'https' if module.params['use_ssl'] else 'http' + host = module.params['host'] + url = '{}://{}'.format(proto, host) + port = module.params['port'] + if module.params['port']: + url = '{}:{}'.format(url, module.params['port']) + url = '{}/ins'.format(url) + return url + +def nxapi_body(commands, command_type, **kwargs): + """Encodes a NXAPI JSON request message + """ + if isinstance(commands, (list, set, tuple)): + commands = ' ;'.join(commands) + + msg = { + 'version': kwargs.get('version') or '1.2', + 'type': command_type, + 'chunk': kwargs.get('chunk') or '0', + 'sid': kwargs.get('sid'), + 'input': commands, + 'output_format': 'json' + } + + return dict(ins_api=msg) + +def nxapi_command(module, commands, command_type=None, **kwargs): + """Sends the list of commands to the device over NXAPI + """ + url = nxapi_url(module) + + command_type = command_type or module.params['command_type'] + + data = nxapi_body(commands, command_type) + data = module.jsonify(data) + + headers = {'Content-Type': 'text/json'} + + response, headers = fetch_url(module, url, data=data, headers=headers, + method='POST') + + status = kwargs.get('status') or 200 + if headers['status'] != status: + module.fail_json(**headers) + + response = module.from_json(response.read()) + return response, headers + From d3d36eb7660ce27fc76c94df1f8d468c5e0aafa7 Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Mon, 9 Nov 2015 09:36:17 -0500 Subject: [PATCH 2/3] fixes conditional statement for py24 compatibility --- lib/ansible/module_utils/nxapi.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/ansible/module_utils/nxapi.py b/lib/ansible/module_utils/nxapi.py index 150620d65ff..04499c8a3ea 100644 --- a/lib/ansible/module_utils/nxapi.py +++ b/lib/ansible/module_utils/nxapi.py @@ -77,7 +77,10 @@ def nxapi_argument_spec(spec=None): def nxapi_url(module): """Constructs a valid NXAPI url """ - proto = 'https' if module.params['use_ssl'] else 'http' + if module.params['use_ssl']: + proto = 'https' + else: + proto = 'http' host = module.params['host'] url = '{}://{}'.format(proto, host) port = module.params['port'] From f88273eb87ca54cc305f8356d13fd91352d27d3f Mon Sep 17 00:00:00 2001 From: Peter Sprygada Date: Mon, 9 Nov 2015 09:45:26 -0500 Subject: [PATCH 3/3] changes the nxapi argument spec to require url_password --- lib/ansible/module_utils/nxapi.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ansible/module_utils/nxapi.py b/lib/ansible/module_utils/nxapi.py index 04499c8a3ea..0589b9a50c3 100644 --- a/lib/ansible/module_utils/nxapi.py +++ b/lib/ansible/module_utils/nxapi.py @@ -66,7 +66,7 @@ def nxapi_argument_spec(spec=None): host=dict(required=True), port=dict(), url_username=dict(required=True, aliases=['username']), - url_password=dict(required=False, aliases=['password']), + url_password=dict(required=True, aliases=['password']), use_ssl=dict(default=False, type='bool'), command_type=dict(default='cli_show_ascii', choices=NXAPI_COMMAND_TYPES) ))