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,8 +4,29 @@
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
DOCUMENTATION = """
---
author: Ansible Networking Team
httpapi: eos
short_description: Use eAPI to run command on eos platform
description:
- This eos plugin provides low level abstraction api's for
sending and receiving CLI commands with eos network devices.
version_added: "2.6"
options:
eos_use_sessions:
type: int
default: 1
description:
- Specifies if sessions should be used on remote host or not
env:
- name: ANSIBLE_EOS_USE_SESSIONS
vars:
- name: ansible_eos_use_sessions
version_added: '2.8'
"""
import json
import time
from ansible.module_utils._text import to_text
from ansible.module_utils.connection import ConnectionError
@ -16,7 +37,39 @@ 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 __init__(self, *args, **kwargs):
super(HttpApi, self).__init__(*args, **kwargs)
self._device_info = None
self._session_support = None
@property
def supports_sessions(self):
use_session = self.get_option('eos_use_sessions')
try:
use_session = int(use_session)
except ValueError:
pass
if not bool(use_session):
self._session_support = False
else:
if self._session_support:
return self._session_support
response = self.send_request('show configuration sessions')
self._session_support = 'error' not in response
return self._session_support
def send_request(self, data, **message_kwargs):
data = to_list(data)
if self._become:
@ -45,117 +98,51 @@ class HttpApi(HttpApiBase):
return results
def get_prompt(self):
# Fake a prompt for @enable_mode
if self._become:
return '#'
return '>'
def get_device_info(self):
if self._device_info:
return self._device_info
# Imported from module_utils
def edit_config(self, config, commit=False, replace=False):
"""Loads the configuration onto the remote devices
device_info = {}
If the device doesn't support configuration sessions, this will
fallback to using configure() to load the commands. If that happens,
there will be no returned diff or session values
"""
session = 'ansible_%s' % int(time.time())
result = {'session': session}
banner_cmd = None
banner_input = []
device_info['network_os'] = 'eos'
reply = self.send_request('show version | json')
data = json.loads(reply)
commands = ['configure session %s' % session]
if replace:
commands.append('rollback clean-config')
device_info['network_os_version'] = data['version']
device_info['network_os_model'] = data['modelName']
for command in config:
if command.startswith('banner'):
banner_cmd = command
banner_input = []
elif banner_cmd:
if command == 'EOF':
command = {'cmd': banner_cmd, 'input': '\n'.join(banner_input)}
banner_cmd = None
commands.append(command)
else:
banner_input.append(command)
continue
else:
commands.append(command)
reply = self.send_request('show hostname | json')
data = json.loads(reply)
try:
response = self.send_request(commands)
except Exception:
commands = ['configure session %s' % session, 'abort']
response = self.send_request(commands, output='text')
raise
device_info['network_os_hostname'] = data['hostname']
commands = ['configure session %s' % session, 'show session-config diffs']
if commit:
commands.append('commit')
else:
commands.append('abort')
self._device_info = device_info
return self._device_info
response = self.send_request(commands, output='text')
diff = response[1].strip()
if diff:
result['diff'] = diff
def get_device_operations(self):
return {
'supports_diff_replace': True,
'supports_commit': bool(self.supports_sessions),
'supports_rollback': False,
'supports_defaults': False,
'supports_onbox_diff': bool(self.supports_sessions),
'supports_commit_comment': False,
'supports_multiline_delimiter': False,
'supports_diff_match': True,
'supports_diff_ignore_lines': True,
'supports_generate_diff': not bool(self.supports_sessions),
'supports_replace': bool(self.supports_sessions),
}
return result
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'] = 'eapi'
def run_commands(self, commands, check_rc=True):
"""Runs list of commands on remote device and returns results
"""
output = None
queue = list()
responses = list()
def run_queue(queue, output):
try:
response = to_list(self.send_request(queue, output=output))
except Exception as exc:
if check_rc:
raise
return to_text(exc)
if output == 'json':
response = [json.loads(item) for item in response]
return response
for item in to_list(commands):
cmd_output = 'text'
if isinstance(item, dict):
command = item['command']
if 'output' in item:
cmd_output = item['output']
else:
command = item
# Emulate '| json' from CLI
if command.endswith('| json'):
command = command.rsplit('|', 1)[0]
cmd_output = 'json'
if output and output != cmd_output:
responses.extend(run_queue(queue, output))
queue = list()
output = cmd_output
queue.append(command)
if queue:
responses.extend(run_queue(queue, output))
return responses
def load_config(self, config, commit=False, replace=False):
"""Loads the configuration onto the remote devices
If the device doesn't support configuration sessions, this will
fallback to using configure() to load the commands. If that happens,
there will be no returned diff or session values
"""
return self.edit_config(config, commit, replace)
return json.dumps(result)
def handle_response(response):
@ -170,6 +157,7 @@ def handle_response(response):
raise ConnectionError(error_text, code=error['code'])
results = []
for result in response['result']:
if 'messages' in result:
results.append(result['messages'][0])