mirror of
https://github.com/checktheroads/hyperglass
synced 2024-05-11 05:55:08 +00:00
restructure models
This commit is contained in:
@@ -6,7 +6,7 @@ from typing import Union
|
||||
from pydantic import BaseModel, StrictStr
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import StrictBytes
|
||||
from hyperglass.models import StrictBytes
|
||||
|
||||
|
||||
class EncodedRequest(BaseModel):
|
||||
|
@@ -33,7 +33,7 @@ from hyperglass.configuration.models import routers as _routers
|
||||
from hyperglass.configuration.models import commands as _commands
|
||||
from hyperglass.configuration.markdown import get_markdown
|
||||
|
||||
set_app_path()
|
||||
set_app_path(required=True)
|
||||
|
||||
CONFIG_PATH = Path(os.environ["hyperglass_directory"])
|
||||
log.info("Configuration directory: {d}", d=str(CONFIG_PATH))
|
||||
|
@@ -2,140 +2,10 @@
|
||||
|
||||
# Standard Library
|
||||
import os
|
||||
import re
|
||||
from typing import TypeVar
|
||||
from pathlib import Path
|
||||
|
||||
# Third Party
|
||||
from pydantic import HttpUrl, BaseModel, StrictInt, StrictFloat
|
||||
|
||||
# Project
|
||||
from hyperglass.util import log, clean_name
|
||||
|
||||
IntFloat = TypeVar("IntFloat", StrictInt, StrictFloat)
|
||||
|
||||
|
||||
class HyperglassModel(BaseModel):
|
||||
"""Base model for all hyperglass configuration models."""
|
||||
|
||||
class Config:
|
||||
"""Default Pydantic configuration.
|
||||
|
||||
See https://pydantic-docs.helpmanual.io/usage/model_config
|
||||
"""
|
||||
|
||||
validate_all = True
|
||||
extra = "forbid"
|
||||
validate_assignment = True
|
||||
alias_generator = clean_name
|
||||
json_encoders = {HttpUrl: lambda v: str(v)}
|
||||
|
||||
def export_json(self, *args, **kwargs):
|
||||
"""Return instance as JSON.
|
||||
|
||||
Returns:
|
||||
{str} -- Stringified JSON.
|
||||
"""
|
||||
return self.json(by_alias=True, exclude_unset=False, *args, **kwargs)
|
||||
|
||||
def export_dict(self, *args, **kwargs):
|
||||
"""Return instance as dictionary.
|
||||
|
||||
Returns:
|
||||
{dict} -- Python dictionary.
|
||||
"""
|
||||
return self.dict(by_alias=True, exclude_unset=False, *args, **kwargs)
|
||||
|
||||
def export_yaml(self, *args, **kwargs):
|
||||
"""Return instance as YAML.
|
||||
|
||||
Returns:
|
||||
{str} -- Stringified YAML.
|
||||
"""
|
||||
import json
|
||||
import yaml
|
||||
|
||||
return yaml.safe_dump(json.loads(self.export_json()), *args, **kwargs)
|
||||
|
||||
|
||||
class HyperglassModelExtra(HyperglassModel):
|
||||
"""Model for hyperglass configuration models with dynamic fields."""
|
||||
|
||||
pass
|
||||
|
||||
class Config:
|
||||
"""Default pydantic configuration."""
|
||||
|
||||
extra = "allow"
|
||||
|
||||
|
||||
class AnyUri(str):
|
||||
"""Custom field type for HTTP URI, e.g. /example."""
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
"""Pydantic custim field method."""
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value):
|
||||
"""Ensure URI string contains a leading forward-slash."""
|
||||
uri_regex = re.compile(r"^(\/.*)$")
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("AnyUri type must be a string")
|
||||
match = uri_regex.fullmatch(value)
|
||||
if not match:
|
||||
raise ValueError(
|
||||
"Invalid format. A URI must begin with a forward slash, e.g. '/example'"
|
||||
)
|
||||
return cls(match.group())
|
||||
|
||||
def __repr__(self):
|
||||
"""Stringify custom field representation."""
|
||||
return f"AnyUri({super().__repr__()})"
|
||||
|
||||
|
||||
class StrictBytes(bytes):
|
||||
"""Custom data type for a strict byte string.
|
||||
|
||||
Used for validating the encoded JWT request payload.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
"""Yield Pydantic validator function.
|
||||
|
||||
See: https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types
|
||||
|
||||
Yields:
|
||||
{function} -- Validator
|
||||
"""
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value):
|
||||
"""Validate type.
|
||||
|
||||
Arguments:
|
||||
value {Any} -- Pre-validated input
|
||||
|
||||
Raises:
|
||||
TypeError: Raised if value is not bytes
|
||||
|
||||
Returns:
|
||||
{object} -- Instantiated class
|
||||
"""
|
||||
if not isinstance(value, bytes):
|
||||
raise TypeError("bytes required")
|
||||
return cls()
|
||||
|
||||
def __repr__(self):
|
||||
"""Return representation of object.
|
||||
|
||||
Returns:
|
||||
{str} -- Representation
|
||||
"""
|
||||
return f"StrictBytes({super().__repr__()})"
|
||||
from hyperglass.log import log
|
||||
|
||||
|
||||
def validate_image(value):
|
||||
|
@@ -7,7 +7,7 @@ from typing import Union
|
||||
from pydantic import Field, StrictInt, StrictStr, StrictBool, IPvAnyAddress
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Cache(HyperglassModel):
|
||||
|
@@ -4,7 +4,7 @@
|
||||
from pydantic import StrictStr
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Command(HyperglassModel):
|
||||
|
@@ -5,7 +5,7 @@ from pydantic import SecretStr, StrictStr
|
||||
|
||||
# Project
|
||||
from hyperglass.util import clean_name
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Credential(HyperglassModel):
|
||||
|
@@ -3,19 +3,10 @@
|
||||
from pydantic import Field, HttpUrl, StrictStr, StrictBool, constr
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import AnyUri, HyperglassModel
|
||||
from hyperglass.models import AnyUri, HyperglassModel
|
||||
|
||||
|
||||
class HyperglassLevel3(HyperglassModel):
|
||||
"""Automatic docs sorting subclass."""
|
||||
|
||||
class Config:
|
||||
"""Pydantic model configuration."""
|
||||
|
||||
schema_extra = {"level": 3}
|
||||
|
||||
|
||||
class EndpointConfig(HyperglassLevel3):
|
||||
class EndpointConfig(HyperglassModel):
|
||||
"""Validation model for per API endpoint documentation."""
|
||||
|
||||
title: StrictStr = Field(
|
||||
@@ -106,4 +97,3 @@ class Docs(HyperglassModel):
|
||||
"description": "`/api/devices` API documentation options.",
|
||||
},
|
||||
}
|
||||
schema_extra = {"level": 2}
|
||||
|
@@ -22,7 +22,7 @@ from pydantic import (
|
||||
|
||||
# Project
|
||||
from hyperglass.constants import __version__
|
||||
from hyperglass.configuration.models._utils import HyperglassModel, HyperglassModelExtra
|
||||
from hyperglass.models import HyperglassModel, HyperglassModelExtra
|
||||
|
||||
|
||||
class Syslog(HyperglassModel):
|
||||
|
@@ -4,7 +4,7 @@
|
||||
from pydantic import Field, StrictStr
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Messages(HyperglassModel):
|
||||
|
@@ -5,7 +5,7 @@ from pydantic import Field, StrictStr
|
||||
|
||||
# Project
|
||||
from hyperglass.util import clean_name
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Network(HyperglassModel):
|
||||
|
@@ -10,7 +10,8 @@ import PIL.Image as PilImage
|
||||
from pydantic import StrictInt, StrictStr, root_validator
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel, validate_image
|
||||
from hyperglass.models import HyperglassModel
|
||||
from hyperglass.configuration.models._utils import validate_image
|
||||
|
||||
CONFIG_PATH = Path(os.environ["hyperglass_directory"])
|
||||
|
||||
|
@@ -16,10 +16,10 @@ from pydantic import (
|
||||
)
|
||||
|
||||
# Project
|
||||
from hyperglass.models import IntFloat, HyperglassModel
|
||||
from hyperglass.configuration.models.web import Web
|
||||
from hyperglass.configuration.models.docs import Docs
|
||||
from hyperglass.configuration.models.cache import Cache
|
||||
from hyperglass.configuration.models._utils import IntFloat, HyperglassModel
|
||||
from hyperglass.configuration.models.logging import Logging
|
||||
from hyperglass.configuration.models.queries import Queries
|
||||
from hyperglass.configuration.models.messages import Messages
|
||||
|
@@ -5,8 +5,8 @@ from pydantic import StrictInt, StrictStr, validator
|
||||
|
||||
# Project
|
||||
from hyperglass.util import clean_name
|
||||
from hyperglass.models import HyperglassModel
|
||||
from hyperglass.exceptions import UnsupportedDevice
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.configuration.models.credentials import Credential
|
||||
|
||||
|
||||
|
@@ -4,8 +4,8 @@
|
||||
from pydantic import Field, StrictStr, StrictBool, constr
|
||||
|
||||
# Project
|
||||
from hyperglass.models import HyperglassModel
|
||||
from hyperglass.constants import SUPPORTED_QUERY_TYPES
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
|
||||
|
||||
class HyperglassLevel3(HyperglassModel):
|
||||
|
@@ -12,11 +12,11 @@ from pydantic import StrictInt, StrictStr, validator
|
||||
# Project
|
||||
from hyperglass.log import log
|
||||
from hyperglass.util import clean_name
|
||||
from hyperglass.models import HyperglassModel, HyperglassModelExtra
|
||||
from hyperglass.constants import SCRAPE_HELPERS, TRANSPORT_REST, TRANSPORT_SCRAPE
|
||||
from hyperglass.exceptions import ConfigError, UnsupportedDevice
|
||||
from hyperglass.configuration.models.ssl import Ssl
|
||||
from hyperglass.configuration.models.vrfs import Vrf, Info
|
||||
from hyperglass.configuration.models._utils import HyperglassModel, HyperglassModelExtra
|
||||
from hyperglass.configuration.models.proxies import Proxy
|
||||
from hyperglass.configuration.models.commands import Command
|
||||
from hyperglass.configuration.models.networks import Network
|
||||
|
@@ -7,7 +7,7 @@ from typing import Optional
|
||||
from pydantic import Field, FilePath, StrictBool
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel
|
||||
from hyperglass.models import HyperglassModel
|
||||
|
||||
|
||||
class Ssl(HyperglassModel):
|
||||
|
@@ -17,7 +17,7 @@ from pydantic import (
|
||||
)
|
||||
|
||||
# Project
|
||||
from hyperglass.configuration.models._utils import HyperglassModel, HyperglassModelExtra
|
||||
from hyperglass.models import HyperglassModel, HyperglassModelExtra
|
||||
|
||||
|
||||
class AccessList4(HyperglassModel):
|
||||
|
@@ -17,8 +17,9 @@ from pydantic import (
|
||||
from pydantic.color import Color
|
||||
|
||||
# Project
|
||||
from hyperglass.models import HyperglassModel
|
||||
from hyperglass.constants import DNS_OVER_HTTPS, FUNC_COLOR_MAP
|
||||
from hyperglass.configuration.models._utils import HyperglassModel, validate_image
|
||||
from hyperglass.configuration.models._utils import validate_image
|
||||
from hyperglass.configuration.models.opengraph import OpenGraph
|
||||
|
||||
|
||||
|
134
hyperglass/models.py
Normal file
134
hyperglass/models.py
Normal file
@@ -0,0 +1,134 @@
|
||||
# Standard Library
|
||||
import re
|
||||
from typing import TypeVar
|
||||
|
||||
# Third Party
|
||||
from pydantic import HttpUrl, BaseModel, StrictInt, StrictFloat
|
||||
|
||||
# Project
|
||||
from hyperglass.util import clean_name
|
||||
|
||||
IntFloat = TypeVar("IntFloat", StrictInt, StrictFloat)
|
||||
|
||||
|
||||
class HyperglassModel(BaseModel):
|
||||
"""Base model for all hyperglass configuration models."""
|
||||
|
||||
class Config:
|
||||
"""Default Pydantic configuration.
|
||||
|
||||
See https://pydantic-docs.helpmanual.io/usage/model_config
|
||||
"""
|
||||
|
||||
validate_all = True
|
||||
extra = "forbid"
|
||||
validate_assignment = True
|
||||
alias_generator = clean_name
|
||||
json_encoders = {HttpUrl: lambda v: str(v)}
|
||||
|
||||
def export_json(self, *args, **kwargs):
|
||||
"""Return instance as JSON.
|
||||
|
||||
Returns:
|
||||
{str} -- Stringified JSON.
|
||||
"""
|
||||
return self.json(by_alias=True, exclude_unset=False, *args, **kwargs)
|
||||
|
||||
def export_dict(self, *args, **kwargs):
|
||||
"""Return instance as dictionary.
|
||||
|
||||
Returns:
|
||||
{dict} -- Python dictionary.
|
||||
"""
|
||||
return self.dict(by_alias=True, exclude_unset=False, *args, **kwargs)
|
||||
|
||||
def export_yaml(self, *args, **kwargs):
|
||||
"""Return instance as YAML.
|
||||
|
||||
Returns:
|
||||
{str} -- Stringified YAML.
|
||||
"""
|
||||
import json
|
||||
import yaml
|
||||
|
||||
return yaml.safe_dump(json.loads(self.export_json()), *args, **kwargs)
|
||||
|
||||
|
||||
class HyperglassModelExtra(HyperglassModel):
|
||||
"""Model for hyperglass configuration models with dynamic fields."""
|
||||
|
||||
pass
|
||||
|
||||
class Config:
|
||||
"""Default pydantic configuration."""
|
||||
|
||||
extra = "allow"
|
||||
|
||||
|
||||
class AnyUri(str):
|
||||
"""Custom field type for HTTP URI, e.g. /example."""
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
"""Pydantic custim field method."""
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value):
|
||||
"""Ensure URI string contains a leading forward-slash."""
|
||||
uri_regex = re.compile(r"^(\/.*)$")
|
||||
if not isinstance(value, str):
|
||||
raise TypeError("AnyUri type must be a string")
|
||||
match = uri_regex.fullmatch(value)
|
||||
if not match:
|
||||
raise ValueError(
|
||||
"Invalid format. A URI must begin with a forward slash, e.g. '/example'"
|
||||
)
|
||||
return cls(match.group())
|
||||
|
||||
def __repr__(self):
|
||||
"""Stringify custom field representation."""
|
||||
return f"AnyUri({super().__repr__()})"
|
||||
|
||||
|
||||
class StrictBytes(bytes):
|
||||
"""Custom data type for a strict byte string.
|
||||
|
||||
Used for validating the encoded JWT request payload.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def __get_validators__(cls):
|
||||
"""Yield Pydantic validator function.
|
||||
|
||||
See: https://pydantic-docs.helpmanual.io/usage/types/#custom-data-types
|
||||
|
||||
Yields:
|
||||
{function} -- Validator
|
||||
"""
|
||||
yield cls.validate
|
||||
|
||||
@classmethod
|
||||
def validate(cls, value):
|
||||
"""Validate type.
|
||||
|
||||
Arguments:
|
||||
value {Any} -- Pre-validated input
|
||||
|
||||
Raises:
|
||||
TypeError: Raised if value is not bytes
|
||||
|
||||
Returns:
|
||||
{object} -- Instantiated class
|
||||
"""
|
||||
if not isinstance(value, bytes):
|
||||
raise TypeError("bytes required")
|
||||
return cls()
|
||||
|
||||
def __repr__(self):
|
||||
"""Return representation of object.
|
||||
|
||||
Returns:
|
||||
{str} -- Representation
|
||||
"""
|
||||
return f"StrictBytes({super().__repr__()})"
|
@@ -1,4 +1,5 @@
|
||||
"""Validate example files."""
|
||||
|
||||
# Standard Library
|
||||
import re
|
||||
import sys
|
||||
@@ -9,9 +10,6 @@ import yaml
|
||||
|
||||
# Project
|
||||
from hyperglass.util import set_app_path
|
||||
from hyperglass.configuration.models.params import Params
|
||||
from hyperglass.configuration.models.routers import Routers
|
||||
from hyperglass.configuration.models.commands import Commands
|
||||
|
||||
EXAMPLES = Path(__file__).parent.parent / "hyperglass" / "examples"
|
||||
|
||||
@@ -52,6 +50,8 @@ def _comment_optional_files():
|
||||
|
||||
|
||||
def _validate_devices():
|
||||
from hyperglass.configuration.models.routers import Routers
|
||||
|
||||
with DEVICES.open() as raw:
|
||||
devices_dict = yaml.safe_load(raw.read()) or {}
|
||||
try:
|
||||
@@ -62,6 +62,8 @@ def _validate_devices():
|
||||
|
||||
|
||||
def _validate_commands():
|
||||
from hyperglass.configuration.models.commands import Commands
|
||||
|
||||
with COMMANDS.open() as raw:
|
||||
commands_dict = yaml.safe_load(raw.read()) or {}
|
||||
try:
|
||||
@@ -72,6 +74,8 @@ def _validate_commands():
|
||||
|
||||
|
||||
def _validate_main():
|
||||
from hyperglass.configuration.models.params import Params
|
||||
|
||||
with MAIN.open() as raw:
|
||||
main_dict = yaml.safe_load(raw.read()) or {}
|
||||
try:
|
||||
|
Reference in New Issue
Block a user