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

fix docstrings; refactor to strict types

This commit is contained in:
checktheroads
2019-12-31 18:29:43 -07:00
parent d9bca3b8f0
commit 08c33f678f
13 changed files with 384 additions and 327 deletions

View File

@@ -1,10 +1,10 @@
repos: repos:
# - repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
# rev: v2.3.0 rev: v2.3.0
# hooks: hooks:
# - id: flake8 - id: flake8
# stages: stages:
# - push - push
- repo: local - repo: local
hooks: hooks:
- id: line_count - id: line_count

View File

@@ -136,7 +136,7 @@ except ValidationError as validation_errors:
raise ConfigInvalid( raise ConfigInvalid(
field=": ".join([str(item) for item in error["loc"]]), field=": ".join([str(item) for item in error["loc"]]),
error_msg=error["msg"], error_msg=error["msg"],
) from None )
# Re-evaluate debug state after config is validated # Re-evaluate debug state after config is validated
_set_log_level(params.general.debug) _set_log_level(params.general.debug)
@@ -239,7 +239,7 @@ def _build_networks():
"""Build filtered JSON Structure of networks & devices for Jinja templates. """Build filtered JSON Structure of networks & devices for Jinja templates.
Raises: Raises:
ConfigError: Raised if parsing/building error occurs. ConfigError: Raised if parsing/building error occurs.
Returns: Returns:
{dict} -- Networks & devices {dict} -- Networks & devices

View File

@@ -1,7 +1,5 @@
""" """Define models for all config variables.
Defines models for all config variables.
Imports config variables and overrides default class attributes. Import config variables and overrides default class attributes.
Validate input for overridden parameters.
Validates input for overridden parameters.
""" """

View File

@@ -69,7 +69,7 @@ class Branding(HyperglassModel):
favicons: str = "ui/images/favicons/" favicons: str = "ui/images/favicons/"
@validator("favicons") @validator("favicons")
def favicons_trailing_slash(cls, value): # noqa: N805 def favicons_trailing_slash(cls, value):
"""If the favicons path does not end in a '/', append it.""" """If the favicons path does not end in a '/', append it."""
chars = list(value) chars = list(value)
if chars[len(chars) - 1] != "/": if chars[len(chars) - 1] != "/":

View File

