1
0
mirror of https://github.com/checktheroads/hyperglass synced 2024-05-11 05:55:08 +00:00
Files
checktheroads-hyperglass/hyperglass/api/routes.py

187 lines
5.5 KiB
Python
Raw Normal View History

2020-01-21 17:27:57 -07:00
"""API Routes."""
2020-02-03 02:34:50 -07:00
# Standard Library
import time
import typing as t
2020-06-26 12:23:11 -07:00
from datetime import datetime
2020-02-03 02:34:50 -07:00
# Third Party
from fastapi import Depends, Request, HTTPException, BackgroundTasks
2020-02-03 02:34:50 -07:00
from fastapi.openapi.docs import get_redoc_html, get_swagger_ui_html
2020-02-03 02:34:50 -07:00
# Project
2020-04-18 23:18:50 -07:00
from hyperglass.log import log
2021-09-15 18:25:37 -07:00
from hyperglass.state import HyperglassState, use_state
2020-07-17 01:06:19 -07:00
from hyperglass.constants import __version__
from hyperglass.models.ui import UIParameters
from hyperglass.exceptions import HyperglassError
2021-09-15 18:25:37 -07:00
from hyperglass.models.api import Query
2021-09-21 07:54:16 -07:00
from hyperglass.models.data import OutputDataModel
from hyperglass.util.typing import is_type
from hyperglass.execution.main import execute
from hyperglass.models.config.params import Params
from hyperglass.models.config.devices import Devices
# Local
2021-12-17 20:17:52 -07:00
from .tasks import send_webhook
from .fake_output import fake_output
def get_state(attr: t.Optional[str] = None):
2021-09-15 18:25:37 -07:00
"""Get hyperglass state as a FastAPI dependency."""
return use_state(attr)
def get_params():
"""Get hyperglass params as FastAPI dependency."""
return use_state("params")
def get_devices():
"""Get hyperglass devices as FastAPI dependency."""
return use_state("devices")
def get_ui_params():
"""Get hyperglass ui_params as FastAPI dependency."""
return use_state("ui_params")
2021-09-15 00:57:45 -07:00
2021-09-15 18:25:37 -07:00
async def query(
query_data: Query,
request: Request,
background_tasks: BackgroundTasks,
state: "HyperglassState" = Depends(get_state),
):
"""Ingest request data pass it to the backend application to perform the query."""
2020-06-26 12:23:11 -07:00
timestamp = datetime.utcnow()
background_tasks.add_task(send_webhook, query_data, request, timestamp)
2020-04-13 01:05:24 -07:00
# Initialize cache
2021-09-15 18:25:37 -07:00
cache = state.redis
2020-04-13 01:05:24 -07:00
# Use hashed query_data string as key for for k/v cache store so
# each command output value is unique.
2021-09-15 00:57:45 -07:00
cache_key = f"hyperglass.query.{query_data.digest()}"
2021-12-17 20:37:03 -07:00
log.info("{!r} starting query execution", query_data)
2020-04-16 09:30:20 -07:00
cache_response = cache.get_map(cache_key, "output")
2020-07-17 01:43:17 -07:00
json_output = False
2020-04-16 23:43:02 -07:00
cached = False
runtime = 65535
2021-12-17 20:37:03 -07:00
2020-04-16 23:43:02 -07:00
if cache_response:
2021-12-17 20:37:03 -07:00
log.debug("{!r} cache hit (cache key {!r})", query_data, cache_key)
2020-04-19 09:50:52 -07:00
2020-04-16 23:43:02 -07:00
# If a cached response exists, reset the expiration time.
cache.expire(cache_key, expire_in=state.params.cache.timeout)
2020-04-16 23:43:02 -07:00
cached = True
runtime = 0
timestamp = cache.get_map(cache_key, "timestamp")
2020-04-16 23:43:02 -07:00
elif not cache_response:
2021-12-17 20:37:03 -07:00
log.debug("{!r} cache miss (cache key {!r})", query_data, cache_key)
2020-04-18 07:58:46 -07:00
timestamp = query_data.timestamp
starttime = time.time()
2021-09-15 18:25:37 -07:00
if state.params.fake_output:
# Return fake, static data for development purposes, if enabled.
2021-12-13 22:15:06 -07:00
output = await fake_output(query_data.device.structured_output or False)
else:
# Pass request to execution module
2021-09-21 07:54:16 -07:00
output = await execute(query_data)
endtime = time.time()
elapsedtime = round(endtime - starttime, 4)
2021-12-17 20:37:03 -07:00
log.debug("{!r} runtime: {!s} seconds", query_data, elapsedtime)
2021-09-21 07:54:16 -07:00
if output is None:
2021-09-15 18:25:37 -07:00
raise HyperglassError(message=state.params.messages.general, alert="danger")
2021-09-21 07:54:16 -07:00
json_output = is_type(output, OutputDataModel)
2020-07-17 01:43:17 -07:00
if json_output:
2021-09-21 07:54:16 -07:00
raw_output = output.export_dict()
2020-05-29 17:47:53 -07:00
else:
2021-09-21 07:54:16 -07:00
raw_output = str(output)
cache.set_map_item(cache_key, "output", raw_output)
cache.set_map_item(cache_key, "timestamp", timestamp)
cache.expire(cache_key, expire_in=state.params.cache.timeout)
2021-12-17 20:37:03 -07:00
log.debug("{!r} cached for {!s} seconds", query_data, state.params.cache.timeout)
2020-04-16 23:43:02 -07:00
runtime = int(round(elapsedtime, 0))
# If it does, return the cached entry
cache_response = cache.get_map(cache_key, "output")
2021-09-21 07:54:16 -07:00
json_output = is_type(cache_response, t.Dict)
2020-07-13 01:54:38 -07:00
response_format = "text/plain"
2020-07-17 01:43:17 -07:00
if json_output:
2020-05-29 17:47:53 -07:00
response_format = "application/json"
2021-12-17 20:37:03 -07:00
log.success("{!r} execution completed", query_data)
2020-04-16 23:43:02 -07:00
return {
"output": cache_response,
"id": cache_key,
"cached": cached,
"runtime": runtime,
2020-04-18 07:58:46 -07:00
"timestamp": timestamp,
2020-05-29 17:47:53 -07:00
"format": response_format,
2020-04-18 07:58:46 -07:00
"random": query_data.random(),
2020-04-16 23:43:02 -07:00
"level": "success",
"keywords": [],
}
2020-01-21 17:27:57 -07:00
async def docs(params: "Params" = Depends(get_params)):
2020-01-21 17:27:57 -07:00
"""Serve custom docs."""
if params.docs.enable:
2020-01-21 17:27:57 -07:00
docs_func_map = {"swagger": get_swagger_ui_html, "redoc": get_redoc_html}
docs_func = docs_func_map[params.docs.mode]
2020-01-21 17:27:57 -07:00
return docs_func(
openapi_url=params.docs.openapi_url, title=params.site_title + " - API Docs"
2020-01-21 17:27:57 -07:00
)
raise HTTPException(detail="Not found", status_code=404)
2020-01-21 17:27:57 -07:00
async def router(id: str, devices: "Devices" = Depends(get_devices)):
2021-09-11 13:56:20 -07:00
"""Get a device's API-facing attributes."""
return devices[id].export_api()
2020-02-01 02:24:52 -10:00
2020-04-18 11:34:23 -07:00
async def routers(devices: "Devices" = Depends(get_devices)):
2021-09-11 13:56:20 -07:00
"""Serve list of configured routers and attributes."""
return devices.export_api()
2020-04-18 11:34:23 -07:00
async def queries(params: "Params" = Depends(get_params)):
2020-02-01 02:24:52 -10:00
"""Serve list of enabled query types."""
return params.queries.list
2020-02-01 02:24:52 -10:00
async def info(params: "Params" = Depends(get_params)):
2020-07-17 01:06:19 -07:00
"""Serve general information about this instance of hyperglass."""
return {
"name": params.site_title,
"organization": params.org_name,
"primary_asn": int(params.primary_asn),
2021-09-11 13:56:20 -07:00
"version": __version__,
2020-07-17 01:06:19 -07:00
}
async def ui_props(ui_params: "UIParameters" = Depends(get_ui_params)):
"""Serve UI configration."""
return ui_params
endpoints = [query, docs, routers, info, ui_props]