2020-07-23 09:20:04 -07:00
|
|
|
"""Execute validated & constructed query on device.
|
|
|
|
|
|
|
|
Accepts input from front end application, validates the input and
|
|
|
|
returns errors if input is invalid. Passes validated parameters to
|
|
|
|
construct.py, which is used to build & run the Netmiko connections or
|
|
|
|
hyperglass-frr API calls, returns the output back to the front end.
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Standard Library
|
|
|
|
import signal
|
2020-12-13 01:48:20 -07:00
|
|
|
from typing import Any, Dict, Union, Callable, Sequence
|
2020-07-23 09:20:04 -07:00
|
|
|
|
|
|
|
# Project
|
|
|
|
from hyperglass.log import log
|
|
|
|
from hyperglass.util import validate_nos
|
|
|
|
from hyperglass.exceptions import DeviceTimeout, ResponseEmpty
|
2020-10-05 12:10:27 -07:00
|
|
|
from hyperglass.models.api import Query
|
2020-07-30 01:30:01 -07:00
|
|
|
from hyperglass.configuration import params
|
2020-10-05 12:10:27 -07:00
|
|
|
|
2020-10-11 13:14:57 -07:00
|
|
|
# Local
|
2020-10-05 12:10:27 -07:00
|
|
|
from .drivers import AgentConnection, NetmikoConnection, ScrapliConnection
|
2020-07-23 17:47:54 -07:00
|
|
|
|
|
|
|
DRIVER_MAP = {
|
|
|
|
"scrapli": ScrapliConnection,
|
|
|
|
"netmiko": NetmikoConnection,
|
|
|
|
"hyperglass_agent": AgentConnection,
|
|
|
|
}
|
2020-07-23 09:20:04 -07:00
|
|
|
|
|
|
|
|
|
|
|
def handle_timeout(**exc_args: Any) -> Callable:
|
|
|
|
"""Return a function signal can use to raise a timeout exception."""
|
|
|
|
|
|
|
|
def handler(*args: Any, **kwargs: Any) -> None:
|
|
|
|
raise DeviceTimeout(**exc_args)
|
|
|
|
|
|
|
|
return handler
|
|
|
|
|
|
|
|
|
2020-12-13 01:48:20 -07:00
|
|
|
async def execute(query: Query) -> Union[str, Sequence[Dict]]:
|
2020-07-23 09:20:04 -07:00
|
|
|
"""Initiate query validation and execution."""
|
|
|
|
|
|
|
|
output = params.messages.general
|
|
|
|
|
2020-09-28 12:37:44 -07:00
|
|
|
log.debug("Received query for {}", query.json())
|
|
|
|
log.debug("Matched device config: {}", query.device)
|
2020-07-23 09:20:04 -07:00
|
|
|
|
2020-07-30 01:30:01 -07:00
|
|
|
supported, driver_name = validate_nos(query.device.nos)
|
2020-07-23 09:20:04 -07:00
|
|
|
|
2020-07-23 17:47:54 -07:00
|
|
|
mapped_driver = DRIVER_MAP.get(driver_name, NetmikoConnection)
|
2020-07-30 01:30:01 -07:00
|
|
|
driver = mapped_driver(query.device, query)
|
2020-07-23 09:20:04 -07:00
|
|
|
|
|
|
|
timeout_args = {
|
|
|
|
"unformatted_msg": params.messages.connection_error,
|
2020-07-30 01:30:01 -07:00
|
|
|
"device_name": query.device.display_name,
|
2020-07-23 09:20:04 -07:00
|
|
|
"error": params.messages.request_timeout,
|
|
|
|
}
|
|
|
|
|
2020-07-30 01:30:01 -07:00
|
|
|
if query.device.proxy:
|
|
|
|
timeout_args["proxy"] = query.device.proxy.name
|
2020-07-23 09:20:04 -07:00
|
|
|
|
|
|
|
signal.signal(signal.SIGALRM, handle_timeout(**timeout_args))
|
|
|
|
signal.alarm(params.request_timeout - 1)
|
|
|
|
|
2020-07-30 01:30:01 -07:00
|
|
|
if query.device.proxy:
|
2020-07-23 09:20:04 -07:00
|
|
|
proxy = driver.setup_proxy()
|
|
|
|
with proxy() as tunnel:
|
2020-07-23 17:47:54 -07:00
|
|
|
response = await driver.collect(
|
|
|
|
tunnel.local_bind_host, tunnel.local_bind_port
|
|
|
|
)
|
2020-07-23 09:20:04 -07:00
|
|
|
else:
|
2020-07-23 17:47:54 -07:00
|
|
|
response = await driver.collect()
|
2020-07-23 09:20:04 -07:00
|
|
|
|
|
|
|
output = await driver.parsed_response(response)
|
|
|
|
|
|
|
|
if output == "" or output == "\n":
|
2020-07-30 01:30:01 -07:00
|
|
|
raise ResponseEmpty(
|
|
|
|
params.messages.no_output, device_name=query.device.display_name
|
|
|
|
)
|
2020-07-23 09:20:04 -07:00
|
|
|
|
2020-09-28 12:37:44 -07:00
|
|
|
log.debug("Output for query: {}:\n{}", query.json(), repr(output))
|
2020-07-23 09:20:04 -07:00
|
|
|
signal.alarm(0)
|
|
|
|
|
|
|
|
return output
|