@@ -1,50 +1,50 @@
"""Validate command configuration variables.""" """Validate command configuration variables."""
# Disable string length warnings so I can actually read these commands # Third Party Imports
# flake8: noqa: E501 from pydantic import StrictStr
# Project Imports # Project Imports
from hyperglass.configuration.models._utils import HyperglassModel from hyperglass.configuration.models._utils import HyperglassModel
class Command(HyperglassModel): class Command(HyperglassModel):
"""Class model for non-default commands""" """Validation model for non-default commands."""
class IPv4(HyperglassModel): class IPv4(HyperglassModel):
"""Class model for non-default dual afi commands""" """Validation model for non-default dual afi commands."""
bgp_route: str = "" bgp_route: StrictStr = ""
bgp_aspath: str = "" bgp_aspath: StrictStr = ""
bgp_community: str = "" bgp_community: StrictStr = ""
ping: str = "" ping: StrictStr = ""
traceroute: str = "" traceroute: StrictStr = ""
class IPv6(HyperglassModel): class IPv6(HyperglassModel):
"""Class model for non-default ipv4 commands""" """Validation model for non-default ipv4 commands."""
bgp_route: str = "" bgp_route: StrictStr = ""
bgp_aspath: str = "" bgp_aspath: StrictStr = ""
bgp_community: str = "" bgp_community: StrictStr = ""
ping: str = "" ping: StrictStr = ""
traceroute: str = "" traceroute: StrictStr = ""
class VPNIPv4(HyperglassModel): class VPNIPv4(HyperglassModel):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "" bgp_route: StrictStr = ""
bgp_aspath: str = "" bgp_aspath: StrictStr = ""
bgp_community: str = "" bgp_community: StrictStr = ""
ping: str = "" ping: StrictStr = ""
traceroute: str = "" traceroute: StrictStr = ""
class VPNIPv6(HyperglassModel): class VPNIPv6(HyperglassModel):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "" bgp_route: StrictStr = ""
bgp_aspath: str = "" bgp_aspath: StrictStr = ""
bgp_community: str = "" bgp_community: StrictStr = ""
ping: str = "" ping: StrictStr = ""
traceroute: str = "" traceroute: StrictStr = ""
ipv4_default: IPv4 = IPv4() ipv4_default: IPv4 = IPv4()
ipv6_default: IPv6 = IPv6() ipv6_default: IPv6 = IPv6()
@@ -53,13 +53,19 @@ class Command(HyperglassModel):
class Commands(HyperglassModel): class Commands(HyperglassModel):
"""Base class for commands class""" """Base class for command definitions."""
@classmethod @classmethod
def import_params(cls, input_params): def import_params(cls, input_params):
""" """Import loaded YAML, initialize per-command definitions.
Imports passed dict from YAML config, dynamically sets
attributes for the commands class. Dynamically set attributes for the command class.
Arguments:
input_params {dict} -- Unvalidated command definitions
Returns:
{object} -- Validated commands object
""" """
obj = Commands() obj = Commands()
for (nos, cmds) in input_params.items(): for (nos, cmds) in input_params.items():
@@ -67,47 +73,47 @@ class Commands(HyperglassModel):
return obj return obj
class CiscoIOS(Command): class CiscoIOS(Command):
"""Class model for default cisco_ios commands""" """Validation model for default cisco_ios commands."""
class VPNIPv4(Command.VPNIPv4): class VPNIPv4(Command.VPNIPv4):
"""Default commands for dual afi commands""" """Default commands for dual afi commands."""
bgp_community: str = "show bgp vpnv4 unicast vrf {vrf} community {target}" bgp_community: StrictStr = "show bgp vpnv4 unicast vrf {vrf} community {target}"
bgp_aspath: str = 'show bgp vpnv4 unicast vrf {vrf} quote-regexp "{target}"' bgp_aspath: StrictStr = 'show bgp vpnv4 unicast vrf {vrf} quote-regexp "{target}"'
bgp_route: str = "show bgp vpnv4 unicast vrf {vrf} {target}" bgp_route: StrictStr = "show bgp vpnv4 unicast vrf {vrf} {target}"
ping: str = "ping vrf {vrf} {target} repeat 5 source {source}" ping: StrictStr = "ping vrf {vrf} {target} repeat 5 source {source}"
traceroute: str = ( traceroute: StrictStr = (
"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}"
) )
class VPNIPv6(Command.VPNIPv6): class VPNIPv6(Command.VPNIPv6):
"""Default commands for dual afi commands""" """Default commands for dual afi commands."""
bgp_community: str = "show bgp vpnv6 unicast vrf {vrf} community {target}" bgp_community: StrictStr = "show bgp vpnv6 unicast vrf {vrf} community {target}"
bgp_aspath: str = 'show bgp vpnv6 unicast vrf {vrf} quote-regexp "{target}"' bgp_aspath: StrictStr = 'show bgp vpnv6 unicast vrf {vrf} quote-regexp "{target}"'
bgp_route: str = "show bgp vpnv6 unicast vrf {vrf} {target}" bgp_route: StrictStr = "show bgp vpnv6 unicast vrf {vrf} {target}"
ping: str = "ping vrf {vrf} {target} repeat 5 source {source}" ping: StrictStr = "ping vrf {vrf} {target} repeat 5 source {source}"
traceroute: str = ( traceroute: StrictStr = (
"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" "traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}"
) )
class IPv4(Command.IPv4): class IPv4(Command.IPv4):
"""Default commands for ipv4 commands""" """Default commands for ipv4 commands."""
bgp_community: str = "show bgp ipv4 unicast community {target}" bgp_community: StrictStr = "show bgp ipv4 unicast community {target}"
bgp_aspath: str = 'show bgp ipv4 unicast quote-regexp "{target}"' bgp_aspath: StrictStr = 'show bgp ipv4 unicast quote-regexp "{target}"'
bgp_route: str = "show bgp ipv4 unicast {target} | exclude pathid:|Epoch" bgp_route: StrictStr = "show bgp ipv4 unicast {target} | exclude pathid:|Epoch"
ping: str = "ping {target} repeat 5 source {source}" ping: StrictStr = "ping {target} repeat 5 source {source}"
traceroute: str = "traceroute {target} timeout 1 probe 2 source {source}" traceroute: StrictStr = "traceroute {target} timeout 1 probe 2 source {source}"
class IPv6(Command.IPv6): class IPv6(Command.IPv6):
"""Default commands for ipv6 commands""" """Default commands for ipv6 commands."""
bgp_community: str = "show bgp ipv6 unicast community {target}" bgp_community: StrictStr = "show bgp ipv6 unicast community {target}"
bgp_aspath: str = 'show bgp ipv6 unicast quote-regexp "{target}"' bgp_aspath: StrictStr = 'show bgp ipv6 unicast quote-regexp "{target}"'
bgp_route: str = "show bgp ipv6 unicast {target} | exclude pathid:|Epoch" bgp_route: StrictStr = "show bgp ipv6 unicast {target} | exclude pathid:|Epoch"
ping: str = ("ping ipv6 {target} repeat 5 source {source}") ping: StrictStr = ("ping ipv6 {target} repeat 5 source {source}")
traceroute: str = ( traceroute: StrictStr = (
"traceroute ipv6 {target} timeout 1 probe 2 source {source}" "traceroute ipv6 {target} timeout 1 probe 2 source {source}"
) )
@@ -117,43 +123,43 @@ class Commands(HyperglassModel):
ipv6_vpn: VPNIPv6 = VPNIPv6() ipv6_vpn: VPNIPv6 = VPNIPv6()
class CiscoXR(Command): class CiscoXR(Command):
"""Class model for default cisco_xr commands""" """Validation model for default cisco_xr commands."""
class IPv4(Command.IPv4): class IPv4(Command.IPv4):
"""Class model for non-default dual afi commands""" """Validation model for non-default dual afi commands."""
bgp_route: str = r"show bgp ipv4 unicast {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)" bgp_route: StrictStr = r"show bgp ipv4 unicast {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)"
bgp_aspath: str = r"show bgp ipv4 unicast regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_aspath: StrictStr = r"show bgp ipv4 unicast regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
bgp_community: str = r"show bgp ipv4 unicast community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_community: StrictStr = r"show bgp ipv4 unicast community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
ping: str = r"ping ipv4 {target} count 5 source {source}" ping: StrictStr = r"ping ipv4 {target} count 5 source {source}"
traceroute: str = r"traceroute ipv4 {target} timeout 1 probe 2 source {source}" traceroute: StrictStr = r"traceroute ipv4 {target} timeout 1 probe 2 source {source}"
class IPv6(Command.IPv6): class IPv6(Command.IPv6):
"""Class model for non-default ipv4 commands""" """Validation model for non-default ipv4 commands."""
bgp_route: str = r"show bgp ipv6 unicast {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)" bgp_route: StrictStr = r"show bgp ipv6 unicast {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)"
bgp_aspath: str = r"show bgp ipv6 unicast regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_aspath: StrictStr = r"show bgp ipv6 unicast regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
bgp_community: str = r"show bgp ipv6 unicast community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_community: StrictStr = r"show bgp ipv6 unicast community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
ping: str = r"ping ipv6 {target} count 5 source {source}" ping: StrictStr = r"ping ipv6 {target} count 5 source {source}"
traceroute: str = r"traceroute ipv6 {target} timeout 1 probe 2 source {source}" traceroute: StrictStr = r"traceroute ipv6 {target} timeout 1 probe 2 source {source}"
class VPNIPv4(Command.VPNIPv4): class VPNIPv4(Command.VPNIPv4):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = r"show bgp vpnv4 unicast vrf {vrf} {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)" bgp_route: StrictStr = r"show bgp vpnv4 unicast vrf {vrf} {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)"
bgp_aspath: str = r"show bgp vpnv4 unicast vrf {vrf} regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_aspath: StrictStr = r"show bgp vpnv4 unicast vrf {vrf} regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
bgp_community: str = r"show bgp vpnv4 unicast vrf {vrf} community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_community: StrictStr = r"show bgp vpnv4 unicast vrf {vrf} community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
ping: str = r"ping vrf {vrf} {target} count 5 source {source}" ping: StrictStr = r"ping vrf {vrf} {target} count 5 source {source}"
traceroute: str = r"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" traceroute: StrictStr = r"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}"
class VPNIPv6(Command.VPNIPv6): class VPNIPv6(Command.VPNIPv6):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = r"show bgp vpnv6 unicast vrf {vrf} {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)" bgp_route: StrictStr = r"show bgp vpnv6 unicast vrf {vrf} {target} | util egrep \\(BGP routing table entry|Path \\#|aggregated by|Origin |Community:|validity| from \\)"
bgp_aspath: str = r"show bgp vpnv6 unicast vrf {vrf} regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_aspath: StrictStr = r"show bgp vpnv6 unicast vrf {vrf} regexp {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
bgp_community: str = r"show bgp vpnv6 unicast vrf {vrf} community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)" bgp_community: StrictStr = r"show bgp vpnv6 unicast vrf {vrf} community {target} | utility egrep -v \\(BGP |Table |Non-stop\\)"
ping: str = r"ping vrf {vrf} {target} count 5 source {source}" ping: StrictStr = r"ping vrf {vrf} {target} count 5 source {source}"
traceroute: str = r"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}" traceroute: StrictStr = r"traceroute vrf {vrf} {target} timeout 1 probe 2 source {source}"
ipv4_default: IPv4 = IPv4() ipv4_default: IPv4 = IPv4()
ipv6_default: IPv6 = IPv6() ipv6_default: IPv6 = IPv6()
@@ -161,43 +167,43 @@ class Commands(HyperglassModel):
ipv6_vpn: VPNIPv6 = VPNIPv6() ipv6_vpn: VPNIPv6 = VPNIPv6()
class Juniper(Command): class Juniper(Command):
"""Class model for default juniper commands""" """Validation model for default juniper commands."""
class IPv4(Command.IPv4): class IPv4(Command.IPv4):
"""Class model for non-default dual afi commands""" """Validation model for non-default dual afi commands."""
bgp_route: str = "show route protocol bgp table inet.0 {target} detail" bgp_route: StrictStr = "show route protocol bgp table inet.0 {target} detail"
bgp_aspath: str = "show route protocol bgp table inet.0 aspath-regex {target}" bgp_aspath: StrictStr = "show route protocol bgp table inet.0 aspath-regex {target}"
bgp_community: str = "show route protocol bgp table inet.0 community {target}" bgp_community: StrictStr = "show route protocol bgp table inet.0 community {target}"
ping: str = "ping inet {target} count 5 source {source}" ping: StrictStr = "ping inet {target} count 5 source {source}"
traceroute: str = "traceroute inet {target} wait 1 source {source}" traceroute: StrictStr = "traceroute inet {target} wait 1 source {source}"
class IPv6(Command.IPv6): class IPv6(Command.IPv6):
"""Class model for non-default ipv4 commands""" """Validation model for non-default ipv4 commands."""
bgp_route: str = "show route protocol bgp table inet6.0 {target} detail" bgp_route: StrictStr = "show route protocol bgp table inet6.0 {target} detail"
bgp_aspath: str = "show route protocol bgp community {target}" bgp_aspath: StrictStr = "show route protocol bgp community {target}"
bgp_community: str = "show route protocol bgp aspath-regex {target}" bgp_community: StrictStr = "show route protocol bgp aspath-regex {target}"
ping: str = "ping inet6 {target} count 5 source {source}" ping: StrictStr = "ping inet6 {target} count 5 source {source}"
traceroute: str = "traceroute inet6 {target} wait 1 source {source}" traceroute: StrictStr = "traceroute inet6 {target} wait 1 source {source}"
class VPNIPv4(Command.VPNIPv4): class VPNIPv4(Command.VPNIPv4):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "show route protocol bgp table {vrf} {target} detail" bgp_route: StrictStr = "show route protocol bgp table {vrf} {target} detail"
bgp_aspath: str = "show route protocol bgp table {vrf} aspath-regex {target}" bgp_aspath: StrictStr = "show route protocol bgp table {vrf} aspath-regex {target}"
bgp_community: str = "show route protocol bgp table {vrf} community {target}" bgp_community: StrictStr = "show route protocol bgp table {vrf} community {target}"
ping: str = "ping inet routing-instance {vrf} {target} count 5 source {source}" ping: StrictStr = "ping inet routing-instance {vrf} {target} count 5 source {source}"
traceroute: str = "traceroute inet routing-instance {vrf} {target} wait 1 source {source}" traceroute: StrictStr = "traceroute inet routing-instance {vrf} {target} wait 1 source {source}"
class VPNIPv6(Command.VPNIPv6): class VPNIPv6(Command.VPNIPv6):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "show route protocol bgp table {vrf} {target} detail" bgp_route: StrictStr = "show route protocol bgp table {vrf} {target} detail"
bgp_aspath: str = "show route protocol bgp table {vrf} aspath-regex {target}" bgp_aspath: StrictStr = "show route protocol bgp table {vrf} aspath-regex {target}"
bgp_community: str = "show route protocol bgp table {vrf} community {target}" bgp_community: StrictStr = "show route protocol bgp table {vrf} community {target}"
ping: str = "ping inet6 routing-instance {vrf} {target} count 5 source {source}" ping: StrictStr = "ping inet6 routing-instance {vrf} {target} count 5 source {source}"
traceroute: str = "traceroute inet6 routing-instance {vrf} {target} wait 1 source {source}" traceroute: StrictStr = "traceroute inet6 routing-instance {vrf} {target} wait 1 source {source}"
ipv4_default: IPv4 = IPv4() ipv4_default: IPv4 = IPv4()
ipv6_default: IPv6 = IPv6() ipv6_default: IPv6 = IPv6()
@@ -205,43 +211,43 @@ class Commands(HyperglassModel):
ipv6_vpn: VPNIPv6 = VPNIPv6() ipv6_vpn: VPNIPv6 = VPNIPv6()
class Huawei(Command): class Huawei(Command):
"""Class model for default huawei commands""" """Validation model for default huawei commands."""
class IPv4(Command.IPv4): class IPv4(Command.IPv4):
"""Default commands for ipv4 commands""" """Default commands for ipv4 commands."""
bgp_community: str = "display bgp routing-table regular-expression {target}" bgp_community: StrictStr = "display bgp routing-table regular-expression {target}"
bgp_aspath: str = "display bgp routing-table regular-expression {target}" bgp_aspath: StrictStr = "display bgp routing-table regular-expression {target}"
bgp_route: str = "display bgp routing-table {target}" bgp_route: StrictStr = "display bgp routing-table {target}"
ping: str = "ping -c 5 -a {source} {target}" ping: StrictStr = "ping -c 5 -a {source} {target}"
traceroute: str = "tracert -q 2 -f 1 -a {source} {target}" traceroute: StrictStr = "tracert -q 2 -f 1 -a {source} {target}"
class IPv6(Command.IPv6): class IPv6(Command.IPv6):
"""Default commands for ipv6 commands""" """Default commands for ipv6 commands."""
bgp_community: str = "display bgp ipv6 routing-table community {target}" bgp_community: StrictStr = "display bgp ipv6 routing-table community {target}"
bgp_aspath: str = "display bgp ipv6 routing-table regular-expression {target}" bgp_aspath: StrictStr = "display bgp ipv6 routing-table regular-expression {target}"
bgp_route: str = "display bgp ipv6 routing-table {target}" bgp_route: StrictStr = "display bgp ipv6 routing-table {target}"
ping: str = "ping ipv6 -c 5 -a {source} {target}" ping: StrictStr = "ping ipv6 -c 5 -a {source} {target}"
traceroute: str = "tracert ipv6 -q 2 -f 1 -a {source} {target}" traceroute: StrictStr = "tracert ipv6 -q 2 -f 1 -a {source} {target}"
class VPNIPv4(Command.VPNIPv4): class VPNIPv4(Command.VPNIPv4):
"""Default commands for dual afi commands""" """Default commands for dual afi commands."""
bgp_community: str = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}" bgp_community: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}"
bgp_aspath: str = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}" bgp_aspath: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table regular-expression {target}"
bgp_route: str = "display bgp vpnv4 vpn-instance {vrf} routing-table {target}" bgp_route: StrictStr = "display bgp vpnv4 vpn-instance {vrf} routing-table {target}"
ping: str = "ping -vpn-instance {vrf} -c 5 -a {source} {target}" ping: StrictStr = "ping -vpn-instance {vrf} -c 5 -a {source} {target}"
traceroute: str = "tracert -q 2 -f 1 -vpn-instance {vrf} -a {source} {target}" traceroute: StrictStr = "tracert -q 2 -f 1 -vpn-instance {vrf} -a {source} {target}"
class VPNIPv6(Command.VPNIPv6): class VPNIPv6(Command.VPNIPv6):
"""Default commands for dual afi commands""" """Default commands for dual afi commands."""
bgp_community: str = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}" bgp_community: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}"
bgp_aspath: str = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}" bgp_aspath: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table regular-expression {target}"
bgp_route: str = "display bgp vpnv6 vpn-instance {vrf} routing-table {target}" bgp_route: StrictStr = "display bgp vpnv6 vpn-instance {vrf} routing-table {target}"
ping: str = "ping vpnv6 vpn-instance {vrf} -c 5 -a {source} {target}" ping: StrictStr = "ping vpnv6 vpn-instance {vrf} -c 5 -a {source} {target}"
traceroute: str = "tracert -q 2 -f 1 vpn-instance {vrf} -a {source} {target}" traceroute: StrictStr = "tracert -q 2 -f 1 vpn-instance {vrf} -a {source} {target}"
ipv4_default: IPv4 = IPv4() ipv4_default: IPv4 = IPv4()
ipv6_default: IPv6 = IPv6() ipv6_default: IPv6 = IPv6()
@@ -249,43 +255,43 @@ class Commands(HyperglassModel):
ipv6_vpn: VPNIPv6 = VPNIPv6() ipv6_vpn: VPNIPv6 = VPNIPv6()
class Arista(Command): class Arista(Command):
"""Class model for non-default commands""" """Validation model for non-default commands."""
class IPv4(Command.IPv4): class IPv4(Command.IPv4):
"""Class model for non-default dual afi commands""" """Validation model for non-default dual afi commands."""
bgp_route: str = "show ip bgp {target}" bgp_route: StrictStr = "show ip bgp {target}"
bgp_aspath: str = "show ip bgp regexp {target}" bgp_aspath: StrictStr = "show ip bgp regexp {target}"
bgp_community: str = "show ip bgp community {target}" bgp_community: StrictStr = "show ip bgp community {target}"
ping: str = "ping ip {target} source {source}" ping: StrictStr = "ping ip {target} source {source}"
traceroute: str = "traceroute ip {target} source {source}" traceroute: StrictStr = "traceroute ip {target} source {source}"
class IPv6(Command.IPv6): class IPv6(Command.IPv6):
"""Class model for non-default ipv4 commands""" """Validation model for non-default ipv4 commands."""
bgp_route: str = "show ipv6 bgp {target}" bgp_route: StrictStr = "show ipv6 bgp {target}"
bgp_aspath: str = "show ipv6 bgp regexp {target}" bgp_aspath: StrictStr = "show ipv6 bgp regexp {target}"
bgp_community: str = "show ipv6 bgp community {target}" bgp_community: StrictStr = "show ipv6 bgp community {target}"
ping: str = "ping ipv6 {target} source {source}" ping: StrictStr = "ping ipv6 {target} source {source}"
traceroute: str = "traceroute ipv6 {target} source {source}" traceroute: StrictStr = "traceroute ipv6 {target} source {source}"
class VPNIPv4(Command.VPNIPv4): class VPNIPv4(Command.VPNIPv4):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "show ip bgp {target} vrf {vrf}" bgp_route: StrictStr = "show ip bgp {target} vrf {vrf}"
bgp_aspath: str = "show ip bgp regexp {target} vrf {vrf}" bgp_aspath: StrictStr = "show ip bgp regexp {target} vrf {vrf}"
bgp_community: str = "show ip bgp community {target} vrf {vrf}" bgp_community: StrictStr = "show ip bgp community {target} vrf {vrf}"
ping: str = "ping vrf {vrf} ip {target} source {source}" ping: StrictStr = "ping vrf {vrf} ip {target} source {source}"
traceroute: str = "traceroute vrf {vrf} ip {target} source {source}" traceroute: StrictStr = "traceroute vrf {vrf} ip {target} source {source}"
class VPNIPv6(Command.VPNIPv6): class VPNIPv6(Command.VPNIPv6):
"""Class model for non-default ipv6 commands""" """Validation model for non-default ipv6 commands."""
bgp_route: str = "show ipv6 bgp {target} vrf {vrf}" bgp_route: StrictStr = "show ipv6 bgp {target} vrf {vrf}"
bgp_aspath: str = "show ipv6 bgp regexp {target} vrf {vrf}" bgp_aspath: StrictStr = "show ipv6 bgp regexp {target} vrf {vrf}"
bgp_community: str = "show ipv6 bgp community {target} vrf {vrf}" bgp_community: StrictStr = "show ipv6 bgp community {target} vrf {vrf}"
ping: str = "ping vrf {vrf} ipv6 {target} source {source}" ping: StrictStr = "ping vrf {vrf} ipv6 {target} source {source}"
traceroute: str = "traceroute vrf {vrf} ipv6 {target} source {source}" traceroute: StrictStr = "traceroute vrf {vrf} ipv6 {target} source {source}"
ipv4_default: IPv4 = IPv4() ipv4_default: IPv4 = IPv4()
ipv6_default: IPv6 = IPv6() ipv6_default: IPv6 = IPv6()
@@ -299,6 +305,6 @@ class Commands(HyperglassModel):
arista: Command = Arista() arista: Command = Arista()
class Config: class Config:
"""Pydantic Config Overrides""" """Override pydantic config."""
validate_all = False validate_all = False

