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

additions for new ui

This commit is contained in:
checktheroads
2020-01-16 02:52:00 -07:00
parent b901568997
commit 5c6ac2336a
6 changed files with 292 additions and 7 deletions

View File

@@ -1,13 +1,17 @@
"""Validate branding configuration variables."""
# Standard Library Imports
from pathlib import Path
from typing import Optional
# Third Party Imports
from pydantic import FilePath
from pydantic import HttpUrl
from pydantic import StrictBool
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import constr
from pydantic import root_validator
from pydantic import validator
from pydantic.color import Color
@@ -65,11 +69,14 @@ class Branding(HyperglassModel):
"""Validation model for params.branding.help_menu."""
enable: StrictBool = True
file: Optional[FilePath]
title: StrictStr = "Help"
class Logo(HyperglassModel):
"""Validation model for params.branding.logo."""
logo_path: StrictStr = "ui/images/hyperglass-dark.png"
light: Optional[FilePath]
dark: Optional[FilePath]
width: StrictInt = 384
height: Optional[StrictInt]
favicons: StrictStr = "ui/images/favicons/"
@@ -82,20 +89,72 @@ class Branding(HyperglassModel):
chars.append("/")
return "".join(chars)
@root_validator(pre=True)
def validate_logo_model(cls, values):
"""Set default opengraph image location.
Arguments:
values {dict} -- Unvalidated model
Returns:
{dict} -- Modified model
"""
logo_light = values.get("light")
logo_dark = values.get("dark")
default_logo_light = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-light.png"
)
default_logo_dark = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-dark.png"
)
# Use light logo as dark logo if dark logo is undefined.
if logo_light is not None and logo_dark is None:
values["dark"] = logo_light
# Use dark logo as light logo if light logo is undefined.
if logo_dark is not None and logo_light is None:
values["light"] = logo_dark
# Set default logo paths if logo is undefined.
if logo_light is None and logo_dark is None:
values["light"] = default_logo_light
values["dark"] = default_logo_dark
return values
@validator("light", "dark")
def validate_logos(cls, value):
"""Convert file path to URL path.
Arguments:
value {FilePath} -- Path to logo file.
Returns:
{str} -- Formatted logo path
"""
return "".join(str(value).split("static")[1::])
class Config:
"""Override pydantic config."""
fields = {"logo_path": "path"}
class PeeringDb(HyperglassModel):
"""Validation model for params.branding.peering_db."""
class ExternalLink(HyperglassModel):
"""Validation model for params.branding.external_link."""
enable: StrictBool = True
title: StrictStr = "PeeringDB"
url: HttpUrl = "https://www.peeringdb.com/AS{primary_asn}"
class Terms(HyperglassModel):
"""Validation model for params.branding.terms."""
enable: StrictBool = True
file: Optional[FilePath]
title: StrictStr = "Terms"
class Text(HyperglassModel):
"""Validation model for params.branding.text."""
@@ -138,6 +197,6 @@ class Branding(HyperglassModel):
font: Font = Font()
help_menu: HelpMenu = HelpMenu()
logo: Logo = Logo()
peering_db: PeeringDb = PeeringDb()
external_link: ExternalLink = ExternalLink()
terms: Terms = Terms()
text: Text = Text()

View File

