annet 0.6__py3-none-any.whl → 0.8__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of annet might be problematic. Click here for more details.

Files changed (36) hide show
  1. annet/adapters/__init__.py +0 -0
  2. annet/adapters/netbox/__init__.py +0 -0
  3. annet/adapters/netbox/common/__init__.py +0 -0
  4. annet/adapters/netbox/common/client.py +87 -0
  5. annet/adapters/netbox/common/manufacturer.py +62 -0
  6. annet/adapters/netbox/common/models.py +98 -0
  7. annet/adapters/netbox/common/query.py +23 -0
  8. annet/adapters/netbox/common/status_client.py +24 -0
  9. annet/adapters/netbox/common/storage_opts.py +14 -0
  10. annet/adapters/netbox/provider.py +34 -0
  11. annet/adapters/netbox/v24/__init__.py +0 -0
  12. annet/adapters/netbox/v24/api_models.py +72 -0
  13. annet/adapters/netbox/v24/client.py +59 -0
  14. annet/adapters/netbox/v24/storage.py +190 -0
  15. annet/adapters/netbox/v37/__init__.py +0 -0
  16. annet/adapters/netbox/v37/api_models.py +37 -0
  17. annet/adapters/netbox/v37/client.py +62 -0
  18. annet/adapters/netbox/v37/storage.py +143 -0
  19. annet/annlib/jsontools.py +23 -0
  20. annet/api/__init__.py +18 -6
  21. annet/cli.py +6 -2
  22. annet/cli_args.py +10 -0
  23. annet/diff.py +1 -2
  24. annet/gen.py +34 -4
  25. annet/generators/__init__.py +78 -67
  26. annet/output.py +3 -1
  27. {annet-0.6.dist-info → annet-0.8.dist-info}/METADATA +3 -1
  28. {annet-0.6.dist-info → annet-0.8.dist-info}/RECORD +33 -17
  29. {annet-0.6.dist-info → annet-0.8.dist-info}/WHEEL +1 -1
  30. annet-0.8.dist-info/entry_points.txt +5 -0
  31. {annet-0.6.dist-info → annet-0.8.dist-info}/top_level.txt +0 -1
  32. annet-0.6.dist-info/entry_points.txt +0 -6
  33. annet_nbexport/__init__.py +0 -220
  34. annet_nbexport/main.py +0 -46
  35. {annet-0.6.dist-info → annet-0.8.dist-info}/AUTHORS +0 -0
  36. {annet-0.6.dist-info → annet-0.8.dist-info}/LICENSE +0 -0
@@ -296,8 +296,8 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
296
296
  for gen in gens:
297
297
  try:
298
298
  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)
299
+ except NotSupportedDevice as exc:
300
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
301
301
  continue
302
302
 
303
303
  if not result:
