annet 0.16.25__py3-none-any.whl → 0.16.27__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- annet/adapters/file/provider.py +28 -10
- annet/adapters/netbox/v37/storage.py +1 -1
- annet/annlib/netdev/devdb/data/devdb.json +3 -2
- annet/annlib/patching.py +50 -14
- annet/bgp_models.py +28 -0
- annet/mesh/__init__.py +4 -0
- annet/mesh/basemodel.py +5 -0
- annet/mesh/device_models.py +2 -0
- annet/mesh/executor.py +90 -66
- annet/mesh/peer_models.py +3 -3
- annet/mesh/port_processor.py +18 -0
- annet/mesh/registry.py +12 -4
- annet/rpl/match_builder.py +30 -9
- annet/rpl/routemap.py +5 -3
- annet/rpl/statement_builder.py +31 -7
- annet/rpl_generators/__init__.py +24 -0
- annet/rpl_generators/aspath.py +57 -0
- annet/rpl_generators/community.py +242 -0
- annet/rpl_generators/cumulus_frr.py +458 -0
- annet/rpl_generators/entities.py +70 -0
- annet/rpl_generators/execute.py +12 -0
- annet/rpl_generators/policy.py +676 -0
- annet/rpl_generators/prefix_lists.py +158 -0
- annet/rpl_generators/rd.py +40 -0
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/METADATA +2 -2
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/RECORD +36 -25
- annet_generators/rpl_example/__init__.py +3 -5
- annet_generators/rpl_example/generator.py +127 -0
- annet_generators/rpl_example/items.py +21 -31
- annet_generators/rpl_example/mesh.py +9 -0
- annet_generators/rpl_example/route_policy.py +43 -9
- annet_generators/rpl_example/policy_generator.py +0 -233
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/AUTHORS +0 -0
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/LICENSE +0 -0
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/WHEEL +0 -0
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/entry_points.txt +0 -0
- {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from collections.abc import Sequence, Iterable
|
|
3
|
+
from ipaddress import ip_interface
|
|
4
|
+
from typing import Any, Literal
|
|
5
|
+
|
|
6
|
+
from annet.generators import PartialGenerator
|
|
7
|
+
from annet.rpl import RouteMap, PrefixMatchValue, MatchField, SingleCondition, RoutingPolicy
|
|
8
|
+
from .entities import IpPrefixList, mangle_ranged_prefix_list_name
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def get_used_prefix_lists(prefix_lists: Sequence[IpPrefixList], policies: list[RoutingPolicy]) -> list[IpPrefixList]:
|
|
12
|
+
plist_map = {c.name: c for c in prefix_lists}
|
|
13
|
+
used_names = set()
|
|
14
|
+
for policy in policies:
|
|
15
|
+
for statement in policy.statements:
|
|
16
|
+
for condition in statement.match.find_all(MatchField.ipv6_prefix):
|
|
17
|
+
used_names.update(condition.value.names)
|
|
18
|
+
for condition in statement.match.find_all(MatchField.ip_prefix):
|
|
19
|
+
used_names.update(condition.value.names)
|
|
20
|
+
return [plist_map[name] for name in sorted(used_names)]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
24
|
+
TAGS = ["policy", "rpl", "routing"]
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
28
|
+
raise NotImplementedError()
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def get_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
32
|
+
raise NotImplementedError()
|
|
33
|
+
|
|
34
|
+
def get_used_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
35
|
+
return get_used_prefix_lists(
|
|
36
|
+
prefix_lists=self.get_prefix_lists(device),
|
|
37
|
+
policies=self.get_policies(device),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# huawei
|
|
41
|
+
def acl_huawei(self, _):
|
|
42
|
+
return r"""
|
|
43
|
+
ip ip-prefix
|
|
44
|
+
ip ipv6-prefix
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def _huawei_prefix_list(
|
|
48
|
+
self,
|
|
49
|
+
name: str,
|
|
50
|
+
prefix_type: Literal["ipv6-prefix", "ip-prefix"],
|
|
51
|
+
match: PrefixMatchValue,
|
|
52
|
+
plist: IpPrefixList,
|
|
53
|
+
) -> Iterable[Sequence[str]]:
|
|
54
|
+
for i, prefix in enumerate(plist.members):
|
|
55
|
+
addr_mask = ip_interface(prefix)
|
|
56
|
+
yield (
|
|
57
|
+
"ip",
|
|
58
|
+
prefix_type,
|
|
59
|
+
name,
|
|
60
|
+
f"index {i * 10 + 5}",
|
|
61
|
+
"permit",
|
|
62
|
+
str(addr_mask.ip).upper(),
|
|
63
|
+
str(addr_mask.network.prefixlen),
|
|
64
|
+
) + (
|
|
65
|
+
("greater-equal", str(match.greater_equal)) if match.greater_equal is not None else ()
|
|
66
|
+
) + (
|
|
67
|
+
("less-equal", str(match.less_equal)) if match.less_equal is not None else ()
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
def run_huawei(self, device: Any):
|
|
71
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device)}
|
|
72
|
+
policies = self.get_policies(device)
|
|
73
|
+
precessed_names = set()
|
|
74
|
+
for policy in policies:
|
|
75
|
+
for statement in policy.statements:
|
|
76
|
+
cond: SingleCondition[PrefixMatchValue]
|
|
77
|
+
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
78
|
+
for name in cond.value.names:
|
|
79
|
+
mangled_name = mangle_ranged_prefix_list_name(
|
|
80
|
+
name=name,
|
|
81
|
+
greater_equal=cond.value.greater_equal,
|
|
82
|
+
less_equal=cond.value.less_equal,
|
|
83
|
+
)
|
|
84
|
+
if mangled_name in precessed_names:
|
|
85
|
+
continue
|
|
86
|
+
yield from self._huawei_prefix_list(mangled_name, "ip-prefix", cond.value, plists[name])
|
|
87
|
+
precessed_names.add(mangled_name)
|
|
88
|
+
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
89
|
+
for name in cond.value.names:
|
|
90
|
+
mangled_name = mangle_ranged_prefix_list_name(
|
|
91
|
+
name=name,
|
|
92
|
+
greater_equal=cond.value.greater_equal,
|
|
93
|
+
less_equal=cond.value.less_equal,
|
|
94
|
+
)
|
|
95
|
+
if mangled_name in precessed_names:
|
|
96
|
+
continue
|
|
97
|
+
yield from self._huawei_prefix_list(mangled_name, "ipv6-prefix", cond.value, plists[name])
|
|
98
|
+
precessed_names.add(mangled_name)
|
|
99
|
+
|
|
100
|
+
# arista
|
|
101
|
+
def acl_arista(self, _):
|
|
102
|
+
return r"""
|
|
103
|
+
ip prefix-list
|
|
104
|
+
ipv6 prefix-list
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def _arista_prefix_list(
|
|
108
|
+
self,
|
|
109
|
+
name: str,
|
|
110
|
+
prefix_type: Literal["ipv6", "ip"],
|
|
111
|
+
match: PrefixMatchValue,
|
|
112
|
+
plist: IpPrefixList,
|
|
113
|
+
) -> Iterable[Sequence[str]]:
|
|
114
|
+
for i, prefix in enumerate(plist.members):
|
|
115
|
+
addr_mask = ip_interface(prefix)
|
|
116
|
+
yield (
|
|
117
|
+
prefix_type,
|
|
118
|
+
"prefix-list",
|
|
119
|
+
name,
|
|
120
|
+
f"seq {i * 10 + 5}",
|
|
121
|
+
"permit",
|
|
122
|
+
str(addr_mask.ip).upper(),
|
|
123
|
+
str(addr_mask.network.prefixlen),
|
|
124
|
+
) + (
|
|
125
|
+
("ge", str(match.greater_equal)) if match.greater_equal is not None else ()
|
|
126
|
+
) + (
|
|
127
|
+
("le", str(match.less_equal)) if match.less_equal is not None else ()
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def run_arista(self, device: Any):
|
|
131
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device)}
|
|
132
|
+
policies = self.get_policies(device)
|
|
133
|
+
precessed_names = set()
|
|
134
|
+
for policy in policies:
|
|
135
|
+
for statement in policy.statements:
|
|
136
|
+
cond: SingleCondition[PrefixMatchValue]
|
|
137
|
+
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
138
|
+
for name in cond.value.names:
|
|
139
|
+
mangled_name = mangle_ranged_prefix_list_name(
|
|
140
|
+
name=name,
|
|
141
|
+
greater_equal=cond.value.greater_equal,
|
|
142
|
+
less_equal=cond.value.less_equal,
|
|
143
|
+
)
|
|
144
|
+
if mangled_name in precessed_names:
|
|
145
|
+
continue
|
|
146
|
+
yield from self._arista_prefix_list(mangled_name, "ip", cond.value, plists[name])
|
|
147
|
+
precessed_names.add(mangled_name)
|
|
148
|
+
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
149
|
+
for name in cond.value.names:
|
|
150
|
+
mangled_name = mangle_ranged_prefix_list_name(
|
|
151
|
+
name=name,
|
|
152
|
+
greater_equal=cond.value.greater_equal,
|
|
153
|
+
less_equal=cond.value.less_equal,
|
|
154
|
+
)
|
|
155
|
+
if mangled_name in precessed_names:
|
|
156
|
+
continue
|
|
157
|
+
yield from self._arista_prefix_list(mangled_name, "ipv6", cond.value, plists[name])
|
|
158
|
+
precessed_names.add(mangled_name)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from abc import abstractmethod, ABC
|
|
2
|
+
from collections.abc import Sequence
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from annet.generators import PartialGenerator
|
|
6
|
+
from annet.rpl import RouteMap, MatchField, RoutingPolicy
|
|
7
|
+
from .entities import RDFilter
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RDFilterFilterGenerator(PartialGenerator, ABC):
|
|
11
|
+
TAGS = ["policy", "rpl", "routing"]
|
|
12
|
+
|
|
13
|
+
@abstractmethod
|
|
14
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
15
|
+
raise NotImplementedError()
|
|
16
|
+
|
|
17
|
+
@abstractmethod
|
|
18
|
+
def get_rd_filters(self, device: Any) -> Sequence[RDFilter]:
|
|
19
|
+
raise NotImplementedError()
|
|
20
|
+
|
|
21
|
+
def get_used_rd_filters(self, device: Any) -> Sequence[RDFilter]:
|
|
22
|
+
filters = {c.name: c for c in self.get_rd_filters(device)}
|
|
23
|
+
policies = self.get_policies(device)
|
|
24
|
+
used_filters = set()
|
|
25
|
+
for policy in policies:
|
|
26
|
+
for statement in policy.statements:
|
|
27
|
+
for condition in statement.match.find_all(MatchField.rd):
|
|
28
|
+
used_filters.update(condition.value)
|
|
29
|
+
return [filters[name] for name in sorted(used_filters)]
|
|
30
|
+
|
|
31
|
+
def acl_huawei(self, _):
|
|
32
|
+
return r"""
|
|
33
|
+
ip rd-filter
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def run_huawei(self, device: Any):
|
|
37
|
+
for rd_filter in self.get_used_rd_filters(device):
|
|
38
|
+
for i, route_distinguisher in enumerate(rd_filter.members):
|
|
39
|
+
rd_id = (i + 1) * 10 + 5
|
|
40
|
+
yield "ip rd-filter", rd_filter.number, f"index {rd_id}", "permit", route_distinguisher
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: annet
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.27
|
|
4
4
|
Summary: annet
|
|
5
5
|
Home-page: https://github.com/annetutil/annet
|
|
6
6
|
License: MIT
|
|
@@ -23,4 +23,4 @@ Requires-Dist: yarl>=1.8.2
|
|
|
23
23
|
Requires-Dist: adaptix==3.0.0b7
|
|
24
24
|
Requires-Dist: dataclass-rest==0.4
|
|
25
25
|
Provides-Extra: netbox
|
|
26
|
-
Requires-Dist: annetbox[sync]>=0.1.
|
|
26
|
+
Requires-Dist: annetbox[sync]>=0.1.10; extra == "netbox"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
annet/__init__.py,sha256=W8kkZ3Axu-6VJwgQ0cn4UeOVNy6jab0cqgHKLQny1D0,2141
|
|
2
2
|
annet/annet.py,sha256=TMdEuM7GJQ4TjRVmuK3bCTZN-21lxjQ9sXqEdILUuBk,725
|
|
3
3
|
annet/argparse.py,sha256=v1MfhjR0B8qahza0WinmXClpR8UiDFhmwDDWtNroJPA,12855
|
|
4
|
-
annet/bgp_models.py,sha256=
|
|
4
|
+
annet/bgp_models.py,sha256=pju2KImDhBxU3lkyvP5TfeYuWztYj4i8OfcZqCX3xVY,8823
|
|
5
5
|
annet/cli.py,sha256=hDpjIr3w47lgQ_CvCQS1SXFDK-SJrf5slbT__5u6GIA,12342
|
|
6
6
|
annet/cli_args.py,sha256=KQlihxSl-Phhq1-9oJDdNSbIllEX55LlPfH6viEKOuw,13483
|
|
7
7
|
annet/connectors.py,sha256=-Lghz3PtWCBU8Ohrp0KKQcmm1AUZtN0EnOaZ6IQgCQI,5105
|
|
@@ -27,7 +27,7 @@ annet/adapters/fetchers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
|
27
27
|
annet/adapters/fetchers/stub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
annet/adapters/fetchers/stub/fetcher.py,sha256=bJGNJcvjrxSa4d8gly6NkaRcxoK57zbZhzkW5vq278I,656
|
|
29
29
|
annet/adapters/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
30
|
-
annet/adapters/file/provider.py,sha256=
|
|
30
|
+
annet/adapters/file/provider.py,sha256=hfeHHJ4aeTT8p_K5Wj_euwjf0Y1Ir9Qijup6J9wk5RM,6555
|
|
31
31
|
annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
annet/adapters/netbox/provider.py,sha256=3IrfZ6CfCxf-lTnJlIC2TQ8M_rDxOB_B7HGXZ92vAgA,1643
|
|
33
33
|
annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -40,7 +40,7 @@ annet/adapters/netbox/common/storage_opts.py,sha256=5tt6wxUUJTIzNbOVXMnYBwZedNAI
|
|
|
40
40
|
annet/adapters/netbox/v24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
annet/adapters/netbox/v24/storage.py,sha256=THI592VLx3ehixsPZQ1Ko3mYIAZQbnDY-toIziqBbL8,6005
|
|
42
42
|
annet/adapters/netbox/v37/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
43
|
-
annet/adapters/netbox/v37/storage.py,sha256=
|
|
43
|
+
annet/adapters/netbox/v37/storage.py,sha256=G-nj-iuNN5inR0tY2XnjFnXXFYrgMZeTJ9DQ7OgjK44,10336
|
|
44
44
|
annet/annlib/__init__.py,sha256=fT1l4xV5fqqg8HPw9HqmZVN2qwS8i6X1aIm2zGDjxKY,252
|
|
45
45
|
annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
|
|
46
46
|
annet/annlib/diff.py,sha256=MZ6eQAU3cadQp8KaSE6uAYFtcfMDCIe_eNuVROnYkCk,4496
|
|
@@ -49,13 +49,13 @@ annet/annlib/filter_acl.py,sha256=0w1VF6WcONiTYTQh0yWi6_j9rCTc_kMLAUMr0hbdkNU,72
|
|
|
49
49
|
annet/annlib/jsontools.py,sha256=BS7s4rI8R9c_y3zz0zYl1l6con65oQ0MvfsC1dsXZts,5535
|
|
50
50
|
annet/annlib/lib.py,sha256=eJ4hcVuQ6pdYBzLs4YKCHFtq45idOfZCYp92XfF7_QI,15317
|
|
51
51
|
annet/annlib/output.py,sha256=_SjJ6G6bejvnTKqNHw6xeio0FT9oO3OIkLaOC3cEga4,7569
|
|
52
|
-
annet/annlib/patching.py,sha256=
|
|
52
|
+
annet/annlib/patching.py,sha256=p5u3jl3_Iod0CGcnfZsfFR3Izo_roorny_v0stjDCWs,21142
|
|
53
53
|
annet/annlib/tabparser.py,sha256=Xsje7t2bEZqZ8hhgnEYgjQGaDZ5mBWgNxwE2wpCkwXQ,28961
|
|
54
54
|
annet/annlib/types.py,sha256=VHU0CBADfYmO0xzB_c5f-mcuU3dUumuJczQnqGlib9M,852
|
|
55
55
|
annet/annlib/netdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
56
56
|
annet/annlib/netdev/db.py,sha256=fI_u5aya4l61mbYSjj4JwlVfi3s7obt2jqERSuXGRUI,1634
|
|
57
57
|
annet/annlib/netdev/devdb/__init__.py,sha256=aKYjjLbJebdKBjnGDpVLQdSqrV2JL24spGm58tmMWVU,892
|
|
58
|
-
annet/annlib/netdev/devdb/data/devdb.json,sha256=
|
|
58
|
+
annet/annlib/netdev/devdb/data/devdb.json,sha256=OM3FLBUGPJ_d97Q3hnavj51CoWlilJsoJLkHSzE-nPg,6127
|
|
59
59
|
annet/annlib/netdev/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
60
60
|
annet/annlib/netdev/views/dump.py,sha256=rIlyvnA3uM8bB_7oq1nS2KDxTp6dQh2hz-FbNhYIpOU,4630
|
|
61
61
|
annet/annlib/netdev/views/hardware.py,sha256=3JCZLH7deIHhCguwPJTUX-WDvWjG_xt6BdSEZSO6zkQ,4226
|
|
@@ -81,22 +81,32 @@ annet/generators/ref.py,sha256=QVdeL8po1D0kBsVLOpCjFR81D8yNTk-kaQj5WUM4hng,438
|
|
|
81
81
|
annet/generators/result.py,sha256=zMAvGOYQU803bGy6datZduHLgrEqK2Zba_Jcf9Qn9p0,4976
|
|
82
82
|
annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
83
|
annet/generators/common/initial.py,sha256=qYBxXFhyOPy34cxc6hsIXseod-lYCmmbuNHpM0uteY0,1244
|
|
84
|
-
annet/mesh/__init__.py,sha256=
|
|
85
|
-
annet/mesh/basemodel.py,sha256=
|
|
86
|
-
annet/mesh/device_models.py,sha256=
|
|
87
|
-
annet/mesh/executor.py,sha256=
|
|
84
|
+
annet/mesh/__init__.py,sha256=lcgdnBIxc2MAN7Er1bcErEKPqrjWO4uIp_1FldMXTYg,557
|
|
85
|
+
annet/mesh/basemodel.py,sha256=TXVINzmvKlyxiVcIdOaoewY_c4TvuMstluqfzlbNmPU,4859
|
|
86
|
+
annet/mesh/device_models.py,sha256=YcL6_vGjnt67BTovN8Eq38U5wGcbJDhqiq8613WpYtQ,3381
|
|
87
|
+
annet/mesh/executor.py,sha256=vJaw6cGwOkCQDABK1mkYk67BPAeWArpTHA9jNKtJDc0,14684
|
|
88
88
|
annet/mesh/match_args.py,sha256=CR3kdIV9NGtyk9E2JbcOQ3TRuYEryTWP3m2yCo2VCWg,5751
|
|
89
89
|
annet/mesh/models_converter.py,sha256=3q2zs7K8S3pfYSUKKRdtl5CJGbeg4TtYxofAVs_MBsk,3085
|
|
90
|
-
annet/mesh/peer_models.py,sha256=
|
|
91
|
-
annet/mesh/
|
|
90
|
+
annet/mesh/peer_models.py,sha256=KgVHXzoLfYweBVDCGDeEA8CuPvaTDe-4kreXyZOLQzM,2670
|
|
91
|
+
annet/mesh/port_processor.py,sha256=RHiMS5W8qoDkTKiarQ748bcr8bNx4g_R4Y4vZg2k4TU,478
|
|
92
|
+
annet/mesh/registry.py,sha256=VvZKZ5ujGcRXvWFP051rr2KoXI8OrTivkhpTOrUm33A,9087
|
|
92
93
|
annet/rpl/__init__.py,sha256=0kcIktE3AmS0rlm9xzVDf53xk08OeZXgD-6ZLCt_KCs,731
|
|
93
94
|
annet/rpl/action.py,sha256=PY6W66j908RuqQ1_ioxayqVN-70rxDk5Z59EGHtxI98,1246
|
|
94
95
|
annet/rpl/condition.py,sha256=MJri4MbWtPkLHIsLMAtsIEF7e8IAS9dIImjmJs5vS5U,3418
|
|
95
|
-
annet/rpl/match_builder.py,sha256=
|
|
96
|
+
annet/rpl/match_builder.py,sha256=vK0faYyg3vJGOp6LSKscLCno3Fc2PfznOmVCEUEhZfk,4774
|
|
96
97
|
annet/rpl/policy.py,sha256=P1Kt-8fHFxEczeP-RwkK_wrGN0p7IR-hOApEd2vC55E,448
|
|
97
98
|
annet/rpl/result.py,sha256=PHFn1zhDeqLBn07nkYw5vsoXew4nTwkklOwqvFWzBLg,141
|
|
98
|
-
annet/rpl/routemap.py,sha256=
|
|
99
|
-
annet/rpl/statement_builder.py,sha256=
|
|
99
|
+
annet/rpl/routemap.py,sha256=SIyk73OzPp2oH_XwrDv2xczuY2Zt1VsJmB0TT5r7F5g,2593
|
|
100
|
+
annet/rpl/statement_builder.py,sha256=sVGOYsCV0s_SFQUy2WtUyQqKy5H4MOfmRCJWGj-UOJ4,9403
|
|
101
|
+
annet/rpl_generators/__init__.py,sha256=ZLWs-flcpyIbdhxSDfNt-ORDrLe8ins25sWdXTWeUoA,748
|
|
102
|
+
annet/rpl_generators/aspath.py,sha256=kZakwPLfGGiXu9fC6I1z-pvy7Fe-4dy93_-lYcx39_4,2038
|
|
103
|
+
annet/rpl_generators/community.py,sha256=8HxeUS2i015lXP7NxVW5EIbFjpg4mdljfQrOIUENtmk,11500
|
|
104
|
+
annet/rpl_generators/cumulus_frr.py,sha256=yMxm264PZq8MnSYQ2jmLv2CSa2Jl2ais_7XBTXKowj0,20053
|
|
105
|
+
annet/rpl_generators/entities.py,sha256=052ggNCGbEE-m--MdzLJhJ-1ch6IxDsL6xnMH0OHgtg,1491
|
|
106
|
+
annet/rpl_generators/execute.py,sha256=wS6e6fwcPWywsHB0gBMqZ17eF0s4YOBgDgwPB_cr5Rw,431
|
|
107
|
+
annet/rpl_generators/policy.py,sha256=YbhlTHNdv-GrpWv05iXMtbX-AdylWSZgJuTuzQREz7E,31896
|
|
108
|
+
annet/rpl_generators/prefix_lists.py,sha256=bLacWP4M303mefZXK04VL7R_uIukZJ4itB5hKpFzCwY,6692
|
|
109
|
+
annet/rpl_generators/rd.py,sha256=YGXgx1D2D0-pixgspXJzA6NvW8lx3AmHMxIY2l5rraI,1457
|
|
100
110
|
annet/rulebook/__init__.py,sha256=oafL5HC8QHdkO9CH2q_fxohPMxOgjn-dNQa5kPjuqsA,3942
|
|
101
111
|
annet/rulebook/common.py,sha256=zK1s2c5lc5HQbIlMUQ4HARQudXSgOYiZ_Sxc2I_tHqg,721
|
|
102
112
|
annet/rulebook/deploying.py,sha256=XV0XQvc3YvwM8SOgOQlc-fCW4bnjQg_1CTZkTwMp14A,2972
|
|
@@ -160,14 +170,15 @@ annet_generators/example/lldp.py,sha256=24bGvShxbio-JxUdaehyPRu31LhH9YwSwFDrWVRn
|
|
|
160
170
|
annet_generators/mesh_example/__init__.py,sha256=NfNWgXn1TNiWI6A5tmU6Y-4QV2i33td0Qs3re0MNNMo,218
|
|
161
171
|
annet_generators/mesh_example/bgp.py,sha256=jzyDndSSGYyYBquDnLlR-7P5lzmUKcSyYCml3VsoMC0,1385
|
|
162
172
|
annet_generators/mesh_example/mesh_logic.py,sha256=DJS5JMCTs0rs0LN__0LulNgo2ekUcWiOMe02BlOeFas,1454
|
|
163
|
-
annet_generators/rpl_example/__init__.py,sha256=
|
|
164
|
-
annet_generators/rpl_example/
|
|
165
|
-
annet_generators/rpl_example/
|
|
166
|
-
annet_generators/rpl_example/
|
|
167
|
-
|
|
168
|
-
annet-0.16.
|
|
169
|
-
annet-0.16.
|
|
170
|
-
annet-0.16.
|
|
171
|
-
annet-0.16.
|
|
172
|
-
annet-0.16.
|
|
173
|
-
annet-0.16.
|
|
173
|
+
annet_generators/rpl_example/__init__.py,sha256=A7DTn-SVSlUpeO7mKT_obqimp29p9zWfVRPBSxmENQY,209
|
|
174
|
+
annet_generators/rpl_example/generator.py,sha256=zndIGfV4ZlTxPgAGYs7bMQvTc_tYScODqJz3fuyermY,3871
|
|
175
|
+
annet_generators/rpl_example/items.py,sha256=Ez1RF5YhcXNCusBmeApIjRL3rBlMazNZd29Gpw1_IsA,766
|
|
176
|
+
annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
|
|
177
|
+
annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
|
|
178
|
+
annet-0.16.27.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
|
|
179
|
+
annet-0.16.27.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
|
|
180
|
+
annet-0.16.27.dist-info/METADATA,sha256=BE6PMNs83GUP7TVnBYRrdbz6drAvR9AK1c4mimsEysI,729
|
|
181
|
+
annet-0.16.27.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
182
|
+
annet-0.16.27.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
|
|
183
|
+
annet-0.16.27.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
|
|
184
|
+
annet-0.16.27.dist-info/RECORD,,
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from annet.generators import BaseGenerator
|
|
4
2
|
from annet.storage import Storage
|
|
5
|
-
from . import
|
|
3
|
+
from . import generator
|
|
6
4
|
|
|
7
5
|
|
|
8
|
-
def get_generators(
|
|
9
|
-
return
|
|
6
|
+
def get_generators(storage: Storage) -> list[BaseGenerator]:
|
|
7
|
+
return generator.get_generators(storage)
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from annet.generators import BaseGenerator, Entire
|
|
4
|
+
from annet.mesh import MeshExecutor
|
|
5
|
+
from annet.rpl import RouteMap, RoutingPolicy
|
|
6
|
+
from annet.rpl_generators import (
|
|
7
|
+
CommunityListGenerator, RoutingPolicyGenerator, AsPathFilterGenerator, CommunityList, AsPathFilter,
|
|
8
|
+
RDFilterFilterGenerator, RDFilter, PrefixListFilterGenerator, IpPrefixList, CumulusPolicyGenerator,
|
|
9
|
+
get_policies,
|
|
10
|
+
)
|
|
11
|
+
from annet.storage import Storage
|
|
12
|
+
from .items import COMMUNITIES, AS_PATH_FILTERS, RD_FILTERS, PREFIX_LISTS
|
|
13
|
+
from .mesh import registry
|
|
14
|
+
from .route_policy import routemap
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CommunityGenerator(CommunityListGenerator):
|
|
18
|
+
def get_community_lists(self, device: Any) -> list[CommunityList]:
|
|
19
|
+
return COMMUNITIES
|
|
20
|
+
|
|
21
|
+
def get_routemap(self) -> RouteMap:
|
|
22
|
+
return routemap
|
|
23
|
+
|
|
24
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
25
|
+
return get_policies(
|
|
26
|
+
routemap=routemap,
|
|
27
|
+
device=device,
|
|
28
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class PolicyGenerator(RoutingPolicyGenerator):
|
|
33
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
34
|
+
return get_policies(
|
|
35
|
+
routemap=routemap,
|
|
36
|
+
device=device,
|
|
37
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
def get_community_lists(self, device: Any) -> list[CommunityList]:
|
|
41
|
+
return COMMUNITIES
|
|
42
|
+
|
|
43
|
+
def get_rd_filters(self, device: Any) -> list[RDFilter]:
|
|
44
|
+
return RD_FILTERS
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class AsPathGenerator(AsPathFilterGenerator):
|
|
48
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
49
|
+
return get_policies(
|
|
50
|
+
routemap=routemap,
|
|
51
|
+
device=device,
|
|
52
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def get_as_path_filters(self, device: Any) -> list[AsPathFilter]:
|
|
56
|
+
return AS_PATH_FILTERS
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class RDGenerator(RDFilterFilterGenerator):
|
|
60
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
61
|
+
return get_policies(
|
|
62
|
+
routemap=routemap,
|
|
63
|
+
device=device,
|
|
64
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def get_rd_filters(self, device: Any) -> list[RDFilter]:
|
|
68
|
+
return RD_FILTERS
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class PrefixListGenerator(PrefixListFilterGenerator):
|
|
72
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
73
|
+
return get_policies(
|
|
74
|
+
routemap=routemap,
|
|
75
|
+
device=device,
|
|
76
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def get_prefix_lists(self, device: Any) -> list[IpPrefixList]:
|
|
80
|
+
return PREFIX_LISTS
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
FRR_HEADER = """\
|
|
84
|
+
!!! This file is auto-generated
|
|
85
|
+
!
|
|
86
|
+
frr defaults datacenter
|
|
87
|
+
log syslog informational
|
|
88
|
+
log timestamp precision 6
|
|
89
|
+
service integrated-vtysh-config"""
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class FrrGenerator(Entire, CumulusPolicyGenerator):
|
|
93
|
+
def get_policies(self, device: Any) -> list[RoutingPolicy]:
|
|
94
|
+
return get_policies(
|
|
95
|
+
routemap=routemap,
|
|
96
|
+
device=device,
|
|
97
|
+
mesh_executor=MeshExecutor(registry, self.storage),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def get_community_lists(self, device: Any) -> list[CommunityList]:
|
|
101
|
+
return COMMUNITIES
|
|
102
|
+
|
|
103
|
+
def get_prefix_lists(self, device: Any) -> list[IpPrefixList]:
|
|
104
|
+
return PREFIX_LISTS
|
|
105
|
+
|
|
106
|
+
def get_as_path_filters(self, device: Any) -> list[AsPathFilter]:
|
|
107
|
+
return AS_PATH_FILTERS
|
|
108
|
+
|
|
109
|
+
def path(self, device):
|
|
110
|
+
if device.hw.PC.Mellanox or device.hw.PC.NVIDIA:
|
|
111
|
+
return "/etc/frr/frr.conf"
|
|
112
|
+
|
|
113
|
+
def run(self, device):
|
|
114
|
+
yield FRR_HEADER
|
|
115
|
+
yield from self.generate_cumulus_rpl(device)
|
|
116
|
+
yield "line vty"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def get_generators(store: Storage) -> list[BaseGenerator]:
|
|
120
|
+
return [
|
|
121
|
+
AsPathGenerator(store),
|
|
122
|
+
PolicyGenerator(store),
|
|
123
|
+
CommunityGenerator(store),
|
|
124
|
+
RDGenerator(store),
|
|
125
|
+
PrefixListGenerator(store),
|
|
126
|
+
FrrGenerator(store),
|
|
127
|
+
]
|
|
@@ -1,31 +1,21 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
COMMUNITIES = {
|
|
24
|
-
"COMMUNITY_EXAMPLE_ADD": Community(["1234:1000"]),
|
|
25
|
-
"COMMUNITY_EXAMPLE_REMOVE": Community(["12345:999"]),
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
EXT_COMMUNITIES = {
|
|
29
|
-
"COMMUNITY_EXAMPLE_ADD": ExtCommunity(["1234:1000"]),
|
|
30
|
-
"COMMUNITY_EXAMPLE_REMOVE": ExtCommunity(["12345:999"]),
|
|
31
|
-
}
|
|
1
|
+
from annet.rpl_generators import AsPathFilter, CommunityList, CommunityType, RDFilter, IpPrefixList
|
|
2
|
+
|
|
3
|
+
AS_PATH_FILTERS = [
|
|
4
|
+
AsPathFilter("ASP_EXAMPLE", [".*123456.*"]),
|
|
5
|
+
]
|
|
6
|
+
COMMUNITIES = [
|
|
7
|
+
CommunityList("COMMUNITY_EXAMPLE_ADD", ["1234:1000", "1234:1001"]),
|
|
8
|
+
CommunityList("COMMUNITY_EXAMPLE_REMOVE", ["1234:999"]),
|
|
9
|
+
CommunityList("EXTCOMMUNITY_EXAMPLE_ADD", ["12345:1000"], CommunityType.RT),
|
|
10
|
+
CommunityList("EXTCOMMUNITY_EXAMPLE_REMOVE", ["12345:999"], CommunityType.RT),
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
RD_FILTERS = [
|
|
14
|
+
RDFilter("RD_EXAMPLE1", 1, ["100:1", "200:1"]),
|
|
15
|
+
RDFilter("RD_EXAMPLE2", 2, ["10.2.2.2:1", "10.3.3.3:1"]),
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
PREFIX_LISTS = [
|
|
19
|
+
IpPrefixList("IPV6_LIST_EXAMPLE", ["2a13:5941::/32"]),
|
|
20
|
+
IpPrefixList("IPV4_LIST_EXAMPLE", ["0.0.0.0/8", "10.0.0.0/8"]),
|
|
21
|
+
]
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from annet.mesh import MeshRulesRegistry, GlobalOptions
|
|
2
|
+
|
|
3
|
+
registry = MeshRulesRegistry()
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@registry.device("{name:.*}")
|
|
7
|
+
def device_handler(global_opts: GlobalOptions):
|
|
8
|
+
global_opts.groups["GROUP1"].import_policy = "example1"
|
|
9
|
+
global_opts.groups["GROUP1"].export_policy = "example2"
|
|
@@ -1,25 +1,47 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
from annet.adapters.netbox.common.models import NetboxDevice
|
|
2
4
|
from annet.rpl import R, RouteMap, Route
|
|
3
5
|
|
|
4
6
|
routemap = RouteMap[NetboxDevice]()
|
|
5
7
|
|
|
6
8
|
|
|
9
|
+
def find_loopback(device: NetboxDevice) -> Optional[str]:
|
|
10
|
+
for iface in device.interfaces:
|
|
11
|
+
if iface.name.lower().startswith("lo"):
|
|
12
|
+
return iface.name
|
|
13
|
+
return None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
SOME_CONDITION = (R.rd.has_any("RD_EXAMPLE1")) & (R.protocol == "bgp")
|
|
17
|
+
|
|
18
|
+
|
|
7
19
|
@routemap
|
|
8
20
|
def example1(device: NetboxDevice, route: Route):
|
|
9
|
-
condition
|
|
10
|
-
with route(
|
|
21
|
+
# condition can be referenced as global constant
|
|
22
|
+
with route(SOME_CONDITION, number=1, name="n1") as rule:
|
|
11
23
|
rule.set_local_pref(100)
|
|
12
24
|
rule.set_metric(100)
|
|
13
25
|
rule.add_metric(200)
|
|
14
26
|
rule.community.set("COMMUNITY_EXAMPLE_ADD")
|
|
15
27
|
rule.as_path.set(12345, "123456")
|
|
16
28
|
rule.allow()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
|
|
30
|
+
loopback = find_loopback(device)
|
|
31
|
+
if loopback: # rules can be generated dynamically
|
|
32
|
+
with route(
|
|
33
|
+
R.community.has("COMMUNITY_EXAMPLE_REMOVE"),
|
|
34
|
+
R.interface == loopback, # conditions can reference calculated values
|
|
35
|
+
number=2, name="n2",
|
|
36
|
+
) as rule:
|
|
37
|
+
rule.set_local_pref(100)
|
|
38
|
+
rule.add_metric(200)
|
|
39
|
+
rule.community.add("COMMUNITY_EXAMPLE_ADD")
|
|
40
|
+
rule.community.remove("COMMUNITY_EXAMPLE_REMOVE")
|
|
41
|
+
rule.allow()
|
|
42
|
+
# default rule with no conditions
|
|
43
|
+
with route(number=100, name="last") as rule:
|
|
44
|
+
rule.deny()
|
|
23
45
|
|
|
24
46
|
|
|
25
47
|
@routemap
|
|
@@ -28,6 +50,18 @@ def example2(device: NetboxDevice, route: Route):
|
|
|
28
50
|
rule.deny()
|
|
29
51
|
with route(R.match_v6("IPV6_LIST_EXAMPLE"), number=4, name="n4") as rule:
|
|
30
52
|
rule.allow()
|
|
53
|
+
with route(R.match_v4("IPV4_LIST_EXAMPLE", or_longer=(29, 48)), number=5, name="n5") as rule:
|
|
54
|
+
rule.allow()
|
|
31
55
|
|
|
32
|
-
with route(R.as_path_length >= 1, R.as_path_length <= 20, number=
|
|
56
|
+
with route(R.as_path_length >= 1, R.as_path_length <= 20, number=6, name="n6") as rule:
|
|
33
57
|
rule.allow()
|
|
58
|
+
with route(R.extcommunity_rt.has("EXTCOMMUNITY_EXAMPLE_REMOVE"), number=7, name="n7") as rule:
|
|
59
|
+
rule.extcommunity_rt.remove("EXTCOMMUNITY_EXAMPLE_REMOVE")
|
|
60
|
+
rule.deny()
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# this policy is not used in mesh model, so will be filtered out
|
|
64
|
+
@routemap
|
|
65
|
+
def unused(device: NetboxDevice, route: Route):
|
|
66
|
+
with route(number=3, name="n3") as rule:
|
|
67
|
+
rule.deny()
|