annet 0.16.33__py3-none-any.whl → 0.16.35__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/query.py +36 -1
- annet/adapters/netbox/v37/storage.py +85 -23
- annet/annlib/netdev/devdb/data/devdb.json +3 -1
- annet/annlib/rulebook/common.py +2 -2
- annet/bgp_models.py +12 -16
- annet/mesh/device_models.py +1 -2
- annet/mesh/executor.py +9 -0
- annet/mesh/registry.py +12 -12
- annet/rulebook/b4com/iface.py +42 -0
- annet/rulebook/texts/b4com.rul +9 -4
- annet/rulebook/texts/huawei.deploy +1 -1
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/METADATA +2 -2
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/RECORD +18 -17
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/AUTHORS +0 -0
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/LICENSE +0 -0
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/WHEEL +0 -0
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/entry_points.txt +0 -0
- {annet-0.16.33.dist-info → annet-0.16.35.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
1
2
|
from dataclasses import dataclass
|
|
2
|
-
from typing import List, Union, Iterable, Optional
|
|
3
|
+
from typing import cast, List, Union, Iterable, Optional, TypedDict
|
|
3
4
|
|
|
4
5
|
from annet.storage import Query
|
|
5
6
|
|
|
7
|
+
FIELD_VALUE_SEPARATOR = ":"
|
|
8
|
+
ALLOWED_GLOB_GROUPS = ["site", "tag", "role"]
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Filter(TypedDict, total=False):
|
|
12
|
+
site: list[str]
|
|
13
|
+
tag: list[str]
|
|
14
|
+
role: list[str]
|
|
15
|
+
name: list[str]
|
|
16
|
+
|
|
6
17
|
|
|
7
18
|
@dataclass
|
|
8
19
|
class NetboxQuery(Query):
|
|
@@ -22,5 +33,29 @@ class NetboxQuery(Query):
|
|
|
22
33
|
# We process every query host as a glob
|
|
23
34
|
return self.query
|
|
24
35
|
|
|
36
|
+
def parse_query(self) -> Filter:
|
|
37
|
+
query_groups = defaultdict(list)
|
|
38
|
+
for q in self.globs:
|
|
39
|
+
if FIELD_VALUE_SEPARATOR in q:
|
|
40
|
+
glob_type, param = q.split(FIELD_VALUE_SEPARATOR, 2)
|
|
41
|
+
if glob_type not in ALLOWED_GLOB_GROUPS:
|
|
42
|
+
raise Exception(f"unknown query type: '{glob_type}'")
|
|
43
|
+
if not param:
|
|
44
|
+
raise Exception(f"empty param for '{glob_type}'")
|
|
45
|
+
query_groups[glob_type].append(param)
|
|
46
|
+
else:
|
|
47
|
+
query_groups["name"].append(q)
|
|
48
|
+
|
|
49
|
+
query_groups.default_factory = None
|
|
50
|
+
return cast(Filter, query_groups)
|
|
51
|
+
|
|
25
52
|
def is_empty(self) -> bool:
|
|
26
53
|
return len(self.query) == 0
|
|
54
|
+
|
|
55
|
+
def is_host_query(self) -> bool:
|
|
56
|
+
if not self.globs:
|
|
57
|
+
return False
|
|
58
|
+
for q in self.globs:
|
|
59
|
+
if FIELD_VALUE_SEPARATOR in q:
|
|
60
|
+
return False
|
|
61
|
+
return True
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from logging import getLogger
|
|
2
|
-
from typing import Any, Optional, List, Union, Dict
|
|
3
|
-
from ipaddress import ip_interface
|
|
4
|
-
from collections import defaultdict
|
|
5
1
|
import ssl
|
|
2
|
+
from collections import defaultdict
|
|
3
|
+
from ipaddress import ip_interface
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from typing import Any, Optional, List, Union, Dict, cast
|
|
6
6
|
|
|
7
7
|
from adaptix import P
|
|
8
8
|
from adaptix.conversion import impl_converter, link, link_constant
|
|
@@ -13,7 +13,7 @@ from annet.adapters.netbox.common import models
|
|
|
13
13
|
from annet.adapters.netbox.common.manufacturer import (
|
|
14
14
|
get_hw, get_breed,
|
|
15
15
|
)
|
|
16
|
-
from annet.adapters.netbox.common.query import NetboxQuery
|
|
16
|
+
from annet.adapters.netbox.common.query import NetboxQuery, FIELD_VALUE_SEPARATOR
|
|
17
17
|
from annet.adapters.netbox.common.storage_opts import NetboxStorageOpts
|
|
18
18
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
19
19
|
from annet.storage import Storage, Device, Interface
|
|
@@ -101,6 +101,9 @@ class NetboxStorageV37(Storage):
|
|
|
101
101
|
self.exact_host_filter = opts.exact_host_filter
|
|
102
102
|
self.netbox = NetboxV37(url=url, token=token, ssl_context=ctx)
|
|
103
103
|
self._all_fqdns: Optional[list[str]] = None
|
|
104
|
+
self._id_devices: dict[int, models.NetboxDevice] = {}
|
|
105
|
+
self._name_devices: dict[str, models.NetboxDevice] = {}
|
|
106
|
+
self._short_name_devices: dict[str, models.NetboxDevice] = {}
|
|
104
107
|
|
|
105
108
|
def __enter__(self):
|
|
106
109
|
return self
|
|
@@ -136,6 +139,37 @@ class NetboxStorageV37(Storage):
|
|
|
136
139
|
) -> List[models.NetboxDevice]:
|
|
137
140
|
if isinstance(query, list):
|
|
138
141
|
query = NetboxQuery.new(query)
|
|
142
|
+
|
|
143
|
+
devices = []
|
|
144
|
+
if query.is_host_query():
|
|
145
|
+
globs = []
|
|
146
|
+
for glob in query.globs:
|
|
147
|
+
if glob in self._name_devices:
|
|
148
|
+
devices.append(self._name_devices[glob])
|
|
149
|
+
if glob in self._short_name_devices:
|
|
150
|
+
devices.append(self._short_name_devices[glob])
|
|
151
|
+
else:
|
|
152
|
+
globs.append(glob)
|
|
153
|
+
if not globs:
|
|
154
|
+
return devices
|
|
155
|
+
query = NetboxQuery.new(globs)
|
|
156
|
+
|
|
157
|
+
return devices + self._make_devices(
|
|
158
|
+
query=query,
|
|
159
|
+
preload_neighbors=preload_neighbors,
|
|
160
|
+
use_mesh=use_mesh,
|
|
161
|
+
preload_extra_fields=preload_extra_fields,
|
|
162
|
+
**kwargs
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
def _make_devices(
|
|
166
|
+
self,
|
|
167
|
+
query: NetboxQuery,
|
|
168
|
+
preload_neighbors=False,
|
|
169
|
+
use_mesh=None,
|
|
170
|
+
preload_extra_fields=False,
|
|
171
|
+
**kwargs,
|
|
172
|
+
) -> List[models.NetboxDevice]:
|
|
139
173
|
device_ids = {
|
|
140
174
|
device.id: extend_device(
|
|
141
175
|
device=device,
|
|
@@ -148,6 +182,9 @@ class NetboxStorageV37(Storage):
|
|
|
148
182
|
if not device_ids:
|
|
149
183
|
return []
|
|
150
184
|
|
|
185
|
+
for device in device_ids.values():
|
|
186
|
+
self._record_device(device)
|
|
187
|
+
|
|
151
188
|
interfaces = self._load_interfaces(list(device_ids))
|
|
152
189
|
neighbours = {x.id: x for x in self._load_neighbours(interfaces)}
|
|
153
190
|
neighbours_seen: dict[str, set] = defaultdict(set)
|
|
@@ -162,21 +199,22 @@ class NetboxStorageV37(Storage):
|
|
|
162
199
|
|
|
163
200
|
return list(device_ids.values())
|
|
164
201
|
|
|
202
|
+
def _record_device(self, device: models.NetboxDevice):
|
|
203
|
+
self._id_devices[device.id] = device
|
|
204
|
+
self._short_name_devices[device.name] = device
|
|
205
|
+
if not self.exact_host_filter:
|
|
206
|
+
short_name = device.name.split(".")[0]
|
|
207
|
+
self._short_name_devices[short_name] = device
|
|
208
|
+
|
|
165
209
|
def _load_devices(self, query: NetboxQuery) -> List[api_models.Device]:
|
|
166
210
|
if not query.globs:
|
|
167
211
|
return []
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
for device in self.netbox.dcim_all_devices(
|
|
175
|
-
name__ic=query.globs,
|
|
176
|
-
).results
|
|
177
|
-
if _match_query(query, device)
|
|
178
|
-
]
|
|
179
|
-
return devices
|
|
212
|
+
query_groups = parse_glob(self.exact_host_filter, query)
|
|
213
|
+
return [
|
|
214
|
+
device
|
|
215
|
+
for device in self.netbox.dcim_all_devices(**query_groups).results
|
|
216
|
+
if _match_query(self.exact_host_filter, query, device)
|
|
217
|
+
]
|
|
180
218
|
|
|
181
219
|
def _extend_interfaces(self, interfaces: List[models.Interface]) -> List[models.Interface]:
|
|
182
220
|
extended_ifaces = {
|
|
@@ -227,6 +265,9 @@ class NetboxStorageV37(Storage):
|
|
|
227
265
|
self, obj_id, preload_neighbors=False, use_mesh=None,
|
|
228
266
|
**kwargs,
|
|
229
267
|
) -> models.NetboxDevice:
|
|
268
|
+
if obj_id in self._id_devices:
|
|
269
|
+
return self._id_devices[obj_id]
|
|
270
|
+
|
|
230
271
|
device = self.netbox.dcim_device(obj_id)
|
|
231
272
|
interfaces = self._load_interfaces([device.id])
|
|
232
273
|
neighbours = self._load_neighbours(interfaces)
|
|
@@ -237,6 +278,7 @@ class NetboxStorageV37(Storage):
|
|
|
237
278
|
interfaces=interfaces,
|
|
238
279
|
neighbours=neighbours,
|
|
239
280
|
)
|
|
281
|
+
self._record_device(res)
|
|
240
282
|
return res
|
|
241
283
|
|
|
242
284
|
def flush_perf(self):
|
|
@@ -261,14 +303,23 @@ class NetboxStorageV37(Storage):
|
|
|
261
303
|
return res
|
|
262
304
|
|
|
263
305
|
|
|
264
|
-
def _match_query(query: NetboxQuery, device_data: api_models.Device) -> bool:
|
|
265
|
-
|
|
266
|
-
|
|
306
|
+
def _match_query(exact_host_filter: bool, query: NetboxQuery, device_data: api_models.Device) -> bool:
|
|
307
|
+
"""
|
|
308
|
+
Additional filtering after netbox due to limited backend logic.
|
|
309
|
+
"""
|
|
310
|
+
if exact_host_filter:
|
|
311
|
+
return True # nothing to check, all filtering is done by netbox
|
|
312
|
+
hostnames = [subquery.strip() for subquery in query.globs if FIELD_VALUE_SEPARATOR not in subquery]
|
|
313
|
+
if not hostnames:
|
|
314
|
+
return True # no hostnames to check
|
|
315
|
+
short_name = device_data.name.split(".")[0]
|
|
316
|
+
for hostname in hostnames:
|
|
317
|
+
if short_name == hostname or device_data.name == hostname:
|
|
267
318
|
return True
|
|
268
319
|
return False
|
|
269
320
|
|
|
270
321
|
|
|
271
|
-
def _hostname_dot_hack(
|
|
322
|
+
def _hostname_dot_hack(raw_query: str) -> str:
|
|
272
323
|
# there is no proper way to lookup host by its hostname
|
|
273
324
|
# ie find "host" with fqdn "host.example.com"
|
|
274
325
|
# besides using name__ic (ie startswith)
|
|
@@ -280,9 +331,20 @@ def _hostname_dot_hack(netbox_query: NetboxQuery) -> NetboxQuery:
|
|
|
280
331
|
raw_query = raw_query + "."
|
|
281
332
|
return raw_query
|
|
282
333
|
|
|
283
|
-
raw_query = netbox_query.query
|
|
284
334
|
if isinstance(raw_query, list):
|
|
285
335
|
for i, name in enumerate(raw_query):
|
|
286
336
|
raw_query[i] = add_dot(name)
|
|
337
|
+
elif isinstance(raw_query, str):
|
|
338
|
+
raw_query = add_dot(raw_query)
|
|
287
339
|
|
|
288
|
-
return
|
|
340
|
+
return raw_query
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def parse_glob(exact_host_filter: bool, query: NetboxQuery) -> dict[str, list[str]]:
|
|
344
|
+
query_groups = cast(dict[str, list[str]], query.parse_query())
|
|
345
|
+
if names := query_groups.pop("name", None):
|
|
346
|
+
if exact_host_filter:
|
|
347
|
+
query_groups["name__ie"] = names
|
|
348
|
+
else:
|
|
349
|
+
query_groups["name__ic"] = [_hostname_dot_hack(name) for name in names]
|
|
350
|
+
return query_groups
|
|
@@ -166,6 +166,8 @@
|
|
|
166
166
|
|
|
167
167
|
"B4com": "^[Bb]4com",
|
|
168
168
|
"B4com.CS2148P": "^[Bb]4com B4T-CS2148P.*",
|
|
169
|
+
"B4com.CS4100": "^[Bb]4com B4T-CS41.*",
|
|
170
|
+
"B4com.CS4132U": "^[Bb]4com B4T-CS4132U.*",
|
|
169
171
|
"B4com.CS4148Q": "^[Bb]4com B4T-CS4148Q.*",
|
|
170
|
-
"B4com.
|
|
172
|
+
"B4com.CS4164U": "^[Bb]4com B4T-CS4164U.*"
|
|
171
173
|
}
|
annet/annlib/rulebook/common.py
CHANGED
|
@@ -346,14 +346,14 @@ def apply(hw, do_commit, do_finalize, **_):
|
|
|
346
346
|
after.add_cmd(Command("exit"))
|
|
347
347
|
elif hw.B4com.CS2148P:
|
|
348
348
|
before.add_cmd(Command("conf t"))
|
|
349
|
-
after.add_cmd(Command("
|
|
349
|
+
after.add_cmd(Command("end"))
|
|
350
350
|
if do_finalize:
|
|
351
351
|
after.add_cmd(Command("write", timeout=40))
|
|
352
352
|
elif hw.B4com:
|
|
353
353
|
before.add_cmd(Command("conf t"))
|
|
354
|
-
after.add_cmd(Command("exit"))
|
|
355
354
|
if do_commit:
|
|
356
355
|
after.add_cmd(Command("commit"))
|
|
356
|
+
after.add_cmd(Command("end"))
|
|
357
357
|
if do_finalize:
|
|
358
358
|
after.add_cmd(Command("write", timeout=40))
|
|
359
359
|
elif hw.H3C:
|
annet/bgp_models.py
CHANGED
|
@@ -142,7 +142,6 @@ class Peer:
|
|
|
142
142
|
class Aggregate:
|
|
143
143
|
policy: str = ""
|
|
144
144
|
routes: tuple[str, ...] = () # "182.168.1.0/24",
|
|
145
|
-
export_policy: str = ""
|
|
146
145
|
as_path: str = ""
|
|
147
146
|
reference: str = ""
|
|
148
147
|
suppress: bool = False
|
|
@@ -290,21 +289,18 @@ def _used_policies(peer: Union[Peer, PeerGroup]) -> Iterable[str]:
|
|
|
290
289
|
|
|
291
290
|
|
|
292
291
|
def _used_redistribute_policies(opts: Union[GlobalOptions, VrfOptions]) -> Iterable[str]:
|
|
293
|
-
for
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
if
|
|
304
|
-
yield
|
|
305
|
-
for red in opts.l2vpn_evpn.redistributes:
|
|
306
|
-
if red.policy:
|
|
307
|
-
yield red.policy
|
|
292
|
+
for family_opts in (
|
|
293
|
+
opts.ipv4_unicast,
|
|
294
|
+
opts.ipv6_unicast,
|
|
295
|
+
opts.ipv4_labeled_unicast,
|
|
296
|
+
opts.ipv6_labeled_unicast,
|
|
297
|
+
opts.l2vpn_evpn,
|
|
298
|
+
):
|
|
299
|
+
for red in family_opts.redistributes:
|
|
300
|
+
if red.policy:
|
|
301
|
+
yield red.policy
|
|
302
|
+
if family_opts.aggregate and family_opts.aggregate.policy:
|
|
303
|
+
yield family_opts.aggregate.policy
|
|
308
304
|
|
|
309
305
|
|
|
310
306
|
def extract_policies(config: BgpConfig) -> Sequence[str]:
|
annet/mesh/device_models.py
CHANGED
|
@@ -8,7 +8,6 @@ from .peer_models import MeshPeerGroup
|
|
|
8
8
|
class Aggregate(BaseMeshModel):
|
|
9
9
|
policy: str
|
|
10
10
|
routes: Annotated[tuple[str, ...], Concat()]
|
|
11
|
-
export_policy: str
|
|
12
11
|
as_path: str
|
|
13
12
|
reference: str
|
|
14
13
|
suppress: bool
|
|
@@ -22,7 +21,7 @@ class FamilyOptions(BaseMeshModel):
|
|
|
22
21
|
super().__init__(**kwargs)
|
|
23
22
|
family: Family
|
|
24
23
|
vrf_name: str
|
|
25
|
-
multipath: int
|
|
24
|
+
multipath: int
|
|
26
25
|
global_multipath: int
|
|
27
26
|
aggregate: Annotated[Aggregate, Merge()]
|
|
28
27
|
redistributes: Annotated[tuple[Redistribute, ...], Concat()]
|
annet/mesh/executor.py
CHANGED
|
@@ -144,8 +144,17 @@ class MeshExecutor:
|
|
|
144
144
|
for rule in rules:
|
|
145
145
|
handler_name = self._handler_name(rule.handler)
|
|
146
146
|
if rule.direct_order:
|
|
147
|
+
if rule.name_right not in neigbors:
|
|
148
|
+
print(list(neigbors), flush=True)
|
|
149
|
+
raise ValueError(
|
|
150
|
+
f"Device `{device.fqdn}` has no neighbor `{rule.name_right}` required for `{handler_name}`. {list(neigbors)}",
|
|
151
|
+
)
|
|
147
152
|
neighbor_device = neigbors[rule.name_right]
|
|
148
153
|
else:
|
|
154
|
+
if rule.name_left not in neigbors:
|
|
155
|
+
raise ValueError(
|
|
156
|
+
f"Device `{device.fqdn}` has no neighbor `{rule.name_left}` required for `{handler_name}`",
|
|
157
|
+
)
|
|
149
158
|
neighbor_device = neigbors[rule.name_left]
|
|
150
159
|
all_connected_ports = [
|
|
151
160
|
(p1.name, p2.name)
|
annet/mesh/registry.py
CHANGED
|
@@ -208,11 +208,11 @@ class MeshRulesRegistry:
|
|
|
208
208
|
|
|
209
209
|
def lookup_direct(self, device: str, neighbors: list[str]) -> list[MatchedDirectPair]:
|
|
210
210
|
found = []
|
|
211
|
-
|
|
211
|
+
device_norm = self._normalize_host(device)
|
|
212
212
|
for neighbor in neighbors:
|
|
213
|
-
|
|
213
|
+
neighbor_norm = self._normalize_host(neighbor)
|
|
214
214
|
for rule in self.direct_rules:
|
|
215
|
-
if args := rule.matcher.match_pair(
|
|
215
|
+
if args := rule.matcher.match_pair(device_norm, neighbor_norm):
|
|
216
216
|
found.append(MatchedDirectPair(
|
|
217
217
|
handler=rule.handler,
|
|
218
218
|
port_processor=rule.port_processor,
|
|
@@ -222,7 +222,7 @@ class MeshRulesRegistry:
|
|
|
222
222
|
match_left=args[0],
|
|
223
223
|
match_right=args[1],
|
|
224
224
|
))
|
|
225
|
-
if args := rule.matcher.match_pair(
|
|
225
|
+
if args := rule.matcher.match_pair(neighbor_norm, device_norm):
|
|
226
226
|
found.append(MatchedDirectPair(
|
|
227
227
|
handler=rule.handler,
|
|
228
228
|
port_processor=rule.port_processor,
|
|
@@ -238,11 +238,11 @@ class MeshRulesRegistry:
|
|
|
238
238
|
|
|
239
239
|
def lookup_indirect(self, device: str, devices: list[str]) -> list[MatchedIndirectPair]:
|
|
240
240
|
found = []
|
|
241
|
-
|
|
241
|
+
device_norm = self._normalize_host(device)
|
|
242
242
|
for other_device in devices:
|
|
243
|
-
|
|
243
|
+
other_device_norm = self._normalize_host(other_device)
|
|
244
244
|
for rule in self.indirect_rules:
|
|
245
|
-
if args := rule.matcher.match_pair(
|
|
245
|
+
if args := rule.matcher.match_pair(device_norm, other_device_norm):
|
|
246
246
|
found.append(MatchedIndirectPair(
|
|
247
247
|
handler=rule.handler,
|
|
248
248
|
direct_order=True,
|
|
@@ -251,7 +251,7 @@ class MeshRulesRegistry:
|
|
|
251
251
|
match_left=args[0],
|
|
252
252
|
match_right=args[1],
|
|
253
253
|
))
|
|
254
|
-
if args := rule.matcher.match_pair(
|
|
254
|
+
if args := rule.matcher.match_pair(other_device_norm, device_norm):
|
|
255
255
|
found.append(MatchedIndirectPair(
|
|
256
256
|
handler=rule.handler,
|
|
257
257
|
direct_order=False,
|
|
@@ -266,9 +266,9 @@ class MeshRulesRegistry:
|
|
|
266
266
|
|
|
267
267
|
def lookup_virtual(self, device: str) -> list[MatchedVirtualPair]:
|
|
268
268
|
found = []
|
|
269
|
-
|
|
269
|
+
device_norm = self._normalize_host(device)
|
|
270
270
|
for rule in self.virtual_rules:
|
|
271
|
-
if args := rule.matcher.match_one(
|
|
271
|
+
if args := rule.matcher.match_one(device_norm):
|
|
272
272
|
found.append(MatchedVirtualPair(
|
|
273
273
|
handler=rule.handler,
|
|
274
274
|
match=args,
|
|
@@ -280,9 +280,9 @@ class MeshRulesRegistry:
|
|
|
280
280
|
|
|
281
281
|
def lookup_global(self, device: str) -> list[MatchedGlobal]:
|
|
282
282
|
found = []
|
|
283
|
-
|
|
283
|
+
device_norm = self._normalize_host(device)
|
|
284
284
|
for rule in self.global_rules:
|
|
285
|
-
if args := rule.matcher.match_one(
|
|
285
|
+
if args := rule.matcher.match_one(device_norm):
|
|
286
286
|
found.append(MatchedGlobal(
|
|
287
287
|
handler=rule.handler,
|
|
288
288
|
match=args,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from annet.annlib.types import Op
|
|
2
|
+
|
|
3
|
+
from annet.rulebook import common
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def mtu(rule, key, diff, **kwargs):
|
|
7
|
+
"""
|
|
8
|
+
Удаляем mtu без указания значения
|
|
9
|
+
"""
|
|
10
|
+
if diff[Op.REMOVED]:
|
|
11
|
+
yield (False, "no mtu", None)
|
|
12
|
+
elif diff[Op.ADDED]:
|
|
13
|
+
yield from common.default(rule, key, diff, **kwargs)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def sflow(rule, key, diff, **kwargs):
|
|
17
|
+
"""
|
|
18
|
+
Команда sflow sampling-rate * direction ingress max-header-size *
|
|
19
|
+
сносится без указания sampling-rate и max-header-size
|
|
20
|
+
"""
|
|
21
|
+
result = common.default(rule, key, diff, **kwargs)
|
|
22
|
+
for op, cmd, ch in result:
|
|
23
|
+
if diff[Op.REMOVED]:
|
|
24
|
+
if "ingress" in diff[Op.REMOVED][0]["row"]:
|
|
25
|
+
yield (op, "no sflow sampling-rate direction ingress", ch)
|
|
26
|
+
elif "egress" in diff[Op.REMOVED][0]["row"]:
|
|
27
|
+
yield (op, "no sflow sampling-rate direction egress", ch)
|
|
28
|
+
else:
|
|
29
|
+
yield (op, cmd, ch)
|
|
30
|
+
return result
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def lldp(rule, key, diff, **kwargs):
|
|
34
|
+
"""
|
|
35
|
+
Не удаляем все что начинается с set, т.к. set перезаписывает предыдущий конфиг
|
|
36
|
+
"""
|
|
37
|
+
result = common.default(rule, key, diff, **kwargs)
|
|
38
|
+
for op, cmd, ch in result:
|
|
39
|
+
if diff[Op.REMOVED] and "set lldp" in cmd:
|
|
40
|
+
pass
|
|
41
|
+
else:
|
|
42
|
+
yield (op, cmd, ch)
|
annet/rulebook/texts/b4com.rul
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Операторы:
|
|
2
|
-
# * Один аргумент в
|
|
3
|
-
# ~ Несколько аргументов (минимум один) в
|
|
2
|
+
# * Один аргумент в no
|
|
3
|
+
# ~ Несколько аргументов (минимум один) в no
|
|
4
4
|
#
|
|
5
5
|
# Параметры:
|
|
6
6
|
# %global Команда действует на любом уровне ниже
|
|
@@ -11,8 +11,13 @@
|
|
|
11
11
|
# Сделано в основном для того чтобы генерировать специальные команды для наливки
|
|
12
12
|
# -----
|
|
13
13
|
# Physical
|
|
14
|
+
sflow *
|
|
15
|
+
|
|
14
16
|
interface */(ce|xe)[0-9\/]+$/ %logic=common.permanent %diff_logic=cisco.iface.diff
|
|
15
17
|
ipv6 address *
|
|
16
|
-
mtu *
|
|
18
|
+
mtu * %logic=b4com.iface.mtu
|
|
19
|
+
sflow * %logic=b4com.iface.sflow
|
|
17
20
|
lldp-agent
|
|
18
|
-
|
|
21
|
+
~ %global %logic=b4com.iface.lldp
|
|
22
|
+
!dcbx *
|
|
23
|
+
!exit
|
|
@@ -32,7 +32,7 @@ undo route-distinguisher *
|
|
|
32
32
|
dialog: Warning: All VPN targets of this EVPN instance and all EVPN address family-related configurations under BGP will be deleted. Continue? [Y/N]: ::: Y
|
|
33
33
|
|
|
34
34
|
save
|
|
35
|
-
dialog: Warning: The current configuration will be written to the device. Continue
|
|
35
|
+
dialog: /Warning: The current configuration will be written to the device. Continue\? \[Y/N\]:?/ ::: Y
|
|
36
36
|
dialog: Warning: The current configuration will be written to the device. Are you sure to continue? [Y/N]: ::: Y
|
|
37
37
|
dialog: Are you sure to continue?[Y/N] ::: Y
|
|
38
38
|
dialog: Are you sure to continue? [Y/N] ::: Y
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: annet
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.35
|
|
4
4
|
Summary: annet
|
|
5
5
|
Home-page: https://github.com/annetutil/annet
|
|
6
6
|
License: MIT
|
|
@@ -23,7 +23,7 @@ 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.12; extra == "netbox"
|
|
27
27
|
Dynamic: home-page
|
|
28
28
|
Dynamic: license
|
|
29
29
|
Dynamic: provides-extra
|
|
@@ -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=M2gtOJMsua8SlUySc57VtG71-TmydxP1bthQEcUVpNo,9528
|
|
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
|
|
@@ -34,13 +34,13 @@ annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NM
|
|
|
34
34
|
annet/adapters/netbox/common/client.py,sha256=PaxHG4W9H8_uunIwMBNYkLq4eQJYoO6p6gY-ciQs7Nc,2563
|
|
35
35
|
annet/adapters/netbox/common/manufacturer.py,sha256=LAPT6OlV_ew96GhtwNrCpeiT0IGrg2_9__MMdZk431U,1733
|
|
36
36
|
annet/adapters/netbox/common/models.py,sha256=Xq6Dc3kY9_QyvS9DiKEq1AxjTxiF4qEKhs1EMtBw-k4,7384
|
|
37
|
-
annet/adapters/netbox/common/query.py,sha256=
|
|
37
|
+
annet/adapters/netbox/common/query.py,sha256=mv5WlU6gGge8gUYwloXDXEABmfP5teYyq8DyBtGdFkw,1761
|
|
38
38
|
annet/adapters/netbox/common/status_client.py,sha256=XXx0glomaBaglmkUEy6YtFOxQQkHb59CDA0h1I-IhxM,592
|
|
39
39
|
annet/adapters/netbox/common/storage_opts.py,sha256=5tt6wxUUJTIzNbOVXMnYBwZedNAIqYlve3YWl6GdbZM,1197
|
|
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=noG-Lt1ZRiXm_uJE8xTRBsEvnEWcB1p2Uszuz6zECiI,12721
|
|
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
|
|
@@ -55,7 +55,7 @@ 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=DBl8iU2K_tydteB_a8mXUlgrv_Na3UXSJschuhyjsqs,6284
|
|
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
|
|
@@ -66,7 +66,7 @@ annet/annlib/rbparser/ordering.py,sha256=DiKqyY8Khz-5MTxNF1GSNtZgtyKwT3YYCXpahIP
|
|
|
66
66
|
annet/annlib/rbparser/platform.py,sha256=hnxznTfV9txXi1PkR1hZrprTrQJvlwgqXVL8vXkYmv4,1558
|
|
67
67
|
annet/annlib/rbparser/syntax.py,sha256=iZ7Y-4QQBw4L3UtjEh54qisiRDhobl7HZxFNdP8mi54,3577
|
|
68
68
|
annet/annlib/rulebook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
69
|
-
annet/annlib/rulebook/common.py,sha256=
|
|
69
|
+
annet/annlib/rulebook/common.py,sha256=hqwmmNofm5q2f-hV2usMY-IPMeiANLth28tZcRBYJTw,16640
|
|
70
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
|
|
@@ -83,13 +83,13 @@ annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
|
83
83
|
annet/generators/common/initial.py,sha256=qYBxXFhyOPy34cxc6hsIXseod-lYCmmbuNHpM0uteY0,1244
|
|
84
84
|
annet/mesh/__init__.py,sha256=lcgdnBIxc2MAN7Er1bcErEKPqrjWO4uIp_1FldMXTYg,557
|
|
85
85
|
annet/mesh/basemodel.py,sha256=E6NTOneiMDwB1NCpjDRECoaeQ0f3n_fmTLnKTrSHTU4,4917
|
|
86
|
-
annet/mesh/device_models.py,sha256=
|
|
87
|
-
annet/mesh/executor.py,sha256=
|
|
86
|
+
annet/mesh/device_models.py,sha256=aFbwVWNSDBcph_Kvv6qZT-uUz0Tbg3z45EvP4i1z2ao,3600
|
|
87
|
+
annet/mesh/executor.py,sha256=6NBi7KBZzKf_92TgxypkoBN0WhZt1egXx0qALzo9Cq8,17497
|
|
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=9vn5ENiEZqOZFRFSOJReT8E3E2GzBte628mkmS3cplI,2770
|
|
91
91
|
annet/mesh/port_processor.py,sha256=RHiMS5W8qoDkTKiarQ748bcr8bNx4g_R4Y4vZg2k4TU,478
|
|
92
|
-
annet/mesh/registry.py,sha256=
|
|
92
|
+
annet/mesh/registry.py,sha256=xmWF7yxWXmwqX2_jyMAKrbGd2G9sjb4rYDx4Xk61QKc,9607
|
|
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
|
|
@@ -118,6 +118,7 @@ annet/rulebook/aruba/ap_env.py,sha256=5fUVLhXH4-0DAtv8t0yoqJUibaRMuzF8Q7mGFzNsEN
|
|
|
118
118
|
annet/rulebook/aruba/misc.py,sha256=O0p_wsR07gtB8rm1eJvJ7VYnGm5T8Uau_SVKR9FVInI,234
|
|
119
119
|
annet/rulebook/b4com/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
120
120
|
annet/rulebook/b4com/file.py,sha256=zK7RwBk1YaVoDSFSg1u7Pt8u0Fk3nhhu27aJRngemwc,137
|
|
121
|
+
annet/rulebook/b4com/iface.py,sha256=0MXq01fhgd0ZxrS99TMxEP9qqIW2ofadsIBthCZVPTc,1392
|
|
121
122
|
annet/rulebook/cisco/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
122
123
|
annet/rulebook/cisco/iface.py,sha256=WISkzjp_G7WKPpg098FCIm4b7ipOxtRLOQbu-7gMUL0,1792
|
|
123
124
|
annet/rulebook/cisco/misc.py,sha256=zgKdWGmjRYmvq58dh7Lbn7ofwSYZoISgXsUh5lkGKF8,2318
|
|
@@ -142,11 +143,11 @@ annet/rulebook/texts/aruba.order,sha256=ZMakkn0EJ9zomgY6VssoptJImrHrUmYnCqivzLBF
|
|
|
142
143
|
annet/rulebook/texts/aruba.rul,sha256=zvGVpoYyJvMoL0fb1NQ8we_GCLZXno8nwWpZIOScLQQ,2584
|
|
143
144
|
annet/rulebook/texts/b4com.deploy,sha256=SVX8-yLHM90tJC4M-ekpGuGM1aQZW3euSGyg67l--R0,781
|
|
144
145
|
annet/rulebook/texts/b4com.order,sha256=G3aToAIHHzKzDCM3q7_lyr9wJvuVOXVbVvF3wm5PiTE,707
|
|
145
|
-
annet/rulebook/texts/b4com.rul,sha256=
|
|
146
|
+
annet/rulebook/texts/b4com.rul,sha256=OiQroBySk33he1YNN3jOn5sGL_WDBJEejDI6F7jIWo0,1224
|
|
146
147
|
annet/rulebook/texts/cisco.deploy,sha256=Hu0NkcGv3f1CWUrnbzI3eQOPXJxtH4NNOPRV68IrW4U,1226
|
|
147
148
|
annet/rulebook/texts/cisco.order,sha256=OvNHMNqkCc-DN2dEjLCTKv_7ZhiaHt4q2X4Y4Z8dvR4,1901
|
|
148
149
|
annet/rulebook/texts/cisco.rul,sha256=jgL5_xnSwd_H4E8cx4gcneSvJC5W1zz6_BWSb64iuxI,3017
|
|
149
|
-
annet/rulebook/texts/huawei.deploy,sha256=
|
|
150
|
+
annet/rulebook/texts/huawei.deploy,sha256=uUsZCHUrC5Zyb_MePrR5svnE1QyGQlg7UxcKf00sJyg,10451
|
|
150
151
|
annet/rulebook/texts/huawei.order,sha256=ENllPX4kO6xNw2mUQcx11yhxo3tKStZ5mUyc0C6s3d0,10657
|
|
151
152
|
annet/rulebook/texts/huawei.rul,sha256=02Fi1RG4YYea2clHCluBuJDKNbT0hS9jtsk6_h6GK8k,12958
|
|
152
153
|
annet/rulebook/texts/juniper.rul,sha256=EmtrEJZesnmc2nXjURRD2G0WOq4zLluI_PNupKhSOJs,2654
|
|
@@ -175,10 +176,10 @@ annet_generators/rpl_example/generator.py,sha256=zndIGfV4ZlTxPgAGYs7bMQvTc_tYScO
|
|
|
175
176
|
annet_generators/rpl_example/items.py,sha256=Ez1RF5YhcXNCusBmeApIjRL3rBlMazNZd29Gpw1_IsA,766
|
|
176
177
|
annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
|
|
177
178
|
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.
|
|
179
|
+
annet-0.16.35.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
|
|
180
|
+
annet-0.16.35.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
|
|
181
|
+
annet-0.16.35.dist-info/METADATA,sha256=XiL8MGmXK4eW_mXEUkn6P6AoDJYlKpqdzAeom1JFr8Q,854
|
|
182
|
+
annet-0.16.35.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
183
|
+
annet-0.16.35.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
|
|
184
|
+
annet-0.16.35.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
|
|
185
|
+
annet-0.16.35.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|