View File

@@ -20,10 +20,13 @@ class Credentials(HyperglassModel):
@classmethod @classmethod
def import_params(cls, input_params): def import_params(cls, input_params):
""" """Import credentials with corrected field names.
Imports passed dict from YAML config, removes unsupported
characters from device names, dynamically sets attributes for Arguments:
the credentials class. input_params {dict} -- Credential definition
Returns:
{object} -- Validated credential object
""" """
obj = Credentials() obj = Credentials()
for (credname, params) in input_params.items(): for (credname, params) in input_params.items():

View File

@@ -4,6 +4,9 @@
from math import ceil from math import ceil
# Third Party Imports # Third Party Imports
from pydantic import StrictBool
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import constr from pydantic import constr
# Project Imports # Project Imports
@@ -16,33 +19,33 @@ class Features(HyperglassModel):
class BgpRoute(HyperglassModel): class BgpRoute(HyperglassModel):
"""Validation model for params.features.bgp_route.""" """Validation model for params.features.bgp_route."""
enable: bool = True enable: StrictBool = True
class BgpCommunity(HyperglassModel): class BgpCommunity(HyperglassModel):
"""Validation model for params.features.bgp_community.""" """Validation model for params.features.bgp_community."""
enable: bool = True enable: StrictBool = True
class Regex(HyperglassModel): class Regex(HyperglassModel):
"""Validation model for params.features.bgp_community.regex.""" """Validation model for params.features.bgp_community.regex."""
decimal: str = r"^[0-9]{1,10}$" decimal: StrictStr = r"^[0-9]{1,10}$"
extended_as: str = r"^([0-9]{0,5})\:([0-9]{1,5})$" extended_as: StrictStr = r"^([0-9]{0,5})\:([0-9]{1,5})$"
large: str = r"^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$" large: StrictStr = r"^([0-9]{1,10})\:([0-9]{1,10})\:[0-9]{1,10}$"
regex: Regex = Regex() regex: Regex = Regex()
class BgpAsPath(HyperglassModel): class BgpAsPath(HyperglassModel):
"""Validation model for params.features.bgp_aspath.""" """Validation model for params.features.bgp_aspath."""
enable: bool = True enable: StrictBool = True
class Regex(HyperglassModel): class Regex(HyperglassModel):
"""Validation model for params.bgp_aspath.regex.""" """Validation model for params.bgp_aspath.regex."""
mode: constr(regex="asplain|asdot") = "asplain" mode: constr(regex="asplain|asdot") = "asplain"
asplain: str = r"^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$" asplain: StrictStr = r"^(\^|^\_)(\d+\_|\d+\$|\d+\(\_\.\+\_\))+$"
asdot: str = ( asdot: StrictStr = (
r"^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$" r"^(\^|^\_)((\d+\.\d+)\_|(\d+\.\d+)\$|(\d+\.\d+)\(\_\.\+\_\))+$"
) )
@@ -51,61 +54,61 @@ class Features(HyperglassModel):
class Ping(HyperglassModel): class Ping(HyperglassModel):
"""Validation model for params.features.ping.""" """Validation model for params.features.ping."""
enable: bool = True enable: StrictBool = True
class Traceroute(HyperglassModel): class Traceroute(HyperglassModel):
"""Validation model for params.features.traceroute.""" """Validation model for params.features.traceroute."""
enable: bool = True enable: StrictBool = True
class Cache(HyperglassModel): class Cache(HyperglassModel):
"""Validation model for params.features.cache.""" """Validation model for params.features.cache."""
redis_id: int = 0 redis_id: StrictInt = 0
timeout: int = 120 timeout: StrictInt = 120
show_text: bool = True show_text: StrictBool = True
text: str = "Results will be cached for {timeout} minutes.".format( text: StrictStr = "Results will be cached for {timeout} minutes.".format(
timeout=ceil(timeout / 60) timeout=ceil(timeout / 60)
) )
class MaxPrefix(HyperglassModel): class MaxPrefix(HyperglassModel):
"""Validation model for params.features.max_prefix.""" """Validation model for params.features.max_prefix."""
enable: bool = False enable: StrictBool = False
ipv4: int = 24 ipv4: StrictInt = 24
ipv6: int = 64 ipv6: StrictInt = 64
message: str = ( message: StrictStr = (
"Prefix length must be smaller than /{m}. <b>{i}</b> is too specific." "Prefix length must be smaller than /{m}. <b>{i}</b> is too specific."
) )
class RateLimit(HyperglassModel): class RateLimit(HyperglassModel):
"""Validation model for params.features.rate_limit.""" """Validation model for params.features.rate_limit."""
redis_id: int = 1 redis_id: StrictInt = 1
class Query(HyperglassModel): class Query(HyperglassModel):
"""Validation model for params.features.rate_limit.query.""" """Validation model for params.features.rate_limit.query."""
rate: int = 5 rate: StrictInt = 5
period: str = "minute" period: StrictStr = "minute"
title: str = "Query Limit Reached" title: StrictStr = "Query Limit Reached"
message: str = ( message: StrictStr = (
"Query limit of {rate} per {period} reached. " "Query limit of {rate} per {period} reached. "
"Please wait one minute and try again." "Please wait one minute and try again."
).format(rate=rate, period=period) ).format(rate=rate, period=period)
button: str = "Try Again" button: StrictStr = "Try Again"
class Site(HyperglassModel): class Site(HyperglassModel):
"""Validation model for params.features.rate_limit.site.""" """Validation model for params.features.rate_limit.site."""
rate: int = 60 rate: StrictInt = 60
period: str = "minute" period: StrictStr = "minute"
title: str = "Limit Reached" title: StrictStr = "Limit Reached"
subtitle: str = ( subtitle: StrictStr = (
"You have accessed this site more than {rate} " "You have accessed this site more than {rate} "
"times in the last {period}." "times in the last {period}."
).format(rate=rate, period=period) ).format(rate=rate, period=period)
button: str = "Try Again" button: StrictStr = "Try Again"
query: Query = Query() query: Query = Query()
site: Site = Site() site: Site = Site()