@@ -17,6 +17,7 @@ from pydantic import validator
# Project Imports
from hyperglass.configuration.models._utils import HyperglassModel
from hyperglass.configuration.models.opengraph import OpenGraph
class General(HyperglassModel):
@@ -25,6 +26,24 @@ class General(HyperglassModel):
debug: StrictBool = False
primary_asn: StrictStr = "65001"
org_name: StrictStr = "The Company"
site_description: StrictStr = "{org_name} Network Looking Glass"
site_keywords: List[StrictStr] = [
"hyperglass",
"looking glass",
"lg",
"peer",
"peering",
"ipv4",
"ipv6",
"transit",
"community",
"communities",
"bgp",
"routing",
"network",
"isp",
]
opengraph: OpenGraph = OpenGraph()
google_analytics: StrictStr = ""
redis_host: StrictStr = "localhost"
redis_port: StrictInt = 6379
@@ -34,6 +53,19 @@ class General(HyperglassModel):
listen_port: StrictInt = 8001
log_file: Optional[FilePath]
@validator("site_description")
def validate_site_description(cls, value, values):
"""Format the site descripion with the org_name field.
Arguments:
value {str} -- site_description
values {str} -- Values before site_description
Returns:
{str} -- Formatted description
"""
return value.format(org_name=values["org_name"])
@validator("log_file")
def validate_log_file(cls, value):
"""Set default logfile location if none is configured.

View File

@@ -0,0 +1,42 @@
# Standard Library Imports
from pathlib import Path
from typing import Optional
# Third Party Imports
import PIL.Image as PilImage
from pydantic import FilePath
from pydantic import StrictInt
from pydantic import validator
# Project Imports
from hyperglass.configuration.models._utils import HyperglassModel
class OpenGraph(HyperglassModel):
"""Validation model for params.general.opengraph."""
width: Optional[StrictInt]
height: Optional[StrictInt]
image: Optional[FilePath]
@validator("image")
def validate_image(cls, value, values):
"""Set default opengraph image location.
Arguments:
value {FilePath} -- Path to opengraph image file.
Returns:
{Path} -- Opengraph image file path object
"""
if value is None:
value = (
Path(__file__).parent.parent.parent
/ "static/ui/images/hyperglass-opengraph.png"
)
with PilImage.open(value) as img:
width, height = img.size
values["width"] = width
values["height"] = height
return "".join(str(value).split("static")[1::])

View File

@@ -149,7 +149,7 @@ class Router(HyperglassModel):
log.debug(
f'Field "display_name" for VRF "{vrf["name"]}" was not set. '
f'Generated "display_name" {vrf["display_name"]}'
f"Generated '{vrf['display_name']}'"
)
# Validate the non-default VRF against the standard
@@ -167,6 +167,7 @@ class Routers(HyperglassModelExtra):
vrfs: List[StrictStr] = []
display_vrfs: List[StrictStr] = []
routers: List[Router] = []
networks: List[StrictStr] = []
@classmethod
def _import(cls, input_params):
@@ -183,7 +184,9 @@ class Routers(HyperglassModelExtra):
{object} -- Validated routers object
"""
vrfs = set()
networks = set()
display_vrfs = set()
vrf_objects = set()
routers = Routers()
routers.routers = []
routers.hostnames = []
@@ -224,9 +227,19 @@ class Routers(HyperglassModelExtra):
"display_name": vrf.display_name,
}
# Add the native VRF objects to a set (for automatic
# de-duping), but exlcude device-specific fields.
_copy_params = {
"deep": True,
"exclude": {"ipv4": {"source_address"}, "ipv6": {"source_address"}},
}
vrf_objects.add(vrf.copy(**_copy_params))
# Convert the de-duplicated sets to a standard list, add lists
# as class attributes
routers.vrfs = list(vrfs)
routers.display_vrfs = list(display_vrfs)
routers.vrf_objects = list(vrf_objects)
routers.networks = list(networks)
return routers

View File