@@ -317,13 +317,17 @@ def run_partial_generators(gens: List["PartialGenerator"], run_args: GeneratorPa
317
317
 
318
318
 
319
319
  @tracing.function(name="run_partial_generator")
320
- def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> GeneratorPartialResult:
320
+ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> Optional[GeneratorPartialResult]:
321
321
  logger = get_logger(generator=_make_generator_ctx(gen))
322
322
  device = run_args.device
323
323
  output = ""
324
324
  config = odict()
325
325
  safe_config = odict()
326
326
 
327
+ if not gen.supports_device(device):
328
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
329
+ return None
330
+
327
331
  span = tracing_connector.get().get_current_span()
328
332
  if span:
329
333
  tracing_connector.get().set_device_attributes(span, run_args.device)
@@ -439,8 +443,8 @@ def run_file_generators(
439
443
  raise RuntimeError(f"Unknown generator class type: cls={gen.__class__} TYPE={gen.__class__.TYPE}")
440
444
  try:
441
445
  result = run_generator_fn(gen, device, storage)
442
- except NotSupportedDevice:
443
- logger.info("generator %s is not supported for this device", gen)
446
+ except NotSupportedDevice as exc:
447
+ logger.info("generator %s raised unsupported error: %r", gen, exc)
444
448
  continue
445
449
  if result:
446
450
  add_result_fn(result)
@@ -450,33 +454,34 @@ def run_file_generators(
450
454
 
451
455
  @tracing.function(min_duration="0.5")
452
456
  def _run_entire_generator(gen: "Entire", device: "Device", storage: Storage) -> Optional[GeneratorResult]:
457
+ logger = get_logger(generator=_make_generator_ctx(gen))
458
+ if not gen.supports_device(device):
459
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
460
+ return
461
+
453
462
  span = tracing_connector.get().get_current_span()
454
463
  if span:
455
464
  tracing_connector.get().set_device_attributes(span, device)
456
465
  tracing_connector.get().set_dimensions_attributes(span, gen, device)
457
466
 
458
- logger = get_logger(generator=_make_generator_ctx(gen))
459
467
  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
468
+ if not path:
469
+ raise RuntimeError("entire generator should return non-empty path")
470
+
471
+ logger.info("Generating ENTIRE ...")
472
+ with GeneratorPerfMesurer(gen, storage, trace_min_duration="0.5") as pm:
473
+ output = gen(device)
474
+
475
+ return GeneratorEntireResult(
476
+ name=gen.__class__.__name__,
477
+ tags=gen.TAGS,
478
+ path=path,
479
+ output=output,
480
+ reload=gen.get_reload_cmds(device),
481
+ prio=gen.prio,
482
+ perf=pm.last_result,
483
+ is_safe=gen.is_safe(device),
484
+ )
480
485
 
481
486
 
482
487
  def _make_generator_ctx(gen):
@@ -489,34 +494,36 @@ def _run_json_fragment_generator(
489
494
  storage: Storage,
490
495
  ) -> Optional[GeneratorResult]:
491
496
  logger = get_logger(generator=_make_generator_ctx(gen))
497
+ if not gen.supports_device(device):
498
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
499
+
492
500
  path = gen.path(device)
501
+ if not path:
502
+ raise RuntimeError("json fragment generator should return non-empty path")
493
503
 
494
504
  acl_item_or_list_of_items = gen.acl(device)
505
+ if not acl_item_or_list_of_items:
506
+ raise RuntimeError("json fragment generator should return non-empty acl")
495
507
  if isinstance(acl_item_or_list_of_items, list):
496
508
  acl = acl_item_or_list_of_items
497
509
  else:
498
510
  acl = [acl_item_or_list_of_items]
499
511
 
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
512
+ logger.info("Generating JSON_FRAGMENT ...")
513
+ with GeneratorPerfMesurer(gen, storage) as pm:
514
+ config = gen(device)
515
+ reload_cmds = gen.get_reload_cmds(device)
516
+ return GeneratorJSONFragmentResult(
517
+ name=gen.__class__.__name__,
518
+ tags=gen.TAGS,
519
+ path=path,
520
+ acl=acl,
521
+ config=config,
522
+ reload=reload_cmds,
523
+ perf=pm.last_result,
524
+ is_safe=gen.is_safe(device),
525
+ reload_prio=gen.reload_prio,
526
+ )
520
527
 
521
528
 
522
529
  def _get_generators(module_paths: Union[List[str], dict], storage, device=None):
@@ -538,15 +545,7 @@ def _get_generators(module_paths: Union[List[str], dict], storage, device=None):
538
545
  module = importlib.import_module(module_path)
539
546
  if hasattr(module, "get_generators"):
540
547
  generators: List[BaseGenerator] = module.get_generators(storage)
541
- if device is None:
542
- res_generators += generators
543
- else:
544
- logger = get_logger()
545
- for gen in generators:
546
- if gen.supports_vendor(device.hw.vendor):
547
- res_generators.append(gen)
548
- else:
549
- logger.info("generator %s does not support device vendor %s, skipping", gen, device.hw.vendor)
548
+ res_generators += generators
550
549
  return res_generators
551
550
 
552
551
 
@@ -595,7 +594,7 @@ class BaseGenerator:
595
594
  TYPE: str
596
595
  TAGS: list[str]
597
596
 
598
- def supports_vendor(self, vendor: str) -> bool: # pylint: disable=unused-argument
597
+ def supports_device(self, device: Device) -> bool: # pylint: disable=unused-argument
599
598
  return True
600
599
 
601
600
 
@@ -689,30 +688,36 @@ class PartialGenerator(TreeGenerator):
689
688
  self._annotations = []
690
689
  self._annotation_module = self.__class__.__module__ or ""
691
690
 
692
- def supports_vendor(self, vendor: str) -> bool:
691
+ def supports_device(self, device: Device) -> bool:
693
692
  if self.__class__.run is PartialGenerator.run:
694
- return hasattr(self, f"run_{vendor}")
693
+ return bool(self._get_vendor_func(device.hw.vendor, "run"))
695
694
  else:
696
695
  return True
697
696
 
698
697
  def acl(self, device):
699
- if hasattr(self, "acl_" + device.hw.vendor):
700
- return getattr(self, "acl_" + device.hw.vendor)(device)
698
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl")
699
+ if acl_func:
700
+ return acl_func(device)
701
701
 
702
702
  def acl_safe(self, device):
703
- if hasattr(self, "acl_safe_" + device.hw.vendor):
704
- return getattr(self, "acl_safe_" + device.hw.vendor)(device)
703
+ acl_func = self._get_vendor_func(device.hw.vendor, "acl_safe")
704
+ if acl_func:
705
+ return acl_func(device)
705
706
 
706
707
  def run(self, device) -> Iterable[Union[str, tuple]]:
707
- if hasattr(self, "run_" + device.hw.vendor):
708
- return getattr(self, "run_" + device.hw.vendor)(device)
709
- return iter(())
708
+ run_func = self._get_vendor_func(device.hw.vendor, "run")
709
+ if run_func:
710
+ return run_func(device)
710
711
 
711
712
  def get_user_runner(self, device):
712
713
  if self.__class__.run is not PartialGenerator.run:
713
714
  return self.run
714
- elif hasattr(self, "run_" + device.hw.vendor):
715
- return getattr(self, "run_" + device.hw.vendor)
715
+ return self._get_vendor_func(device.hw.vendor, "run")
716
+
717
+ def _get_vendor_func(self, vendor: str, func_name: str):
718
+ attr_name = f"{func_name}_{vendor}"
719
+ if hasattr(self, attr_name):
720
+ return getattr(self, attr_name)
716
721
  return None
717
722
 
718
723
  # =====
@@ -792,6 +797,9 @@ class Entire(BaseGenerator):
792
797
  self.prio = 100
793
798
  self.__device = None
794
799
 
800
+ def supports_device(self, device: Device):
801
+ return bool(self.path(device))
802
+
795
803
  def run(self, device) -> Union[None, str, Iterable[Union[str, tuple]]]:
796
804
  raise NotImplementedError
797
805
 
@@ -909,6 +917,9 @@ class JSONFragment(TreeGenerator):
909
917
  if not hasattr(self, "reload_prio"):
910
918
  self.reload_prio = 100
911
919
 
920
+ def supports_device(self, device: Device):
921
+ return bool(self.path(device))
922
+
912
923
  def path(self, device: Device) -> Optional[str]:
913
924
  raise NotImplementedError("Required PATH for JSON_FRAGMENT generator")
914
925
 
annet/output.py CHANGED
@@ -133,7 +133,9 @@ class OutputDriverBasic(OutputDriver):
133
133
  ret = []
134
134
  fqdns = {}
135
135
  if args:
136
- with storage_connector.get().storage()(args) as storage:
136
+ connector = storage_connector.get()
137
+ storage_opts = connector.opts().from_cli_opts(args)
138
+ with connector.storage()(storage_opts) as storage:
137
139
  fqdns = storage.resolve_fdnds_by_query(args.query)
138
140
  for (assignment, exc) in fail.items():
139
141
  label = assignment
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: annet
3
- Version: 0.6
3
+ Version: 0.8
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -21,4 +21,6 @@ Requires-Dist: contextlog >=1.1
21
21
  Requires-Dist: valkit >=0.1.4
22
22
  Requires-Dist: aiohttp >=3.8.4
23
23
  Requires-Dist: yarl >=1.8.2
24
+ Requires-Dist: adaptix ==3.0.0b2
25
+ Requires-Dist: dataclass-rest ==0.4
24
26
 
@@ -1,18 +1,18 @@
1
1
  annet/__init__.py,sha256=oyElxAW97sHgQccGafhaWVBveBn1gSjjdP_xHROaRLg,2139
2
2
  annet/annet.py,sha256=TMdEuM7GJQ4TjRVmuK3bCTZN-21lxjQ9sXqEdILUuBk,725
3
3
  annet/argparse.py,sha256=MoBD0LPnHdA6HU7z1uQNArYlkD92znoeGIFTMnS4dRM,12608
4
- annet/cli.py,sha256=HoVD9DDZZurxG7YHrU8d2kDJS2tBOCMsmMgJFgarNKI,8284
5
- annet/cli_args.py,sha256=FuyHLANmTXZl4W9Q4pfT3mMtRiVswM88L5WFhSzu6eA,15871
4
+ annet/cli.py,sha256=bkAaHKBYmvdaFawm_KOM7CehMm18RTR8TkMYAxm-7lo,8464
5
+ annet/cli_args.py,sha256=rmBTJ64sljQWFTIbhTEBZRIo08Gi9NAoFZrMermZEuY,16152
6
6
  annet/connectors.py,sha256=M2NeyFgBEGJ_tMR9HgQ4cpli0nBciqZJDVMQXEueMws,1908
7
7
  annet/deploy.py,sha256=B8E0P_VvCrS2URjFvgmUiIkHp95g7pAWfmT34igaDeo,18893
8
- annet/diff.py,sha256=0QG_3Ur5k7ajRuWUrM089yt_Fxss06OTBSUE3h54Nxw,3460
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=aDrTS3X9Lo47F7Mj21PaGzn27TIgAehcsJ5k5uihB30,31246
11
+ annet/gen.py,sha256=MI82tdc-r3leiyp4Oa35PBsrfj0jNKdbASRnQ8Rk39g,32336
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
15
- annet/output.py,sha256=fEKQ_EncOeZzzlO43tctZVbi37QS1j2m4MYxbvpLxAo,6984
15
+ annet/output.py,sha256=XsX3o4vxe7Ayw7ocY-pV5VY-9-D5_Q_yeWy2ZY1Hubk,7090
16
16
  annet/parallel.py,sha256=hLkzEht0KhzmzUWDdO4QFYQHzhxs3wPlTA8DxbB2ziw,17160
17
17
  annet/patching.py,sha256=nILbY5oJajN0b1j3f0HEJm05H3HVThnWvB7vDVh7UQw,559
18
18
  annet/reference.py,sha256=B8mH8VUMcecPnzULiTVb_kTQ7jQrCL7zp4pfIZQa5fk,4035
@@ -21,12 +21,30 @@ 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
23
  annet/types.py,sha256=f0iwSJGSQ7GvWeXgXqBCoxzYNezMa4YASQEHJzWUrWU,6962
24
+ annet/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
+ annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
26
+ annet/adapters/netbox/provider.py,sha256=OM7Hq2vnMHNVBfubJvx2qJlMYm3VvKhomdLMNO8YnLQ,1024
27
+ annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ annet/adapters/netbox/common/client.py,sha256=-lWZmphD-OPuLIHNKhW_h2bnjrVaiyKYAD_MUPasEbo,2483
29
+ annet/adapters/netbox/common/manufacturer.py,sha256=UH_tEKT3GXC8WSm15q0xxXRE7aj0b0icgwmR--PRWBs,1771
30
+ annet/adapters/netbox/common/models.py,sha256=Kti1T54emv6_FUkhgRiSIo_ZdmvWZZTRvooHO2_H1bM,1731
31
+ annet/adapters/netbox/common/query.py,sha256=OgUuF-bvshpoBUkrOs0tsMUAhjTsttzx3VV30ryFl0Y,577
32
+ annet/adapters/netbox/common/status_client.py,sha256=3oozmdfewPXODEkNrDRfcz_pTeXRVDQiNiD-AE6Lrug,567
33
+ annet/adapters/netbox/common/storage_opts.py,sha256=rl_0pr3VzmOy6PDZIUMkKSBfJh90gD9TFL3yBhK_8ME,337
34
+ annet/adapters/netbox/v24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ annet/adapters/netbox/v24/api_models.py,sha256=xG3n9ILYdD2jNxav8IH6Bm6fX4vbHPUkHBLze4Ly1bc,1314
36
+ annet/adapters/netbox/v24/client.py,sha256=-r91zVTThn12mN1zBj3XNyqlP6BGOrAPm_GbBT2bXpI,1497
37
+ annet/adapters/netbox/v24/storage.py,sha256=SCTSOea8OHiLAPmBXIop80mIUOLuA48rLpKlitYJt1k,5620
38
+ annet/adapters/netbox/v37/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
39
+ annet/adapters/netbox/v37/api_models.py,sha256=O59bYesIWlxHx3C2TW4TQnfN9YMsE_rbiw2mYsormcc,886
40
+ annet/adapters/netbox/v37/client.py,sha256=GgFIV5IGlyEplvwOLLd1BxtsbPWkeHEtL22kWV_X0b0,1668
41
+ annet/adapters/netbox/v37/storage.py,sha256=5jp2AgalijTdFkMnjvGGGnxqjwk-bn6QWzTRTfNyyHM,4216
24
42
  annet/annlib/__init__.py,sha256=fT1l4xV5fqqg8HPw9HqmZVN2qwS8i6X1aIm2zGDjxKY,252
25
43
  annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
26
44
  annet/annlib/diff.py,sha256=UPt3kYFQdQdVVy3ePYzNHPAxMVmHxCrCnZnMCV6ou2Q,4587
27
45
  annet/annlib/errors.py,sha256=jBcSFzY6Vj-FxR__vqjFm-87AwYQ0xHuAopTirii5AU,287
28
46
  annet/annlib/filter_acl.py,sha256=0w1VF6WcONiTYTQh0yWi6_j9rCTc_kMLAUMr0hbdkNU,7203
29
- annet/annlib/jsontools.py,sha256=X4gtKcnDArnzKcZDJmRKcZGuUgi3nEFSVKdGPi2uGDw,2644
47
+ annet/annlib/jsontools.py,sha256=TJazOP-1njwbGN7iIz8oGYgWPdry4XfKLjpzNYxdvjg,3364
30
48
  annet/annlib/lib.py,sha256=eJ4hcVuQ6pdYBzLs4YKCHFtq45idOfZCYp92XfF7_QI,15317
31
49
  annet/annlib/output.py,sha256=_SjJ6G6bejvnTKqNHw6xeio0FT9oO3OIkLaOC3cEga4,7569
32
50
  annet/annlib/patching.py,sha256=Gh8uUjFyYND9TJBBQH-DH6-AwFiiR-dXVXOisMS7elg,19784
@@ -47,10 +65,10 @@ annet/annlib/rbparser/platform.py,sha256=iyqy4A-6vUKM4j6hrOA6tsgWBXk7LcvkFrS2QHg
47
65
  annet/annlib/rbparser/syntax.py,sha256=eEUmszwPjdw57aofUYNQVcaxHPNV9yx9JNapjYY-BGM,3572
48
66
  annet/annlib/rulebook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
49
67
  annet/annlib/rulebook/common.py,sha256=9kCZwZpsH5UliF2OzaN9nLs2eLlz_d__4L7_oZ3SrCw,16054
50
- annet/api/__init__.py,sha256=dF9Dnnr31IOKBP0zV-ZP6z1HcwQ8yGTPOHmFC3GdW7g,33212
68
+ annet/api/__init__.py,sha256=VooP9u9f6e8vF7FPUE-r0JAGqCjck9-2b0dVNWAtoLM,33752
51
69
  annet/configs/context.yml,sha256=nt4QnzYzrG2hqmaWOUsab2wgFl-CXmFSzbto6rqqFt4,291
52
70
  annet/configs/logging.yaml,sha256=Hu42lRK248dssp9TgkbHCaZNl0E6f4IChGb0XaQnTVo,970
53
- annet/generators/__init__.py,sha256=7yp4OxluAMSozXRaDPabDqfyGJDh4YJXKcAb4KYEzyM,34049
71
+ annet/generators/__init__.py,sha256=iOTaIfTQfGoExmSAzqt1RSWP7V1O_pEkuqvjnsEjBkQ,34476
54
72
  annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
73
  annet/generators/common/initial.py,sha256=XI7uWLMuLrHC-uXm38oRbM1YVuELAvMHLZFHM9x5IF8,1229
56
74
  annet/rulebook/__init__.py,sha256=14IpOfTbeJtre7JKrfXVYiH0qAXsUSOL7AatUFmSQs0,3847
@@ -102,12 +120,10 @@ annet/rulebook/texts/routeros.rul,sha256=ipfxjj0mjFef6IsUlupqx4BY_Je_OUb8u_U1019
102
120
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
103
121
  annet_generators/example/__init__.py,sha256=zVd8_DrXuOASrNzg2Ab94rPyvJff83L-_HppDFxnUjM,228
104
122
  annet_generators/example/lldp.py,sha256=68CLrK7vxTQQy9XIBxtywuEdBNlIlfXGYj8_wYWs5UI,1146
105
- annet_nbexport/__init__.py,sha256=dr_q90ziWR20ckM6l-IWI3PqngHZwqrgPns7NDtq5B8,6362
106
- annet_nbexport/main.py,sha256=f65WKdS2xvbttllCe5IYkoenPEy8C4SzvNydhh4ZEVM,1216
107
- annet-0.6.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
108
- annet-0.6.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
109
- annet-0.6.dist-info/METADATA,sha256=OO8K6pmnZ0fyA9DZ-V6kysoqMoOKzlk_ONv36XLACJc,622
110
- annet-0.6.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
111
- annet-0.6.dist-info/entry_points.txt,sha256=g_pJ55300jUXfafHl7izmDJ6RVSo6iW9IaNfysM3Y0w,151
112
- annet-0.6.dist-info/top_level.txt,sha256=uL47QByRwuZV5yWC58gd4IkHM8sVSAJ1pZb-6W-gMfY,38
113
- annet-0.6.dist-info/RECORD,,
123
+ annet-0.8.dist-info/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
124
+ annet-0.8.dist-info/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
125
+ annet-0.8.dist-info/METADATA,sha256=biTv9OtMLS-HnbvanEbTm3B-tYG9SGFV-TSQCtem7cE,691
126
+ annet-0.8.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
127
+ annet-0.8.dist-info/entry_points.txt,sha256=yHimujIzR2bwNIbb--MTs_GpXiAve89Egpu2LlgTEkg,119
128
+ annet-0.8.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
129
+ annet-0.8.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: bdist_wheel (0.43.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,5 @@
1
+ [annet.connectors]
2
+ storage = annet.adapters.netbox.provider:NetboxProvider
3
+
4
+ [console_scripts]
5
+ annet = annet.annet:main
@@ -1,3 +1,2 @@
1
1
  annet
2
2
  annet_generators
3
- annet_nbexport
@@ -1,6 +0,0 @@
1
- [annet.connectors]
2
- storage = annet_nbexport:AnnetNbExportProvder
3
-
4
- [console_scripts]
5
- annet = annet.annet:main
6
- annet_nbexport = annet_nbexport.main:main
@@ -1,220 +0,0 @@
1
- import csv
2
- import dataclasses
3
- import operator
4
- import os
5
- import os.path
6
- import pathlib
7
- from typing import List, Optional
8
-
9
- from annet.storage import Device, Storage, Query, StorageOpts, StorageProvider
10
- from annet.annlib.netdev.views.hardware import HardwareView
11
-
12
-
13
- class AnnetNbExportProvder(StorageProvider):
14
- def storage(self):
15
- return AnnetNbExportStorage
16
-
17
- def opts(self):
18
- return AnnetNbExportStorageOpts
19
-
20
- def query(self):
21
- return AnnetNbExportQuery
22
-
23
-
24
- @dataclasses.dataclass
25
- class AnnetNbExportQuery(Query):
26
- query: List[str]
27
-
28
- @classmethod
29
- def new(cls, query, hosts_range) -> "AnnetNbExportQuery":
30
- if hosts_range is not None:
31
- raise ValueError("host_range is not supported")
32
- return cls(query=query)
33
-
34
- @property
35
- def globs(self):
36
- # We process every query host as a glob
37
- return self.query
38
-
39
-
40
- @dataclasses.dataclass
41
- class AnnetNbExportStorageOpts(StorageOpts):
42
- @classmethod
43
- def from_cli_opts(cls, cli_opts) -> "AnnetNbExportQuery":
44
- return cls()
45
-
46
-
47
- class DeviceNb(Device):
48
- def __init__(self, storage: "Storage", dto: "NetboxDTO", region: str):
49
- self.dto = dto
50
- self.storage = storage
51
- self.region = region
52
-
53
- def __hash__(self):
54
- return hash(self.id)
55
-
56
- def is_pc(self):
57
- return self.dto.manufacturer == "Mellanox"
58
-
59
- @property
60
- def hw(self):
61
- manufacturer = self.dto.manufacturer
62
- model_name = self.dto.model_name
63
- # по какой-то причине модели mellanox SN в нетбоксе называются MSN
64
- # чтобы использовать выгрузку as is и не править devdb.json патчим тут
65
- if manufacturer == "Mellanox" and model_name.startswith("MSN"):
66
- model_name = model_name.replace("MSN", "SN", 1)
67
- hw = _vendor_to_hw(manufacturer + " " + model_name)
68
- assert hw.vendor, "unsupported manufacturer %s" % self.dto.manufacturer
69
- return hw
70
-
71
- @property
72
- def id(self):
73
- return self.dto.name
74
-
75
- @property
76
- def fqdn(self):
77
- return self.dto.name
78
-
79
- @property
80
- def hostname(self):
81
- return self.dto.name
82
-
83
- @property
84
- def neighbours_ids(self):
85
- return set()
86
-
87
- @property
88
- def breed(self):
89
- if self.dto.manufacturer == "Huawei" and self.dto.model_name.startswith("CE"):
90
- return "vrp85"
91
- elif self.dto.manufacturer == "Huawei" and self.dto.model_name.startswith("NE"):
92
- return "vrp85"
93
- elif self.dto.manufacturer == "Huawei":
94
- return "vrp55"
95
- elif self.dto.manufacturer == "Mellanox":
96
- return "cuml2"
97
- elif self.dto.manufacturer == "Juniper":
98
- return "jun10"
99
- elif self.dto.manufacturer == "Cisco":
100
- return "ios12"
101
- elif self.dto.manufacturer == "Adva":
102
- return "adva8"
103
- elif self.dto.manufacturer == "Arista":
104
- return "eos4"
105
- assert False, "unknown manufacturer %s" % self.dto.manufacturer
106
-
107
-
108
- @dataclasses.dataclass
109
- class NetboxDTO:
110
- name: str
111
- device_role: str
112
- tenant: str
113
- manufacturer: str
114
- model_name: str
115
- platform: str
116
- serial: str
117
- asset_tag: str
118
- status: str
119
- site: str
120
- rack_group: str
121
- rack_name: str
122
- position: str
123
- face: str
124
- comments: str
125
-
126
-
127
- class AnnetNbExportStorage(Storage):
128
- def __init__(self, opts: Optional[StorageOpts] = None):
129
- self._dump_dir = os.path.join(os.path.dirname(__file__))
130
-
131
- def __enter__(self):
132
- return self
133
-
134
- def __exit__(self, _, __, ___):
135
- pass
136
-
137
- def resolve_object_ids_by_query(self, query):
138
- ret = []
139
- for device_data in _read_dump(self._dump_dir):
140
- if _match_query(query, device_data):
141
- ret.append(device_data["name"])
142
- return ret
143
-
144
- def resolve_fdnds_by_query(self, query):
145
- return self.resolve_object_ids_by_query(query)
146
-
147
- def make_devices(
148
- self,
149
- query,
150
- preload_neighbors=False,
151
- use_mesh=None,
152
- preload_extra_fields=False,
153
- **kwargs,
154
- ):
155
- ret = []
156
- for file_path, device_data in _read_dump(self._dump_dir):
157
- if _match_query(query, device_data):
158
- ret.append(DeviceNb(self, NetboxDTO(**device_data), region=file_path.parent.name))
159
- return ret
160
-
161
- def get_device(self, obj_id, preload_neighbors=False, use_mesh=None, **kwargs) -> Device:
162
- for file_path, device_data in _read_dump(self._dump_dir):
163
- if device_data["name"] == obj_id:
164
- return DeviceNb(self, NetboxDTO(**device_data), region=file_path.parent.name)
165
-
166
- def flush_perf(self):
167
- pass
168
-
169
-
170
- def _read_dump(dump_dir):
171
- for (dirpath, _dirnames, filenames) in os.walk(dump_dir):
172
- for filename in filenames:
173
- if filename != "devices.csv":
174
- continue
175
- with open(os.path.join(dirpath, filename)) as fh:
176
- file_path = pathlib.Path(os.path.join(dirpath, filename))
177
- for device_data in csv.DictReader(fh):
178
- yield file_path, device_data
179
-
180
-
181
- def _match_query(query, device_data) -> bool:
182
- for subquery in query.globs:
183
- matches = []
184
- for field_filter in subquery.split("@"):
185
- if "=" in field_filter:
186
- field, value = field_filter.split("=")
187
- field = field.strip()
188
- value = value.strip()
189
- op = operator.eq
190
- else:
191
- field = "name"
192
- value = field_filter.strip()
193
- op = operator.contains
194
- if field in device_data and op(device_data[field], value):
195
- matches.append(True)
196
- else:
197
- matches.append(False)
198
- if all(matches):
199
- return True
200
- return False
201
-
202
-
203
- def _vendor_to_hw(vendor):
204
- hw = HardwareView(
205
- {
206
- "cisco": "Cisco",
207
- "catalyst": "Cisco Catalyst",
208
- "nexus": "Cisco Nexus",
209
- "huawei": "Huawei",
210
- "juniper": "Juniper",
211
- "arista": "Arista",
212
- "pc": "PC",
213
- "nokia": "Nokia",
214
- "aruba": "Aruba",
215
- "routeros": "RouterOS",
216
- "ribbon": "Ribbon",
217
- }.get(vendor.lower(), vendor),
218
- None,
219
- )
220
- return hw
annet_nbexport/main.py DELETED
@@ -1,46 +0,0 @@
1
- #!/usr/bin/env python3
2
-
3
- import argparse
4
- import asyncio
5
- import os
6
- import typing
7
-
8
- import aiohttp
9
- import yarl
10
-
11
-
12
- async def download(url: yarl.URL, dest: typing.BinaryIO):
13
- async with aiohttp.ClientSession() as session:
14
- async with session.get(url) as resp:
15
- async for b in resp.content.iter_any():
16
- dest.write(b)
17
-
18
-
19
- async def download_devices(url: yarl.URL, region: str):
20
- basedir = os.path.dirname(__file__)
21
- regiondir = os.path.join(basedir, "data", region)
22
- os.makedirs(regiondir, exist_ok=True)
23
- with open(os.path.join(regiondir, "devices.csv"), "wb") as fh:
24
- await download(url.with_path("/dcim/devices/").with_query("export"), fh)
25
-
26
-
27
- async def amain():
28
- p = argparse.ArgumentParser()
29
- p.add_argument("--region-name", default=None, help="The name of the region to download cvs to")
30
- p.add_argument("netbox_url", type=yarl.URL, help="The URL to access netbox API")
31
- args = p.parse_args()
32
-
33
- if args.region_name is None:
34
- region_name = args.netbox_url.host
35
- else:
36
- region_name = args.region_name
37
-
38
- await download_devices(args.netbox_url, region_name)
39
-
40
-
41
- def main():
42
- asyncio.run(amain())
43
-
44
-
45
- if __name__ == "__main__":
46
- main()
File without changes
File without changes