annet 0.7__py3-none-any.whl → 0.9__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/annlib/jsontools.py CHANGED
@@ -87,3 +87,26 @@ def apply_patch(content: Optional[bytes], patch_bytes: bytes) -> bytes:
87
87
 
88
88
  new_contents = format_json(new_doc, stable=True).encode()
89
89
  return new_contents
90
+
91
+
92
+ def apply_acl_filters(content: Dict[str, Any], filters: List[str]) -> Dict[str, Any]:
93
+ result = {}
94
+ for f in filters:
95
+ pointer = jsonpointer.JsonPointer(f)
96
+
97
+ try:
98
+ part = pointer.get(copy.deepcopy(content))
99
+
100
+ sub_tree = result
101
+ for i in pointer.get_parts():
102
+ if i not in sub_tree:
103
+ sub_tree[i] = {}
104
+ sub_tree = sub_tree[i]
105
+
106
+ patch = jsonpatch.JsonPatch([{"op": "add", "path": f, "value": part}])
107
+ result = patch.apply(result)
108
+ except jsonpointer.JsonPointerException:
109
+ # no value found in content by the pointer, skip the ACL item
110
+ continue
111
+
112
+ return result
annet/gen.py CHANGED
@@ -182,6 +182,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
182
182
  if not ctx.args.no_acl:
183
183
  acl_rules = generators.compile_acl_text(res.acl_text(), device.hw.vendor)
184
184
  old = (old and patching.apply_acl(old, acl_rules))
185
+
185
186
  new = patching.apply_acl(
186
187
  new,
187
188
  acl_rules,
@@ -215,6 +216,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
215
216
 
216
217
  new_files = {}
217
218
  safe_new_files = {}
219
+ safe_new_json_fragment_files = {}
218
220
  if not ctx.no_new:
219
221
  if device in ctx.fetched_packages:
220
222
  if ctx.args.required_packages_check:
@@ -232,10 +234,39 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
232
234
 
233
235
  entire_results = res.entire_results
234
236
  json_fragment_results = res.json_fragment_results
237
+ old_json_fragment_files = old_files.json_fragment_files
238
+
235
239
  new_files = res.new_files()
236
- new_json_fragment_files = res.new_json_fragment_files(old_files.json_fragment_files)
240
+ new_json_fragment_files = res.new_json_fragment_files(old_json_fragment_files)
241
+
242
+ filters: List[str] = []
243
+ filters_text = build_filter_text(filterer, device, ctx.stdin, ctx.args, ctx.config)
244
+ if filters_text:
245
+ filters = filters_text.split("\n")
246
+
247
+ for file_name in new_json_fragment_files:
248
+ new_json_fragment_files = _update_json_config(
249
+ new_json_fragment_files,
250
+ file_name,
251
+ jsontools.apply_acl_filters(new_json_fragment_files[file_name][0], filters)
252
+ )
253
+ for file_name in old_json_fragment_files:
254
+ old_json_fragment_files = _update_json_config(
255
+ old_json_fragment_files,
256
+ file_name,
257
+ jsontools.apply_acl_filters(old_json_fragment_files[file_name][0], filters)
258
+ )
259
+
237
260
  if ctx.args.acl_safe:
238
261
  safe_new_files = res.new_files(safe=True)
262
+ safe_new_json_fragment_files = res.new_json_fragment_files(old_json_fragment_files, safe=True)
263
+ if filters:
264
+ for file_name in safe_new_json_fragment_files:
265
+ safe_new_json_fragment_files = _update_json_config(
266
+ safe_new_json_fragment_files,
267
+ file_name,
268
+ jsontools.apply_acl_filters(safe_new_json_fragment_files[file_name][0], filters)
269
+ )
239
270
 
240
271
  if ctx.args.profile:
241
272
  perf = res.perf_mesures()
@@ -253,7 +284,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
253
284
  new_files=new_files,
254
285
  partial_result=partial_results,
255
286
  entire_result=entire_results,
256
- old_json_fragment_files=old_files.json_fragment_files,
287
+ old_json_fragment_files=old_json_fragment_files,
257
288
  new_json_fragment_files=new_json_fragment_files,
258
289
  json_fragment_result=json_fragment_results,
259
290
  implicit_rules=implicit_rules,
@@ -262,10 +293,18 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
262
293
  safe_old=safe_old,
263
294
  safe_new=safe_new,
264
295
  safe_new_files=safe_new_files,
296
+ safe_new_json_fragment_files=safe_new_json_fragment_files,
265
297
  filter_acl_rules=filter_acl_rules,
266
298
  )