View File

@@ -1,5 +1,8 @@
"""Validate error message configuration variables.""" """Validate error message configuration variables."""
# Third Party Imports
from pydantic import StrictStr
# Project Imports # Project Imports
from hyperglass.configuration.models._utils import HyperglassModel from hyperglass.configuration.models._utils import HyperglassModel
@@ -7,24 +10,24 @@ from hyperglass.configuration.models._utils import HyperglassModel
class Messages(HyperglassModel): class Messages(HyperglassModel):
"""Validation model for params.messages.""" """Validation model for params.messages."""
no_input: str = "{field} must be specified." no_input: StrictStr = "{field} must be specified."
acl_denied: str = "{target} is a member of {denied_network}, which is not allowed." acl_denied: StrictStr = "{target} is a member of {denied_network}, which is not allowed."
acl_not_allowed: str = "{target} is not allowed." acl_not_allowed: StrictStr = "{target} is not allowed."
max_prefix: str = ( max_prefix: StrictStr = (
"Prefix length must be shorter than /{max_length}. {target} is too specific." "Prefix length must be shorter than /{max_length}. {target} is too specific."
) )
requires_ipv6_cidr: str = ( requires_ipv6_cidr: StrictStr = (
"{device_name} requires IPv6 BGP lookups to be in CIDR notation." "{device_name} requires IPv6 BGP lookups to be in CIDR notation."
) )
feature_not_enabled: str = "{feature} is not enabled for {device_name}." feature_not_enabled: StrictStr = "{feature} is not enabled for {device_name}."
invalid_input: str = "{target} is not a valid {query_type} target." invalid_input: StrictStr = "{target} is not a valid {query_type} target."
invalid_field: str = "{input} is an invalid {field}." invalid_field: StrictStr = "{input} is an invalid {field}."
general: str = "Something went wrong." general: StrictStr = "Something went wrong."
directed_cidr: str = "{query_type} queries can not be in CIDR format." directed_cidr: StrictStr = "{query_type} queries can not be in CIDR format."
request_timeout: str = "Request timed out." request_timeout: StrictStr = "Request timed out."
connection_error: str = "Error connecting to {device_name}: {error}" connection_error: StrictStr = "Error connecting to {device_name}: {error}"
authentication_error: str = "Authentication error occurred." authentication_error: StrictStr = "Authentication error occurred."
noresponse_error: str = "No response." noresponse_error: StrictStr = "No response."
vrf_not_associated: str = "VRF {vrf_name} is not associated with {device_name}." vrf_not_associated: StrictStr = "VRF {vrf_name} is not associated with {device_name}."
no_matching_vrfs: str = "No VRFs in Common" no_matching_vrfs: StrictStr = "No VRFs in Common"
no_output: str = "No output." no_output: StrictStr = "No output."