@@ -10,6 +10,7 @@ from typing import List
from typing import Optional
# Third Party Imports
from pydantic import FilePath
from pydantic import IPvAnyNetwork
from pydantic import StrictStr
from pydantic import constr
@@ -19,6 +20,13 @@ from pydantic import validator
from hyperglass.configuration.models._utils import HyperglassModel
class Info(HyperglassModel):
"""Validation model for per-VRF help files."""
bgp_aspath: Optional[FilePath]
bgp_community: Optional[FilePath]
class DeviceVrf4(HyperglassModel):
"""Validation model for IPv4 AFI definitions."""
@@ -36,8 +44,9 @@ class DeviceVrf6(HyperglassModel):
class Vrf(HyperglassModel):
"""Validation model for per VRF/afi config in devices.yaml."""
name: str
display_name: str
name: StrictStr
display_name: StrictStr
info: Info = Info()
ipv4: Optional[DeviceVrf4]
ipv6: Optional[DeviceVrf6]
access_list: List[Dict[constr(regex=("allow|deny")), IPvAnyNetwork]] = [
@@ -71,12 +80,32 @@ class Vrf(HyperglassModel):
li[action] = str(network)
return value
def __hash__(self):
"""Make VRF object hashable so the object can be deduplicated with set().
Returns:
{int} -- Hash of VRF name
"""
return hash((self.name,))
def __eq__(self, other):
"""Make VRF object comparable so the object can be deduplicated with set().
Arguments:
other {object} -- Object to compare
Returns:
{bool} -- True if comparison attributes are the same value
"""
return self.name == other.name
class DefaultVrf(HyperglassModel):
"""Validation model for default routing table VRF."""
name: StrictStr = "default"
display_name: StrictStr = "Global"
info: Info = Info()
access_list: List[Dict[constr(regex=("allow|deny")), IPvAnyNetwork]] = [
{"allow": IPv4Network("0.0.0.0/0")},
{"allow": IPv6Network("::/0")},

110
hyperglass/models/query.py Normal file
View File

@@ -0,0 +1,110 @@
"""Input query validation model."""
# Standard Library Imports
import operator
from typing import Optional
# Third Party Imports
from pydantic import BaseModel
from pydantic import StrictStr
from pydantic import validator
# Project Imports
from hyperglass.configuration import devices
from hyperglass.configuration import params
from hyperglass.constants import Supported
from hyperglass.exceptions import InputInvalid
class Query(BaseModel):
"""Validation model for input query parameters."""
query_location: StrictStr
query_type: StrictStr
query_vrf: Optional[StrictStr]
query_target: StrictStr
@validator("query_location")
def validate_query_location(cls, value):
"""Ensure query_location is defined.
Arguments:
value {str} -- Unvalidated query_location
Raises:
InputInvalid: Raised if query_location is not defined.
Returns:
{str} -- Valid query_location
"""
if value not in devices.hostnames:
raise InputInvalid(
params.messages.invalid_field,
alert="warning",
input=value,
field=params.branding.text.query_location,
)
return value
@validator("query_type")
def validate_query_type(cls, value):
"""Ensure query_type is supported.
Arguments:
value {str} -- Unvalidated query_type
Raises:
InputInvalid: Raised if query_type is not supported.
Returns:
{str} -- Valid query_type
"""
if value not in Supported.query_types:
raise InputInvalid(
params.messages.invalid_field,
alert="warning",
input=value,
field=params.branding.text.query_type,
)
else:
enabled = operator.attrgetter(f"{value}.enable")(params.features)
if not enabled:
raise InputInvalid(
params.messages.invalid_field,
alert="warning",
input=value,
field=params.branding.text.query_type,
)
return value
@validator("query_vrf", always=True, pre=True)
def validate_query_vrf(cls, value, values):
"""Ensure query_vrf is defined.
Arguments:
value {str} -- Unvalidated query_vrf
Raises:
InputInvalid: Raised if query_vrf is not defined.
Returns:
{str} -- Valid query_vrf
"""
device = getattr(devices, values["query_location"])
default_vrf = "default"
if value is not None and value != default_vrf:
for vrf in device.vrfs:
if value == vrf.name:
value = vrf.name
elif value == vrf.display_name:
value = vrf.name
else:
raise InputInvalid(
params.messages.vrf_not_associated,
alert="warning",
vrf_name=value,
device_name=device.display_name,
)
if value is None:
value = default_vrf
return value