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:
# - repo: https://github.com/pre-commit/pre-commit-hooks
# rev: v2.3.0
# hooks:
# - id: flake8
# stages:
# - push
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.3.0
hooks:
- id: flake8
stages:
- push
- repo: local
hooks:
- id: line_count

View File

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

View File

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

View File

@@ -69,7 +69,7 @@ class Branding(HyperglassModel):
favicons: str = "ui/images/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."""
chars = list(value)
if chars[len(chars) - 1] != "/":

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,8 @@
"""Validate network configuration variables."""
# Third Party Imports
from pydantic import StrictStr
# Project Imports
from hyperglass.configuration.models._utils import HyperglassModel
from hyperglass.configuration.models._utils import clean_name
@@ -8,8 +11,8 @@ from hyperglass.configuration.models._utils import clean_name
class Network(HyperglassModel):
"""Validation Model for per-network/asn config in devices.yaml."""
name: str
display_name: str
name: StrictStr
display_name: StrictStr
class Networks(HyperglassModel):
@@ -17,10 +20,17 @@ class Networks(HyperglassModel):
@classmethod
def import_params(cls, input_params):
"""
Imports passed dict from YAML config, removes unsupported
characters from device names, dynamically sets attributes for
the credentials class.
"""Import loaded YAML, initialize per-network definitions.
Remove unsupported characters from network names, dynamically
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()
networks = {}

View File

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

View File

@@ -6,6 +6,8 @@ from typing import List
from typing import Union
# Third Party Imports
from pydantic import StrictInt
from pydantic import StrictStr
from pydantic import validator
# Project Imports
@@ -27,46 +29,65 @@ from hyperglass.util import log
class Router(HyperglassModel):
"""Validation model for per-router config in devices.yaml."""
name: str
address: str
name: StrictStr
address: StrictStr
network: Network
credential: Credential
proxy: Union[Proxy, None] = None
location: str
display_name: str
port: int
nos: str
location: StrictStr
display_name: StrictStr
port: StrictInt
nos: StrictStr
commands: Union[Command, None] = None
vrfs: List[Vrf] = [DefaultVrf()]
display_vrfs: List[str] = []
vrf_names: List[str] = []
display_vrfs: List[StrictStr] = []
vrf_names: List[StrictStr] = []
@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(v):
raise UnsupportedDevice(f'"{v}" device type is not supported.')
return v
if not Supported.is_supported(value):
raise UnsupportedDevice(f'"{value}" device type is not supported.')
return value
@validator("name", "location")
def clean_name(cls, v): # noqa: N805
"""Remove or replace unsupported characters from field values"""
return clean_name(v)
def clean_name(cls, value):
"""Remove or replace unsupported characters from field values.
Arguments:
value {str} -- Raw name/location
Returns:
{} -- Valid name/location
"""
return clean_name(value)
@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 v is None:
v = values["nos"]
return v
if value is None:
value = values["nos"]
return value
@validator("vrfs", pre=True)
def validate_vrfs(cls, value, values):
"""
"""Validate VRF definitions.
- Ensures source IP addresses are set for the default VRF
(global routing table).
- Initializes the default VRF with the DefaultVRF() class so
@@ -74,6 +95,16 @@ class Router(HyperglassModel):
table.
- If the 'display_name' is not set for a non-default VRF, try
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 = []
for vrf in value:
@@ -101,7 +132,9 @@ class Router(HyperglassModel):
# class. (See vrfs.py)
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
# to make one by replacing non-alphanumeric characters
@@ -128,25 +161,32 @@ class Router(HyperglassModel):
class Routers(HyperglassModelExtra):
"""Validation model for device configurations."""
hostnames: List[str] = []
vrfs: List[str] = []
display_vrfs: List[str] = []
hostnames: List[StrictStr] = []
vrfs: List[StrictStr] = []
display_vrfs: List[StrictStr] = []
routers: List[Router] = []
@classmethod
def _import(cls, input_params):
"""
Imports passed list of dictionaries from YAML config, validates
each router config, sets class attributes for each router for
easy access. Also builds lists of common attributes for easy
access in other modules.
"""Import loaded YAML, initialize per-network definitions.
Remove unsupported characters from device names, dynamically
set attributes for the devices class. Builds lists of common
attributes for easy access in other modules.
Arguments:
input_params {dict} -- Unvalidated router definitions
Returns:
{object} -- Validated routers object
"""
vrfs = set()
display_vrfs = set()
setattr(cls, "routers", [])
setattr(cls, "hostnames", [])
setattr(cls, "vrfs", [])
setattr(cls, "display_vrfs", [])
routers = Routers()
routers.routers = []
routers.hostnames = []
routers.vrfs = []
routers.display_vrfs = []
for definition in input_params:
# 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
# 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
# class lists, e.g. so all hostnames can be accessed as a
# list with `devices.hostnames`, same for all router
# classes, for when iteration over all routers is required.
cls.hostnames.append(router.name)
cls.routers.append(router)
routers.hostnames.append(router.name)
routers.routers.append(router)
for vrf in router.vrfs:
# 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
# which contains the configured default VRF display name
if vrf.name == "default" and not hasattr(cls, "default_vrf"):
setattr(
cls,
"default_vrf",
{"name": vrf.name, "display_name": vrf.display_name},
)
routers.default_vrf = {
"name": vrf.name,
"display_name": vrf.display_name,
}
# Convert the de-duplicated sets to a standard list, add lists
# as class attributes
setattr(cls, "vrfs", list(vrfs))
setattr(cls, "display_vrfs", list(display_vrfs))
routers.vrfs = list(vrfs)
routers.display_vrfs = list(display_vrfs)
return cls
return routers

View File

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

View File

@@ -17,7 +17,7 @@
<text x="45.0" y="14">Lines of Code</text>
</g>
<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="105.0" y="14">3173</text>
<text x="106.0" y="15" fill="#010101" fill-opacity=".3">3202</text>
<text x="105.0" y="14">3202</text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB