mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-26 23:00:36 +00:00
190 lines
6.0 KiB
Python
190 lines
6.0 KiB
Python
|
# (c) 2018 Red Hat Inc.
|
||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||
|
|
||
|
from __future__ import (absolute_import, division, print_function)
|
||
|
__metaclass__ = type
|
||
|
|
||
|
import json
|
||
|
import time
|
||
|
|
||
|
from ansible.module_utils._text import to_text
|
||
|
from ansible.module_utils.connection import ConnectionError
|
||
|
from ansible.module_utils.network.common.utils import to_list
|
||
|
from ansible.plugins.httpapi import HttpApiBase
|
||
|
|
||
|
try:
|
||
|
from __main__ import display
|
||
|
except ImportError:
|
||
|
from ansible.utils.display import Display
|
||
|
display = Display()
|
||
|
|
||
|
|
||
|
class HttpApi(HttpApiBase):
|
||
|
def send_request(self, data, **message_kwargs):
|
||
|
data = to_list(data)
|
||
|
if self._become:
|
||
|
display.vvvv('firing event: on_become')
|
||
|
data.insert(0, {"cmd": "enable", "input": self._become_pass})
|
||
|
|
||
|
output = message_kwargs.get('output', 'text')
|
||
|
request = request_builder(data, output)
|
||
|
headers = {'Content-Type': 'application/json-rpc'}
|
||
|
|
||
|
response, response_data = self.connection.send('/command-api', 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:]
|
||
|
if len(results) == 1:
|
||
|
results = results[0]
|
||
|
|
||
|
return results
|
||
|
|
||
|
def get_prompt(self):
|
||
|
# Fake a prompt for @enable_mode
|
||
|
if self._become:
|
||
|
return '#'
|
||
|
return '>'
|
||
|
|
||
|
# Imported from module_utils
|
||
|
def edit_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
|
||
|
"""
|
||
|
session = 'ansible_%s' % int(time.time())
|
||
|
result = {'session': session}
|
||
|
banner_cmd = None
|
||
|
banner_input = []
|
||
|
|
||
|
commands = ['configure session %s' % session]
|
||
|
if replace:
|
||
|
commands.append('rollback clean-config')
|
||
|
|
||
|
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)
|
||
|
|
||
|
try:
|
||
|
response = self.send_request(commands)
|
||
|
except Exception:
|
||
|
commands = ['configure session %s' % session, 'abort']
|
||
|
response = self.send_request(commands, output='text')
|
||
|
raise
|
||
|
|
||
|
commands = ['configure session %s' % session, 'show session-config diffs']
|
||
|
if commit:
|
||
|
commands.append('commit')
|
||
|
else:
|
||
|
commands.append('abort')
|
||
|
|
||
|
response = self.send_request(commands, output='text')
|
||
|
diff = response[1].strip()
|
||
|
if diff:
|
||
|
result['diff'] = diff
|
||
|
|
||
|
return result
|
||
|
|
||
|
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)
|
||
|
|
||
|
|
||
|
def handle_response(response):
|
||
|
if 'error' in response:
|
||
|
error = response['error']
|
||
|
|
||
|
error_text = []
|
||
|
for data in error['data']:
|
||
|
error_text.extend(data.get('errors', []))
|
||
|
error_text = '\n'.join(error_text) or error['message']
|
||
|
|
||
|
raise ConnectionError(error_text, code=error['code'])
|
||
|
|
||
|
results = []
|
||
|
for result in response['result']:
|
||
|
if 'messages' in result:
|
||
|
results.append(result['messages'][0])
|
||
|
elif 'output' in result:
|
||
|
results.append(result['output'].strip())
|
||
|
else:
|
||
|
results.append(json.dumps(result))
|
||
|
|
||
|
return results
|
||
|
|
||
|
|
||
|
def request_builder(commands, output, reqid=None):
|
||
|
params = dict(version=1, cmds=commands, format=output)
|
||
|
return json.dumps(dict(jsonrpc='2.0', id=reqid, method='runCmds', params=params))
|