Remove cliconf from httpapi connection (#46813)

* Bare minimum rip out cliconf

* nxapi changeover

* Update documentation, move options

* Memoize device_info

* Gratuitous rename to underscore use of local api implementation

Fixup eos module_utils like nxos

* Streamline version and image scans

* Expose get_capabilities through module_utils

* Add load_config to module_utils

* Support rpcs using both args and kwargs

* Add get_config for nxos

* Add get_diff

* module context, pulled from nxapi

We could probably do this correctly later

* Fix eos issues

* Limit connection._sub_plugin to only one plugin
This commit is contained in:
Nathaniel Case 2018-12-11 16:26:59 -05:00 committed by GitHub
parent 32dbb99bb8
commit 02432565cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 568 additions and 255 deletions

View file

@ -4,7 +4,19 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
---
author: Ansible Networking Team
httpapi: nxos
short_description: Use NX-API to run command on nxos platform
description:
- This eos plugin provides low level abstraction api's for
sending and receiving CLI commands with nxos network devices.
version_added: "2.6"
"""
import json
import re
from ansible.module_utils._text import to_text
from ansible.module_utils.connection import ConnectionError
@ -15,29 +27,18 @@ from ansible.utils.display import Display
display = Display()
OPTIONS = {
'format': ['text', 'json'],
'diff_match': ['line', 'strict', 'exact', 'none'],
'diff_replace': ['line', 'block', 'config'],
'output': ['text', 'json']
}
class HttpApi(HttpApiBase):
def _run_queue(self, queue, output):
if self._become:
display.vvvv('firing event: on_become')
queue.insert(0, 'enable')
request = request_builder(queue, output)
headers = {'Content-Type': 'application/json'}
response, response_data = self.connection.send('/ins', request, headers=headers, method='POST')
try:
response_data = json.loads(to_text(response_data.getvalue()))
except ValueError:
raise ConnectionError('Response was not valid JSON, got {0}'.format(
to_text(response_data.getvalue())
))
results = handle_response(response_data)
if self._become:
results = results[1:]
return results
def __init__(self, *args, **kwargs):
super(HttpApi, self).__init__(*args, **kwargs)
self._device_info = None
def send_request(self, data, **message_kwargs):
output = None
@ -72,46 +73,93 @@ class HttpApi(HttpApiBase):
return responses[0]
return responses
def edit_config(self, candidate=None, commit=True, replace=None, comment=None):
resp = list()
def _run_queue(self, queue, output):
if self._become:
display.vvvv('firing event: on_become')
queue.insert(0, 'enable')
operations = self.connection.get_device_operations()
self.connection.check_edit_config_capability(operations, candidate, commit, replace, comment)
request = request_builder(queue, output)
headers = {'Content-Type': 'application/json'}
if replace:
device_info = self.connection.get_device_info()
if '9K' not in device_info.get('network_os_platform', ''):
raise ConnectionError(msg=u'replace is supported only on Nexus 9K devices')
candidate = 'config replace {0}'.format(replace)
response, response_data = self.connection.send('/ins', request, headers=headers, method='POST')
responses = self.send_request(candidate, output='config')
for response in to_list(responses):
if response != '{}':
resp.append(response)
if not resp:
resp = ['']
return resp
def run_commands(self, commands, check_rc=True):
"""Runs list of commands on remote device and returns results
"""
try:
out = self.send_request(commands)
except ConnectionError as exc:
if check_rc is True:
raise
out = to_text(exc)
response_data = json.loads(to_text(response_data.getvalue()))
except ValueError:
raise ConnectionError('Response was not valid JSON, got {0}'.format(
to_text(response_data.getvalue())
))
out = to_list(out)
if not out[0]:
return out
results = handle_response(response_data)
for index, response in enumerate(out):
if response[0] == '{':
out[index] = json.loads(response)
if self._become:
results = results[1:]
return results
return out
def get_device_info(self):
if self._device_info:
return self._device_info
device_info = {}
device_info['network_os'] = 'nxos'
reply = self.send_request('show version')
platform_reply = self.send_request('show inventory')
find_os_version = [r'\s+system:\s+version\s*(\S+)', r'\s+kickstart:\s+version\s*(\S+)', r'\s+NXOS:\s+version\s*(\S+)']
for regex in find_os_version:
match_ver = re.search(regex, reply, re.M)
if match_ver:
device_info['network_os_version'] = match_ver.group(1)
break
match_chassis_id = re.search(r'Hardware\n\s+cisco\s*(\S+\s+\S+)', reply, re.M)
if match_chassis_id:
device_info['network_os_model'] = match_chassis_id.group(1)
match_host_name = re.search(r'\s+Device name:\s*(\S+)', reply, re.M)
if match_host_name:
device_info['network_os_hostname'] = match_host_name.group(1)
find_os_image = [r'\s+system image file is:\s*(\S+)', r'\s+kickstart image file is:\s*(\S+)', r'\s+NXOS image file is:\s*(\S+)']
for regex in find_os_image:
match_file_name = re.search(regex, reply, re.M)
if match_file_name:
device_info['network_os_image'] = match_file_name.group(1)
break
match_os_platform = re.search(r'NAME: "Chassis",\s*DESCR:.*\nPID:\s*(\S+)', platform_reply, re.M)
if match_os_platform:
device_info['network_os_platform'] = match_os_platform.group(1)
self._device_info = device_info
return self._device_info
def get_device_operations(self):
platform = self.get_device_info().get('network_os_platform', '')
return {
'supports_diff_replace': True,
'supports_commit': False,
'supports_rollback': False,
'supports_defaults': True,
'supports_onbox_diff': False,
'supports_commit_comment': False,
'supports_multiline_delimiter': False,
'supports_diff_match': True,
'supports_diff_ignore_lines': True,
'supports_generate_diff': True,
'supports_replace': True if '9K' in platform else False,
}
def get_capabilities(self):
result = {}
result['rpc'] = []
result['device_info'] = self.get_device_info()
result['device_operations'] = self.get_device_operations()
result.update(OPTIONS)
result['network_api'] = 'nxapi'
return json.dumps(result)
def handle_response(response):