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.
- annet/adapters/__init__.py +0 -0
- annet/adapters/netbox/__init__.py +0 -0
- annet/adapters/netbox/common/__init__.py +0 -0
- annet/adapters/netbox/common/client.py +87 -0
- annet/adapters/netbox/common/manufacturer.py +62 -0
- annet/adapters/netbox/common/models.py +98 -0
- annet/adapters/netbox/common/query.py +23 -0
- annet/adapters/netbox/common/status_client.py +24 -0
- annet/adapters/netbox/common/storage_opts.py +14 -0
- annet/adapters/netbox/provider.py +34 -0
- annet/adapters/netbox/v24/__init__.py +0 -0
- annet/adapters/netbox/v24/api_models.py +72 -0
- annet/adapters/netbox/v24/client.py +59 -0
- annet/adapters/netbox/v24/storage.py +190 -0
- annet/adapters/netbox/v37/__init__.py +0 -0
- annet/adapters/netbox/v37/api_models.py +37 -0
- annet/adapters/netbox/v37/client.py +62 -0
- annet/adapters/netbox/v37/storage.py +143 -0
- annet/annlib/jsontools.py +23 -0
- annet/api/__init__.py +18 -6
- annet/cli.py +6 -2
- annet/cli_args.py +10 -0
- annet/diff.py +1 -2
- annet/gen.py +34 -4
- annet/generators/__init__.py +78 -67
- annet/output.py +3 -1
- {annet-0.6.dist-info → annet-0.8.dist-info}/METADATA +3 -1
- {annet-0.6.dist-info → annet-0.8.dist-info}/RECORD +33 -17
- {annet-0.6.dist-info → annet-0.8.dist-info}/WHEEL +1 -1
- annet-0.8.dist-info/entry_points.txt +5 -0
- {annet-0.6.dist-info → annet-0.8.dist-info}/top_level.txt +0 -1
- annet-0.6.dist-info/entry_points.txt +0 -6
- annet_nbexport/__init__.py +0 -220
- annet_nbexport/main.py +0 -46
- {annet-0.6.dist-info → annet-0.8.dist-info}/AUTHORS +0 -0
- {annet-0.6.dist-info → annet-0.8.dist-info}/LICENSE +0 -0
annet/generators/__init__.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
691
|
+
def supports_device(self, device: Device) -> bool:
|
|
693
692
|
if self.__class__.run is PartialGenerator.run:
|
|
694
|
-
return
|
|
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
|
-
|
|
700
|
-
|
|
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
|
-
|
|
704
|
-
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
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
|
-
|
|
715
|
-
|
|
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
|
-
|
|
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.
|
|
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=
|
|
5
|
-
annet/cli_args.py,sha256=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
annet-0.
|
|
108
|
-
annet-0.
|
|
109
|
-
annet-0.
|
|
110
|
-
annet-0.
|
|
111
|
-
annet-0.
|
|
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,,
|
annet_nbexport/__init__.py
DELETED
|
@@ -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
|