annet 0.16.30__py3-none-any.whl → 0.16.31__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/netbox/common/manufacturer.py +2 -0
- annet/adapters/netbox/common/models.py +1 -1
- annet/api/__init__.py +24 -15
- annet/mesh/basemodel.py +3 -0
- annet/mesh/executor.py +20 -1
- annet/mesh/registry.py +15 -0
- annet/rpl_generators/cumulus_frr.py +30 -16
- annet/rpl_generators/entities.py +25 -12
- annet/rpl_generators/policy.py +26 -15
- annet/rpl_generators/prefix_lists.py +26 -16
- annet/rulebook/texts/cisco.deploy +1 -1
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/METADATA +1 -1
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/RECORD +18 -18
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/AUTHORS +0 -0
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/LICENSE +0 -0
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/WHEEL +0 -0
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/entry_points.txt +0 -0
- {annet-0.16.30.dist-info → annet-0.16.31.dist-info}/top_level.txt +0 -0
|
@@ -214,7 +214,7 @@ class NetboxDevice(Entity):
|
|
|
214
214
|
return type(self) is type(other) and self.url == other.url
|
|
215
215
|
|
|
216
216
|
def is_pc(self) -> bool:
|
|
217
|
-
return self.device_type.manufacturer.name
|
|
217
|
+
return self.device_type.manufacturer.name in ("Mellanox", "Moxa") or self.breed == "pc"
|
|
218
218
|
|
|
219
219
|
def _make_interface(self, name: str, type: InterfaceType) -> Interface:
|
|
220
220
|
return Interface(
|
annet/api/__init__.py
CHANGED
|
@@ -274,12 +274,14 @@ def patch(args: cli_args.ShowPatchOptions, loader: ann_gen.Loader):
|
|
|
274
274
|
|
|
275
275
|
def _patch_worker(device_id, args: cli_args.ShowPatchOptions, stdin, loader: ann_gen.Loader, filterer: filtering.Filterer):
|
|
276
276
|
for res, _, patch_tree in res_diff_patch(device_id, args, stdin, loader, filterer):
|
|
277
|
+
old_files = res.old_files
|
|
277
278
|
new_files = res.get_new_files(args.acl_safe)
|
|
278
279
|
new_json_fragment_files = res.get_new_file_fragments(args.acl_safe)
|
|
279
280
|
if new_files:
|
|
280
281
|
for path, (cfg_text, _cmds) in new_files.items():
|
|
281
282
|
label = res.device.hostname + os.sep + path
|
|
282
|
-
|
|
283
|
+
if old_files.get(path) != cfg_text:
|
|
284
|
+
yield label, cfg_text, False
|
|
283
285
|
elif res.old_json_fragment_files or new_json_fragment_files:
|
|
284
286
|
for path, (new_json_cfg, _cmds) in new_json_fragment_files.items():
|
|
285
287
|
label = res.device.hostname + os.sep + path
|
|
@@ -468,6 +470,9 @@ class PCDeployerJob(DeployerJob):
|
|
|
468
470
|
elif not new_files and not new_json_fragment_files:
|
|
469
471
|
return
|
|
470
472
|
|
|
473
|
+
enable_reload = self.args.entire_reload is not cli_args.EntireReloadFlag.no
|
|
474
|
+
force_reload = self.args.entire_reload is cli_args.EntireReloadFlag.force
|
|
475
|
+
|
|
471
476
|
upload_files: Dict[str, bytes] = {}
|
|
472
477
|
reload_cmds: Dict[str, bytes] = {}
|
|
473
478
|
generator_types: Dict[str, GeneratorType] = {}
|
|
@@ -483,18 +488,23 @@ class PCDeployerJob(DeployerJob):
|
|
|
483
488
|
old_text = jsontools.format_json(old_json_cfg)
|
|
484
489
|
new_text = jsontools.format_json(file_content_or_json_cfg)
|
|
485
490
|
diff_content = "\n".join(_diff_file(old_text, new_text))
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
491
|
+
|
|
492
|
+
if diff_content or force_reload:
|
|
493
|
+
self._has_diff |= True
|
|
494
|
+
|
|
495
|
+
upload_files[file] = file_content.encode()
|
|
496
|
+
generator_types[file] = generator_type
|
|
497
|
+
self.cmd_lines.append("= %s/%s " % (device.hostname, file))
|
|
498
|
+
self.cmd_lines.extend([file_content, ""])
|
|
499
|
+
self.diff_lines.append("= %s/%s " % (device.hostname, file))
|
|
500
|
+
self.diff_lines.extend([diff_content, ""])
|
|
501
|
+
|
|
502
|
+
if enable_reload:
|
|
503
|
+
reload_cmds[file] = cmds.encode()
|
|
504
|
+
self.cmd_lines.append("= Deploy cmds %s/%s " % (device.hostname, file))
|
|
505
|
+
self.cmd_lines.extend([cmds, ""])
|
|
506
|
+
|
|
507
|
+
if self._has_diff:
|
|
498
508
|
self.deploy_cmds[device] = {
|
|
499
509
|
"files": upload_files,
|
|
500
510
|
"cmds": reload_cmds,
|
|
@@ -534,13 +544,12 @@ class Deployer:
|
|
|
534
544
|
self._filterer = filtering.filterer_connector.get()
|
|
535
545
|
|
|
536
546
|
def parse_result(self, job: DeployerJob, result: ann_gen.OldNewResult):
|
|
537
|
-
entire_reload = self.args.entire_reload
|
|
538
547
|
logger = get_logger(job.device.hostname)
|
|
539
548
|
|
|
540
549
|
job.parse_result(result)
|
|
541
550
|
self.failed_configs.update(job.failed_configs)
|
|
542
551
|
|
|
543
|
-
if job.has_diff()
|
|
552
|
+
if job.has_diff():
|
|
544
553
|
self.cmd_lines.extend(job.cmd_lines)
|
|
545
554
|
self.deploy_cmds.update(job.deploy_cmds)
|
|
546
555
|
self.diffs.update(job.diffs)
|
annet/mesh/basemodel.py
CHANGED
|
@@ -133,6 +133,9 @@ class BaseMeshModel:
|
|
|
133
133
|
raise AttributeError(f"{self.__class__.__name__} has no field {key}")
|
|
134
134
|
super().__setattr__(key, value)
|
|
135
135
|
|
|
136
|
+
def is_empty(self):
|
|
137
|
+
return not self.__dict__
|
|
138
|
+
|
|
136
139
|
|
|
137
140
|
ModelT = TypeVar("ModelT", bound=BaseMeshModel)
|
|
138
141
|
|
annet/mesh/executor.py
CHANGED
|
@@ -57,6 +57,11 @@ class MeshExecutor:
|
|
|
57
57
|
rule_global_opts = MeshGlobalOptions(rule.match, device)
|
|
58
58
|
logger.debug("Running device handler: %s", handler_name)
|
|
59
59
|
rule.handler(rule_global_opts)
|
|
60
|
+
|
|
61
|
+
if rule_global_opts.is_empty():
|
|
62
|
+
# nothing was set
|
|
63
|
+
continue
|
|
64
|
+
|
|
60
65
|
try:
|
|
61
66
|
global_opts = merge(global_opts, rule_global_opts)
|
|
62
67
|
except MergeForbiddenError as e:
|
|
@@ -79,7 +84,7 @@ class MeshExecutor:
|
|
|
79
84
|
rule: MatchedDirectPair,
|
|
80
85
|
ports: list[tuple[str, str]],
|
|
81
86
|
all_connected_ports: list[tuple[str, str]],
|
|
82
|
-
) -> Pair:
|
|
87
|
+
) -> Optional[Pair]:
|
|
83
88
|
session = MeshSession()
|
|
84
89
|
handler_name = self._handler_name(rule.handler)
|
|
85
90
|
logger.debug("Running direct handler: %s", handler_name)
|
|
@@ -102,6 +107,10 @@ class MeshExecutor:
|
|
|
102
107
|
else:
|
|
103
108
|
rule.handler(peer_neighbor, peer_device, session)
|
|
104
109
|
|
|
110
|
+
if peer_neighbor.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
111
|
+
# nothing was set
|
|
112
|
+
return None
|
|
113
|
+
|
|
105
114
|
try:
|
|
106
115
|
neighbor_dto = merge(DirectPeerDTO(), peer_neighbor, session)
|
|
107
116
|
except MergeForbiddenError as e:
|
|
@@ -144,6 +153,9 @@ class MeshExecutor:
|
|
|
144
153
|
]
|
|
145
154
|
for ports in rule.port_processor(all_connected_ports):
|
|
146
155
|
pair = self._execute_direct_pair(device, neighbor_device, rule, ports, all_connected_ports)
|
|
156
|
+
if pair is None:
|
|
157
|
+
# nothing was set
|
|
158
|
+
continue
|
|
147
159
|
addr = getattr(pair.connected, "addr", None)
|
|
148
160
|
if addr is None:
|
|
149
161
|
raise ValueError(f"Handler `{handler_name}` returned no peer addr")
|
|
@@ -179,6 +191,9 @@ class MeshExecutor:
|
|
|
179
191
|
peer_virtual = VirtualPeer(num=order_number)
|
|
180
192
|
|
|
181
193
|
rule.handler(peer_device, peer_virtual, session)
|
|
194
|
+
if peer_virtual.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
195
|
+
# nothing was set
|
|
196
|
+
continue
|
|
182
197
|
|
|
183
198
|
try:
|
|
184
199
|
virtual_dto = merge(VirtualPeerDTO(), peer_virtual, session)
|
|
@@ -232,6 +247,10 @@ class MeshExecutor:
|
|
|
232
247
|
peer_device = IndirectPeer(rule.match_right, device)
|
|
233
248
|
rule.handler(peer_connected, peer_device, session)
|
|
234
249
|
|
|
250
|
+
if peer_connected.is_empty() and peer_device.is_empty() and session.is_empty():
|
|
251
|
+
# nothing was set
|
|
252
|
+
continue
|
|
253
|
+
|
|
235
254
|
try:
|
|
236
255
|
connected_dto = merge(IndirectPeerDTO(), peer_connected, session)
|
|
237
256
|
except MergeForbiddenError as e:
|
annet/mesh/registry.py
CHANGED
|
@@ -21,6 +21,9 @@ class DirectPeer(DirectPeerDTO):
|
|
|
21
21
|
self.ports = ports
|
|
22
22
|
self.all_connected_ports = all_connected_ports
|
|
23
23
|
|
|
24
|
+
def is_empty(self):
|
|
25
|
+
return self.__dict__.keys() == {"match", "device", "ports", "all_connected_ports"}
|
|
26
|
+
|
|
24
27
|
|
|
25
28
|
class IndirectPeer(IndirectPeerDTO):
|
|
26
29
|
match: MatchedArgs
|
|
@@ -31,6 +34,9 @@ class IndirectPeer(IndirectPeerDTO):
|
|
|
31
34
|
self.match = match
|
|
32
35
|
self.device = device
|
|
33
36
|
|
|
37
|
+
def is_empty(self):
|
|
38
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
39
|
+
|
|
34
40
|
|
|
35
41
|
class VirtualLocal(VirtualLocalDTO):
|
|
36
42
|
match: MatchedArgs
|
|
@@ -41,10 +47,16 @@ class VirtualLocal(VirtualLocalDTO):
|
|
|
41
47
|
self.match = match
|
|
42
48
|
self.device = device
|
|
43
49
|
|
|
50
|
+
def is_empty(self):
|
|
51
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
52
|
+
|
|
44
53
|
|
|
45
54
|
class VirtualPeer(VirtualPeerDTO):
|
|
46
55
|
num: int
|
|
47
56
|
|
|
57
|
+
def is_empty(self):
|
|
58
|
+
return self.__dict__.keys() == {"num"}
|
|
59
|
+
|
|
48
60
|
|
|
49
61
|
class GlobalOptions(GlobalOptionsDTO):
|
|
50
62
|
match: MatchedArgs
|
|
@@ -55,6 +67,9 @@ class GlobalOptions(GlobalOptionsDTO):
|
|
|
55
67
|
self.match = match
|
|
56
68
|
self.device = device
|
|
57
69
|
|
|
70
|
+
def is_empty(self):
|
|
71
|
+
return self.__dict__.keys() == {"match", "device"}
|
|
72
|
+
|
|
58
73
|
|
|
59
74
|
GlobalHandler = Callable[[GlobalOptions], None]
|
|
60
75
|
|
|
@@ -11,10 +11,10 @@ from annet.rpl.statement_builder import NextHopActionValue, AsPathActionValue, C
|
|
|
11
11
|
from .aspath import get_used_as_path_filters
|
|
12
12
|
from .community import get_used_united_community_lists
|
|
13
13
|
from .entities import (
|
|
14
|
-
AsPathFilter, IpPrefixList,
|
|
15
|
-
mangle_united_community_list_name,
|
|
14
|
+
AsPathFilter, IpPrefixList, CommunityList, CommunityLogic, CommunityType,
|
|
15
|
+
mangle_united_community_list_name, PrefixListNameGenerator,
|
|
16
16
|
)
|
|
17
|
-
from .prefix_lists import get_used_prefix_lists
|
|
17
|
+
from .prefix_lists import get_used_prefix_lists, new_prefix_list_name_generator
|
|
18
18
|
|
|
19
19
|
FRR_RESULT_MAP = {
|
|
20
20
|
ResultType.ALLOW: "permit",
|
|
@@ -51,6 +51,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
51
51
|
def get_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
52
52
|
raise NotImplementedError()
|
|
53
53
|
|
|
54
|
+
def get_used_prefix_lists(self, device: Any, name_generator: PrefixListNameGenerator) -> Sequence[IpPrefixList]:
|
|
55
|
+
return get_used_prefix_lists(
|
|
56
|
+
prefix_lists=self.get_prefix_lists(device),
|
|
57
|
+
name_generator=name_generator,
|
|
58
|
+
)
|
|
59
|
+
|
|
54
60
|
@abstractmethod
|
|
55
61
|
def get_community_lists(self, device: Any) -> list[CommunityList]:
|
|
56
62
|
raise NotImplementedError()
|
|
@@ -61,11 +67,13 @@ class CumulusPolicyGenerator(ABC):
|
|
|
61
67
|
|
|
62
68
|
def generate_cumulus_rpl(self, device: Any) -> Iterator[Sequence[str]]:
|
|
63
69
|
policies = self.get_policies(device)
|
|
70
|
+
prefix_list_name_generator = new_prefix_list_name_generator(policies)
|
|
71
|
+
|
|
64
72
|
communities = {c.name: c for c in self.get_community_lists(device)}
|
|
65
73
|
yield from self._cumulus_as_path_filters(device, policies)
|
|
66
74
|
yield from self._cumulus_communities(device, communities, policies)
|
|
67
|
-
yield from self._cumulus_prefix_lists(device, policies)
|
|
68
|
-
yield from self._cumulus_policy_config(device, communities, policies)
|
|
75
|
+
yield from self._cumulus_prefix_lists(device, policies, prefix_list_name_generator)
|
|
76
|
+
yield from self._cumulus_policy_config(device, communities, policies, prefix_list_name_generator)
|
|
69
77
|
|
|
70
78
|
def _cumulus_as_path_filters(
|
|
71
79
|
self,
|
|
@@ -100,11 +108,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
100
108
|
("le", str(match.less_equal)) if match.less_equal is not None else ()
|
|
101
109
|
)
|
|
102
110
|
|
|
103
|
-
def _cumulus_prefix_lists(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
def _cumulus_prefix_lists(
|
|
112
|
+
self, device: Any,
|
|
113
|
+
policies: list[RoutingPolicy],
|
|
114
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
115
|
+
) -> Iterable[Sequence[str]]:
|
|
116
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device, prefix_list_name_generator)}
|
|
108
117
|
if not plists.values():
|
|
109
118
|
return
|
|
110
119
|
|
|
@@ -114,7 +123,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
114
123
|
cond: SingleCondition[PrefixMatchValue]
|
|
115
124
|
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
116
125
|
for name in cond.value.names:
|
|
117
|
-
mangled_name =
|
|
126
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
118
127
|
name=name,
|
|
119
128
|
greater_equal=cond.value.greater_equal,
|
|
120
129
|
less_equal=cond.value.less_equal,
|
|
@@ -125,7 +134,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
125
134
|
precessed_names.add(mangled_name)
|
|
126
135
|
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
127
136
|
for name in cond.value.names:
|
|
128
|
-
mangled_name =
|
|
137
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
129
138
|
name=name,
|
|
130
139
|
greater_equal=cond.value.greater_equal,
|
|
131
140
|
less_equal=cond.value.less_equal,
|
|
@@ -217,6 +226,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
217
226
|
self,
|
|
218
227
|
device: Any,
|
|
219
228
|
condition: SingleCondition[Any],
|
|
229
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
220
230
|
) -> Iterator[Sequence[str]]:
|
|
221
231
|
if condition.field == MatchField.community:
|
|
222
232
|
for comm_name in self._get_match_community_names(condition):
|
|
@@ -236,7 +246,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
236
246
|
return
|
|
237
247
|
if condition.field == MatchField.ip_prefix:
|
|
238
248
|
for name in condition.value.names:
|
|
239
|
-
mangled_name =
|
|
249
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
240
250
|
name=name,
|
|
241
251
|
greater_equal=condition.value.greater_equal,
|
|
242
252
|
less_equal=condition.value.less_equal,
|
|
@@ -245,7 +255,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
245
255
|
return
|
|
246
256
|
if condition.field == MatchField.ipv6_prefix:
|
|
247
257
|
for name in condition.value.names:
|
|
248
|
-
mangled_name =
|
|
258
|
+
mangled_name = prefix_list_name_generator.get_prefix_name(
|
|
249
259
|
name=name,
|
|
250
260
|
greater_equal=condition.value.greater_equal,
|
|
251
261
|
less_equal=condition.value.less_equal,
|
|
@@ -421,11 +431,12 @@ class CumulusPolicyGenerator(ABC):
|
|
|
421
431
|
device: Any,
|
|
422
432
|
policy: RoutingPolicy,
|
|
423
433
|
statement: RoutingPolicyStatement,
|
|
434
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
424
435
|
) -> Iterable[Sequence[str]]:
|
|
425
436
|
yield "route-map", policy.name, FRR_RESULT_MAP[statement.result], str(statement.number)
|
|
426
437
|
|
|
427
438
|
for condition in statement.match:
|
|
428
|
-
for row in self._cumulus_policy_match(device, condition):
|
|
439
|
+
for row in self._cumulus_policy_match(device, condition, prefix_list_name_generator):
|
|
429
440
|
yield FRR_INDENT, *row
|
|
430
441
|
for action in statement.then:
|
|
431
442
|
for row in self._cumulus_policy_then(communities, device, action):
|
|
@@ -439,6 +450,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
439
450
|
device: Any,
|
|
440
451
|
communities: dict[str, CommunityList],
|
|
441
452
|
policies: list[RoutingPolicy],
|
|
453
|
+
prefix_list_name_generator: PrefixListNameGenerator,
|
|
442
454
|
) -> Iterable[Sequence[str]]:
|
|
443
455
|
""" Route maps configuration """
|
|
444
456
|
|
|
@@ -454,5 +466,7 @@ class CumulusPolicyGenerator(ABC):
|
|
|
454
466
|
raise RuntimeError(
|
|
455
467
|
f"Multiple statements have same number {statement.number} for policy `{policy.name}`: "
|
|
456
468
|
f"`{statement.name}` and `{applied_stmts[statement.number]}`")
|
|
457
|
-
yield from self._cumulus_policy_statement(
|
|
469
|
+
yield from self._cumulus_policy_statement(
|
|
470
|
+
communities, device, policy, statement, prefix_list_name_generator,
|
|
471
|
+
)
|
|
458
472
|
applied_stmts[statement.number] = statement.name
|
annet/rpl_generators/entities.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
1
2
|
from collections.abc import Sequence
|
|
2
3
|
from dataclasses import dataclass
|
|
3
4
|
from enum import Enum
|
|
@@ -56,15 +57,27 @@ def mangle_united_community_list_name(values: Sequence[str]) -> str:
|
|
|
56
57
|
return "_OR_".join(values)
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
class PrefixListNameGenerator:
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self._prefix_lists = defaultdict(set)
|
|
63
|
+
|
|
64
|
+
def add_prefix(self, name: str, greater_equal: Optional[int], less_equal: Optional[int]) -> None:
|
|
65
|
+
self._prefix_lists[name].add((greater_equal, less_equal))
|
|
66
|
+
|
|
67
|
+
def is_used(self, name: str):
|
|
68
|
+
return name in self._prefix_lists
|
|
69
|
+
|
|
70
|
+
def get_prefix_name(self, name: str, greater_equal: Optional[int], less_equal: Optional[int]) -> str:
|
|
71
|
+
if len(self._prefix_lists[name]) == 1:
|
|
72
|
+
return name
|
|
73
|
+
if greater_equal is less_equal is None:
|
|
74
|
+
return name
|
|
75
|
+
if greater_equal is None:
|
|
76
|
+
ge_str = "unset"
|
|
77
|
+
else:
|
|
78
|
+
ge_str = str(greater_equal)
|
|
79
|
+
if less_equal is None:
|
|
80
|
+
le_str = "unset"
|
|
81
|
+
else:
|
|
82
|
+
le_str = str(less_equal)
|
|
83
|
+
return f"{name}_{ge_str}_{le_str}"
|
annet/rpl_generators/policy.py
CHANGED
|
@@ -6,13 +6,14 @@ from annet.generators import PartialGenerator
|
|
|
6
6
|
from annet.rpl import (
|
|
7
7
|
CommunityActionValue,
|
|
8
8
|
ResultType, RoutingPolicyStatement, RoutingPolicy, ConditionOperator, SingleCondition, SingleAction, ActionType,
|
|
9
|
-
|
|
9
|
+
MatchField,
|
|
10
10
|
)
|
|
11
11
|
from annet.rpl.statement_builder import AsPathActionValue, NextHopActionValue, ThenField
|
|
12
12
|
from annet.rpl_generators.entities import (
|
|
13
13
|
arista_well_known_community,
|
|
14
|
-
CommunityList, RDFilter,
|
|
14
|
+
CommunityList, RDFilter, PrefixListNameGenerator, CommunityLogic, mangle_united_community_list_name,
|
|
15
15
|
)
|
|
16
|
+
from annet.rpl_generators.prefix_lists import new_prefix_list_name_generator
|
|
16
17
|
|
|
17
18
|
HUAWEI_MATCH_COMMAND_MAP: dict[str, str] = {
|
|
18
19
|
MatchField.as_path_filter: "as-path-filter {option_value}",
|
|
@@ -86,6 +87,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
86
87
|
condition: SingleCondition[Any],
|
|
87
88
|
communities: dict[str, CommunityList],
|
|
88
89
|
rd_filters: dict[str, RDFilter],
|
|
90
|
+
prefix_name_generator: PrefixListNameGenerator,
|
|
89
91
|
) -> Iterator[Sequence[str]]:
|
|
90
92
|
if condition.field == MatchField.community:
|
|
91
93
|
if condition.operator is ConditionOperator.HAS:
|
|
@@ -134,7 +136,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
134
136
|
return
|
|
135
137
|
if condition.field == MatchField.ip_prefix:
|
|
136
138
|
for name in condition.value.names:
|
|
137
|
-
mangled_name =
|
|
139
|
+
mangled_name = prefix_name_generator.get_prefix_name(
|
|
138
140
|
name=name,
|
|
139
141
|
greater_equal=condition.value.greater_equal,
|
|
140
142
|
less_equal=condition.value.less_equal,
|
|
@@ -143,7 +145,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
143
145
|
return
|
|
144
146
|
if condition.field == MatchField.ipv6_prefix:
|
|
145
147
|
for name in condition.value.names:
|
|
146
|
-
mangled_name =
|
|
148
|
+
mangled_name = prefix_name_generator.get_prefix_name(
|
|
147
149
|
name=name,
|
|
148
150
|
greater_equal=condition.value.greater_equal,
|
|
149
151
|
less_equal=condition.value.less_equal,
|
|
@@ -180,7 +182,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
180
182
|
action: SingleAction[CommunityActionValue],
|
|
181
183
|
) -> Iterator[Sequence[str]]:
|
|
182
184
|
if action.value.replaced is not None:
|
|
183
|
-
if action.value.added or action.value.
|
|
185
|
+
if action.value.added or action.value.removed:
|
|
184
186
|
raise NotImplementedError(
|
|
185
187
|
"Cannot set community together with add/remove on huawei",
|
|
186
188
|
)
|
|
@@ -202,7 +204,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
202
204
|
action: SingleAction[CommunityActionValue],
|
|
203
205
|
) -> Iterator[Sequence[str]]:
|
|
204
206
|
if action.value.replaced is not None:
|
|
205
|
-
if action.value.added or action.value.
|
|
207
|
+
if action.value.added or action.value.removed:
|
|
206
208
|
raise NotImplementedError(
|
|
207
209
|
"Cannot set large-community together with add/remove on huawei",
|
|
208
210
|
)
|
|
@@ -334,6 +336,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
334
336
|
device: Any,
|
|
335
337
|
policy: RoutingPolicy,
|
|
336
338
|
statement: RoutingPolicyStatement,
|
|
339
|
+
prefix_name_generator: PrefixListNameGenerator,
|
|
337
340
|
) -> Iterator[Sequence[str]]:
|
|
338
341
|
if statement.number is None:
|
|
339
342
|
raise RuntimeError(f"Statement number should not be empty on Huawei (found for policy: {policy.name})")
|
|
@@ -343,19 +346,21 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
343
346
|
"node", statement.number
|
|
344
347
|
):
|
|
345
348
|
for condition in statement.match:
|
|
346
|
-
yield from self._huawei_match(device, condition, communities, rd_filters)
|
|
349
|
+
yield from self._huawei_match(device, condition, communities, rd_filters, prefix_name_generator)
|
|
347
350
|
for action in statement.then:
|
|
348
351
|
yield from self._huawei_then(communities, device, action)
|
|
349
352
|
if statement.result is ResultType.NEXT:
|
|
350
353
|
yield "goto next-node"
|
|
351
354
|
|
|
352
355
|
def run_huawei(self, device):
|
|
356
|
+
policies = self.get_policies(device)
|
|
353
357
|
communities = {c.name: c for c in self.get_community_lists(device)}
|
|
354
358
|
rd_filters = {f.name: f for f in self.get_rd_filters(device)}
|
|
359
|
+
prefix_name_generator = new_prefix_list_name_generator(policies)
|
|
355
360
|
|
|
356
361
|
for policy in self.get_policies(device):
|
|
357
362
|
for statement in policy.statements:
|
|
358
|
-
yield from self._huawei_statement(communities, rd_filters, device, policy, statement)
|
|
363
|
+
yield from self._huawei_statement(communities, rd_filters, device, policy, statement, prefix_name_generator)
|
|
359
364
|
|
|
360
365
|
# arista
|
|
361
366
|
def acl_arista(self, device):
|
|
@@ -378,6 +383,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
378
383
|
condition: SingleCondition[Any],
|
|
379
384
|
communities: dict[str, CommunityList],
|
|
380
385
|
rd_filters: dict[str, RDFilter],
|
|
386
|
+
prefix_name_generator: PrefixListNameGenerator,
|
|
381
387
|
) -> Iterator[Sequence[str]]:
|
|
382
388
|
if condition.field == MatchField.community:
|
|
383
389
|
if condition.operator is ConditionOperator.HAS_ANY:
|
|
@@ -430,7 +436,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
430
436
|
return
|
|
431
437
|
if condition.field == MatchField.ip_prefix:
|
|
432
438
|
for name in condition.value.names:
|
|
433
|
-
mangled_name =
|
|
439
|
+
mangled_name = prefix_name_generator.get_prefix_name(
|
|
434
440
|
name=name,
|
|
435
441
|
greater_equal=condition.value.greater_equal,
|
|
436
442
|
less_equal=condition.value.less_equal,
|
|
@@ -439,7 +445,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
439
445
|
return
|
|
440
446
|
if condition.field == MatchField.ipv6_prefix:
|
|
441
447
|
for name in condition.value.names:
|
|
442
|
-
mangled_name =
|
|
448
|
+
mangled_name = prefix_name_generator.get_prefix_name(
|
|
443
449
|
name=name,
|
|
444
450
|
greater_equal=condition.value.greater_equal,
|
|
445
451
|
less_equal=condition.value.less_equal,
|
|
@@ -477,7 +483,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
477
483
|
action: SingleAction[CommunityActionValue],
|
|
478
484
|
) -> Iterator[Sequence[str]]:
|
|
479
485
|
if action.value.replaced is not None:
|
|
480
|
-
if action.value.added or action.value.
|
|
486
|
+
if action.value.added or action.value.removed:
|
|
481
487
|
raise NotImplementedError(
|
|
482
488
|
"Cannot set community together with add/remove on arista",
|
|
483
489
|
)
|
|
@@ -500,7 +506,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
500
506
|
action: SingleAction[CommunityActionValue],
|
|
501
507
|
) -> Iterator[Sequence[str]]:
|
|
502
508
|
if action.value.replaced is not None:
|
|
503
|
-
if action.value.added or action.value.
|
|
509
|
+
if action.value.added or action.value.removed:
|
|
504
510
|
raise NotImplementedError(
|
|
505
511
|
"Cannot set large-community together with add/remove on arista",
|
|
506
512
|
)
|
|
@@ -653,6 +659,7 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
653
659
|
device: Any,
|
|
654
660
|
policy: RoutingPolicy,
|
|
655
661
|
statement: RoutingPolicyStatement,
|
|
662
|
+
prefix_name_generator: PrefixListNameGenerator,
|
|
656
663
|
) -> Iterator[Sequence[str]]:
|
|
657
664
|
with self.block(
|
|
658
665
|
"route-map",
|
|
@@ -661,16 +668,20 @@ class RoutingPolicyGenerator(PartialGenerator, ABC):
|
|
|
661
668
|
statement.number,
|
|
662
669
|
):
|
|
663
670
|
for condition in statement.match:
|
|
664
|
-
yield from self._arista_match(device, condition, communities, rd_filters)
|
|
671
|
+
yield from self._arista_match(device, condition, communities, rd_filters, prefix_name_generator)
|
|
665
672
|
for action in statement.then:
|
|
666
673
|
yield from self._arista_then(communities, device, action)
|
|
667
674
|
if statement.result is ResultType.NEXT:
|
|
668
675
|
yield "continue"
|
|
669
676
|
|
|
670
677
|
def run_arista(self, device):
|
|
678
|
+
policies = self.get_policies(device)
|
|
679
|
+
prefix_name_generator = new_prefix_list_name_generator(policies)
|
|
671
680
|
communities = {c.name: c for c in self.get_community_lists(device)}
|
|
672
681
|
rd_filters = {f.name: f for f in self.get_rd_filters(device)}
|
|
673
682
|
|
|
674
|
-
for policy in
|
|
683
|
+
for policy in policies:
|
|
675
684
|
for statement in policy.statements:
|
|
676
|
-
yield from self._arista_statement(
|
|
685
|
+
yield from self._arista_statement(
|
|
686
|
+
communities, rd_filters, device, policy, statement, prefix_name_generator,
|
|
687
|
+
)
|
|
@@ -4,20 +4,28 @@ from ipaddress import ip_interface
|
|
|
4
4
|
from typing import Any, Literal
|
|
5
5
|
|
|
6
6
|
from annet.generators import PartialGenerator
|
|
7
|
-
from annet.rpl import
|
|
8
|
-
from .entities import IpPrefixList,
|
|
7
|
+
from annet.rpl import PrefixMatchValue, MatchField, SingleCondition, RoutingPolicy
|
|
8
|
+
from .entities import IpPrefixList, PrefixListNameGenerator
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def get_used_prefix_lists(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
def get_used_prefix_lists(
|
|
12
|
+
prefix_lists: Sequence[IpPrefixList], name_generator: PrefixListNameGenerator,
|
|
13
|
+
) -> list[IpPrefixList]:
|
|
14
|
+
return [c for c in prefix_lists if name_generator.is_used(c.name)]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def new_prefix_list_name_generator(policies: list[RoutingPolicy]) -> PrefixListNameGenerator:
|
|
18
|
+
name_gen = PrefixListNameGenerator()
|
|
14
19
|
for policy in policies:
|
|
15
20
|
for statement in policy.statements:
|
|
21
|
+
condition: SingleCondition[PrefixMatchValue]
|
|
16
22
|
for condition in statement.match.find_all(MatchField.ipv6_prefix):
|
|
17
|
-
|
|
23
|
+
for name in condition.value.names:
|
|
24
|
+
name_gen.add_prefix(name, condition.value.greater_equal, condition.value.less_equal)
|
|
18
25
|
for condition in statement.match.find_all(MatchField.ip_prefix):
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
for name in condition.value.names:
|
|
27
|
+
name_gen.add_prefix(name, condition.value.greater_equal, condition.value.less_equal)
|
|
28
|
+
return name_gen
|
|
21
29
|
|
|
22
30
|
|
|
23
31
|
class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
@@ -31,10 +39,10 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
31
39
|
def get_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
32
40
|
raise NotImplementedError()
|
|
33
41
|
|
|
34
|
-
def get_used_prefix_lists(self, device: Any) -> Sequence[IpPrefixList]:
|
|
42
|
+
def get_used_prefix_lists(self, device: Any, name_generator: PrefixListNameGenerator) -> Sequence[IpPrefixList]:
|
|
35
43
|
return get_used_prefix_lists(
|
|
36
44
|
prefix_lists=self.get_prefix_lists(device),
|
|
37
|
-
|
|
45
|
+
name_generator=name_generator,
|
|
38
46
|
)
|
|
39
47
|
|
|
40
48
|
# huawei
|
|
@@ -68,15 +76,16 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
68
76
|
)
|
|
69
77
|
|
|
70
78
|
def run_huawei(self, device: Any):
|
|
71
|
-
plists = {p.name: p for p in self.get_used_prefix_lists(device)}
|
|
72
79
|
policies = self.get_policies(device)
|
|
80
|
+
name_generator = new_prefix_list_name_generator(policies)
|
|
81
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device, name_generator)}
|
|
73
82
|
precessed_names = set()
|
|
74
83
|
for policy in policies:
|
|
75
84
|
for statement in policy.statements:
|
|
76
85
|
cond: SingleCondition[PrefixMatchValue]
|
|
77
86
|
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
78
87
|
for name in cond.value.names:
|
|
79
|
-
mangled_name =
|
|
88
|
+
mangled_name = name_generator.get_prefix_name(
|
|
80
89
|
name=name,
|
|
81
90
|
greater_equal=cond.value.greater_equal,
|
|
82
91
|
less_equal=cond.value.less_equal,
|
|
@@ -87,7 +96,7 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
87
96
|
precessed_names.add(mangled_name)
|
|
88
97
|
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
89
98
|
for name in cond.value.names:
|
|
90
|
-
mangled_name =
|
|
99
|
+
mangled_name = name_generator.get_prefix_name(
|
|
91
100
|
name=name,
|
|
92
101
|
greater_equal=cond.value.greater_equal,
|
|
93
102
|
less_equal=cond.value.less_equal,
|
|
@@ -128,15 +137,16 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
128
137
|
)
|
|
129
138
|
|
|
130
139
|
def run_arista(self, device: Any):
|
|
131
|
-
plists = {p.name: p for p in self.get_used_prefix_lists(device)}
|
|
132
140
|
policies = self.get_policies(device)
|
|
141
|
+
name_generator = new_prefix_list_name_generator(policies)
|
|
142
|
+
plists = {p.name: p for p in self.get_used_prefix_lists(device, name_generator)}
|
|
133
143
|
precessed_names = set()
|
|
134
144
|
for policy in policies:
|
|
135
145
|
for statement in policy.statements:
|
|
136
146
|
cond: SingleCondition[PrefixMatchValue]
|
|
137
147
|
for cond in statement.match.find_all(MatchField.ip_prefix):
|
|
138
148
|
for name in cond.value.names:
|
|
139
|
-
mangled_name =
|
|
149
|
+
mangled_name = name_generator.get_prefix_name(
|
|
140
150
|
name=name,
|
|
141
151
|
greater_equal=cond.value.greater_equal,
|
|
142
152
|
less_equal=cond.value.less_equal,
|
|
@@ -147,7 +157,7 @@ class PrefixListFilterGenerator(PartialGenerator, ABC):
|
|
|
147
157
|
precessed_names.add(mangled_name)
|
|
148
158
|
for cond in statement.match.find_all(MatchField.ipv6_prefix):
|
|
149
159
|
for name in cond.value.names:
|
|
150
|
-
mangled_name =
|
|
160
|
+
mangled_name = name_generator.get_prefix_name(
|
|
151
161
|
name=name,
|
|
152
162
|
greater_equal=cond.value.greater_equal,
|
|
153
163
|
less_equal=cond.value.less_equal,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
crypto key generate rsa %timeout=60
|
|
21
21
|
dialog: Do you really want to replace them? [yes/no]: ::: no
|
|
22
22
|
dialog: How many bits in the modulus [512]: ::: 2048
|
|
23
|
-
no username * privilege * secret
|
|
23
|
+
no username * privilege * secret * *
|
|
24
24
|
dialog: This operation will remove all username related configurations with same name.Do you want to continue? [confirm] ::: Y %send_nl=0
|
|
25
25
|
|
|
26
26
|
copy running-config startup-config
|
|
@@ -32,8 +32,8 @@ annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
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
|
|
34
34
|
annet/adapters/netbox/common/client.py,sha256=PaxHG4W9H8_uunIwMBNYkLq4eQJYoO6p6gY-ciQs7Nc,2563
|
|
35
|
-
annet/adapters/netbox/common/manufacturer.py,sha256=
|
|
36
|
-
annet/adapters/netbox/common/models.py,sha256=
|
|
35
|
+
annet/adapters/netbox/common/manufacturer.py,sha256=LAPT6OlV_ew96GhtwNrCpeiT0IGrg2_9__MMdZk431U,1733
|
|
36
|
+
annet/adapters/netbox/common/models.py,sha256=TnWEKI6_MxuZe24xfpkAijofkn-dOXwWe_ZOCWaYX58,7374
|
|
37
37
|
annet/adapters/netbox/common/query.py,sha256=ziUFM7cUEbEIf3k1szTll4aO-OCUa-2Ogxbebi7Tegs,646
|
|
38
38
|
annet/adapters/netbox/common/status_client.py,sha256=W4nTb2yvBlJ2UkWUmUhKQ2PaSQb1shjhHj5ebb4s2s4,591
|
|
39
39
|
annet/adapters/netbox/common/storage_opts.py,sha256=5tt6wxUUJTIzNbOVXMnYBwZedNAIqYlve3YWl6GdbZM,1197
|
|
@@ -67,7 +67,7 @@ annet/annlib/rbparser/platform.py,sha256=hnxznTfV9txXi1PkR1hZrprTrQJvlwgqXVL8vXk
|
|
|
67
67
|
annet/annlib/rbparser/syntax.py,sha256=iZ7Y-4QQBw4L3UtjEh54qisiRDhobl7HZxFNdP8mi54,3577
|
|
68
68
|
annet/annlib/rulebook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
69
|
annet/annlib/rulebook/common.py,sha256=bx_Iwui-JJeyctUPF1OsEll0Aa-IQZadBPQjaeuoWgw,16638
|
|
70
|
-
annet/api/__init__.py,sha256=
|
|
70
|
+
annet/api/__init__.py,sha256=WGpVMfIxVy9F_jH6nqHSQypEEcsSNa9yF2WFEwhUVwI,34156
|
|
71
71
|
annet/configs/context.yml,sha256=RVLrKLIHpCty7AGwOnmqf7Uu0iZQCn-AjYhophDJer8,259
|
|
72
72
|
annet/configs/logging.yaml,sha256=EUagfir99QqA73Scc3k7sfQccbU3E1SvEQdyhLFtCl4,997
|
|
73
73
|
annet/generators/__init__.py,sha256=rVHHDTPKHPZsml1eNEAj3o-8RweFTN8J7LX3tKMXdIY,16402
|
|
@@ -82,14 +82,14 @@ annet/generators/result.py,sha256=zMAvGOYQU803bGy6datZduHLgrEqK2Zba_Jcf9Qn9p0,49
|
|
|
82
82
|
annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
83
83
|
annet/generators/common/initial.py,sha256=qYBxXFhyOPy34cxc6hsIXseod-lYCmmbuNHpM0uteY0,1244
|
|
84
84
|
annet/mesh/__init__.py,sha256=lcgdnBIxc2MAN7Er1bcErEKPqrjWO4uIp_1FldMXTYg,557
|
|
85
|
-
annet/mesh/basemodel.py,sha256=
|
|
85
|
+
annet/mesh/basemodel.py,sha256=E6NTOneiMDwB1NCpjDRECoaeQ0f3n_fmTLnKTrSHTU4,4917
|
|
86
86
|
annet/mesh/device_models.py,sha256=YcL6_vGjnt67BTovN8Eq38U5wGcbJDhqiq8613WpYtQ,3381
|
|
87
|
-
annet/mesh/executor.py,sha256=
|
|
87
|
+
annet/mesh/executor.py,sha256=0RLsdtldozoFgDGFuhU5mdckuMwGg0y_DFIji1RfTQg,16969
|
|
88
88
|
annet/mesh/match_args.py,sha256=CR3kdIV9NGtyk9E2JbcOQ3TRuYEryTWP3m2yCo2VCWg,5751
|
|
89
89
|
annet/mesh/models_converter.py,sha256=3q2zs7K8S3pfYSUKKRdtl5CJGbeg4TtYxofAVs_MBsk,3085
|
|
90
90
|
annet/mesh/peer_models.py,sha256=uD3h7HWl9_zNdBlQqZCMcjCnzjr8dTbSFqc8216JDu0,2735
|
|
91
91
|
annet/mesh/port_processor.py,sha256=RHiMS5W8qoDkTKiarQ748bcr8bNx4g_R4Y4vZg2k4TU,478
|
|
92
|
-
annet/mesh/registry.py,sha256=
|
|
92
|
+
annet/mesh/registry.py,sha256=BCRIOrINP0krcgvF59uFJqyhmSviQ03GSzWpxLVYlIg,9527
|
|
93
93
|
annet/rpl/__init__.py,sha256=0kcIktE3AmS0rlm9xzVDf53xk08OeZXgD-6ZLCt_KCs,731
|
|
94
94
|
annet/rpl/action.py,sha256=PY6W66j908RuqQ1_ioxayqVN-70rxDk5Z59EGHtxI98,1246
|
|
95
95
|
annet/rpl/condition.py,sha256=MJri4MbWtPkLHIsLMAtsIEF7e8IAS9dIImjmJs5vS5U,3418
|
|
@@ -101,11 +101,11 @@ annet/rpl/statement_builder.py,sha256=sVGOYsCV0s_SFQUy2WtUyQqKy5H4MOfmRCJWGj-UOJ
|
|
|
101
101
|
annet/rpl_generators/__init__.py,sha256=ZLWs-flcpyIbdhxSDfNt-ORDrLe8ins25sWdXTWeUoA,748
|
|
102
102
|
annet/rpl_generators/aspath.py,sha256=kZakwPLfGGiXu9fC6I1z-pvy7Fe-4dy93_-lYcx39_4,2038
|
|
103
103
|
annet/rpl_generators/community.py,sha256=SWpaOvoQUNISuRm41-IvGPFmntvgFv9ee4zegsMyeo0,11496
|
|
104
|
-
annet/rpl_generators/cumulus_frr.py,sha256=
|
|
105
|
-
annet/rpl_generators/entities.py,sha256=
|
|
104
|
+
annet/rpl_generators/cumulus_frr.py,sha256=D8JnnXTXYaIUayPwL_KORUCkpBV32-CHatlyKQSSJWw,20855
|
|
105
|
+
annet/rpl_generators/entities.py,sha256=DIpgAQ8Tslo2hq6iFBaYkJX12BFBiccN8GOaRVxR1Uk,1985
|
|
106
106
|
annet/rpl_generators/execute.py,sha256=wS6e6fwcPWywsHB0gBMqZ17eF0s4YOBgDgwPB_cr5Rw,431
|
|
107
|
-
annet/rpl_generators/policy.py,sha256=
|
|
108
|
-
annet/rpl_generators/prefix_lists.py,sha256=
|
|
107
|
+
annet/rpl_generators/policy.py,sha256=NeqB0reRN_KuY8LYkeGT3dRPe2HFDT9RfmVy5fcA3zw,32570
|
|
108
|
+
annet/rpl_generators/prefix_lists.py,sha256=2x2NO52npcB3VKcQ4tthsWFwirVpexLdLtWPA5sHzjg,7246
|
|
109
109
|
annet/rpl_generators/rd.py,sha256=YGXgx1D2D0-pixgspXJzA6NvW8lx3AmHMxIY2l5rraI,1457
|
|
110
110
|
annet/rulebook/__init__.py,sha256=oafL5HC8QHdkO9CH2q_fxohPMxOgjn-dNQa5kPjuqsA,3942
|
|
111
111
|
annet/rulebook/common.py,sha256=zK1s2c5lc5HQbIlMUQ4HARQudXSgOYiZ_Sxc2I_tHqg,721
|
|
@@ -143,7 +143,7 @@ annet/rulebook/texts/aruba.rul,sha256=zvGVpoYyJvMoL0fb1NQ8we_GCLZXno8nwWpZIOScLQ
|
|
|
143
143
|
annet/rulebook/texts/b4com.deploy,sha256=SVX8-yLHM90tJC4M-ekpGuGM1aQZW3euSGyg67l--R0,781
|
|
144
144
|
annet/rulebook/texts/b4com.order,sha256=G3aToAIHHzKzDCM3q7_lyr9wJvuVOXVbVvF3wm5PiTE,707
|
|
145
145
|
annet/rulebook/texts/b4com.rul,sha256=5mqyUg_oLRSny2iH6QdhfDWVu6kzgDURtlSATD7DFno,1056
|
|
146
|
-
annet/rulebook/texts/cisco.deploy,sha256=
|
|
146
|
+
annet/rulebook/texts/cisco.deploy,sha256=Hu0NkcGv3f1CWUrnbzI3eQOPXJxtH4NNOPRV68IrW4U,1226
|
|
147
147
|
annet/rulebook/texts/cisco.order,sha256=OvNHMNqkCc-DN2dEjLCTKv_7ZhiaHt4q2X4Y4Z8dvR4,1901
|
|
148
148
|
annet/rulebook/texts/cisco.rul,sha256=jgL5_xnSwd_H4E8cx4gcneSvJC5W1zz6_BWSb64iuxI,3017
|
|
149
149
|
annet/rulebook/texts/huawei.deploy,sha256=azEC6_jQRzwnTSrNgag0hHh6L7hezS_eMk6ZDZfWyXI,10444
|
|
@@ -175,10 +175,10 @@ annet_generators/rpl_example/generator.py,sha256=zndIGfV4ZlTxPgAGYs7bMQvTc_tYScO
|
|
|
175
175
|
annet_generators/rpl_example/items.py,sha256=Ez1RF5YhcXNCusBmeApIjRL3rBlMazNZd29Gpw1_IsA,766
|
|
176
176
|
annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
|
|
177
177
|
annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
|
|
178
|
-
annet-0.16.
|
|
179
|
-
annet-0.16.
|
|
180
|
-
annet-0.16.
|
|
181
|
-
annet-0.16.
|
|
182
|
-
annet-0.16.
|
|
183
|
-
annet-0.16.
|
|
184
|
-
annet-0.16.
|
|
178
|
+
annet-0.16.31.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
|
|
179
|
+
annet-0.16.31.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
|
|
180
|
+
annet-0.16.31.dist-info/METADATA,sha256=CpR6RlgdzRXD13wUkktgrcw2nuWeqI0dZNJ1dyNn7hE,854
|
|
181
|
+
annet-0.16.31.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
182
|
+
annet-0.16.31.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
|
|
183
|
+
annet-0.16.31.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
|
|
184
|
+
annet-0.16.31.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|