267
299
 
268
300
 
301
+ def _update_json_config(json_files, file_name, new_config):
302
+ file = list(json_files[file_name])
303
+ file[0] = new_config
304
+ json_files[file_name] = tuple(file)
305
+ return json_files
306
+
307
+
269
308
  @dataclasses.dataclass
270
309
  class DeviceDownloadedFiles:
271
310
  # map file path to file content for entire generators
@@ -180,9 +180,8 @@ class RunGeneratorResult:
180
180
  def new_json_fragment_files(
181
181
  self,
182
182
  old_files: Dict[str, Optional[str]],
183
- safe: bool = False, # pylint: disable=unused-argument
183
+ safe: bool = False,
184
184
  ) -> Dict[str, Tuple[Any, Optional[str]]]:
185
- # TODO: safe
186
185
  files: Dict[str, Tuple[Any, Optional[str]]] = {}
187
186
  reload_prios: Dict[str, int] = {}
188
187
  for generator_result in self.json_fragment_results.values():
@@ -194,7 +193,12 @@ class RunGeneratorResult:
194
193
  files[filepath] = ({}, None)
195
194
  previous_config: Dict[str, Any] = files[filepath][0]
196
195
  new_fragment = generator_result.config
197
- new_config = jsontools.apply_json_fragment(previous_config, new_fragment, generator_result.acl)
196
+
197
+ if safe:
198
+ new_config = jsontools.apply_json_fragment(previous_config, new_fragment, generator_result.acl_safe)
199
+ else:
200
+ new_config = jsontools.apply_json_fragment(previous_config, new_fragment, generator_result.acl)
201
+
198
202
  if filepath in reload_prios and reload_prios[filepath] > generator_result.reload_prio:
199
203
  _, reload_cmd = files[filepath]
200
204
  else:
@@ -296,8 +300,8 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
296
300
  for gen in gens:
297
301
  try:
298
302
  result = _run_partial_generator(gen, run_args)
299
- except NotSupportedDevice:
300
- logger.info("generator %s is not supported for this device, skip generator for this devices!", gen)
303
+ except NotSupportedDevice as exc:
304
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
301
305
  continue
302
306
 
303
307
  if not result:
@@ -317,13 +321,17 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
317
321
 
318
322
 
319
323
  @tracing.function(name="run_partial_generator")
320
- def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> GeneratorPartialResult:
324
+ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> Optional[GeneratorPartialResult]:
321
325
  logger = get_logger(generator=_make_generator_ctx(gen))
322
326
  device = run_args.device
323
327
  output = ""
324
328
  config = odict()
325
329
  safe_config = odict()
326
330
 
331
+ if not gen.supports_device(device):
332
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
333
+ return None
334
+
327
335
  span = tracing_connector.get().get_current_span()
328
336
  if span:
329
337
  tracing_connector.get().set_device_attributes(span, run_args.device)
