From 4f18b6c8effcfe84f65752fb6f68adf2e6109290 Mon Sep 17 00:00:00 2001 From: elkmaennchen Date: Sun, 10 Mar 2024 12:04:00 +0100 Subject: [PATCH 1/6] relax temperature alert for quanta switch --- group_vars/prom/prometheus/quanta.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/group_vars/prom/prometheus/quanta.yml b/group_vars/prom/prometheus/quanta.yml index cedd93b..682d062 100644 --- a/group_vars/prom/prometheus/quanta.yml +++ b/group_vars/prom/prometheus/quanta.yml @@ -68,7 +68,7 @@ prometheus__rules_quanta: severity: critical - alert: QuantaIntakeTemp expr: - 0.5 * snAgentTempValue{snAgentTempSensorDescr=~".*Intake.*"} > 45 + 0.5 * snAgentTempValue{snAgentTempSensorDescr=~".*Intake.*"} > 60 for: 10m keep_firing_for: 30m labels: @@ -78,7 +78,7 @@ prometheus__rules_quanta: Description: !unsafe "{{ $labels.snAgentTempSensorDescr }}" - alert: QuantaIntakeTemp expr: - 0.5 * snAgentTempValue{snAgentTempSensorDescr=~".*Intake.*"} > 60 + 0.5 * snAgentTempValue{snAgentTempSensorDescr=~".*Intake.*"} > 70 for: 10m keep_firing_for: 30m labels: From 7f9ccf3e594fe7295d08434594f49938284606d1 Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine Date: Sun, 31 Mar 2024 16:12:51 +0200 Subject: [PATCH 2/6] chore(black): Add configuration file (line-limit) --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a8f43fe --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[tool.black] +line-length = 79 From 8d0139925e7364ed7e448a28ad73c5df67ba9bae Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine Date: Sun, 31 Mar 2024 18:12:38 +0200 Subject: [PATCH 3/6] feat(bird): Add as_path.{contains,len}, net.len --- roles/bird/filter_plugins/bird.py | 73 ++++++++++++++++++++++++++++++- roles/bird/templates/bird.conf.j2 | 36 +++++++-------- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/roles/bird/filter_plugins/bird.py b/roles/bird/filter_plugins/bird.py index 504178c..b2764d9 100644 --- a/roles/bird/filter_plugins/bird.py +++ b/roles/bird/filter_plugins/bird.py @@ -1,6 +1,7 @@ from __future__ import annotations import itertools +import re from dataclasses import dataclass from ipaddress import IPv4Address from typing import Any, Generic, Iterator, Literal, TypeVar @@ -29,6 +30,30 @@ class AutoList(list[T], Generic[T]): return [parse_obj_as(T, value)] +VARIABLES = { + "net.len": "net.len", +} + + +def interpolate(string: str, ctx: Context) -> str: + pattern = r"(? 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): protos: AutoList[str] @@ -49,7 +74,28 @@ class Not(BaseModel): condition: Condition = Field(alias="not") -Condition = Proto | Source | And | Or | Not +class AsPathContains(BaseModel): + 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() Or.update_forward_refs() @@ -61,6 +107,10 @@ Accept = Literal["accept"] Reject = Literal["reject"] +class RejectWithMsg(BaseModel): + reject: str + + class PrefSrc(BaseModel): pref_src: AutoList[IPvAnyAddress] @@ -70,7 +120,7 @@ class Conditional(BaseModel): actions: AutoList[Action] = Field(alias="then") -Action = Accept | Reject | PrefSrc | Conditional +Action = Accept | Reject | RejectWithMsg | PrefSrc | Conditional Conditional.update_forward_refs() @@ -144,12 +194,31 @@ def str_of_condition(condition: Condition, ctx: bool) -> str: sources = [str(s) for s in 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]: match action: case "accept" | "reject": yield f"{action};" + case RejectWithMsg(reject=reason): + yield f"reject {interpolate(reason, ctx)};" + case Conditional(condition=condition, actions=actions): yield f"if {str_of_condition(condition, ctx)} then {'{'}" yield from indent( diff --git a/roles/bird/templates/bird.conf.j2 b/roles/bird/templates/bird.conf.j2 index e5884d4..59dbe32 100644 --- a/roles/bird/templates/bird.conf.j2 +++ b/roles/bird/templates/bird.conf.j2 @@ -27,8 +27,8 @@ protocol device { {% for name, kernel in bird__kernel.items() %} {% for version in ["ipv4", "ipv6"] %} -{% set ipv4 = version == "ipv4" %} -protocol kernel {{ name | bird_name(ipv4) }} { +{% set is_ipv4 = version == "ipv4" %} +protocol kernel {{ name | bird_name(is_ipv4) }} { {% if kernel.kernel is defined %} kernel table {{ kernel.kernel }}; {% endif %} @@ -40,9 +40,9 @@ protocol kernel {{ name | bird_name(ipv4) }} { {% endif %} {{ version }} { {% if kernel.table is defined %} - table {{ kernel.table | bird_name(ipv4) }}; + table {{ kernel.table | bird_name(is_ipv4) }}; {% endif %} - {{ import_export(kernel, ipv4) | indent(8) }} + {{ import_export(kernel, is_ipv4) | indent(8) }} }; } {% endfor %} @@ -50,25 +50,25 @@ protocol kernel {{ name | bird_name(ipv4) }} { {% for name, pipe in bird__pipes.items() %} {% for version in ["ipv4", "ipv6"] %} -{% set ipv4 = version == "ipv4" %} -protocol pipe {{ name | bird_name(ipv4) }} { - table {{ pipe.table | bird_name(ipv4) }}; - peer table {{ pipe.peer_table | default("master") | bird_name(ipv4) }}; - {{ import_export(kernel, ipv4) | indent(4) }} +{% set is_ipv4 = version == "ipv4" %} +protocol pipe {{ name | bird_name(is_ipv4) }} { + table {{ pipe.table | bird_name(is_ipv4) }}; + peer table {{ pipe.peer_table | default("master") | bird_name(is_ipv4) }}; + {{ import_export(kernel, is_ipv4) | indent(4) }} } {% endfor %} {% endfor %} {% if bird__ospf is defined %} {% for version in ["ipv4", "ipv6"] %} -{% set ipv4 = version == "ipv4" %} -{% set ospf_version = "v2" if ipv4 else "v3" %} -protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(ipv4) }} { +{% set is_ipv4 = version == "ipv4" %} +{% set ospf_version = "v2" if is_ipv4 else "v3" %} +protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(is_ipv4) }} { {{ version }} { {% if bird__ospf.table is defined %} - table {{ bird__ospf.table | bird_name(ipv4) }}; + table {{ bird__ospf.table | bird_name(is_ipv4) }}; {% endif %} - {{ import_export(bird__ospf, ipv4) | indent(8) }} + {{ import_export(bird__ospf, is_ipv4) | indent(8) }} }; {% for id, area in bird__ospf.areas.items() %} area {{ id }} { @@ -92,8 +92,8 @@ protocol ospf {{ ospf_version }} {{ "ospf" | bird_name(ipv4) }} { {% for name, bgp in bird__bgp.items() %} {% for version in ["ipv4", "ipv6"] %} -{% set ipv4 = version == "ipv4" %} -protocol bgp {{ name | bird_name(ipv4) }} { +{% set is_ipv4 = version == "ipv4" %} +protocol bgp {{ name | bird_name(is_ipv4) }} { local {{ bgp.local.address | ansible.utils.ipaddr(version) | first }} as {{ bgp.local.as }}; @@ -106,12 +106,12 @@ protocol bgp {{ name | bird_name(ipv4) }} { {% endif %} {{ version }} { {% if bgp.table is defined %} - table {{ bgp.table | bird_name(ipv4) }}; + table {{ bgp.table | bird_name(is_ipv4) }}; {% endif %} {% if bgp.next_hop_self is defined %} next hop self; {% endif %} - {{ import_export(bgp, ipv4) | indent(8) }} + {{ import_export(bgp, is_ipv4) | indent(8) }} }; } {% endfor %} From ba033f9099352fb27ba9b6288b615ac12058b8c5 Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine Date: Mon, 1 Apr 2024 00:06:05 +0200 Subject: [PATCH 4/6] feat(bird): Add net.match --- roles/bird/filter_plugins/bird.py | 41 +++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/roles/bird/filter_plugins/bird.py b/roles/bird/filter_plugins/bird.py index b2764d9..2cf01de 100644 --- a/roles/bird/filter_plugins/bird.py +++ b/roles/bird/filter_plugins/bird.py @@ -3,7 +3,7 @@ from __future__ import annotations import itertools import re from dataclasses import dataclass -from ipaddress import IPv4Address +from ipaddress import IPv4Address, IPv4Network, IPv6Network, ip_network from typing import Any, Generic, Iterator, Literal, TypeVar from pydantic import ( @@ -89,12 +89,37 @@ class IPv4orIPv6(BaseModel): ipv6: list[int] = Field(ge=0, min_items=2, max_items=2) +class IPFlag(str): + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, v): + pattern = r"(?P.*?)(?P[+-]|\{[0-9]+,[0-9]+\})?$" + parts = re.match(pattern, v) + + return (ip_network(parts.group("ip")), parts.group("flag")) + + +class NetMatch(BaseModel): + matches: list[IPFlag] = Field(alias="net.match") + + class NetLength(BaseModel): length: IPv4orIPv6 = Field(alias="net.len") Condition = ( - Proto | Source | And | Or | Not | AsPathContains | AsPathLength | NetLength + Proto + | Source + | And + | Or + | Not + | AsPathContains + | AsPathLength + | NetLength + | NetMatch ) And.update_forward_refs() @@ -210,6 +235,18 @@ def str_of_condition(condition: Condition, ctx: bool) -> str: else: return f"{min_v6} <= net.len && net.len <= {max_v6}" + case NetMatch(matches=matches): + if ctx.ipv4: + networks = [ + m for m in matches if isinstance(m[0], IPv4Network) + ] + else: + networks = [ + m for m in matches if isinstance(m[0], IPv6Network) + ] + + return f"net ~ [ {', '.join([f'{network}{str(flag)}' for (network, flag) in networks])} ]" + def lines_of_action(action: Action, ctx: Context) -> Iterable[str]: match action: From 54d227232b504ff0e4ff9a6c43e09db3429be220 Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine Date: Mon, 1 Apr 2024 00:13:22 +0200 Subject: [PATCH 5/6] chore(bird): Fix None flag --- roles/bird/filter_plugins/bird.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/bird/filter_plugins/bird.py b/roles/bird/filter_plugins/bird.py index 2cf01de..5bfae8f 100644 --- a/roles/bird/filter_plugins/bird.py +++ b/roles/bird/filter_plugins/bird.py @@ -99,7 +99,7 @@ class IPFlag(str): pattern = r"(?P.*?)(?P[+-]|\{[0-9]+,[0-9]+\})?$" parts = re.match(pattern, v) - return (ip_network(parts.group("ip")), parts.group("flag")) + return (ip_network(parts.group("ip")), parts.group("flag") or "") class NetMatch(BaseModel): @@ -245,7 +245,7 @@ def str_of_condition(condition: Condition, ctx: bool) -> str: m for m in matches if isinstance(m[0], IPv6Network) ] - return f"net ~ [ {', '.join([f'{network}{str(flag)}' for (network, flag) in networks])} ]" + return f"net ~ [ {', '.join([f'{network}{flag}' for (network, flag) in networks])} ]" def lines_of_action(action: Action, ctx: Context) -> Iterable[str]: From 9aaa6191732e7ac7f3b2a9aecc43df4090ab8e74 Mon Sep 17 00:00:00 2001 From: Vincent Lafeychine Date: Mon, 1 Apr 2024 00:20:41 +0200 Subject: [PATCH 6/6] chore(bird): Improve code readability --- roles/bird/filter_plugins/bird.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/roles/bird/filter_plugins/bird.py b/roles/bird/filter_plugins/bird.py index 5bfae8f..00f38bb 100644 --- a/roles/bird/filter_plugins/bird.py +++ b/roles/bird/filter_plugins/bird.py @@ -89,7 +89,7 @@ class IPv4orIPv6(BaseModel): ipv6: list[int] = Field(ge=0, min_items=2, max_items=2) -class IPFlag(str): +class IPFlag: @classmethod def __get_validators__(cls): yield cls.validate @@ -236,14 +236,8 @@ def str_of_condition(condition: Condition, ctx: bool) -> str: return f"{min_v6} <= net.len && net.len <= {max_v6}" case NetMatch(matches=matches): - if ctx.ipv4: - networks = [ - m for m in matches if isinstance(m[0], IPv4Network) - ] - else: - networks = [ - m for m in matches if isinstance(m[0], IPv6Network) - ] + networkType = IPv4Network if ctx.ipv4 else IPv6Network + networks = [m for m in matches if isinstance(m[0], networkType)] return f"net ~ [ {', '.join([f'{network}{flag}' for (network, flag) in networks])} ]"