mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
add docs via pydantic field schemas
This commit is contained in:
10
.flake8
10
.flake8
@@ -8,14 +8,12 @@ filename=*.py
|
||||
per-file-ignores=
|
||||
# Disable redefinition warning for exception handlers
|
||||
hyperglass/api.py:F811
|
||||
# Disable string length warnings so one can actually read the commands
|
||||
hyperglass/configuration/models/commands.py:E501,C0301
|
||||
hyperglass/configuration/models/docs.py:E501,C0301
|
||||
hyperglass/configuration/models/messages.py:E501,C0301
|
||||
hyperglass/api/models/response.py:E501,C0301
|
||||
# Disable classmethod warning for validator decorators
|
||||
hyperglass/configuration/models/*.py:N805,E0213,R0903
|
||||
hyperglass/models/*.py:N805,E0213,R0903
|
||||
hyperglass/configuration/models/*.py:N805,E0213,R0903
|
||||
# Disable string length warnings so one can actually read the commands
|
||||
hyperglass/configuration/models/*.py:E501,C0301
|
||||
hyperglass/api/models/response.py:E501,C0301
|
||||
ignore=W503,C0330,R504,D202
|
||||
select=B, BLK, C, D, E, F, I, II, N, P, PIE, S, R, W
|
||||
disable-noqa=False
|
||||
|
@@ -51,7 +51,7 @@ app = FastAPI(
|
||||
default_response_class=UJSONResponse,
|
||||
docs_url=None,
|
||||
redoc_url=None,
|
||||
openapi_url=params.docs.openapi_url,
|
||||
openapi_url=params.docs.openapi_uri,
|
||||
)
|
||||
|
||||
# Add Event Handlers
|
||||
@@ -133,7 +133,10 @@ app.add_api_route(
|
||||
tags=[params.docs.query.title],
|
||||
response_class=UJSONResponse,
|
||||
)
|
||||
app.add_api_route(path="/api/docs", endpoint=docs, include_in_schema=False)
|
||||
|
||||
if params.docs.enable:
|
||||
app.add_api_route(path=params.docs.uri, endpoint=docs, include_in_schema=False)
|
||||
|
||||
app.mount("/images", StaticFiles(directory=IMAGES_DIR), name="images")
|
||||
app.mount("/", StaticFiles(directory=UI_DIR, html=True), name="ui")
|
||||
|
||||
|
@@ -1,6 +1,11 @@
|
||||
"""Validation model for Redis cache config."""
|
||||
|
||||
# Standard Library Imports
|
||||
from typing import Union
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import IPvAnyAddress
|
||||
from pydantic import StrictBool
|
||||
from pydantic import StrictInt
|
||||
from pydantic import StrictStr
|
||||
@@ -12,8 +17,26 @@ from hyperglass.configuration.models._utils import HyperglassModel
|
||||
class Cache(HyperglassModel):
|
||||
"""Validation model for params.cache."""
|
||||
|
||||
host: StrictStr = "localhost"
|
||||
port: StrictInt = 6379
|
||||
database: StrictInt = 0
|
||||
timeout: StrictInt = 120
|
||||
show_text: StrictBool = True
|
||||
host: Union[IPvAnyAddress, StrictStr] = Field(
|
||||
"localhost", title="Host", description="Redis server IP address or hostname."
|
||||
)
|
||||
port: StrictInt = Field(6379, title="Port", description="Redis server TCP port.")
|
||||
database: StrictInt = Field(
|
||||
0, title="Database ID", description="Redis server database ID."
|
||||
)
|
||||
timeout: StrictInt = Field(
|
||||
120,
|
||||
title="Timeout",
|
||||
description="Time in seconds query output will be kept in the Redis cache.",
|
||||
)
|
||||
show_text: StrictBool = Field(
|
||||
True,
|
||||
title="Show Text",
|
||||
description="Show the [`cache`](/fixme) text in the hyperglass UI.",
|
||||
)
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Cache"
|
||||
description = "Redis server & cache timeout configuration."
|
||||
|
@@ -1,5 +1,6 @@
|
||||
"""Configuration for API docs feature."""
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import StrictBool
|
||||
from pydantic import StrictStr
|
||||
from pydantic import constr
|
||||
@@ -12,23 +13,49 @@ from hyperglass.configuration.models._utils import HyperglassModel
|
||||
class EndpointConfig(HyperglassModel):
|
||||
"""Validation model for per API endpoint documentation."""
|
||||
|
||||
title: StrictStr
|
||||
description: StrictStr
|
||||
summary: StrictStr
|
||||
title: StrictStr = Field(
|
||||
...,
|
||||
title="Endpoint Title",
|
||||
description="Displayed as the header text above the API endpoint section.",
|
||||
)
|
||||
description: StrictStr = Field(
|
||||
...,
|
||||
title="Endpoint Description",
|
||||
description="Displayed inside each API endpoint section.",
|
||||
)
|
||||
summary: StrictStr = Field(
|
||||
...,
|
||||
title="Endpoint Summary",
|
||||
description="Displayed beside the API endpoint URI.",
|
||||
)
|
||||
|
||||
|
||||
class Docs(HyperglassModel):
|
||||
"""Validation model for params.docs."""
|
||||
|
||||
enable: StrictBool = True
|
||||
mode: constr(regex=r"(swagger|redoc)") = "swagger"
|
||||
uri: AnyUri = "/docs"
|
||||
openapi_url: AnyUri = "/openapi.json"
|
||||
query: EndpointConfig = {
|
||||
"title": "Submit Query",
|
||||
"description": "Request a query response per-location.",
|
||||
"summary": "Query the Looking Glass",
|
||||
}
|
||||
enable: StrictBool = Field(
|
||||
True, title="Enable", description="Enable or disable API documentation."
|
||||
)
|
||||
mode: constr(regex=r"(swagger|redoc)") = Field(
|
||||
"swagger",
|
||||
title="Docs Mode",
|
||||
description="OpenAPI UI library to use for the hyperglass API docs. Currently, the options are [Swagger UI](/fixme) and [Redoc](/fixme).",
|
||||
)
|
||||
uri: AnyUri = Field(
|
||||
"/api/docs",
|
||||
title="URI",
|
||||
description="HTTP URI/path where API documentation can be accessed.",
|
||||
)
|
||||
openapi_uri: AnyUri = Field(
|
||||
"/openapi.json",
|
||||
title="OpenAPI URI",
|
||||
description="Path to the automatically generated `openapi.json` file.",
|
||||
)
|
||||
query: EndpointConfig = EndpointConfig(
|
||||
title="Submit Query",
|
||||
description="Request a query response per-location.",
|
||||
summary="Query the Looking Glass",
|
||||
)
|
||||
devices: EndpointConfig = EndpointConfig(
|
||||
title="Devices",
|
||||
description="List of all devices/locations with associated identifiers, display names, networks, & VRFs.",
|
||||
@@ -39,3 +66,23 @@ class Docs(HyperglassModel):
|
||||
description="List of supported query types.",
|
||||
summary="Query Types",
|
||||
)
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "API Docs"
|
||||
description = "API documentation configuration parameters"
|
||||
fields = {
|
||||
"query": {
|
||||
"title": "Query API Endpoint",
|
||||
"description": "`/api/query/` API documentation options.",
|
||||
},
|
||||
"devices": {
|
||||
"title": "Devices API Endpoint",
|
||||
"description": "`/api/devices` API documentation options.",
|
||||
},
|
||||
"queries": {
|
||||
"title": "Queries API Endpoint",
|
||||
"description": "`/api/devices` API documentation options.",
|
||||
},
|
||||
}
|
||||
|
@@ -1,6 +1,7 @@
|
||||
"""Validate error message configuration variables."""
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import StrictStr
|
||||
|
||||
# Project Imports
|
||||
@@ -10,25 +11,78 @@ from hyperglass.configuration.models._utils import HyperglassModel
|
||||
class Messages(HyperglassModel):
|
||||
"""Validation model for params.messages."""
|
||||
|
||||
no_input: StrictStr = "{field} must be specified."
|
||||
acl_denied: StrictStr = "{target} is a member of {denied_network}, which is not allowed."
|
||||
acl_not_allowed: StrictStr = "{target} is not allowed."
|
||||
max_prefix: StrictStr = (
|
||||
"Prefix length must be shorter than /{max_length}. {target} is too specific."
|
||||
no_input: StrictStr = Field(
|
||||
"{field} must be specified.",
|
||||
title="No Input",
|
||||
description="Displayed when no a required field is not specified. `{field}` may be used to display the `display_name` of the field that was omitted.",
|
||||
)
|
||||
requires_ipv6_cidr: StrictStr = (
|
||||
"{device_name} requires IPv6 BGP lookups to be in CIDR notation."
|
||||
acl_denied: StrictStr = Field(
|
||||
"{target} is a member of {denied_network}, which is not allowed.",
|
||||
title="ACL - Denied",
|
||||
description="Displayed when a query target is explicitly denied by a matched VRF's ACL entry. `{target}` and `{denied_network}` may be used to display the denied query target and the ACL entry that caused it to be denied.",
|
||||
)
|
||||
feature_not_enabled: StrictStr = "{feature} is not enabled for {device_name}."
|
||||
invalid_input: StrictStr = "{target} is not a valid {query_type} target."
|
||||
invalid_field: StrictStr = "{input} is an invalid {field}."
|
||||
general: StrictStr = "Something went wrong."
|
||||
directed_cidr: StrictStr = "{query_type} queries can not be in CIDR format."
|
||||
request_timeout: StrictStr = "Request timed out."
|
||||
connection_error: StrictStr = "Error connecting to {device_name}: {error}"
|
||||
authentication_error: StrictStr = "Authentication error occurred."
|
||||
noresponse_error: StrictStr = "No response."
|
||||
vrf_not_associated: StrictStr = "VRF {vrf_name} is not associated with {device_name}."
|
||||
vrf_not_found: StrictStr = "VRF {vrf_name} is not defined."
|
||||
no_matching_vrfs: StrictStr = "No VRFs in Common"
|
||||
no_output: StrictStr = "No output."
|
||||
acl_not_allowed: StrictStr = Field(
|
||||
"{target} is not allowed.",
|
||||
title="ACL - Not Allowed",
|
||||
description="Displayed when a query target is implicitly denied by a matched VRF's ACL. `{target}` may be used to display the denied query target.",
|
||||
)
|
||||
feature_not_enabled: StrictStr = Field(
|
||||
"{feature} is not enabled for {device_name}.",
|
||||
title="Feature Not Enabled",
|
||||
description="Displayed when a query type is submitted that is not supported or disabled. The hyperglass UI performs validation of supported query types prior to submitting any requests, so this is primarily relevant to the hyperglass API. `{feature}` and `{device_name}` may be used to display the disabled feature and the selected device/location.",
|
||||
)
|
||||
invalid_input: StrictStr = Field(
|
||||
"{target} is not a valid {query_type} target.",
|
||||
title="Invalid Input",
|
||||
description="Displayed when a query target's value is invalid in relation to the corresponding query type. `{target}` and `{query_type}` maybe used to display the invalid target and corresponding query type.",
|
||||
)
|
||||
invalid_field: StrictStr = Field(
|
||||
"{input} is an invalid {field}.",
|
||||
title="Invalid Field",
|
||||
description="Displayed when a query field contains an invalid or unsupported value. `{input}` and `{field}` may be used to display the invalid input value and corresponding field name.",
|
||||
)
|
||||
general: StrictStr = Field(
|
||||
"Something went wrong.",
|
||||
title="General Error",
|
||||
description="Displayed when generalized errors occur. Seeing this error message may indicate a bug in hyperglass, as most other errors produced are highly contextual. If you see this in the wild, try enabling [debug mode](/fixme) and review the logs to pinpoint the source of the error.",
|
||||
)
|
||||
request_timeout: StrictStr = Field(
|
||||
"Request timed out.",
|
||||
title="Request Timeout",
|
||||
description="Displayed when the [`request_timeout`](/fixme) time expires.",
|
||||
)
|
||||
connection_error: StrictStr = Field(
|
||||
"Error connecting to {device_name}: {error}",
|
||||
title="Displayed when hyperglass is unable to connect to a configured device. Usually, this indicates a configuration error. `{device_name}` and `{error}` may be used to display the device in question and the specific connection error.",
|
||||
)
|
||||
authentication_error: StrictStr = Field(
|
||||
"Authentication error occurred.",
|
||||
title="Authentication Error",
|
||||
description="Displayed when hyperglass is unable to authenticate to a configured device. Usually, this indicates a configuration error.",
|
||||
)
|
||||
no_response: StrictStr = Field(
|
||||
"No response.",
|
||||
title="No Response",
|
||||
description="Displayed when hyperglass can connect to a device, but no output able to be read. Seeing this error may indicate a bug in hyperglas or one of its dependencies. If you see this in the wild, try enabling [debug mode](/fixme) and review the logs to pinpoint the source of the error.",
|
||||
)
|
||||
vrf_not_associated: StrictStr = Field(
|
||||
"VRF {vrf_name} is not associated with {device_name}.",
|
||||
title="VRF Not Associated",
|
||||
description="Displayed when a query request's VRF field value contains a VRF that is not configured or associated with the corresponding location/device. The hyperglass UI automatically filters out VRFs that are not configured on a selected device, so this error is most likely to appear when using the hyperglass API. `{vrf_name}` and `{device_name}` may be used to display the VRF in question and corresponding device.",
|
||||
)
|
||||
vrf_not_found: StrictStr = Field(
|
||||
"VRF {vrf_name} is not defined.",
|
||||
title="VRF Not Found",
|
||||
description="Displayed when a query VRF is not configured on any devices. The hyperglass UI only shows configured VRFs, so this error is most likely to appear when using the hyperglass API. `{vrf_name}` may be used to display the VRF in question.",
|
||||
)
|
||||
no_output: StrictStr = Field(
|
||||
"No output.",
|
||||
title="No Output",
|
||||
description="Displayed when hyperglass can connect to a device and execute a query, but the response is empty.",
|
||||
)
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Messages"
|
||||
description = "Customize almost all user-facing UI & API messages."
|
||||
|
@@ -1,6 +1,7 @@
|
||||
"""Validate network configuration variables."""
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import StrictStr
|
||||
|
||||
# Project Imports
|
||||
@@ -11,8 +12,16 @@ from hyperglass.configuration.models._utils import clean_name
|
||||
class Network(HyperglassModel):
|
||||
"""Validation Model for per-network/asn config in devices.yaml."""
|
||||
|
||||
name: StrictStr
|
||||
display_name: StrictStr
|
||||
name: StrictStr = Field(
|
||||
...,
|
||||
title="Network Name",
|
||||
description="Internal name of the device's primary network.",
|
||||
)
|
||||
display_name: StrictStr = Field(
|
||||
...,
|
||||
title="Network Display Name",
|
||||
description="Display name of the device's primary network.",
|
||||
)
|
||||
|
||||
|
||||
class Networks(HyperglassModel):
|
||||
|
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
import PIL.Image as PilImage
|
||||
from pydantic import FilePath
|
||||
from pydantic import StrictInt
|
||||
from pydantic import validator
|
||||
from pydantic import root_validator
|
||||
|
||||
# Project Imports
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
@@ -19,8 +19,8 @@ class OpenGraph(HyperglassModel):
|
||||
height: Optional[StrictInt]
|
||||
image: Optional[FilePath]
|
||||
|
||||
@validator("image")
|
||||
def validate_image(cls, value, values):
|
||||
@root_validator
|
||||
def validate_image(cls, values):
|
||||
"""Set default opengraph image location.
|
||||
|
||||
Arguments:
|
||||
@@ -29,14 +29,48 @@ class OpenGraph(HyperglassModel):
|
||||
Returns:
|
||||
{Path} -- Opengraph image file path object
|
||||
"""
|
||||
if value is None:
|
||||
value = (
|
||||
supported_extensions = (".jpg", ".jpeg", ".png")
|
||||
if (
|
||||
values["image"].suffix is not None
|
||||
and values["image"] not in supported_extensions
|
||||
):
|
||||
raise ValueError(
|
||||
"OpenGraph image must be one of {e}".format(
|
||||
e=", ".join(supported_extensions)
|
||||
)
|
||||
)
|
||||
if values["image"] is None:
|
||||
image = (
|
||||
Path(__file__).parent.parent.parent
|
||||
/ "static/images/hyperglass-opengraph.png"
|
||||
)
|
||||
with PilImage.open(value) as img:
|
||||
values["image"] = "".join(str(image).split("static")[1::])
|
||||
|
||||
with PilImage.open(image) as img:
|
||||
width, height = img.size
|
||||
if values["width"] is None:
|
||||
values["width"] = width
|
||||
if values["height"] is None:
|
||||
values["height"] = height
|
||||
|
||||
return "".join(str(value).split("static")[1::])
|
||||
return values
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "OpenGraph"
|
||||
description = "OpenGraph configuration parameters"
|
||||
fields = {
|
||||
"width": {
|
||||
"title": "Width",
|
||||
"description": "Width of OpenGraph image. If unset, the width will be automatically derived by reading the image file.",
|
||||
},
|
||||
"height": {
|
||||
"title": "Height",
|
||||
"description": "Height of OpenGraph image. If unset, the height will be automatically derived by reading the image file.",
|
||||
},
|
||||
"image": {
|
||||
"title": "Image File",
|
||||
"description": "Valid path to a JPG or PNG file to use as the OpenGraph image.",
|
||||
},
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ from typing import Optional
|
||||
from typing import Union
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import FilePath
|
||||
from pydantic import IPvAnyAddress
|
||||
from pydantic import StrictBool
|
||||
@@ -29,13 +30,38 @@ class Params(HyperglassModel):
|
||||
"""Validation model for all configuration variables."""
|
||||
|
||||
# Top Level Params
|
||||
debug: StrictBool = False
|
||||
developer_mode: StrictBool = False
|
||||
primary_asn: Union[StrictInt, StrictStr] = "65001"
|
||||
org_name: StrictStr = "Beloved Hyperglass User"
|
||||
site_title: StrictStr = "hyperglass"
|
||||
site_description: StrictStr = "{org_name} Network Looking Glass"
|
||||
site_keywords: List[StrictStr] = [
|
||||
debug: StrictBool = Field(
|
||||
False,
|
||||
title="Debug",
|
||||
description="Enable debug mode. Warning: this will generate a *lot* of log output.",
|
||||
)
|
||||
developer_mode: StrictBool = Field(
|
||||
False,
|
||||
title="Developer Mode",
|
||||
description='Enable developer mode. If enabled, the hyperglass backend (Python) and frontend (React/Javascript) applications are "unlinked", so that React tools can be used for front end development. A `<Debugger />` convenience component is also displayed in the UI for easier UI development.',
|
||||
)
|
||||
primary_asn: Union[StrictInt, StrictStr] = Field(
|
||||
"65001",
|
||||
title="Primary ASN",
|
||||
description="Your network's primary ASN. This field is used to set some useful defaults such as the subtitle and PeeringDB URL.",
|
||||
)
|
||||
org_name: StrictStr = Field(
|
||||
"Beloved Hyperglass User",
|
||||
title="Organization Name",
|
||||
description="Your organization's name. This field is used in the UI & API documentation to set fields such as `<meta/>` HTML tags for SEO and the terms & conditions footer component.",
|
||||
)
|
||||
site_title: StrictStr = Field(
|
||||
"hyperglass",
|
||||
title="Site Title",
|
||||
description="The name of your hyperglass site. This field is used in the UI & API documentation to set fields such as the `<title/>` HTML tag, and the terms & conditions footer component.",
|
||||
)
|
||||
site_description: StrictStr = Field(
|
||||
"{org_name} Network Looking Glass",
|
||||
title="Site Description",
|
||||
description='A short description of your hyperglass site. This field is used in th UI & API documentation to set the `<meta name="description"/>` tag. `{org_name}` may be used to insert the value of the `org_name` field.',
|
||||
)
|
||||
site_keywords: List[StrictStr] = Field(
|
||||
[
|
||||
"hyperglass",
|
||||
"looking glass",
|
||||
"lg",
|
||||
@@ -50,13 +76,27 @@ class Params(HyperglassModel):
|
||||
"routing",
|
||||
"network",
|
||||
"isp",
|
||||
]
|
||||
requires_ipv6_cidr: List[StrictStr] = ["cisco_ios", "cisco_nxos"]
|
||||
request_timeout: StrictInt = 30
|
||||
],
|
||||
title="Site Keywords",
|
||||
description='Keywords pertaining to your hyperglass site. This field is used to generate `<meta name="keywords"/>` HTML tags, which helps tremendously with SEO.',
|
||||
)
|
||||
request_timeout: StrictInt = Field(
|
||||
30,
|
||||
title="Request Timeout",
|
||||
description="Global timeout in seconds for all requests. The frontend application (UI) uses this field's exact value when submitting queries. The backend application uses this field's value, minus one second, for its own timeout handling. This is to ensure a contextual timeout error is presented to the end user in the event of a backend application timeout.",
|
||||
)
|
||||
listen_address: Optional[Union[IPvAnyAddress, StrictStr]]
|
||||
listen_port: StrictInt = 8001
|
||||
listen_port: StrictInt = Field(
|
||||
8001,
|
||||
title="Listen Port",
|
||||
description="Local TCP port the hyperglass application listens on to serve web traffic.",
|
||||
)
|
||||
log_file: Optional[FilePath]
|
||||
cors_origins: List[StrictStr] = []
|
||||
cors_origins: List[StrictStr] = Field(
|
||||
[],
|
||||
title="Cross-Origin Resource Sharing",
|
||||
description="Allowed CORS hosts. By default, no CORS hosts are allowed.",
|
||||
)
|
||||
|
||||
# Sub Level Params
|
||||
cache: Cache = Cache()
|
||||
@@ -65,6 +105,20 @@ class Params(HyperglassModel):
|
||||
queries: Queries = Queries()
|
||||
web: Web = Web()
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
fields = {
|
||||
"listen_address": {
|
||||
"title": "Listen Address",
|
||||
"description": "Local IP Address or hostname the hyperglass application listens on to serve web traffic.",
|
||||
},
|
||||
"log_file": {
|
||||
"title": "Log File",
|
||||
"description": "Path to a log file to which hyperglass can write logs. If none is set, hyperglass will write logs to a file located at `/tmp/`, with a uniquely generated name for each time hyperglass is started.",
|
||||
},
|
||||
}
|
||||
|
||||
@validator("listen_address", pre=True, always=True)
|
||||
def validate_listen_address(cls, value, values):
|
||||
"""Set default listen_address based on debug mode.
|
||||
|
@@ -1,8 +1,8 @@
|
||||
"""Validate query configuration parameters."""
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import StrictBool
|
||||
from pydantic import StrictInt
|
||||
from pydantic import StrictStr
|
||||
from pydantic import constr
|
||||
|
||||
@@ -11,73 +11,136 @@ from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.constants import SUPPORTED_QUERY_TYPES
|
||||
|
||||
|
||||
class BgpCommunityPattern(HyperglassModel):
|
||||
"""Validation model for bgp_community regex patterns."""
|
||||
|
||||
decimal: StrictStr = Field(
|
||||
r"^[0-9]{1,10}$",
|
||||
title="Decimal Community",
|
||||
description="Regular expression pattern for validating decimal type BGP Community strings.",
|
||||
)
|
||||
extended_as: StrictStr = Field(
|
||||
r"^([0-9]{0,5})\:([0-9]{1,5})$",
|
||||
title="Extended AS Community",
|
||||
description="Regular expression pattern for validating extended AS type BGP Community strings, e.g. `65000:1`",
|
||||
)
|
||||
large: StrictStr = Field(
|
||||
r"^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$",
|
||||
title="Large Community",
|
||||
description="Regular expression pattern for validating large community strings, e.g. `65000:65001:65002`",
|
||||
)
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Pattern"
|
||||
description = (
|
||||
"Regular expression patterns used to validate BGP Community queries."
|
||||
)
|
||||
|
||||
|
||||
class BgpAsPathPattern(HyperglassModel):
|
||||
"""Validation model for bgp_aspath regex patterns."""
|
||||
|
||||
mode: constr(regex=r"asplain|asdot") = Field(
|
||||
"asplain",
|
||||
title="AS Path Mode",
|
||||
description="Set ASN display mode. This field is dependent on how your network devices are configured.",
|
||||
)
|
||||
asplain: StrictStr = Field(
|
||||
r"^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$",
|
||||
title="AS Plain",
|
||||
description="Regular expression pattern for validating [AS Plain](/fixme) type BGP AS Path queries.",
|
||||
)
|
||||
asdot: StrictStr = Field(
|
||||
r"^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$",
|
||||
title="AS Dot",
|
||||
description="Regular expression pattern for validating [AS Dot](/fixme) type BGP AS Path queries.",
|
||||
)
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Pattern"
|
||||
description = (
|
||||
"Regular expression patterns used to validate BGP AS Path queries."
|
||||
)
|
||||
|
||||
|
||||
class BgpCommunity(HyperglassModel):
|
||||
"""Validation model for bgp_community configuration."""
|
||||
|
||||
class Pattern(HyperglassModel):
|
||||
"""Validation model for bgp_community regex patterns."""
|
||||
|
||||
decimal: StrictStr = r"^[0-9]{1,10}$"
|
||||
extended_as: StrictStr = r"^([0-9]{0,5})\:([0-9]{1,5})$"
|
||||
large: StrictStr = r"^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$"
|
||||
|
||||
enable: StrictBool = True
|
||||
display_name: StrictStr = "BGP Community"
|
||||
pattern: Pattern = Pattern()
|
||||
enable: StrictBool = Field(
|
||||
True,
|
||||
title="Enable",
|
||||
description="Enable or disable the BGP Community query type.",
|
||||
)
|
||||
display_name: StrictStr = Field(
|
||||
"BGP Community",
|
||||
title="Display Name",
|
||||
description="Text displayed for the BGP Community query type in the hyperglas UI.",
|
||||
)
|
||||
pattern: BgpCommunityPattern = BgpCommunityPattern()
|
||||
|
||||
|
||||
class BgpRoute(HyperglassModel):
|
||||
"""Validation model for bgp_route configuration."""
|
||||
|
||||
enable: StrictBool = True
|
||||
display_name: StrictStr = "BGP Route"
|
||||
enable: StrictBool = Field(
|
||||
True, title="Enable", description="Enable or disable the BGP Route query type."
|
||||
)
|
||||
display_name: StrictStr = Field(
|
||||
"BGP Route",
|
||||
title="Display Name",
|
||||
description="Text displayed for the BGP Route query type in the hyperglas UI.",
|
||||
)
|
||||
|
||||
|
||||
class BgpAsPath(HyperglassModel):
|
||||
"""Validation model for bgp_aspath configuration."""
|
||||
|
||||
enable: StrictBool = True
|
||||
display_name: StrictStr = "BGP AS Path"
|
||||
|
||||
class Pattern(HyperglassModel):
|
||||
"""Validation model for bgp_aspath regex patterns."""
|
||||
|
||||
mode: constr(regex="asplain|asdot") = "asplain"
|
||||
asplain: StrictStr = r"^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$"
|
||||
asdot: StrictStr = (
|
||||
r"^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$"
|
||||
enable: StrictBool = Field(
|
||||
True,
|
||||
title="Enable",
|
||||
description="Enable or disable the BGP AS Path query type.",
|
||||
)
|
||||
|
||||
pattern: Pattern = Pattern()
|
||||
display_name: StrictStr = Field(
|
||||
"BGP AS Path",
|
||||
title="Display Name",
|
||||
description="Text displayed for the BGP AS Path query type in the hyperglas UI.",
|
||||
)
|
||||
pattern: BgpAsPathPattern = BgpAsPathPattern()
|
||||
|
||||
|
||||
class Ping(HyperglassModel):
|
||||
"""Validation model for ping configuration."""
|
||||
|
||||
enable: StrictBool = True
|
||||
display_name: StrictStr = "Ping"
|
||||
enable: StrictBool = Field(
|
||||
True, title="Enable", description="Enable or disable the Ping query type."
|
||||
)
|
||||
display_name: StrictStr = Field(
|
||||
"Ping",
|
||||
title="Display Name",
|
||||
description="Text displayed for the Ping query type in the hyperglas UI.",
|
||||
)
|
||||
|
||||
|
||||
class Traceroute(HyperglassModel):
|
||||
"""Validation model for traceroute configuration."""
|
||||
|
||||
enable: StrictBool = True
|
||||
display_name: StrictStr = "Traceroute"
|
||||
enable: StrictBool = Field(
|
||||
True, title="Enable", description="Enable or disable the Traceroute query type."
|
||||
)
|
||||
display_name: StrictStr = Field(
|
||||
"Traceroute",
|
||||
title="Display Name",
|
||||
description="Text displayed for the Traceroute query type in the hyperglas UI.",
|
||||
)
|
||||
|
||||
|
||||
class Queries(HyperglassModel):
|
||||
"""Validation model for all query types."""
|
||||
|
||||
class MaxPrefix(HyperglassModel):
|
||||
"""Validation model for params.features.max_prefix."""
|
||||
|
||||
enable: StrictBool = False
|
||||
ipv4: StrictInt = 24
|
||||
ipv6: StrictInt = 64
|
||||
message: StrictStr = (
|
||||
"Prefix length must be smaller than /{m}. <b>{i}</b> is too specific."
|
||||
)
|
||||
|
||||
@property
|
||||
def map(self):
|
||||
"""Return a dict of all query display names, internal names, and enable state.
|
||||
@@ -119,4 +182,31 @@ class Queries(HyperglassModel):
|
||||
bgp_aspath: BgpAsPath = BgpAsPath()
|
||||
ping: Ping = Ping()
|
||||
traceroute: Traceroute = Traceroute()
|
||||
max_prefix: MaxPrefix = MaxPrefix()
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Queries"
|
||||
description = "Enable, disable, or configure query types."
|
||||
fields = {
|
||||
"bgp_route": {
|
||||
"title": "BGP Route",
|
||||
"description": "Enable, disable, or configure the BGP Route query type.",
|
||||
},
|
||||
"bgp_community": {
|
||||
"title": "BGP Community",
|
||||
"description": "Enable, disable, or configure the BGP Community query type.",
|
||||
},
|
||||
"bgp_aspath": {
|
||||
"title": "BGP AS Path",
|
||||
"description": "Enable, disable, or configure the BGP AS Path query type.",
|
||||
},
|
||||
"ping": {
|
||||
"title": "Ping",
|
||||
"description": "Enable, disable, or configure the Ping query type.",
|
||||
},
|
||||
"traceroute": {
|
||||
"title": "Traceroute",
|
||||
"description": "Enable, disable, or configure the Traceroute query type.",
|
||||
},
|
||||
}
|
||||
|
@@ -1,6 +1,10 @@
|
||||
"""Validate SSL configuration variables."""
|
||||
|
||||
# Standard Library Imports
|
||||
from typing import Optional
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import FilePath
|
||||
from pydantic import StrictBool
|
||||
|
||||
@@ -11,5 +15,21 @@ from hyperglass.configuration.models._utils import HyperglassModel
|
||||
class Ssl(HyperglassModel):
|
||||
"""Validate SSL config parameters."""
|
||||
|
||||
enable: StrictBool = True
|
||||
cert: FilePath
|
||||
enable: StrictBool = Field(
|
||||
True,
|
||||
title="Enable SSL",
|
||||
description="If enabled, hyperglass will use HTTPS to connect to the configured device running [hyperglass-agent](/fixme). If enabled, a certificate file must be specified (hyperglass does not support connecting to a device over an unverified SSL session.)",
|
||||
)
|
||||
cert: Optional[FilePath]
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "SSL"
|
||||
description = "SSL configuration for devices running hyperglass-agent."
|
||||
fields = {
|
||||
"cert": {
|
||||
"title": "Certificate",
|
||||
"description": "Valid path to an SSL certificate. This certificate must be the public key used to serve the hyperglass-agent API on the device running hyperglass-agent.",
|
||||
}
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ from typing import List
|
||||
from typing import Optional
|
||||
|
||||
# Third Party Imports
|
||||
from pydantic import Field
|
||||
from pydantic import FilePath
|
||||
from pydantic import StrictBool
|
||||
from pydantic import StrictStr
|
||||
@@ -25,10 +26,26 @@ from hyperglass.configuration.models._utils import HyperglassModelExtra
|
||||
class AccessList4(HyperglassModel):
|
||||
"""Validation model for IPv4 access-lists."""
|
||||
|
||||
network: IPv4Network = "0.0.0.0/0"
|
||||
action: constr(regex="permit|deny") = "permit"
|
||||
ge: conint(ge=0, le=32) = 0
|
||||
le: conint(ge=0, le=32) = 32
|
||||
network: IPv4Network = Field(
|
||||
"0.0.0.0/0",
|
||||
title="Network",
|
||||
description="IPv4 Network/Prefix that should be permitted or denied. ",
|
||||
)
|
||||
action: constr(regex=r"permit|deny") = Field(
|
||||
"permit",
|
||||
title="Action",
|
||||
description="Permit or deny any networks contained within the prefix.",
|
||||
)
|
||||
ge: conint(ge=0, le=32) = Field(
|
||||
0,
|
||||
title="Greater Than or Equal To",
|
||||
description="Similar to `ge` in a Cisco prefix-list, the `ge` field defines the **bottom** threshold for prefix size. For example, a value of `24` would result in a query for `192.0.2.0/23` being denied, but a query for `192.0.2.0/32` would be permitted. If this field is set to a value smaller than the `network` field's prefix length, this field's value will be overwritten to the prefix length of the prefix in the `network` field.",
|
||||
)
|
||||
le: conint(ge=0, le=32) = Field(
|
||||
32,
|
||||
title="Less Than or Equal To",
|
||||
description="Similar to `le` in a Cisco prefix-list, the `le` field defines the **top** threshold for prefix size. For example, a value of `24` would result in a query for `192.0.2.0/23` being permitted, but a query for `192.0.2.0/32` would be denied.",
|
||||
)
|
||||
|
||||
@validator("ge")
|
||||
def validate_model(cls, value, values):
|
||||
@@ -50,10 +67,31 @@ class AccessList4(HyperglassModel):
|
||||
class AccessList6(HyperglassModel):
|
||||
"""Validation model for IPv6 access-lists."""
|
||||
|
||||
network: IPv6Network = "::/0"
|
||||
action: constr(regex=r"permit|deny") = "permit"
|
||||
ge: conint(ge=0, le=128) = 0
|
||||
le: conint(ge=0, le=128) = 128
|
||||
network: IPv6Network = Field(
|
||||
"::/0",
|
||||
title="Network",
|
||||
description="IPv6 Network/Prefix that should be permitted or denied. ",
|
||||
)
|
||||
action: constr(regex=r"permit|deny") = Field(
|
||||
"permit",
|
||||
title="Action",
|
||||
description="Permit or deny any networks contained within the prefix.",
|
||||
# regex="permit|deny",
|
||||
)
|
||||
ge: conint(ge=0, le=128) = Field(
|
||||
0,
|
||||
title="Greater Than or Equal To",
|
||||
description="Similar to `ge` in a Cisco prefix-list, the `ge` field defines the **bottom** threshold for prefix size. For example, a value of `64` would result in a query for `2001:db8::/48` being denied, but a query for `2001:db8::1/128` would be permitted. If this field is set to a value smaller than the `network` field's prefix length, this field's value will be overwritten to the prefix length of the prefix in the `network` field.",
|
||||
# ge=0,
|
||||
# le=128,
|
||||
)
|
||||
le: conint(ge=0, le=128) = Field(
|
||||
128,
|
||||
title="Less Than or Equal To",
|
||||
description="Similar to `le` in a Cisco prefix-list, the `le` field defines the **top** threshold for prefix size. For example, a value of `64` would result in a query for `2001:db8::/48` being permitted, but a query for `2001:db8::1/128` would be denied.",
|
||||
# ge=0,
|
||||
# le=128,
|
||||
)
|
||||
|
||||
@validator("ge")
|
||||
def validate_model(cls, value, values):
|
||||
@@ -77,6 +115,12 @@ class InfoConfigParams(HyperglassModelExtra):
|
||||
|
||||
title: Optional[StrictStr]
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "Help Parameters"
|
||||
description = "Set dynamic or reusable values which may be used in the help/information content. Params my be access by using Python string formatting syntax, e.g. `{param_name}`. Any arbitrary values may be added."
|
||||
|
||||
|
||||
class InfoConfig(HyperglassModel):
|
||||
"""Validation model for help configuration."""
|
||||
@@ -85,6 +129,20 @@ class InfoConfig(HyperglassModel):
|
||||
file: Optional[FilePath]
|
||||
params: InfoConfigParams = InfoConfigParams()
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
fields = {
|
||||
"enable": {
|
||||
"title": "Enable",
|
||||
"description": "Enable or disable the display of help/information content for this query type.",
|
||||
},
|
||||
"file": {
|
||||
"title": "File Name",
|
||||
"description": "Path to a valid text or Markdown file containing custom content.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class Info(HyperglassModel):
|
||||
"""Validation model for per-VRF, per-Command help."""
|
||||
@@ -95,6 +153,34 @@ class Info(HyperglassModel):
|
||||
ping: InfoConfig = InfoConfig()
|
||||
traceroute: InfoConfig = InfoConfig()
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
title = "VRF Information"
|
||||
description = "Per-VRF help & information content."
|
||||
fields = {
|
||||
"bgp_aspath": {
|
||||
"title": "BGP AS Path",
|
||||
"description": "Show information about bgp_aspath queries when selected.",
|
||||
},
|
||||
"bgp_community": {
|
||||
"title": "BGP Community",
|
||||
"description": "Show information about bgp_community queries when selected.",
|
||||
},
|
||||
"bgp_route": {
|
||||
"title": "BGP Route",
|
||||
"description": "Show information about bgp_route queries when selected.",
|
||||
},
|
||||
"ping": {
|
||||
"title": "Ping",
|
||||
"description": "Show information about ping queries when selected.",
|
||||
},
|
||||
"traceroute": {
|
||||
"title": "Traceroute",
|
||||
"description": "Show information about traceroute queries when selected.",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
class DeviceVrf4(HyperglassModelExtra):
|
||||
"""Validation model for IPv4 AFI definitions."""
|
||||
@@ -186,25 +272,18 @@ class Vrf(HyperglassModel):
|
||||
result = self.name == other.name
|
||||
return result
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
class DefaultVrf(HyperglassModel):
|
||||
"""Validation model for default routing table VRF."""
|
||||
|
||||
name: constr(regex="default") = "default"
|
||||
display_name: StrictStr = "Global"
|
||||
info: Info = Info()
|
||||
|
||||
class DefaultVrf4(HyperglassModel):
|
||||
"""Validation model for IPv4 default routing table VRF definition."""
|
||||
|
||||
source_address: IPv4Address
|
||||
access_list: List[AccessList4] = [AccessList4()]
|
||||
|
||||
class DefaultVrf6(HyperglassModel):
|
||||
"""Validation model for IPv6 default routing table VRF definition."""
|
||||
|
||||
source_address: IPv6Address
|
||||
access_list: List[AccessList6] = [AccessList6()]
|
||||
|
||||
ipv4: Optional[DefaultVrf4]
|
||||
ipv6: Optional[DefaultVrf6]
|
||||
title = "VRF"
|
||||
description = "Per-VRF configuration."
|
||||
fields = {
|
||||
"name": {
|
||||
"title": "Name",
|
||||
"description": "VRF name as configured on the router/device.",
|
||||
},
|
||||
"display_name": {
|
||||
"title": "Display Name",
|
||||
"description": "Display name of VRF for use in the hyperglass UI. If none is specified, hyperglass will attempt to generate one.",
|
||||
},
|
||||
}
|
||||
|
@@ -169,7 +169,7 @@ class Connect:
|
||||
params.messages.connection_error,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.noresponse_error,
|
||||
error=params.messages.no_response,
|
||||
)
|
||||
signal.alarm(0)
|
||||
return response
|
||||
@@ -243,7 +243,7 @@ class Connect:
|
||||
params.messages.connection_error,
|
||||
device_name=self.device.display_name,
|
||||
proxy=None,
|
||||
error=params.messages.noresponse_error,
|
||||
error=params.messages.no_response,
|
||||
)
|
||||
signal.alarm(0)
|
||||
return response
|
||||
@@ -337,7 +337,7 @@ class Connect:
|
||||
raise RestError(
|
||||
params.messages.connection_error,
|
||||
device_name=self.device.display_name,
|
||||
error=params.messages.noresponse_error,
|
||||
error=params.messages.no_response,
|
||||
)
|
||||
|
||||
return response
|
||||
|
Reference in New Issue
Block a user