@@ -439,8 +447,8 @@ def run_file_generators(
439
447
  raise RuntimeError(f"Unknown generator class type: cls={gen.__class__} TYPE={gen.__class__.TYPE}")
440
448
  try:
441
449
  result = run_generator_fn(gen, device, storage)
442
- except NotSupportedDevice:
443
- logger.info("generator %s is not supported for this device", gen)
450
+ except NotSupportedDevice as exc:
451
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
444
452
  continue
445
453
  if result:
446
454
  add_result_fn(result)
@@ -450,33 +458,34 @@ def run_file_generators(
450
458
 
451
459
  @tracing.function(min_duration="0.5")
452
460
  def _run_entire_generator(gen: "Entire", device: "Device", storage: Storage) -> Optional[GeneratorResult]:
461
+ logger = get_logger(generator=_make_generator_ctx(gen))
462
+ if not gen.supports_device(device):
463
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
464
+ return
465
+
453
466
  span = tracing_connector.get().get_current_span()
454
467
  if span:
455
468
  tracing_connector.get().set_device_attributes(span, device)
456
469
  tracing_connector.get().set_dimensions_attributes(span, gen, device)
457
470
 
458
- logger = get_logger(generator=_make_generator_ctx(gen))
459
471
  path = gen.path(device)
460
- if path:
461
- logger.info("Generating ENTIRE ...")
462
-
463
- with GeneratorPerfMesurer(gen, storage, trace_min_duration="0.5") as pm:
464
- output = gen(device)
465
-
466
- reload_cmds = gen.get_reload_cmds(device)
467
- prio = gen.prio
468
-
469
- return GeneratorEntireResult(
470
- name=gen.__class__.__name__,
471
- tags=gen.TAGS,
472
- path=path,
473
- output=output,
474
- reload=reload_cmds,
475
- prio=prio,
476
- perf=pm.last_result,
477
- is_safe=gen.is_safe(device),
478
- )
479
- return None
472
+ if not path:
473
+ raise RuntimeError("entire generator should return non-empty path")
474
+
475
+ logger.info("Generating ENTIRE ...")
476
+ with GeneratorPerfMesurer(gen, storage, trace_min_duration="0.5") as pm:
477
+ output = gen(device)
478
+
479
+ return GeneratorEntireResult(
480
+ name=gen.__class__.__name__,
481
+ tags=gen.TAGS,
482
+ path=path,
483
+ output=output,
484
+ reload=gen.get_reload_cmds(device),
485
+ prio=gen.prio,
486
+ perf=pm.last_result,
487
+ is_safe=gen.is_safe(device),
488
+ )
480
489
 
481
490
 
482
491
  def _make_generator_ctx(gen):
@@ -489,34 +498,41 @@ def _run_json_fragment_generator(
489
498
  storage: Storage,
490
499
  ) -> Optional[GeneratorResult]:
491
500
  logger = get_logger(generator=_make_generator_ctx(gen))
501
+ if not gen.supports_device(device):
502
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
503
+
492
504
  path = gen.path(device)
505
+ if not path:
506
+ raise RuntimeError("json fragment generator should return non-empty path")
493
507
 
494
508
  acl_item_or_list_of_items = gen.acl(device)
509
+ safe_acl_item_or_list_of_items = gen.acl_safe(device)
510
+ if not acl_item_or_list_of_items:
511
+ raise RuntimeError("json fragment generator should return non-empty acl")
495
512
  if isinstance(acl_item_or_list_of_items, list):
496
513
  acl = acl_item_or_list_of_items
497
514
  else:
498
515
  acl = [acl_item_or_list_of_items]
516
+ if isinstance(safe_acl_item_or_list_of_items, list):
517
+ acl_safe = safe_acl_item_or_list_of_items
518
+ else:
519
+ acl_safe = [safe_acl_item_or_list_of_items]
499
520
 
500
- if path:
501
- logger.info("Generating JSON_FRAGMENT ...")
502
-
503
- with GeneratorPerfMesurer(gen, storage) as pm:
504
- config = gen(device)
505
-
506
- reload_cmds = gen.get_reload_cmds(device)
507
-
508
- return GeneratorJSONFragmentResult(
509
- name=gen.__class__.__name__,
510
- tags=gen.TAGS,
511
- path=path,
512
- acl=acl,
513
- config=config,
514
- reload=reload_cmds,
515
- perf=pm.last_result,
516
- is_safe=gen.is_safe(device),
517
- reload_prio=gen.reload_prio,
518
- )
519
- return None
521
+ logger.info("Generating JSON_FRAGMENT ...")
522
+ with GeneratorPerfMesurer(gen, storage) as pm:
523
+ config = gen(device)
524
+ reload_cmds = gen.get_reload_cmds(device)
525
+ return GeneratorJSONFragmentResult(
526
+ name=gen.__class__.__name__,
527
+ tags=gen.TAGS,
528
+ path=path,
529
+ acl=acl,
530
+ acl_safe=acl_safe,
531
+ config=config,
532
+ reload=reload_cmds,
533
+ perf=pm.last_result,
534
+ reload_prio=gen.reload_prio,
535
+ )
520
536
 
521
537
 
522
538
  def _get_generators(module_paths: Union[List[str], dict], storage, device=None):
@@ -587,7 +603,7 @@ class BaseGenerator:
587
603
  TYPE: str
588
604
  TAGS: list[str]
589
605
 
590
- def supports_vendor(self, vendor: str) -> bool: # pylint: disable=unused-argument
606
+ def supports_device(self, device: Device) -> bool: # pylint: disable=unused-argument
591
607
  return True
592
608
 
593
609
 
@@ -681,36 +697,36 @@ class PartialGenerator(TreeGenerator):
681
697
  self._annotations = []
682
698
  self._annotation_module = self.__class__.__module__ or ""
683
699
 
684
- def supports_vendor(self, vendor: str) -> bool:
700
+ def supports_device(self, device: Device) -> bool:
685
701
  if self.__class__.run is PartialGenerator.run:
686
- return hasattr(self, f"run_{vendor}")
702
+ return bool(self._get_vendor_func(device.hw.vendor, "run"))
687
703
  else:
688
704
  return True
689
705
 
690
706
  def acl(self, device):
691
- if hasattr(self, "acl_" + device.hw.vendor):
692
- return getattr(self, "acl_" + device.hw.vendor)(device)
707
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl")
708
+ if acl_func:
709
+ return acl_func(device)
693
710
 
694
711
  def acl_safe(self, device):
695
- if hasattr(self, "acl_safe_" + device.hw.vendor):
696
- return getattr(self, "acl_safe_" + device.hw.vendor)(device)
712
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl_safe")
713
+ if acl_func:
714
+ return acl_func(device)
697
715
 
698
716
  def run(self, device) -> Iterable[Union[str, tuple]]:
699
- if hasattr(self, "run_" + device.hw.vendor):
700
- return getattr(self, "run_" + device.hw.vendor)(device)
701
- logger = get_logger()
702
- logger.info(
703
- "generator %s is not supported for vendor %s",
704
- self,
705
- device.hw.vendor,
706
- )
707
- return iter(())
717
+ run_func = self._get_vendor_func(device.hw.vendor, "run")
718
+ if run_func:
719
+ return run_func(device)
708
720
 
709
721
  def get_user_runner(self, device):
710
722
  if self.__class__.run is not PartialGenerator.run:
711
723
  return self.run
712
- elif hasattr(self, "run_" + device.hw.vendor):
713
- return getattr(self, "run_" + device.hw.vendor)
724
+ return self._get_vendor_func(device.hw.vendor, "run")
725
+
726
+ def _get_vendor_func(self, vendor: str, func_name: str):
727
+ attr_name = f"{func_name}_{vendor}"
728
+ if hasattr(self, attr_name):
729
+ return getattr(self, attr_name)
714
730
  return None
715
731
 
716
732
  # =====
@@ -790,6 +806,9 @@ class Entire(BaseGenerator):
790
806
  self.prio = 100
791
807
  self.__device = None
792
808
 
809
+ def supports_device(self, device: Device):
810
+ return bool(self.path(device))
811
+
793
812
  def run(self, device) -> Union[None, str, Iterable[Union[str, tuple]]]:
794
813
  raise NotImplementedError
795
814
 
@@ -907,6 +926,9 @@ class JSONFragment(TreeGenerator):
907
926
  if not hasattr(self, "reload_prio"):
908
927
  self.reload_prio = 100
909
928
 
929
+ def supports_device(self, device: Device):
930
+ return bool(self.path(device))
931
+
910
932
  def path(self, device: Device) -> Optional[str]:
911
933
  raise NotImplementedError("Required PATH for JSON_FRAGMENT generator")
912
934
 
@@ -922,14 +944,17 @@ class JSONFragment(TreeGenerator):
922
944
  """
923
945
  raise NotImplementedError("Required ACL for JSON_FRAGMENT generator")
924
946
 
947
+ def acl_safe(self, device: Device) -> Union[str, List[str]]:
948
+ """
949
+ Restrict the generator to a specified safe ACL using JSON Pointer syntax.
950
+
951
+ Expected ACL to be a list of strings, but a single string is also allowed.
952
+ """
953
+ raise NotImplementedError("Required ACL for JSON_FRAGMENT generator")
954
+
925
955
  def run(self, device: Device):
926
956
  raise NotImplementedError
927
957
 
928
- # pylint: disable=unused-argument
929
- def is_safe(self, device: Device) -> bool:
930
- """Output gen results when --acl-safe flag is used"""
931
- return False
932
-
933
958
  def get_reload_cmds(self, device: Device) -> str:
934
959
  ret = self.reload(device) or ""
935
960
  return ret
annet/types.py CHANGED
@@ -126,20 +126,20 @@ class GeneratorJSONFragmentResult:
126
126
  tags: List[str],
127
127
  path: str,
128
128
  acl: List[str],
129
+ acl_safe: List[str],
129
130
  config: Dict[str, Any],
130
131
  reload: str,
131
132
  perf: GeneratorPerf,
132
- is_safe: bool,
133
133
  reload_prio: int,
134
134
  ):
135
135
  self.name = name
136
136
  self.tags = tags
137
137
  self.path = path
138
138
  self.acl = acl
139
+ self.acl_safe = acl_safe
139
140
  self.config = config
140
141
  self.reload = reload
141
142
  self.perf = perf
142
- self.is_safe = is_safe
143
143
  self.reload_prio = reload_prio
144
144
 
145
145
 
@@ -171,6 +171,7 @@ class OldNewResult:
171
171
  safe_old=None,
172
172
  safe_new=None,
173
173
  safe_new_files=None,
174
+ safe_new_json_fragment_files=None,
174
175
  filter_acl_rules=None
175
176
  ):
176
177
  self.device: Device = device
@@ -193,6 +194,7 @@ class OldNewResult:
193
194
  self.safe_old: MutableMapping = safe_old if safe_old else OrderedDict()
194
195
  self.safe_new: MutableMapping = safe_new if safe_new else OrderedDict()
195
196
  self.safe_new_files: MutableMapping = safe_new_files if safe_new_files else {}
197
+ self.safe_new_json_fragment_files: MutableMapping = safe_new_json_fragment_files or {}
196
198
 
197
199
  self.filter_acl_rules: Optional[MutableMapping] = filter_acl_rules
198
200
 
@@ -221,5 +223,7 @@ class OldNewResult:
221
223
  return self.new_files
222
224
 
223
225
  def get_new_file_fragments(self, safe: bool = False) -> Dict[str, Tuple[Any, Optional[str]]]: # pylint: disable=unused-argument
224
- # TODO: safe
226
+ if safe:
227
+ return self.safe_new_json_fragment_files
228
+
225
229
  return self.new_json_fragment_files
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annet
3
- Version: 0.7
3
+ Version: 0.9
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -8,7 +8,7 @@ annet/deploy.py,sha256=B8E0P_VvCrS2URjFvgmUiIkHp95g7pAWfmT34igaDeo,18893
8
8
  annet/diff.py,sha256=zLcaCnb4lZRUb7frpH1CstQ3kacRcCblZs1uLG8J5lk,3391
9
9
  annet/executor.py,sha256=bw7COJNtssuxIqbQehlHYJnkdUeYvvL8WO5B-c67XE0,19040
10
10
  annet/filtering.py,sha256=F4ZKUCU3Yb1RlL2zKdyHtVUaWPzjWF4vWyMcdygKNYk,852
11
- annet/gen.py,sha256=WC3ZMjTKtUCRXE4yy-Z3n5MOm3LkLJbXvspyfW8BeVs,31426
11
+ annet/gen.py,sha256=FFIzfDudMihS824wmTPMuFTOWRwn0EZac-VxITrlyTw,33068
12
12
  annet/hardware.py,sha256=HYPDfji_GdRn5S0_0fl4rvM7byOY9aHxG6VMAtsLaxE,1090
13
13
  annet/implicit.py,sha256=QigK4uoxvrFho2h9yFjOq1d9rLsC5xFlAU4bKkCuMWk,5139
14
14
  annet/lib.py,sha256=zQIfNBg7fAAk2BHbHAqy_McAiXhz0RqpBAcfdU-6pAA,3742
@@ -20,7 +20,7 @@ annet/storage.py,sha256=V1AQDGxaAEKC5EXOcPy2DqglR_sUdp3l_AS_2c1CFfA,2299
20
20
  annet/tabparser.py,sha256=ZjiI43ZVbrpMVR8qNbTbNh_U3oZ26VDc_2Y9wkkDkYA,874
21
21
  annet/text_term_format.py,sha256=CHb6viv45vmYl-SK1A1vyPHGhaEni6jVybBusaQnct8,2813
22
22
  annet/tracing.py,sha256=ndpM-au1c88uBBpOuH_z52qWZL773edYozNyys_wA68,4044
23
- annet/types.py,sha256=f0iwSJGSQ7GvWeXgXqBCoxzYNezMa4YASQEHJzWUrWU,6962
23
+ annet/types.py,sha256=wT1z1DM-hcjfHizmLxAe9aUi4uhuQ13POiV_MTygUJM,7158
24
24
  annet/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
26
  annet/adapters/netbox/provider.py,sha256=OM7Hq2vnMHNVBfubJvx2qJlMYm3VvKhomdLMNO8YnLQ,1024
@@ -44,7 +44,7 @@ annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
44
44
  annet/annlib/diff.py,sha256=UPt3kYFQdQdVVy3ePYzNHPAxMVmHxCrCnZnMCV6ou2Q,4587
45
45
  annet/annlib/errors.py,sha256=jBcSFzY6Vj-FxR__vqjFm-87AwYQ0xHuAopTirii5AU,287
46
46
  annet/annlib/filter_acl.py,sha256=0w1VF6WcONiTYTQh0yWi6_j9rCTc_kMLAUMr0hbdkNU,7203
47
- annet/annlib/jsontools.py,sha256=X4gtKcnDArnzKcZDJmRKcZGuUgi3nEFSVKdGPi2uGDw,2644
47
+ annet/annlib/jsontools.py,sha256=_astuRFlt4KlDrneAbuL-HWt1ceu0pWp13skGoV0k-c,3359
48
48
  annet/annlib/lib.py,sha256=eJ4hcVuQ6pdYBzLs4YKCHFtq45idOfZCYp92XfF7_QI,15317
49
49
  annet/annlib/output.py,sha256=_SjJ6G6bejvnTKqNHw6xeio0FT9oO3OIkLaOC3cEga4,7569
50
50
  annet/annlib/patching.py,sha256=Gh8uUjFyYND9TJBBQH-DH6-AwFiiR-dXVXOisMS7elg,19784
@@ -68,7 +68,7 @@ annet/annlib/rulebook/common.py,sha256=9kCZwZpsH5UliF2OzaN9nLs2eLlz_d__4L7_oZ3Sr
68
68
  annet/api/__init__.py,sha256=VooP9u9f6e8vF7FPUE-r0JAGqCjck9-2b0dVNWAtoLM,33752
69
69
  annet/configs/context.yml,sha256=nt4QnzYzrG2hqmaWOUsab2wgFl-CXmFSzbto6rqqFt4,291
70
70
  annet/configs/logging.yaml,sha256=Hu42lRK248dssp9TgkbHCaZNl0E6f4IChGb0XaQnTVo,970
71
- annet/generators/__init__.py,sha256=LQoqGz_0imhtxvyuwBrBpB6iSuzS2S0UxNJEpO77BiM,33829
71
+ annet/generators/__init__.py,sha256=RqB-7CeN4KCKbzB2eh5Rqs6rATOf1obYELQVksCcMTA,34964
72
72
  annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
73
  annet/generators/common/initial.py,sha256=XI7uWLMuLrHC-uXm38oRbM1YVuELAvMHLZFHM9x5IF8,1229
74
74
  annet/rulebook/__init__.py,sha256=14IpOfTbeJtre7JKrfXVYiH0qAXsUSOL7AatUFmSQs0,3847
@@ -120,10 +120,10 @@ annet/rulebook/texts/routeros.rul,sha256=ipfxjj0mjFef6IsUlupqx4BY_Je_OUb8u_U1019
120
120
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
121
  annet_generators/example/__init__.py,sha256=zVd8_DrXuOASrNzg2Ab94rPyvJff83L-_HppDFxnUjM,228
122
122
  annet_generators/example/lldp.py,sha256=68CLrK7vxTQQy9XIBxtywuEdBNlIlfXGYj8_wYWs5UI,1146
123
- annet-0.7.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
124
- annet-0.7.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
125
- annet-0.7.dist-info/METADATA,sha256=hkf7Zql0Tekm9u10L6exfVSV6LjhQ7367c7VZ8WxOw8,691
126
- annet-0.7.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
127
- annet-0.7.dist-info/entry_points.txt,sha256=yHimujIzR2bwNIbb--MTs_GpXiAve89Egpu2LlgTEkg,119
128
- annet-0.7.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
129
- annet-0.7.dist-info/RECORD,,
123
+ annet-0.9.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
124
+ annet-0.9.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
125
+ annet-0.9.dist-info/METADATA,sha256=nJXCVJQ0VuNz_gie2qlv_sw867onqsnOvZOV_7CdfG8,691
126
+ annet-0.9.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
127
+ annet-0.9.dist-info/entry_points.txt,sha256=yHimujIzR2bwNIbb--MTs_GpXiAve89Egpu2LlgTEkg,119
128
+ annet-0.9.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
129
+ annet-0.9.dist-info/RECORD,,
File without changes
File without changes
File without changes