Compare commits

..

No commits in common. "8d0139925e7364ed7e448a28ad73c5df67ba9bae" and "4f18b6c8effcfe84f65752fb6f68adf2e6109290" have entirely different histories.

3 changed files with 20 additions and 91 deletions

View file

@ -1,2 +0,0 @@
[tool.black]
line-length = 79

View file

@ -1,7 +1,6 @@
from __future__ import annotations from __future__ import annotations
import itertools import itertools
import re
from dataclasses import dataclass from dataclasses import dataclass
from ipaddress import IPv4Address from ipaddress import IPv4Address
from typing import Any, Generic, Iterator, Literal, TypeVar from typing import Any, Generic, Iterator, Literal, TypeVar
@ -30,30 +29,6 @@ class AutoList(list[T], Generic[T]):
return [parse_obj_as(T, value)] return [parse_obj_as(T, value)]
VARIABLES = {
"net.len": "net.len",
}
def interpolate(string: str, ctx: Context) -> str:
pattern = r"(?<!\\)(\$\{[_a-z][_a-z0-9.]*\})"
def lookup(var: str) -> str:
try:
return VARIABLES[var]
except KeyError:
return quoted(getattr(ctx, var))
split = re.split(pattern, string)
parts = [
(lookup(p[2:-1]) if re.match(pattern, p) else quoted(p))
for p in split
if p
]
return ", ".join(parts)
class Proto(BaseModel): class Proto(BaseModel):
protos: AutoList[str] protos: AutoList[str]
@ -74,28 +49,7 @@ class Not(BaseModel):
condition: Condition = Field(alias="not") condition: Condition = Field(alias="not")
class AsPathContains(BaseModel): Condition = Proto | Source | And | Or | Not
contains: AutoList[int] = Field(alias="as_path.contains")
class AsPathLength(BaseModel):
length: list[int] = Field(
ge=0, min_items=2, max_items=2, alias="as_path.len"
)
class IPv4orIPv6(BaseModel):
ipv4: list[int] = Field(ge=0, min_items=2, max_items=2)
ipv6: list[int] = Field(ge=0, min_items=2, max_items=2)
class NetLength(BaseModel):
length: IPv4orIPv6 = Field(alias="net.len")
Condition = (
Proto | Source | And | Or | Not | AsPathContains | AsPathLength | NetLength
)
And.update_forward_refs() And.update_forward_refs()
Or.update_forward_refs() Or.update_forward_refs()
@ -107,10 +61,6 @@ Accept = Literal["accept"]
Reject = Literal["reject"] Reject = Literal["reject"]
class RejectWithMsg(BaseModel):
reject: str
class PrefSrc(BaseModel): class PrefSrc(BaseModel):
pref_src: AutoList[IPvAnyAddress] pref_src: AutoList[IPvAnyAddress]
@ -120,7 +70,7 @@ class Conditional(BaseModel):
actions: AutoList[Action] = Field(alias="then") actions: AutoList[Action] = Field(alias="then")
Action = Accept | Reject | RejectWithMsg | PrefSrc | Conditional Action = Accept | Reject | PrefSrc | Conditional
Conditional.update_forward_refs() Conditional.update_forward_refs()
@ -194,31 +144,12 @@ def str_of_condition(condition: Condition, ctx: bool) -> str:
sources = [str(s) for s in sources] sources = [str(s) for s in sources]
return f"krt_source ~ [ {', '.join(sources)} ]" return f"krt_source ~ [ {', '.join(sources)} ]"
case AsPathContains(contains=contains):
return (
f"bgp_path ~ [ {', '.join([str(asn) for asn in contains])} ]"
)
case AsPathLength(length=[min_len, max_len]):
return f"{min_len} <= bgp_path.len && bgp_path.len <= {max_len}"
case NetLength(
length=IPv4orIPv6(ipv4=[min_v4, max_v4], ipv6=[min_v6, max_v6])
):
if ctx.ipv4:
return f"{min_v4} <= net.len && net.len <= {max_v4}"
else:
return f"{min_v6} <= net.len && net.len <= {max_v6}"
def lines_of_action(action: Action, ctx: Context) -> Iterable[str]: def lines_of_action(action: Action, ctx: Context) -> Iterable[str]:
match action: match action:
case "accept" | "reject": case "accept" | "reject":
yield f"{action};" yield f"{action};"
case RejectWithMsg(reject=reason):
yield f"reject {interpolate(reason, ctx)};"
case Conditional(condition=condition, actions=actions): case Conditional(condition=condition, actions=actions):
yield f"if {str_of_condition(condition, ctx)} then {'{'}" yield f"if {str_of_condition(condition, ctx)} then {'{'}"
yield from indent( yield from indent(

View file

@ -27,8 +27,8 @@ protocol device {
{% for name, kernel in bird__kernel.items() %} {% for name, kernel in bird__kernel.items() %}
{% for version in ["ipv4", "ipv6"] %} {% for version in ["ipv4", "ipv6"] %}
{% set is_ipv4 = version == "ipv4" %} {% set ipv4 = version == "ipv4" %}
protocol kernel {{ name | bird_name(is_ipv4) }} { protocol kernel {{ name | bird_name(ipv4) }} {
{% if kernel.kernel is defined %} {% if kernel.kernel is defined %}
kernel table {{ kernel.kernel }}; kernel table {{ kernel.kernel }};
{% endif %} {% endif %}
@ -40,9 +40,9 @@ protocol kernel {{ name | bird_name(is_ipv4) }} {
{% endif %} {% endif %}
{{ version }} { {{ version }} {
{% if kernel.table is defined %} {% if kernel.table is defined %}
table {{ kernel.table | bird_name(is_ipv4) }}; table {{ kernel.table | bird_name(ipv4) }};
{% endif %} {% endif %}
{{ import_export(kernel, is_ipv4) | indent(8) }} {{ import_export(kernel, ipv4) | indent(8) }}
}; };
} }
{% endfor %} {% endfor %}
@ -50,25 +50,25 @@ protocol kernel {{ name | bird_name(is_ipv4) }} {
{% for name, pipe in bird__pipes.items() %} {% for name, pipe in bird__pipes.items() %}
{% for version in ["ipv4", "ipv6"] %} {% for version in ["ipv4", "ipv6"] %}
{% set is_ipv4 = version == "ipv4" %} {% set ipv4 = version == "ipv4" %}
protocol pipe {{ name | bird_name(is_ipv4) }} { protocol pipe {{ name | bird_name(ipv4) }} {
table {{ pipe.table | bird_name(is_ipv4) }}; table {{ pipe.table | bird_name(ipv4) }};
peer table {{ pipe.peer_table | default("master") | bird_name(is_ipv4) }}; peer table {{ pipe.peer_table | default("master") | bird_name(ipv4) }};
{{ import_export(kernel, is_ipv4) | indent(4) }} {{ import_export(kernel, ipv4) | indent(4) }}
} }
{% endfor %} {% endfor %}
{% endfor %} {% endfor %}
{% if bird__ospf is defined %} {% if bird__ospf is defined %}
{% for version in ["ipv4", "ipv6"] %} {% for version in ["ipv4", "ipv6"] %}
{% set is_ipv4 = version == "ipv4" %} {% set ipv4 = version == "ipv4" %}
{% set ospf_version = "v2" if is_ipv4 else "v3" %} {% set ospf_version = "v2" if ipv4 else "v3" %}
protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(is_ipv4) }} { protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(ipv4) }} {
{{ version }} { {{ version }} {
{% if bird__ospf.table is defined %} {% if bird__ospf.table is defined %}
table {{ bird__ospf.table | bird_name(is_ipv4) }}; table {{ bird__ospf.table | bird_name(ipv4) }};
{% endif %} {% endif %}
{{ import_export(bird__ospf, is_ipv4) | indent(8) }} {{ import_export(bird__ospf, ipv4) | indent(8) }}
}; };
{% for id, area in bird__ospf.areas.items() %} {% for id, area in bird__ospf.areas.items() %}
area {{ id }} { area {{ id }} {
@ -92,8 +92,8 @@ protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(is_ipv4) }} {
{% for name, bgp in bird__bgp.items() %} {% for name, bgp in bird__bgp.items() %}
{% for version in ["ipv4", "ipv6"] %} {% for version in ["ipv4", "ipv6"] %}
{% set is_ipv4 = version == "ipv4" %} {% set ipv4 = version == "ipv4" %}
protocol bgp {{ name | bird_name(is_ipv4) }} { protocol bgp {{ name | bird_name(ipv4) }} {
local {{ bgp.local.address local {{ bgp.local.address
| ansible.utils.ipaddr(version) | ansible.utils.ipaddr(version)
| first }} as {{ bgp.local.as }}; | first }} as {{ bgp.local.as }};
@ -106,12 +106,12 @@ protocol bgp {{ name | bird_name(is_ipv4) }} {
{% endif %} {% endif %}
{{ version }} { {{ version }} {
{% if bgp.table is defined %} {% if bgp.table is defined %}
table {{ bgp.table | bird_name(is_ipv4) }}; table {{ bgp.table | bird_name(ipv4) }};
{% endif %} {% endif %}
{% if bgp.next_hop_self is defined %} {% if bgp.next_hop_self is defined %}
next hop self; next hop self;
{% endif %} {% endif %}
{{ import_export(bgp, is_ipv4) | indent(8) }} {{ import_export(bgp, ipv4) | indent(8) }}
}; };
} }
{% endfor %} {% endfor %}