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.

@@ -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
- if self.exact_host_filter:
169
- devices = self.netbox.dcim_all_devices(name__ie=query.globs).results
170
- else:
171
- query = _hostname_dot_hack(query)
172
- devices = [
173
- device
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
- for subquery in query.globs:
266
- if subquery.strip() in device_data.name:
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(netbox_query: NetboxQuery) -> NetboxQuery:
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 NetboxQuery(raw_query)
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.CS4146U": "^[Bb]4com B4T-CS4146U.*"
172
+ "B4com.CS4164U": "^[Bb]4com B4T-CS4164U.*"
171
173
  }
@@ -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("exit"))
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 red in opts.ipv4_unicast.redistributes:
294
- if red.policy:
295
- yield red.policy
296
- for red in opts.ipv6_unicast.redistributes:
297
- if red.policy:
298
- yield red.policy
299
- for red in opts.ipv4_labeled_unicast.redistributes:
300
- if red.policy:
301
- yield red.policy
302
- for red in opts.ipv6_labeled_unicast.redistributes:
303
- if red.policy:
304
- yield red.policy
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]:
@@ -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 = 0
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
- device = self._normalize_host(device)
211
+ device_norm = self._normalize_host(device)
212
212
  for neighbor in neighbors:
213
- neighbor = self._normalize_host(neighbor)
213
+ neighbor_norm = self._normalize_host(neighbor)
214
214
  for rule in self.direct_rules:
215
- if args := rule.matcher.match_pair(device, neighbor):
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(neighbor, device):
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
- device = self._normalize_host(device)
241
+ device_norm = self._normalize_host(device)
242
242
  for other_device in devices:
243
- other_device = self._normalize_host(other_device)
243
+ other_device_norm = self._normalize_host(other_device)
244
244
  for rule in self.indirect_rules:
245
- if args := rule.matcher.match_pair(device, other_device):
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(other_device, device):
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
- device = self._normalize_host(device)
269
+ device_norm = self._normalize_host(device)
270
270
  for rule in self.virtual_rules:
271
- if args := rule.matcher.match_one(device):
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
- device = self._normalize_host(device)
283
+ device_norm = self._normalize_host(device)
284
284
  for rule in self.global_rules:
285
- if args := rule.matcher.match_one(device):
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)
@@ -1,6 +1,6 @@
1
1
  # Операторы:
2
- # * Один аргумент в undo
3
- # ~ Несколько аргументов (минимум один) в undo
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
- ~ %rewrite %global
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? [Y/N] ::: Y
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.33
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.10; extra == "netbox"
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=I9DQpf3LU71RBk9kCUdA-RnGkA3eORngYkC3_obsLmk,9669
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=ziUFM7cUEbEIf3k1szTll4aO-OCUa-2Ogxbebi7Tegs,646
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=G-nj-iuNN5inR0tY2XnjFnXXFYrgMZeTJ9DQ7OgjK44,10336
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=Zt9pj6h2clMi0i26bGVsx0gqQl8vCSk6iLajIYWeKX4,6192
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=bx_Iwui-JJeyctUPF1OsEll0Aa-IQZadBPQjaeuoWgw,16638
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=KkbD8tuWfnh4gR8iOV9C7ihLR4jxjyuF52AU2DPPLXA,3627
87
- annet/mesh/executor.py,sha256=0RLsdtldozoFgDGFuhU5mdckuMwGg0y_DFIji1RfTQg,16969
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=BCRIOrINP0krcgvF59uFJqyhmSviQ03GSzWpxLVYlIg,9527
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=5mqyUg_oLRSny2iH6QdhfDWVu6kzgDURtlSATD7DFno,1056
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=azEC6_jQRzwnTSrNgag0hHh6L7hezS_eMk6ZDZfWyXI,10444
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.33.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
179
- annet-0.16.33.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
180
- annet-0.16.33.dist-info/METADATA,sha256=4FDrHTUVwwM6GIzoAZ96e3XHrSiSHvSShAY7v7jw4qg,854
181
- annet-0.16.33.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
182
- annet-0.16.33.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
183
- annet-0.16.33.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
184
- annet-0.16.33.dist-info/RECORD,,
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,,