View File

@@ -1,5 +1,8 @@
"""Validate network configuration variables.""" """Validate network configuration variables."""
# Third Party Imports
from pydantic import StrictStr
# Project Imports # Project Imports
from hyperglass.configuration.models._utils import HyperglassModel from hyperglass.configuration.models._utils import HyperglassModel
from hyperglass.configuration.models._utils import clean_name from hyperglass.configuration.models._utils import clean_name
@@ -8,8 +11,8 @@ from hyperglass.configuration.models._utils import clean_name
class Network(HyperglassModel): class Network(HyperglassModel):
"""Validation Model for per-network/asn config in devices.yaml.""" """Validation Model for per-network/asn config in devices.yaml."""
name: str name: StrictStr
display_name: str display_name: StrictStr
class Networks(HyperglassModel): class Networks(HyperglassModel):
@@ -17,10 +20,17 @@ class Networks(HyperglassModel):
@classmethod @classmethod
def import_params(cls, input_params): def import_params(cls, input_params):
""" """Import loaded YAML, initialize per-network definitions.
Imports passed dict from YAML config, removes unsupported
characters from device names, dynamically sets attributes for Remove unsupported characters from network names, dynamically
the credentials class. set attributes for the networks class. Add cls.networks
attribute so network objects can be accessed inside a dict.
Arguments:
input_params {dict} -- Unvalidated network definitions
Returns:
{object} -- Validated networks object
""" """
obj = Networks() obj = Networks()
networks = {} networks = {}

