mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
add support for hyperglass-agent
This commit is contained in:
@@ -89,6 +89,7 @@ class Construct:
|
||||
query = []
|
||||
query_protocol = f"ipv{ipaddress.ip_network(self.query_target).version}"
|
||||
afi = getattr(self.device_vrf, query_protocol)
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
|
||||
if self.transport == "rest":
|
||||
query.append(
|
||||
@@ -96,14 +97,13 @@ class Construct:
|
||||
{
|
||||
"query_type": "ping",
|
||||
"vrf": afi.vrf_name,
|
||||
"afi": query_protocol,
|
||||
"afi": cmd_type,
|
||||
"source": afi.source_address.compressed,
|
||||
"target": self.query_target,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif self.transport == "scrape":
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
cmd = self.device_commands(self.device.commands, cmd_type, "ping")
|
||||
query.append(
|
||||
cmd.format(
|
||||
@@ -130,6 +130,7 @@ class Construct:
|
||||
query = []
|
||||
query_protocol = f"ipv{ipaddress.ip_network(self.query_target).version}"
|
||||
afi = getattr(self.device_vrf, query_protocol)
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
|
||||
if self.transport == "rest":
|
||||
query.append(
|
||||
@@ -137,14 +138,13 @@ class Construct:
|
||||
{
|
||||
"query_type": "traceroute",
|
||||
"vrf": afi.vrf_name,
|
||||
"afi": query_protocol,
|
||||
"afi": cmd_type,
|
||||
"source": afi.source_address.compressed,
|
||||
"target": self.query_target,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif self.transport == "scrape":
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
cmd = self.device_commands(self.device.commands, cmd_type, "traceroute")
|
||||
query.append(
|
||||
cmd.format(
|
||||
@@ -168,6 +168,7 @@ class Construct:
|
||||
query = []
|
||||
query_protocol = f"ipv{ipaddress.ip_network(self.query_target).version}"
|
||||
afi = getattr(self.device_vrf, query_protocol)
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
|
||||
if self.transport == "rest":
|
||||
query.append(
|
||||
@@ -175,14 +176,13 @@ class Construct:
|
||||
{
|
||||
"query_type": "bgp_route",
|
||||
"vrf": afi.vrf_name,
|
||||
"afi": query_protocol,
|
||||
"source": afi.source_address.compressed,
|
||||
"afi": cmd_type,
|
||||
"source": None,
|
||||
"target": self.format_target(self.query_target),
|
||||
}
|
||||
)
|
||||
)
|
||||
elif self.transport == "scrape":
|
||||
cmd_type = self.get_cmd_type(query_protocol, self.query_vrf)
|
||||
cmd = self.device_commands(self.device.commands, cmd_type, "bgp_route")
|
||||
query.append(
|
||||
cmd.format(
|
||||
@@ -218,19 +218,20 @@ class Construct:
|
||||
|
||||
for afi in afis:
|
||||
afi_attr = getattr(self.device_vrf, afi)
|
||||
cmd_type = self.get_cmd_type(afi, self.query_vrf)
|
||||
if self.transport == "rest":
|
||||
query.append(
|
||||
json.dumps(
|
||||
{
|
||||
"query_type": "bgp_community",
|
||||
"vrf": afi_attr.vrf_name,
|
||||
"afi": afi,
|
||||
"afi": cmd_type,
|
||||
"target": self.query_target,
|
||||
"source": None,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif self.transport == "scrape":
|
||||
cmd_type = self.get_cmd_type(afi, self.query_vrf)
|
||||
cmd = self.device_commands(
|
||||
self.device.commands, cmd_type, "bgp_community"
|
||||
)
|
||||
@@ -267,19 +268,20 @@ class Construct:
|
||||
|
||||
for afi in afis:
|
||||
afi_attr = getattr(self.device_vrf, afi)
|
||||
cmd_type = self.get_cmd_type(afi, self.query_vrf)
|
||||
if self.transport == "rest":
|
||||
query.append(
|
||||
json.dumps(
|
||||
{
|
||||
"query_type": "bgp_aspath",
|
||||
"vrf": afi_attr.vrf_name,
|
||||
"afi": afi,
|
||||
"afi": cmd_type,
|
||||
"target": self.query_target,
|
||||
"source": None,
|
||||
}
|
||||
)
|
||||
)
|
||||
elif self.transport == "scrape":
|
||||
cmd_type = self.get_cmd_type(afi, self.query_vrf)
|
||||
cmd = self.device_commands(self.device.commands, cmd_type, "bgp_aspath")
|
||||
query.append(
|
||||
cmd.format(
|
||||
|
30
hyperglass/command/encode.py
Normal file
30
hyperglass/command/encode.py
Normal file
@@ -0,0 +1,30 @@
|
||||
# Standard Library Imports
|
||||
import datetime
|
||||
|
||||
# Third Party Imports
|
||||
import jwt
|
||||
|
||||
# Project Imports
|
||||
from hyperglass.exceptions import RestError
|
||||
|
||||
|
||||
async def jwt_decode(payload, secret):
|
||||
"""Decode & validate an encoded JSON Web Token (JWT)"""
|
||||
try:
|
||||
decoded = jwt.decode(payload, secret, algorithm="HS256")
|
||||
decoded = decoded["payload"]
|
||||
return decoded
|
||||
except (KeyError, jwt.PyJWTError) as exp:
|
||||
raise RestError(str(exp)) from None
|
||||
|
||||
|
||||
async def jwt_encode(payload, secret, duration):
|
||||
"""Encode a query to a JSON Web Token (JWT)"""
|
||||
token = {
|
||||
"payload": payload,
|
||||
"nbf": datetime.datetime.utcnow(),
|
||||
"iat": datetime.datetime.utcnow(),
|
||||
"exp": datetime.datetime.utcnow() + datetime.timedelta(seconds=duration),
|
||||
}
|
||||
encoded = jwt.encode(token, secret, algorithm="HS256").decode("utf-8")
|
||||
return encoded
|
@@ -21,6 +21,7 @@ from netmiko import NetMikoTimeoutException
|
||||
# Project Imports
|
||||
from hyperglass.command.construct import Construct
|
||||
from hyperglass.command.validate import Validate
|
||||
from hyperglass.command.encode import jwt_decode, jwt_encode
|
||||
from hyperglass.configuration import devices
|
||||
from hyperglass.configuration import logzero_config # noqa: F401
|
||||
from hyperglass.configuration import params
|
||||
@@ -218,53 +219,51 @@ 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.nos)
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Key": self.device.credential.password.get_secret_value(),
|
||||
}
|
||||
http_protocol = protocol_map.get(self.device.port, "http")
|
||||
endpoint = "{protocol}://{addr}:{port}/{uri}".format(
|
||||
protocol=http_protocol,
|
||||
addr=self.device.address,
|
||||
port=self.device.port,
|
||||
uri=uri,
|
||||
# uri = Supported.map_rest(self.device.nos)
|
||||
headers = {"Content-Type": "application/json"}
|
||||
http_protocol = protocol_map.get(self.device.port, "https")
|
||||
endpoint = "{protocol}://{addr}:{port}/query".format(
|
||||
protocol=http_protocol, addr=self.device.address, port=self.device.port
|
||||
)
|
||||
|
||||
log.debug(f"HTTP Headers: {headers}")
|
||||
log.debug(f"URL endpoint: {endpoint}")
|
||||
|
||||
try:
|
||||
http_client = httpx.AsyncClient()
|
||||
async with httpx.Client() as http_client:
|
||||
responses = []
|
||||
for query in self.query:
|
||||
encoded_query = await jwt_encode(
|
||||
payload=query,
|
||||
secret=self.device.credential.password.get_secret_value(),
|
||||
duration=params.general.request_timeout,
|
||||
)
|
||||
log.debug(f"Encoded JWT: {encoded_query}")
|
||||
raw_response = await http_client.post(
|
||||
endpoint, headers=headers, json=query, timeout=7
|
||||
endpoint,
|
||||
headers=headers,
|
||||
json={"encoded": encoded_query},
|
||||
timeout=params.general.request_timeout,
|
||||
)
|
||||
log.debug(f"HTTP status code: {raw_response.status_code}")
|
||||
|
||||
raw = raw_response.text
|
||||
responses.append(raw)
|
||||
log.debug(f"Raw Response: {raw}")
|
||||
|
||||
if raw_response.status_code == 200:
|
||||
decoded = await jwt_decode(
|
||||
payload=raw_response.json()["encoded"],
|
||||
secret=self.device.credential.password.get_secret_value(),
|
||||
)
|
||||
log.debug(f"Decoded Response: {decoded}")
|
||||
|
||||
responses.append(decoded)
|
||||
else:
|
||||
log.error(raw_response.text)
|
||||
|
||||
response = "\n\n".join(responses)
|
||||
log.debug(f"Output for query {self.query}:\n{response}")
|
||||
except (
|
||||
httpx.exceptions.ConnectTimeout,
|
||||
httpx.exceptions.CookieConflict,
|
||||
httpx.exceptions.DecodingError,
|
||||
httpx.exceptions.InvalidURL,
|
||||
httpx.exceptions.PoolTimeout,
|
||||
httpx.exceptions.ProtocolError,
|
||||
httpx.exceptions.ReadTimeout,
|
||||
httpx.exceptions.RedirectBodyUnavailable,
|
||||
httpx.exceptions.RedirectLoop,
|
||||
httpx.exceptions.ResponseClosed,
|
||||
httpx.exceptions.ResponseNotRead,
|
||||
httpx.exceptions.StreamConsumed,
|
||||
httpx.exceptions.Timeout,
|
||||
httpx.exceptions.TooManyRedirects,
|
||||
httpx.exceptions.WriteTimeout,
|
||||
) as rest_error:
|
||||
except httpx.exceptions.HTTPError as rest_error:
|
||||
rest_msg = " ".join(
|
||||
re.findall(r"[A-Z][^A-Z]*", rest_error.__class__.__name__)
|
||||
)
|
||||
|
@@ -1,7 +1,8 @@
|
||||
aredis==1.1.5
|
||||
click==7.0
|
||||
cryptography==2.8
|
||||
hiredis==1.0.0
|
||||
httpx==0.6.8
|
||||
httpx==0.9.*
|
||||
jinja2==2.10.1
|
||||
logzero==1.5.0
|
||||
markdown2==2.3.8
|
||||
@@ -9,6 +10,7 @@ netmiko==2.4.1
|
||||
passlib==1.7.1
|
||||
prometheus_client==0.7.1
|
||||
pydantic==0.32.2
|
||||
pyjwt==1.7.1
|
||||
pyyaml==5.1.1
|
||||
redis==3.2.1
|
||||
sanic-limiter==0.1.3
|
||||
|
Reference in New Issue
Block a user