mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
Clean up API routes
This commit is contained in:
@@ -18,21 +18,12 @@ from fastapi.middleware.gzip import GZipMiddleware
|
|||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
from hyperglass.util import cpu_count
|
from hyperglass.util import cpu_count
|
||||||
from hyperglass.constants import TRANSPORT_REST, __version__
|
from hyperglass.constants import __version__
|
||||||
from hyperglass.models.ui import UIParameters
|
from hyperglass.models.ui import UIParameters
|
||||||
from hyperglass.api.events import on_startup, on_shutdown
|
from hyperglass.api.events import on_startup, on_shutdown
|
||||||
from hyperglass.api.routes import (
|
from hyperglass.api.routes import docs, info, query, router, queries, routers, ui_props
|
||||||
docs,
|
|
||||||
info,
|
|
||||||
query,
|
|
||||||
queries,
|
|
||||||
routers,
|
|
||||||
ui_props,
|
|
||||||
communities,
|
|
||||||
import_certificate,
|
|
||||||
)
|
|
||||||
from hyperglass.exceptions import HyperglassError
|
from hyperglass.exceptions import HyperglassError
|
||||||
from hyperglass.configuration import URL_DEV, STATIC_PATH, params, devices
|
from hyperglass.configuration import URL_DEV, STATIC_PATH, params
|
||||||
from hyperglass.api.error_handlers import (
|
from hyperglass.api.error_handlers import (
|
||||||
app_handler,
|
app_handler,
|
||||||
http_handler,
|
http_handler,
|
||||||
@@ -211,12 +202,14 @@ app.add_api_route(
|
|||||||
)
|
)
|
||||||
|
|
||||||
app.add_api_route(
|
app.add_api_route(
|
||||||
path="/api/communities",
|
path="/api/devices/{id}",
|
||||||
endpoint=communities,
|
endpoint=router,
|
||||||
methods=["GET"],
|
methods=["GET"],
|
||||||
response_model=List[CommunityResponse],
|
response_model=RoutersResponse,
|
||||||
summary=params.docs.communities.summary,
|
response_class=JSONResponse,
|
||||||
tags=[params.docs.communities.title],
|
summary=params.docs.devices.summary,
|
||||||
|
description=params.docs.devices.description,
|
||||||
|
tags=[params.docs.devices.title],
|
||||||
)
|
)
|
||||||
|
|
||||||
app.add_api_route(
|
app.add_api_route(
|
||||||
@@ -255,15 +248,6 @@ app.add_api_route(
|
|||||||
response_model_by_alias=True,
|
response_model_by_alias=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Enable certificate import route only if a device using
|
|
||||||
# hyperglass-agent is defined.
|
|
||||||
if [n for n in devices.all_nos if n in TRANSPORT_REST]:
|
|
||||||
app.add_api_route(
|
|
||||||
path="/api/import-agent-certificate/",
|
|
||||||
endpoint=import_certificate,
|
|
||||||
methods=["POST"],
|
|
||||||
include_in_schema=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
if params.docs.enable:
|
if params.docs.enable:
|
||||||
app.add_api_route(path=params.docs.uri, endpoint=docs, include_in_schema=False)
|
app.add_api_route(path=params.docs.uri, endpoint=docs, include_in_schema=False)
|
||||||
|
@@ -14,12 +14,11 @@ from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
|
|||||||
# Project
|
# Project
|
||||||
from hyperglass.log import log
|
from hyperglass.log import log
|
||||||
from hyperglass.cache import AsyncCache
|
from hyperglass.cache import AsyncCache
|
||||||
from hyperglass.encode import jwt_decode
|
|
||||||
from hyperglass.external import Webhook, bgptools
|
from hyperglass.external import Webhook, bgptools
|
||||||
from hyperglass.api.tasks import process_headers, import_public_key
|
from hyperglass.api.tasks import process_headers
|
||||||
from hyperglass.constants import __version__
|
from hyperglass.constants import __version__
|
||||||
from hyperglass.exceptions import HyperglassError
|
from hyperglass.exceptions import HyperglassError
|
||||||
from hyperglass.models.api import Query, EncodedRequest
|
from hyperglass.models.api import Query
|
||||||
from hyperglass.configuration import REDIS_CONFIG, params, devices, ui_params
|
from hyperglass.configuration import REDIS_CONFIG, params, devices, ui_params
|
||||||
from hyperglass.execution.main import execute
|
from hyperglass.execution.main import execute
|
||||||
|
|
||||||
@@ -30,15 +29,7 @@ APP_PATH = os.environ["hyperglass_directory"]
|
|||||||
|
|
||||||
|
|
||||||
async def send_webhook(query_data: Query, request: Request, timestamp: datetime):
|
async def send_webhook(query_data: Query, request: Request, timestamp: datetime):
|
||||||
"""If webhooks are enabled, get request info and send a webhook.
|
"""If webhooks are enabled, get request info and send a webhook."""
|
||||||
|
|
||||||
Args:
|
|
||||||
query_data (Query): Valid query
|
|
||||||
request (Request): Starlette/FastAPI request
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
int: Returns 1 regardless of result
|
|
||||||
"""
|
|
||||||
try:
|
try:
|
||||||
if params.logging.http is not None:
|
if params.logging.http is not None:
|
||||||
headers = await process_headers(headers=request.headers)
|
headers = await process_headers(headers=request.headers)
|
||||||
@@ -173,47 +164,6 @@ async def query(query_data: Query, request: Request, background_tasks: Backgroun
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def import_certificate(encoded_request: EncodedRequest):
|
|
||||||
"""Import a certificate from hyperglass-agent."""
|
|
||||||
|
|
||||||
# Try to match the requested device name with configured devices
|
|
||||||
log.debug(
|
|
||||||
"Attempting certificate import for device '{}'", devices[encoded_request.device]
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
matched_device = devices[encoded_request.device]
|
|
||||||
except AttributeError:
|
|
||||||
raise HTTPException(
|
|
||||||
detail=f"Device {str(encoded_request.device)} not found", status_code=404
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Decode JSON Web Token
|
|
||||||
decoded_request = await jwt_decode(
|
|
||||||
payload=encoded_request.encoded,
|
|
||||||
secret=matched_device.credential.password.get_secret_value(),
|
|
||||||
)
|
|
||||||
except HyperglassError as decode_error:
|
|
||||||
raise HTTPException(detail=str(decode_error), status_code=400)
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Write certificate to file
|
|
||||||
import_public_key(
|
|
||||||
app_path=APP_PATH,
|
|
||||||
device_name=matched_device._id,
|
|
||||||
keystring=decoded_request,
|
|
||||||
)
|
|
||||||
except RuntimeError as err:
|
|
||||||
raise HyperglassError(str(err), level="danger")
|
|
||||||
|
|
||||||
log.info("Added public key for {}", encoded_request.device)
|
|
||||||
return {
|
|
||||||
"output": f"Added public key for {encoded_request.device}",
|
|
||||||
"level": "success",
|
|
||||||
"keywords": [encoded_request.device],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async def docs():
|
async def docs():
|
||||||
"""Serve custom docs."""
|
"""Serve custom docs."""
|
||||||
if params.docs.enable:
|
if params.docs.enable:
|
||||||
@@ -226,27 +176,14 @@ async def docs():
|
|||||||
raise HTTPException(detail="Not found", status_code=404)
|
raise HTTPException(detail="Not found", status_code=404)
|
||||||
|
|
||||||
|
|
||||||
|
async def router(id: str):
|
||||||
|
"""Get a device's API-facing attributes."""
|
||||||
|
return devices[id].export_api()
|
||||||
|
|
||||||
|
|
||||||
async def routers():
|
async def routers():
|
||||||
"""Serve list of configured routers and attributes."""
|
"""Serve list of configured routers and attributes."""
|
||||||
return [
|
return devices.export_api()
|
||||||
d.dict(
|
|
||||||
include={
|
|
||||||
"name": ...,
|
|
||||||
"network": ...,
|
|
||||||
"display_name": ...,
|
|
||||||
"vrfs": {-1: {"name", "display_name"}},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
for d in devices.objects
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
async def communities():
|
|
||||||
"""Serve list of configured communities if mode is select."""
|
|
||||||
if params.queries.bgp_community.mode != "select":
|
|
||||||
raise HTTPException(detail="BGP community mode is not select", status_code=404)
|
|
||||||
|
|
||||||
return [c.export_dict() for c in params.queries.bgp_community.communities]
|
|
||||||
|
|
||||||
|
|
||||||
async def queries():
|
async def queries():
|
||||||
@@ -260,7 +197,7 @@ async def info():
|
|||||||
"name": params.site_title,
|
"name": params.site_title,
|
||||||
"organization": params.org_name,
|
"organization": params.org_name,
|
||||||
"primary_asn": int(params.primary_asn),
|
"primary_asn": int(params.primary_asn),
|
||||||
"version": f"hyperglass {__version__}",
|
"version": __version__,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -178,16 +178,24 @@ class Network(BaseModel):
|
|||||||
class RoutersResponse(BaseModel):
|
class RoutersResponse(BaseModel):
|
||||||
"""Response model for /api/devices list items."""
|
"""Response model for /api/devices list items."""
|
||||||
|
|
||||||
|
id: StrictStr
|
||||||
name: StrictStr
|
name: StrictStr
|
||||||
network: Network
|
network: StrictStr
|
||||||
vrfs: List[Vrf]
|
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
"""Pydantic model configuration."""
|
"""Pydantic model configuration."""
|
||||||
|
|
||||||
title = "Device"
|
title = "Device"
|
||||||
description = "Per-device attributes"
|
description = "Device attributes"
|
||||||
schema_extra = {"examples": [{"name": "router01-nyc01", "location": "nyc01"}]}
|
schema_extra = {
|
||||||
|
"examples": [
|
||||||
|
{
|
||||||
|
"id": "nyc_router_1",
|
||||||
|
"name": "NYC Router 1",
|
||||||
|
"network": "New York City, NY",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CommunityResponse(BaseModel):
|
class CommunityResponse(BaseModel):
|
||||||
|
@@ -103,6 +103,14 @@ class Device(HyperglassModel, extra="allow"):
|
|||||||
|
|
||||||
return device_id, {"name": display_name, "display_name": None, **values}
|
return device_id, {"name": display_name, "display_name": None, **values}
|
||||||
|
|
||||||
|
def export_api(self) -> Dict[str, Any]:
|
||||||
|
"""Export API-facing device fields."""
|
||||||
|
return {
|
||||||
|
"id": self._id,
|
||||||
|
"name": self.name,
|
||||||
|
"network": self.network.display_name,
|
||||||
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def directive_commands(self) -> List[str]:
|
def directive_commands(self) -> List[str]:
|
||||||
"""Get all commands associated with the device."""
|
"""Get all commands associated with the device."""
|
||||||
@@ -278,6 +286,10 @@ class Devices(HyperglassModel, extra="allow"):
|
|||||||
|
|
||||||
raise AttributeError(f"No device named '{accessor}'")
|
raise AttributeError(f"No device named '{accessor}'")
|
||||||
|
|
||||||
|
def export_api(self) -> List[Dict[str, Any]]:
|
||||||
|
"""Export API-facing device fields."""
|
||||||
|
return [d.export_api() for d in self.objects]
|
||||||
|
|
||||||
def networks(self, params: Params) -> List[Dict[str, Any]]:
|
def networks(self, params: Params) -> List[Dict[str, Any]]:
|
||||||
"""Group devices by network."""
|
"""Group devices by network."""
|
||||||
names = {device.network.display_name for device in self.objects}
|
names = {device.network.display_name for device in self.objects}
|
||||||
|
Reference in New Issue
Block a user