View File

@@ -1,6 +1,8 @@
"""Validate SSH proxy configuration variables.""" """Validate SSH proxy configuration variables."""
# Third Party Imports # Third Party Imports
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import validator from pydantic import validator
# Project Imports # Project Imports
@@ -13,16 +15,21 @@ from hyperglass.exceptions import UnsupportedDevice
class Proxy(HyperglassModel): class Proxy(HyperglassModel):
"""Validation model for per-proxy config in devices.yaml.""" """Validation model for per-proxy config in devices.yaml."""
name: str name: StrictStr
address: str address: StrictStr
port: int = 22 port: StrictInt = 22
credential: Credential credential: Credential
nos: str = "linux_ssh" nos: StrictStr = "linux_ssh"
@validator("nos") @validator("nos")
def supported_nos(cls, value): # noqa: N805 def supported_nos(cls, value):
""" """Verify NOS is supported by hyperglass.
Validates that passed nos string is supported by hyperglass.
Raises:
UnsupportedDevice: Raised if NOS is not supported.
Returns:
{str} -- Valid NOS name
""" """
if not value == "linux_ssh": if not value == "linux_ssh":
raise UnsupportedDevice(f'"{value}" device type is not supported.') raise UnsupportedDevice(f'"{value}" device type is not supported.')
@@ -34,10 +41,16 @@ class Proxies(HyperglassModel):
@classmethod @classmethod
def import_params(cls, input_params): def import_params(cls, input_params):
""" """Import loaded YAML, initialize per-proxy definitions.
Imports passed dict from YAML config, removes unsupported
characters from device names, dynamically sets attributes for Remove unsupported characters from proxy names, dynamically
the proxies class. set attributes for the proxies class.
Arguments:
input_params {dict} -- Unvalidated proxy definitions
Returns:
{object} -- Validated proxies object
""" """
obj = Proxies() obj = Proxies()
for (devname, params) in input_params.items(): for (devname, params) in input_params.items():

View File

@@ -6,6 +6,8 @@ from typing import List
from typing import Union from typing import Union
# Third Party Imports # Third Party Imports
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import validator from pydantic import validator
# Project Imports # Project Imports
@@ -27,46 +29,65 @@ from hyperglass.util import log
class Router(HyperglassModel): class Router(HyperglassModel):
"""Validation model for per-router config in devices.yaml.""" """Validation model for per-router config in devices.yaml."""
name: str name: StrictStr
address: str address: StrictStr
network: Network network: Network
credential: Credential credential: Credential
proxy: Union[Proxy, None] = None proxy: Union[Proxy, None] = None
location: str location: StrictStr
display_name: str display_name: StrictStr
port: int port: StrictInt
nos: str nos: StrictStr
commands: Union[Command, None] = None commands: Union[Command, None] = None
vrfs: List[Vrf] = [DefaultVrf()] vrfs: List[Vrf] = [DefaultVrf()]
display_vrfs: List[str] = [] display_vrfs: List[StrictStr] = []
vrf_names: List[str] = [] vrf_names: List[StrictStr] = []
@validator("nos") @validator("nos")
def supported_nos(cls, v): # noqa: N805 def supported_nos(cls, value):
"""Validate that nos is supported by hyperglass.
Raises:
UnsupportedDevice: Raised if nos is unsupported.
Returns:
{str} -- Valid NOS
""" """
Validates that passed nos string is supported by hyperglass. if not Supported.is_supported(value):
""" raise UnsupportedDevice(f'"{value}" device type is not supported.')
if not Supported.is_supported(v): return value
raise UnsupportedDevice(f'"{v}" device type is not supported.')
return v
@validator("name", "location") @validator("name", "location")
def clean_name(cls, v): # noqa: N805 def clean_name(cls, value):
"""Remove or replace unsupported characters from field values""" """Remove or replace unsupported characters from field values.
return clean_name(v)
Arguments:
value {str} -- Raw name/location
Returns:
{} -- Valid name/location
"""
return clean_name(value)
@validator("commands", always=True) @validator("commands", always=True)
def validate_commands(cls, v, values): # noqa: N805 def validate_commands(cls, value, values):
"""If a named command profile is not defined, use the NOS name.
Arguments:
value {str} -- Reference to command profile
values {dict} -- Other already-validated fields
Returns:
{str} -- Command profile or NOS name
""" """
If a named command profile is not defined, use the NOS name. if value is None:
""" value = values["nos"]
if v is None: return value
v = values["nos"]
return v
@validator("vrfs", pre=True) @validator("vrfs", pre=True)
def validate_vrfs(cls, value, values): def validate_vrfs(cls, value, values):
""" """Validate VRF definitions.
- Ensures source IP addresses are set for the default VRF - Ensures source IP addresses are set for the default VRF
(global routing table). (global routing table).
- Initializes the default VRF with the DefaultVRF() class so - Initializes the default VRF with the DefaultVRF() class so
@@ -74,6 +95,16 @@ class Router(HyperglassModel):
table. table.
- If the 'display_name' is not set for a non-default VRF, try - If the 'display_name' is not set for a non-default VRF, try
to make one that looks pretty based on the 'name'. to make one that looks pretty based on the 'name'.
Arguments:
value {list} -- List of VRFs
values {dict} -- Other already-validated fields
Raises:
ConfigError: Raised if the VRF is missing a source address
Returns:
{list} -- List of valid VRFs
""" """
vrfs = [] vrfs = []
for vrf in value: for vrf in value:
@@ -101,7 +132,9 @@ class Router(HyperglassModel):
# class. (See vrfs.py) # class. (See vrfs.py)
vrf = DefaultVrf(**vrf) vrf = DefaultVrf(**vrf)
elif vrf_name != "default" and not isinstance(vrf.get("display_name"), str): elif vrf_name != "default" and not isinstance(
vrf.get("display_name"), StrictStr
):
# If no display_name is set for a non-default VRF, try # If no display_name is set for a non-default VRF, try
# to make one by replacing non-alphanumeric characters # to make one by replacing non-alphanumeric characters
@@ -128,25 +161,32 @@ class Router(HyperglassModel):
class Routers(HyperglassModelExtra): class Routers(HyperglassModelExtra):
"""Validation model for device configurations.""" """Validation model for device configurations."""
hostnames: List[str] = [] hostnames: List[StrictStr] = []
vrfs: List[str] = [] vrfs: List[StrictStr] = []
display_vrfs: List[str] = [] display_vrfs: List[StrictStr] = []
routers: List[Router] = [] routers: List[Router] = []
@classmethod @classmethod
def _import(cls, input_params): def _import(cls, input_params):
""" """Import loaded YAML, initialize per-network definitions.
Imports passed list of dictionaries from YAML config, validates
each router config, sets class attributes for each router for Remove unsupported characters from device names, dynamically
easy access. Also builds lists of common attributes for easy set attributes for the devices class. Builds lists of common
access in other modules. attributes for easy access in other modules.
Arguments:
input_params {dict} -- Unvalidated router definitions
Returns:
{object} -- Validated routers object
""" """
vrfs = set() vrfs = set()
display_vrfs = set() display_vrfs = set()
setattr(cls, "routers", []) routers = Routers()
setattr(cls, "hostnames", []) routers.routers = []
setattr(cls, "vrfs", []) routers.hostnames = []
setattr(cls, "display_vrfs", []) routers.vrfs = []
routers.display_vrfs = []
for definition in input_params: for definition in input_params:
# Validate each router config against Router() model/schema # Validate each router config against Router() model/schema
@@ -154,14 +194,14 @@ class Routers(HyperglassModelExtra):
# Set a class attribute for each router so each router's # Set a class attribute for each router so each router's
# attributes can be accessed with `devices.router_hostname` # attributes can be accessed with `devices.router_hostname`
setattr(cls, router.name, router) setattr(routers, router.name, router)
# Add router-level attributes (assumed to be unique) to # Add router-level attributes (assumed to be unique) to
# class lists, e.g. so all hostnames can be accessed as a # class lists, e.g. so all hostnames can be accessed as a
# list with `devices.hostnames`, same for all router # list with `devices.hostnames`, same for all router
# classes, for when iteration over all routers is required. # classes, for when iteration over all routers is required.
cls.hostnames.append(router.name) routers.hostnames.append(router.name)
cls.routers.append(router) routers.routers.append(router)
for vrf in router.vrfs: for vrf in router.vrfs:
# For each configured router VRF, add its name and # For each configured router VRF, add its name and
@@ -177,15 +217,14 @@ class Routers(HyperglassModelExtra):
# Add a 'default_vrf' attribute to the devices class # Add a 'default_vrf' attribute to the devices class
# which contains the configured default VRF display name # which contains the configured default VRF display name
if vrf.name == "default" and not hasattr(cls, "default_vrf"): if vrf.name == "default" and not hasattr(cls, "default_vrf"):
setattr( routers.default_vrf = {
cls, "name": vrf.name,
"default_vrf", "display_name": vrf.display_name,
{"name": vrf.name, "display_name": vrf.display_name}, }
)
# Convert the de-duplicated sets to a standard list, add lists # Convert the de-duplicated sets to a standard list, add lists
# as class attributes # as class attributes
setattr(cls, "vrfs", list(vrfs)) routers.vrfs = list(vrfs)
setattr(cls, "display_vrfs", list(display_vrfs)) routers.display_vrfs = list(display_vrfs)
return cls return routers

