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.

Files changed (37) hide show
  1. annet/adapters/file/provider.py +28 -10
  2. annet/adapters/netbox/v37/storage.py +1 -1
  3. annet/annlib/netdev/devdb/data/devdb.json +3 -2
  4. annet/annlib/patching.py +50 -14
  5. annet/bgp_models.py +28 -0
  6. annet/mesh/__init__.py +4 -0
  7. annet/mesh/basemodel.py +5 -0
  8. annet/mesh/device_models.py +2 -0
  9. annet/mesh/executor.py +90 -66
  10. annet/mesh/peer_models.py +3 -3
  11. annet/mesh/port_processor.py +18 -0
  12. annet/mesh/registry.py +12 -4
  13. annet/rpl/match_builder.py +30 -9
  14. annet/rpl/routemap.py +5 -3
  15. annet/rpl/statement_builder.py +31 -7
  16. annet/rpl_generators/__init__.py +24 -0
  17. annet/rpl_generators/aspath.py +57 -0
  18. annet/rpl_generators/community.py +242 -0
  19. annet/rpl_generators/cumulus_frr.py +458 -0
  20. annet/rpl_generators/entities.py +70 -0
  21. annet/rpl_generators/execute.py +12 -0
  22. annet/rpl_generators/policy.py +676 -0
  23. annet/rpl_generators/prefix_lists.py +158 -0
  24. annet/rpl_generators/rd.py +40 -0
  25. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/METADATA +2 -2
  26. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/RECORD +36 -25
  27. annet_generators/rpl_example/__init__.py +3 -5
  28. annet_generators/rpl_example/generator.py +127 -0
  29. annet_generators/rpl_example/items.py +21 -31
  30. annet_generators/rpl_example/mesh.py +9 -0
  31. annet_generators/rpl_example/route_policy.py +43 -9
  32. annet_generators/rpl_example/policy_generator.py +0 -233
  33. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/AUTHORS +0 -0
  34. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/LICENSE +0 -0
  35. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/WHEEL +0 -0
  36. {annet-0.16.25.dist-info → annet-0.16.27.dist-info}/entry_points.txt +0 -0
  37. {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.25
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.8; extra == "netbox"
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=1qDMUFO6GNMIw5HVcIhRcQuOZ4JoelNlTmlcNpbYiGI,8033
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=Wn3sG2TOI6ZWuRHhPSyg8S0Dnrz5LN3VyuqE6S7r5GM,5930
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=a_KnjkEQrK6o2pG9-c9xHYGtj05E2F01JZy7_GIUp7g,10330
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=2CpAT3T43IUFJR57qTYSwjQ0smg0uHWN43df4n1WArs,19937
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=sv8c-Uwx6ByM5ZMvMCwwDtPZU3CmIzkti76bTtN7bnk,6066
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=ZVFPnNOBgP42d7dUnCypGh3RKy0Ne_ObWVzROlE1nFA,423
85
- annet/mesh/basemodel.py,sha256=bBBx1wu3ftENgf_4PGCV-SkEcxH3tFekvoointT3894,4739
86
- annet/mesh/device_models.py,sha256=nh2SX6-j_xsM-3rzp5JlzZGjJD9HjUkiac4-hzXpy2k,3333
87
- annet/mesh/executor.py,sha256=iDarSYfq9H6MSf-M1HkQOFIaUb8j-nG0sQNbgzq4XIo,13726
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=xyLbWtwJ5Y4OLf-OVZeHwRn2qyWhipOvhD4pDHTbvTE,2665
91
- annet/mesh/registry.py,sha256=G-FszWc_VKeN3eVpb-MRGbAGzbcSuEVAzbDC2k747XA,8611
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=6uutlI9TU75ClpcKZx0t8pqYxr20vr0h9itBrtrm_tQ,4194
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=ZxYSRXWtk3CG5OQuVVVYePpPP9Zbwk-ajWvQ0wqWP_8,2422
99
- annet/rpl/statement_builder.py,sha256=dSYrLosInUb4ewHzX4PeEzbRlIaz6Df4N1EytIJGccQ,8442
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=z4-gsDv06BBpgTwRohc50VBQYFD26QVu8Zr8Ngi_iqY,244
164
- annet_generators/rpl_example/items.py,sha256=6x7b0wZ7Vjn6yCaJ-aGbpTHm7fyqO77b-LRqzzhEbh4,615
165
- annet_generators/rpl_example/policy_generator.py,sha256=KFCqn347CIPcnllOHfINYeKgNSr6Wl-bdM5Xj_YKhYM,11183
166
- annet_generators/rpl_example/route_policy.py,sha256=QjxFjkePHfTo2CpMeRVaDqZXNXLM-gGlE8EocHuOR4Y,1189
167
- annet-0.16.25.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
168
- annet-0.16.25.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
169
- annet-0.16.25.dist-info/METADATA,sha256=uhwaIv4HybNV5NDUTlXoDfRE1Ynbd96MaHfLh6rMojQ,728
170
- annet-0.16.25.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
171
- annet-0.16.25.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
172
- annet-0.16.25.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
173
- annet-0.16.25.dist-info/RECORD,,
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 policy_generator
3
+ from . import generator
6
4
 
7
5
 
8
- def get_generators(store: Storage) -> List[BaseGenerator]:
9
- return policy_generator.get_generators(store)
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 collections.abc import Sequence
2
- from dataclasses import dataclass
3
-
4
- AS_PATH_FILTERS = {
5
- "ASP_EXAMPLE": [".*123456.*"],
6
- }
7
-
8
- IPV6_PREFIX_LISTS = {
9
- "IPV6_LIST_EXAMPLE": ["2a13:5941::/32"],
10
- }
11
-
12
-
13
- @dataclass(frozen=True)
14
- class Community:
15
- values: Sequence[str]
16
-
17
-
18
- @dataclass(frozen=True)
19
- class ExtCommunity:
20
- values: Sequence[str]
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 = (R.interface == "l0.0") & (R.protocol == "bgp")
10
- with route(condition, number=1, name="n1") as rule:
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
- with route(R.protocol == "bgp", R.community.has("comm_name"), number=2, name="n2") as rule:
18
- rule.set_local_pref(100)
19
- rule.add_metric(200)
20
- rule.community.add("COMMUNITY_EXAMPLE_ADD")
21
- rule.community.remove("COMMUNITY_EXAMPLE_REMOVE")
22
- rule.allow()
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=4, name="n4") as rule:
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()