File: //usr/lib/python3.9/site-packages/ansible_collections/arista/eos/plugins/httpapi/eos.py
# (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
DOCUMENTATION = """
author: Ansible Networking Team (@ansible-network)
name: 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: 1.0.0
options:
eos_use_sessions:
type: bool
default: yes
description:
- Specifies if sessions should be used on remote host or not
env:
- name: ANSIBLE_EOS_USE_SESSIONS
vars:
- name: ansible_eos_use_sessions
"""
import json
from ansible.errors import AnsibleConnectionFailure
from ansible.module_utils._text import to_text
from ansible.module_utils.connection import ConnectionError
from ansible_collections.ansible.netcommon.plugins.module_utils.network.common.utils import (
to_list,
)
from ansible_collections.ansible.netcommon.plugins.plugin_utils.httpapi_base import (
HttpApiBase,
)
from ansible_collections.arista.eos.plugins.module_utils.network.eos.eos import (
session_name,
)
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
def supports_sessions(self):
if not self.get_option("eos_use_sessions"):
self._session_support = False
else:
if self._session_support:
return self._session_support
try:
response = self.send_request("show configuration sessions")
self._session_support = "error" not in response
except AnsibleConnectionFailure:
self._session_support = False
return self._session_support
def send_request(self, data, **message_kwargs):
data = to_list(data)
become = self._become
if become:
self.connection.queue_message("vvvv", "firing event: on_become")
data.insert(0, {"cmd": "enable", "input": self._become_pass})
output = message_kwargs.get("output") or "text"
version = message_kwargs.get("version") or "latest"
request = request_builder(data, output, version)
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 become:
results = results[1:]
if len(results) == 1:
results = results[0]
return results
def get_device_info(self):
if self._device_info:
return self._device_info
device_info = {}
device_info["network_os"] = "eos"
reply = self.send_request("show version", output="json")
data = json.loads(reply)
device_info["network_os_version"] = data["version"]
device_info["network_os_model"] = data["modelName"]
reply = self.send_request("show hostname", output="json")
data = json.loads(reply)
device_info["network_os_hostname"] = data["hostname"]
self._device_info = device_info
return self._device_info
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()),
}
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"
return json.dumps(result)
# Shims for resource module support
def get(self, command, output=None):
# This method is ONLY here to support resource modules. Therefore most
# arguments are unsupported and not present.
return self.send_request(data=command, output=output)
def edit_config(self, candidate):
# This method is ONLY here to support resource modules. Therefore most
# arguments are unsupported and not present.
session = None
if self.supports_sessions():
session = session_name()
candidate = ["configure session %s" % session] + candidate
else:
candidate = ["configure"] + candidate
candidate.append("commit")
try:
responses = self.send_request(candidate)
except ConnectionError:
if session:
self.send_request(["configure session %s" % session, "abort"])
raise
return [resp for resp in to_list(responses) if resp != "{}"]
def handle_response(response):
if "error" in response:
error = response["error"]
error_text = []
for data in error.get("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, version, reqid=None):
if version != "latest":
version = int(version)
params = dict(version=version, cmds=commands, format=output)
return json.dumps(
dict(jsonrpc="2.0", id=reqid, method="runCmds", params=params),
)