View File

@@ -11,55 +11,27 @@ from typing import Optional
# Third Party Imports # Third Party Imports
from pydantic import IPvAnyNetwork from pydantic import IPvAnyNetwork
from pydantic import StrictStr
from pydantic import constr from pydantic import constr
from pydantic import validator from pydantic import validator
# Project Imports # Project Imports
from hyperglass.configuration.models._utils import HyperglassModel from hyperglass.configuration.models._utils import HyperglassModel
from hyperglass.exceptions import ConfigError
class DeviceVrf4(HyperglassModel): class DeviceVrf4(HyperglassModel):
"""Validation model for IPv4 AFI definitions.""" """Validation model for IPv4 AFI definitions."""
vrf_name: str vrf_name: StrictStr
source_address: IPv4Address source_address: IPv4Address
@validator("source_address")
def check_ip_type(cls, value, values):
if value is not None and isinstance(value, IPv4Address):
if value.is_loopback:
raise ConfigError(
(
"The default routing table with source IPs must be defined. "
"VRF: {vrf}, Source Address: {value}"
),
vrf=values["vrf_name"],
value=value,
)
return value
class DeviceVrf6(HyperglassModel): class DeviceVrf6(HyperglassModel):
"""Validation model for IPv6 AFI definitions.""" """Validation model for IPv6 AFI definitions."""
vrf_name: str vrf_name: StrictStr
source_address: IPv6Address source_address: IPv6Address
@validator("source_address")
def check_ip_type(cls, value, values):
if value is not None and isinstance(value, IPv4Address):
if value.is_loopback:
raise ConfigError(
(
"The default routing table with source IPs must be defined. "
"VRF: {vrf}, Source Address: {value}"
),
vrf=values["vrf_name"],
value=value,
)
return value
class Vrf(HyperglassModel): class Vrf(HyperglassModel):
"""Validation model for per VRF/afi config in devices.yaml.""" """Validation model for per VRF/afi config in devices.yaml."""
@@ -75,6 +47,11 @@ class Vrf(HyperglassModel):
@validator("ipv4", "ipv6", pre=True, always=True) @validator("ipv4", "ipv6", pre=True, always=True)
def set_default_vrf_name(cls, value, values): def set_default_vrf_name(cls, value, values):
"""If per-AFI name is undefined, set it to the global VRF name.
Returns:
{str} -- VRF Name
"""
if isinstance(value, DefaultVrf) and value.vrf_name is None: if isinstance(value, DefaultVrf) and value.vrf_name is None:
value["vrf_name"] = values["name"] value["vrf_name"] = values["name"]
elif isinstance(value, Dict) and value.get("vrf_name") is None: elif isinstance(value, Dict) and value.get("vrf_name") is None:
@@ -83,6 +60,11 @@ class Vrf(HyperglassModel):
@validator("access_list", pre=True) @validator("access_list", pre=True)
def validate_action(cls, value): def validate_action(cls, value):
"""Transform ACL networks to IPv4Network/IPv6Network objects.
Returns:
{object} -- IPv4Network/IPv6Network object
"""
for li in value: for li in value:
for action, network in li.items(): for action, network in li.items():
if isinstance(network, (IPv4Network, IPv6Network)): if isinstance(network, (IPv4Network, IPv6Network)):
@@ -93,8 +75,8 @@ class Vrf(HyperglassModel):
class DefaultVrf(HyperglassModel): class DefaultVrf(HyperglassModel):
"""Validation model for default routing table VRF.""" """Validation model for default routing table VRF."""
name: str = "default" name: StrictStr = "default"
display_name: str = "Global" display_name: StrictStr = "Global"
access_list: List[Dict[constr(regex=("allow|deny")), IPvAnyNetwork]] = [ access_list: List[Dict[constr(regex=("allow|deny")), IPvAnyNetwork]] = [
{"allow": IPv4Network("0.0.0.0/0")}, {"allow": IPv4Network("0.0.0.0/0")},
{"allow": IPv6Network("::/0")}, {"allow": IPv6Network("::/0")},
@@ -103,14 +85,14 @@ class DefaultVrf(HyperglassModel):
class DefaultVrf4(HyperglassModel): class DefaultVrf4(HyperglassModel):
"""Validation model for IPv4 default routing table VRF definition.""" """Validation model for IPv4 default routing table VRF definition."""
vrf_name: str = "default" vrf_name: StrictStr = "default"
source_address: IPv4Address = IPv4Address("127.0.0.1") source_address: IPv4Address
class DefaultVrf6(HyperglassModel): class DefaultVrf6(HyperglassModel):
"""Validation model for IPv6 default routing table VRF definition.""" """Validation model for IPv6 default routing table VRF definition."""
vrf_name: str = "default" vrf_name: StrictStr = "default"
source_address: IPv6Address = IPv6Address("::1") source_address: IPv6Address
ipv4: DefaultVrf4 = DefaultVrf4() ipv4: Optional[DefaultVrf4]
ipv6: DefaultVrf6 = DefaultVrf6() ipv6: Optional[DefaultVrf6]

View File

@@ -17,7 +17,7 @@
<text x="45.0" y="14">Lines of Code</text> <text x="45.0" y="14">Lines of Code</text>
</g> </g>
<g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11"> <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
<text x="106.0" y="15" fill="#010101" fill-opacity=".3">3173</text> <text x="106.0" y="15" fill="#010101" fill-opacity=".3">3202</text>
<text x="105.0" y="14">3173</text> <text x="105.0" y="14">3202</text>
</g> </g>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB