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

287 lines
9.4 KiB
Python
Raw Normal View History

"""Validate VRF configuration variables."""
2020-02-03 02:35:11 -07:00
# Standard Library
from typing import List, Optional
from ipaddress import IPv4Address, IPv4Network, IPv6Address, IPv6Network
2020-02-03 02:35:11 -07:00
# Third Party
from pydantic import (
Field,
FilePath,
StrictStr,
StrictBool,
conint,
constr,
validator,
root_validator,
)
2020-02-03 02:35:11 -07:00
# Project
from hyperglass.configuration.models._utils import HyperglassModel, HyperglassModelExtra
2020-01-17 02:50:57 -07:00
2020-01-31 02:06:27 -10:00
class AccessList4(HyperglassModel):
"""Validation model for IPv4 access-lists."""
2020-02-01 16:11:01 -10:00
network: IPv4Network = Field(
"0.0.0.0/0",
title="Network",
description="IPv4 Network/Prefix that should be permitted or denied. ",
)
action: constr(regex=r"permit|deny") = Field(
"permit",
title="Action",
description="Permit or deny any networks contained within the prefix.",
)
ge: conint(ge=0, le=32) = Field(
0,
title="Greater Than or Equal To",
description="Similar to `ge` in a Cisco prefix-list, the `ge` field defines the **bottom** threshold for prefix size. For example, a value of `24` would result in a query for `192.0.2.0/23` being denied, but a query for `192.0.2.0/32` would be permitted. If this field is set to a value smaller than the `network` field's prefix length, this field's value will be overwritten to the prefix length of the prefix in the `network` field.",
)
le: conint(ge=0, le=32) = Field(
32,
title="Less Than or Equal To",
description="Similar to `le` in a Cisco prefix-list, the `le` field defines the **top** threshold for prefix size. For example, a value of `24` would result in a query for `192.0.2.0/23` being permitted, but a query for `192.0.2.0/32` would be denied.",
)
2020-01-31 02:06:27 -10:00
@validator("ge")
def validate_model(cls, value, values):
"""Ensure ge is at least the size of the input prefix.
Arguments:
value {int} -- Initial ge value
values {dict} -- Other post-validation fields
Returns:
{int} -- Validated ge value
"""
net_len = values["network"].prefixlen
if net_len > value:
value = net_len
return value
class AccessList6(HyperglassModel):
"""Validation model for IPv6 access-lists."""
2020-02-01 16:11:01 -10:00
network: IPv6Network = Field(
"::/0",
title="Network",
description="IPv6 Network/Prefix that should be permitted or denied. ",
)
action: constr(regex=r"permit|deny") = Field(
"permit",
title="Action",
description="Permit or deny any networks contained within the prefix.",
)
ge: conint(ge=0, le=128) = Field(
0,
title="Greater Than or Equal To",
description="Similar to `ge` in a Cisco prefix-list, the `ge` field defines the **bottom** threshold for prefix size. For example, a value of `64` would result in a query for `2001:db8::/48` being denied, but a query for `2001:db8::1/128` would be permitted. If this field is set to a value smaller than the `network` field's prefix length, this field's value will be overwritten to the prefix length of the prefix in the `network` field.",
)
le: conint(ge=0, le=128) = Field(
128,
title="Less Than or Equal To",
description="Similar to `le` in a Cisco prefix-list, the `le` field defines the **top** threshold for prefix size. For example, a value of `64` would result in a query for `2001:db8::/48` being permitted, but a query for `2001:db8::1/128` would be denied.",
)
2020-01-31 02:06:27 -10:00
@validator("ge")
def validate_model(cls, value, values):
"""Ensure ge is at least the size of the input prefix.
Arguments:
value {int} -- Initial ge value
values {dict} -- Other post-validation fields
Returns:
{int} -- Validated ge value
"""
net_len = values["network"].prefixlen
if net_len > value:
value = net_len
return value
2020-01-17 02:50:57 -07:00
class InfoConfigParams(HyperglassModelExtra):
"""Validation model for per-help params."""
title: Optional[StrictStr]
2020-02-01 16:11:01 -10:00
class Config:
"""Pydantic model configuration."""
title = "Help Parameters"
description = "Set dynamic or reusable values which may be used in the help/information content. Params my be access by using Python string formatting syntax, e.g. `{param_name}`. Any arbitrary values may be added."
2020-01-17 02:50:57 -07:00
class InfoConfig(HyperglassModel):
"""Validation model for help configuration."""
enable: StrictBool = True
file: Optional[FilePath]
params: InfoConfigParams = InfoConfigParams()
2020-02-01 16:11:01 -10:00
class Config:
"""Pydantic model configuration."""
fields = {
"enable": {
"title": "Enable",
"description": "Enable or disable the display of help/information content for this query type.",
},
"file": {
"title": "File Name",
"description": "Path to a valid text or Markdown file containing custom content.",
},
}
2020-01-16 02:52:00 -07:00
class Info(HyperglassModel):
2020-01-17 02:50:57 -07:00
"""Validation model for per-VRF, per-Command help."""
2020-01-16 02:52:00 -07:00
2020-01-17 02:50:57 -07:00
bgp_aspath: InfoConfig = InfoConfig()
bgp_community: InfoConfig = InfoConfig()
bgp_route: InfoConfig = InfoConfig()
ping: InfoConfig = InfoConfig()
traceroute: InfoConfig = InfoConfig()
2020-01-16 02:52:00 -07:00
2020-02-01 16:11:01 -10:00
class Config:
"""Pydantic model configuration."""
title = "VRF Information"
description = "Per-VRF help & information content."
fields = {
"bgp_aspath": {
"title": "BGP AS Path",
"description": "Show information about bgp_aspath queries when selected.",
},
"bgp_community": {
"title": "BGP Community",
"description": "Show information about bgp_community queries when selected.",
},
"bgp_route": {
"title": "BGP Route",
"description": "Show information about bgp_route queries when selected.",
},
"ping": {
"title": "Ping",
"description": "Show information about ping queries when selected.",
},
"traceroute": {
"title": "Traceroute",
"description": "Show information about traceroute queries when selected.",
},
}
2020-01-16 02:52:00 -07:00
2020-01-31 02:06:27 -10:00
class DeviceVrf4(HyperglassModelExtra):
"""Validation model for IPv4 AFI definitions."""
source_address: IPv4Address
2020-01-31 02:06:27 -10:00
access_list: List[AccessList4] = [AccessList4()]
2020-03-22 17:46:08 -07:00
force_cidr: StrictBool = False
2020-01-31 02:06:27 -10:00
class DeviceVrf6(HyperglassModelExtra):
"""Validation model for IPv6 AFI definitions."""
source_address: IPv6Address
2020-01-31 02:06:27 -10:00
access_list: List[AccessList6] = [AccessList6()]
2020-03-22 17:46:08 -07:00
force_cidr: StrictBool = False
2019-10-04 16:54:32 -07:00
class Vrf(HyperglassModel):
"""Validation model for per VRF/afi config in devices.yaml."""
2020-01-16 02:52:00 -07:00
name: StrictStr
display_name: StrictStr
info: Info = Info()
2019-12-30 01:44:19 -07:00
ipv4: Optional[DeviceVrf4]
ipv6: Optional[DeviceVrf6]
2019-09-30 07:51:17 -07:00
2020-01-31 02:06:27 -10:00
@root_validator
def set_dynamic(cls, values):
"""Set dynamic attributes before VRF initialization.
Arguments:
values {dict} -- Post-validation VRF attributes
Returns:
2020-01-31 02:06:27 -10:00
{dict} -- VRF with new attributes set
"""
2020-01-31 02:06:27 -10:00
if values["name"] == "default":
protocol4 = "ipv4_default"
protocol6 = "ipv6_default"
else:
protocol4 = "ipv4_vpn"
protocol6 = "ipv6_vpn"
2020-01-31 02:06:27 -10:00
if values.get("ipv4") is not None:
values["ipv4"].protocol = protocol4
values["ipv4"].version = 4
if values.get("ipv6") is not None:
values["ipv6"].protocol = protocol6
values["ipv6"].version = 6
if values.get("name") == "default" and values.get("display_name") is None:
values["display_name"] = "Global"
2020-01-31 02:06:27 -10:00
return values
def __getitem__(self, i):
"""Access the VRF's AFI by IP protocol number.
Arguments:
i {int} -- IP Protocol number (4|6)
Raises:
AttributeError: Raised if passed number is not 4 or 6.
Returns:
2020-01-31 02:06:27 -10:00
{object} -- AFI object
"""
2020-01-31 02:06:27 -10:00
if i not in (4, 6):
raise AttributeError(f"Must be 4 or 6, got '{i}")
return getattr(self, f"ipv{i}")
2020-01-16 02:52:00 -07:00
def __hash__(self):
"""Make VRF object hashable so the object can be deduplicated with set().
Returns:
{int} -- Hash of VRF name
"""
return hash((self.name,))
def __eq__(self, other):
"""Make VRF object comparable so the object can be deduplicated with set().
Arguments:
other {object} -- Object to compare
Returns:
{bool} -- True if comparison attributes are the same value
"""
2020-01-31 02:06:27 -10:00
result = False
if isinstance(other, HyperglassModel):
result = self.name == other.name
return result
2020-01-16 02:52:00 -07:00
2020-02-01 16:11:01 -10:00
class Config:
"""Pydantic model configuration."""
title = "VRF"
description = "Per-VRF configuration."
fields = {
"name": {
"title": "Name",
"description": "VRF name as configured on the router/device.",
},
"display_name": {
"title": "Display Name",
"description": "Display name of VRF for use in the hyperglass UI. If none is specified, hyperglass will attempt to generate one.",
},
}