mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
model restructure, front-end improvements
This commit is contained in:
@@ -5,6 +5,7 @@ construct.py, which is used to build & run the Netmiko connectoins or
|
||||
hyperglass-frr API calls, returns the output back to the front end.
|
||||
"""
|
||||
|
||||
# Standard Library Imports
|
||||
import re
|
||||
|
||||
# Third Party Imports
|
||||
@@ -20,15 +21,16 @@ from netmiko import NetMikoTimeoutException
|
||||
# Project Imports
|
||||
from hyperglass.command.construct import Construct
|
||||
from hyperglass.command.validate import Validate
|
||||
from hyperglass.configuration import credentials
|
||||
from hyperglass.configuration import devices
|
||||
from hyperglass.configuration import logzero_config # noqa: F401
|
||||
from hyperglass.configuration import stack # NOQA: F401
|
||||
from hyperglass.configuration import params
|
||||
from hyperglass.configuration import proxies
|
||||
from hyperglass.constants import Supported
|
||||
from hyperglass.constants import protocol_map
|
||||
from hyperglass.exceptions import AuthError, RestError, ScrapeError, DeviceTimeout
|
||||
from hyperglass.exceptions import AuthError
|
||||
from hyperglass.exceptions import DeviceTimeout
|
||||
from hyperglass.exceptions import ResponseEmpty
|
||||
from hyperglass.exceptions import RestError
|
||||
from hyperglass.exceptions import ScrapeError
|
||||
|
||||
|
||||
class Connect:
|
||||
@@ -42,18 +44,15 @@ class Connect:
|
||||
rest() connects to devices via HTTP for RESTful API communication
|
||||
"""
|
||||
|
||||
def __init__(self, device_config, query_data, transport):
|
||||
self.device_config = device_config
|
||||
def __init__(self, device, query_data, transport):
|
||||
self.device = device
|
||||
self.query_data = query_data
|
||||
self.query_type = self.query_data["query_type"]
|
||||
self.query_target = self.query_data["query_target"]
|
||||
self.transport = transport
|
||||
self.cred = getattr(credentials, device_config.credential)
|
||||
self.query = getattr(
|
||||
Construct(
|
||||
device=self.device_config,
|
||||
query_data=self.query_data,
|
||||
transport=self.transport,
|
||||
device=self.device, query_data=self.query_data, transport=self.transport
|
||||
),
|
||||
self.query_type,
|
||||
)()
|
||||
@@ -63,50 +62,45 @@ class Connect:
|
||||
Connects to the router via Netmiko library via the sshtunnel
|
||||
library, returns the command output.
|
||||
"""
|
||||
device_proxy = getattr(proxies, self.device_config.proxy)
|
||||
|
||||
log.debug(f"Connecting to {self.device_config.proxy} via sshtunnel library...")
|
||||
log.debug(f"Connecting to {self.device.proxy} via sshtunnel library...")
|
||||
try:
|
||||
tunnel = sshtunnel.open_tunnel(
|
||||
device_proxy.address.compressed,
|
||||
device_proxy.port,
|
||||
ssh_username=device_proxy.username,
|
||||
ssh_password=device_proxy.password.get_secret_value(),
|
||||
remote_bind_address=(
|
||||
self.device_config.address.compressed,
|
||||
self.device_config.port,
|
||||
),
|
||||
self.device.proxy.address,
|
||||
self.device.proxy.port,
|
||||
ssh_username=self.device.proxy.credential.username,
|
||||
ssh_password=self.device.proxy.credential.password.get_secret_value(),
|
||||
remote_bind_address=(self.device.address, self.device.port),
|
||||
local_bind_address=("localhost", 0),
|
||||
skip_tunnel_checkup=False,
|
||||
logger=log,
|
||||
)
|
||||
except sshtunnel.BaseSSHTunnelForwarderError as scrape_proxy_error:
|
||||
log.error(
|
||||
f"Error connecting to device {self.device_config.location} via "
|
||||
f"proxy {self.device_config.proxy}"
|
||||
f"Error connecting to device {self.device.location} via "
|
||||
f"proxy {self.device.proxy.name}"
|
||||
)
|
||||
raise ScrapeError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
proxy=self.device_config.proxy,
|
||||
device_name=self.device.display_name,
|
||||
proxy=self.device.proxy.name,
|
||||
error=scrape_proxy_error,
|
||||
)
|
||||
with tunnel:
|
||||
log.debug(f"Established tunnel with {self.device_config.proxy}")
|
||||
log.debug(f"Established tunnel with {self.device.proxy}")
|
||||
scrape_host = {
|
||||
"host": "localhost",
|
||||
"port": tunnel.local_bind_port,
|
||||
"device_type": self.device_config.nos,
|
||||
"username": self.cred.username,
|
||||
"password": self.cred.password.get_secret_value(),
|
||||
"device_type": self.device.nos,
|
||||
"username": self.device.credential.username,
|
||||
"password": self.device.credential.password.get_secret_value(),
|
||||
"global_delay_factor": 0.2,
|
||||
"timeout": params.general.request_timeout - 1,
|
||||
}
|
||||
log.debug(f"SSH proxy local binding: localhost:{tunnel.local_bind_port}")
|
||||
try:
|
||||
log.debug(
|
||||
f"Connecting to {self.device_config.location} "
|
||||
"via Netmiko library..."
|
||||
f"Connecting to {self.device.location} " "via Netmiko library..."
|
||||
)
|
||||
nm_connect_direct = ConnectHandler(**scrape_host)
|
||||
responses = []
|
||||
@@ -119,42 +113,42 @@ class Connect:
|
||||
|
||||
except (NetMikoTimeoutException, NetmikoTimeoutError) as scrape_error:
|
||||
log.error(
|
||||
f"Timeout connecting to device {self.device_config.location}: "
|
||||
f"Timeout connecting to device {self.device.location}: "
|
||||
f"{scrape_error}"
|
||||
)
|
||||
raise DeviceTimeout(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
proxy=self.device_config.proxy,
|
||||
device_name=self.device.display_name,
|
||||
proxy=self.device.proxy.name,
|
||||
error=params.messages.request_timeout,
|
||||
)
|
||||
except (NetMikoAuthenticationException, NetmikoAuthError) as auth_error:
|
||||
log.error(
|
||||
f"Error authenticating to device {self.device_config.location}: "
|
||||
f"Error authenticating to device {self.device.location}: "
|
||||
f"{auth_error}"
|
||||
)
|
||||
raise AuthError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
proxy=self.device_config.proxy,
|
||||
device_name=self.device.display_name,
|
||||
proxy=self.device.proxy.name,
|
||||
error=params.messages.authentication_error,
|
||||
) from None
|
||||
except sshtunnel.BaseSSHTunnelForwarderError as scrape_error:
|
||||
log.error(
|
||||
f"Error connecting to device proxy {self.device_config.proxy}: "
|
||||
f"Error connecting to device proxy {self.device.proxy}: "
|
||||
f"{scrape_error}"
|
||||
)
|
||||
raise ScrapeError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
proxy=self.device_config.proxy,
|
||||
device_name=self.device.display_name,
|
||||
proxy=self.device.proxy.name,
|
||||
error=params.messages.general,
|
||||
)
|
||||
if response is None:
|
||||
log.error(f"No response from device {self.device_config.location}")
|
||||
log.error(f"No response from device {self.device.location}")
|
||||
raise ScrapeError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.noresponse_error,
|
||||
)
|
||||
@@ -167,23 +161,21 @@ class Connect:
|
||||
command output.
|
||||
"""
|
||||
|
||||
log.debug(f"Connecting directly to {self.device_config.location}...")
|
||||
log.debug(f"Connecting directly to {self.device.location}...")
|
||||
|
||||
scrape_host = {
|
||||
"host": self.device_config.address.compressed,
|
||||
"port": self.device_config.port,
|
||||
"device_type": self.device_config.nos,
|
||||
"username": self.cred.username,
|
||||
"password": self.cred.password.get_secret_value(),
|
||||
"host": self.device.address,
|
||||
"port": self.device.port,
|
||||
"device_type": self.device.nos,
|
||||
"username": self.device.credential.username,
|
||||
"password": self.device.credential.password.get_secret_value(),
|
||||
"global_delay_factor": 0.2,
|
||||
"timeout": params.general.request_timeout - 1,
|
||||
}
|
||||
|
||||
try:
|
||||
log.debug(f"Device Parameters: {scrape_host}")
|
||||
log.debug(
|
||||
f"Connecting to {self.device_config.location} via Netmiko library"
|
||||
)
|
||||
log.debug(f"Connecting to {self.device.location} via Netmiko library")
|
||||
nm_connect_direct = ConnectHandler(**scrape_host)
|
||||
responses = []
|
||||
for query in self.query:
|
||||
@@ -197,25 +189,25 @@ class Connect:
|
||||
log.error(scrape_error)
|
||||
raise DeviceTimeout(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.request_timeout,
|
||||
)
|
||||
except (NetMikoAuthenticationException, NetmikoAuthError) as auth_error:
|
||||
log.error(f"Error authenticating to device {self.device_config.location}")
|
||||
log.error(f"Error authenticating to device {self.device.location}")
|
||||
log.error(auth_error)
|
||||
|
||||
raise AuthError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.authentication_error,
|
||||
)
|
||||
if response is None:
|
||||
log.error(f"No response from device {self.device_config.location}")
|
||||
log.error(f"No response from device {self.device.location}")
|
||||
raise ScrapeError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.noresponse_error,
|
||||
)
|
||||
@@ -226,16 +218,16 @@ class Connect:
|
||||
"""Sends HTTP POST to router running a hyperglass API agent"""
|
||||
log.debug(f"Query parameters: {self.query}")
|
||||
|
||||
uri = Supported.map_rest(self.device_config.nos)
|
||||
uri = Supported.map_rest(self.device.nos)
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": self.cred.password.get_secret_value(),
|
||||
"X-API-Key": self.device.credential.password.get_secret_value(),
|
||||
}
|
||||
http_protocol = protocol_map.get(self.device_config.port, "http")
|
||||
http_protocol = protocol_map.get(self.device.port, "http")
|
||||
endpoint = "{protocol}://{addr}:{port}/{uri}".format(
|
||||
protocol=http_protocol,
|
||||
addr=self.device_config.address.exploded,
|
||||
port=self.device_config.port,
|
||||
addr=self.device.address.exploded,
|
||||
port=self.device.port,
|
||||
uri=uri,
|
||||
)
|
||||
|
||||
@@ -271,18 +263,16 @@ class Connect:
|
||||
rest_msg = " ".join(
|
||||
re.findall(r"[A-Z][^A-Z]*", rest_error.__class__.__name__)
|
||||
)
|
||||
log.error(
|
||||
f"Error connecting to device {self.device_config.location}: {rest_msg}"
|
||||
)
|
||||
log.error(f"Error connecting to device {self.device.location}: {rest_msg}")
|
||||
raise RestError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
error=rest_msg,
|
||||
)
|
||||
except OSError:
|
||||
raise RestError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
error="System error",
|
||||
)
|
||||
|
||||
@@ -290,15 +280,15 @@ class Connect:
|
||||
log.error(f"Response code is {raw_response.status_code}")
|
||||
raise RestError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
error=params.messages.general,
|
||||
)
|
||||
|
||||
if not response:
|
||||
log.error(f"No response from device {self.device_config.location}")
|
||||
log.error(f"No response from device {self.device.location}")
|
||||
raise RestError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device_config.display_name,
|
||||
device_name=self.device.display_name,
|
||||
error=params.messages.noresponse_error,
|
||||
)
|
||||
|
||||
@@ -324,13 +314,13 @@ class Execute:
|
||||
Initializes Execute.filter(), if input fails to pass filter,
|
||||
returns errors to front end. Otherwise, executes queries.
|
||||
"""
|
||||
device_config = getattr(devices, self.query_location)
|
||||
device = getattr(devices, self.query_location)
|
||||
|
||||
log.debug(f"Received query for {self.query_data}")
|
||||
log.debug(f"Matched device config: {device_config}")
|
||||
log.debug(f"Matched device config: {device}")
|
||||
|
||||
# Run query parameters through validity checks
|
||||
validation = Validate(device_config, self.query_data, self.query_target)
|
||||
validation = Validate(device, self.query_data, self.query_target)
|
||||
valid_input = validation.validate_query()
|
||||
if valid_input:
|
||||
log.debug(f"Validation passed for query: {self.query_data}")
|
||||
@@ -339,14 +329,18 @@ class Execute:
|
||||
connect = None
|
||||
output = params.messages.general
|
||||
|
||||
transport = Supported.map_transport(device_config.nos)
|
||||
connect = Connect(device_config, self.query_data, transport)
|
||||
transport = Supported.map_transport(device.nos)
|
||||
connect = Connect(device, self.query_data, transport)
|
||||
|
||||
if Supported.is_rest(device_config.nos):
|
||||
if Supported.is_rest(device.nos):
|
||||
output = await connect.rest()
|
||||
elif Supported.is_scrape(device_config.nos):
|
||||
if device_config.proxy:
|
||||
elif Supported.is_scrape(device.nos):
|
||||
if device.proxy:
|
||||
output = await connect.scrape_proxied()
|
||||
else:
|
||||
output = await connect.scrape_direct()
|
||||
if output == "":
|
||||
raise ResponseEmpty(
|
||||
params.messages.no_output, device_name=device.display_name
|
||||
)
|
||||
return output
|
||||
|
Reference in New Issue
Block a user