annet 0.16.34__tar.gz → 0.16.35__tar.gz
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-0.16.34/annet.egg-info → annet-0.16.35}/PKG-INFO +1 -1
- annet-0.16.35/annet/adapters/netbox/common/query.py +61 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/v37/storage.py +79 -42
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/huawei.deploy +1 -1
- {annet-0.16.34 → annet-0.16.35/annet.egg-info}/PKG-INFO +1 -1
- annet-0.16.34/annet/adapters/netbox/common/query.py +0 -26
- {annet-0.16.34 → annet-0.16.35}/AUTHORS +0 -0
- {annet-0.16.34 → annet-0.16.35}/LICENSE +0 -0
- {annet-0.16.34 → annet-0.16.35}/MANIFEST.in +0 -0
- {annet-0.16.34 → annet-0.16.35}/README.md +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/file/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/file/provider.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/client.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/models.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/provider.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annet.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/command.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/diff.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/errors.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/filter_acl.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/jsontools.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/lib.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/db.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/output.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/patching.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/acl.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/platform.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/rulebook/common.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/tabparser.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/annlib/types.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/api/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/argparse.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/bgp_models.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/cli.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/cli_args.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/configs/context.yml +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/configs/logging.yaml +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/connectors.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/deploy.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/diff.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/executor.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/filtering.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/gen.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/base.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/common/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/common/initial.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/entire.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/exceptions.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/jsonfragment.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/partial.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/perf.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/ref.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/generators/result.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/hardware.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/implicit.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/lib.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/basemodel.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/device_models.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/executor.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/match_args.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/models_converter.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/peer_models.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/port_processor.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/mesh/registry.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/output.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/parallel.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/patching.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/reference.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/action.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/condition.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/match_builder.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/policy.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/result.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/routemap.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl/statement_builder.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/aspath.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/community.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/entities.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/execute.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/policy.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rpl_generators/rd.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/arista/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/arista/iface.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/aruba/misc.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/b4com/file.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/b4com/iface.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/cisco/iface.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/cisco/misc.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/common.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/deploying.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/iface.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/misc.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/nexus/iface.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/patching.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/ribbon/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/routeros/file.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/arista.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/arista.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/aruba.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/b4com.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/cisco.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/huawei.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/nexus.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/pc.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/pc.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/routeros.order +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/storage.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/tabparser.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/text_term_format.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/tracing.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet/types.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet.egg-info/SOURCES.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet.egg-info/dependency_links.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet.egg-info/entry_points.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet.egg-info/requires.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet.egg-info/top_level.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/example/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/example/lldp.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/rpl_example/generator.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/rpl_example/items.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-0.16.34 → annet-0.16.35}/requirements.txt +0 -0
- {annet-0.16.34 → annet-0.16.35}/setup.cfg +0 -0
- {annet-0.16.34 → annet-0.16.35}/setup.py +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import cast, List, Union, Iterable, Optional, TypedDict
|
|
4
|
+
|
|
5
|
+
from annet.storage import Query
|
|
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
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class NetboxQuery(Query):
|
|
20
|
+
query: List[str]
|
|
21
|
+
|
|
22
|
+
@classmethod
|
|
23
|
+
def new(
|
|
24
|
+
cls, query: Union[str, Iterable[str]],
|
|
25
|
+
hosts_range: Optional[slice] = None,
|
|
26
|
+
) -> "NetboxQuery":
|
|
27
|
+
if hosts_range is not None:
|
|
28
|
+
raise ValueError("host_range is not supported")
|
|
29
|
+
return cls(query=list(query))
|
|
30
|
+
|
|
31
|
+
@property
|
|
32
|
+
def globs(self):
|
|
33
|
+
# We process every query host as a glob
|
|
34
|
+
return self.query
|
|
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
|
+
|
|
52
|
+
def is_empty(self) -> bool:
|
|
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,32 +199,22 @@ class NetboxStorageV37(Storage):
|
|
|
162
199
|
|
|
163
200
|
return list(device_ids.values())
|
|
164
201
|
|
|
202
|
+
def _record_device(self, device: models.NetboxDevice):
|
|
203
|
+
self._id_devices[device.id] = device
|
|
204
|
+
self._short_name_devices[device.name] = device
|
|
205
|
+
if not self.exact_host_filter:
|
|
206
|
+
short_name = device.name.split(".")[0]
|
|
207
|
+
self._short_name_devices[short_name] = device
|
|
208
|
+
|
|
165
209
|
def _load_devices(self, query: NetboxQuery) -> List[api_models.Device]:
|
|
166
210
|
if not query.globs:
|
|
167
211
|
return []
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
for grp, params in query_groups.items():
|
|
175
|
-
if not params:
|
|
176
|
-
continue
|
|
177
|
-
try:
|
|
178
|
-
new_devices = self.netbox.dcim_all_devices(**{grp: params}).results
|
|
179
|
-
except Exception as e:
|
|
180
|
-
# tag and site lookup returns 400 in case of unknown tag or site
|
|
181
|
-
if "is not one of the available choices" in str(e):
|
|
182
|
-
continue
|
|
183
|
-
raise
|
|
184
|
-
if grp == "name__ic":
|
|
185
|
-
new_devices = [device for device in new_devices if _match_query(query, device)]
|
|
186
|
-
for device in new_devices:
|
|
187
|
-
if device.id not in device_ids:
|
|
188
|
-
device_ids.add(device.id)
|
|
189
|
-
devices.extend(new_devices)
|
|
190
|
-
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
|
+
]
|
|
191
218
|
|
|
192
219
|
def _extend_interfaces(self, interfaces: List[models.Interface]) -> List[models.Interface]:
|
|
193
220
|
extended_ifaces = {
|
|
@@ -238,6 +265,9 @@ class NetboxStorageV37(Storage):
|
|
|
238
265
|
self, obj_id, preload_neighbors=False, use_mesh=None,
|
|
239
266
|
**kwargs,
|
|
240
267
|
) -> models.NetboxDevice:
|
|
268
|
+
if obj_id in self._id_devices:
|
|
269
|
+
return self._id_devices[obj_id]
|
|
270
|
+
|
|
241
271
|
device = self.netbox.dcim_device(obj_id)
|
|
242
272
|
interfaces = self._load_interfaces([device.id])
|
|
243
273
|
neighbours = self._load_neighbours(interfaces)
|
|
@@ -248,6 +278,7 @@ class NetboxStorageV37(Storage):
|
|
|
248
278
|
interfaces=interfaces,
|
|
249
279
|
neighbours=neighbours,
|
|
250
280
|
)
|
|
281
|
+
self._record_device(res)
|
|
251
282
|
return res
|
|
252
283
|
|
|
253
284
|
def flush_perf(self):
|
|
@@ -272,9 +303,18 @@ class NetboxStorageV37(Storage):
|
|
|
272
303
|
return res
|
|
273
304
|
|
|
274
305
|
|
|
275
|
-
def _match_query(query: NetboxQuery, device_data: api_models.Device) -> bool:
|
|
276
|
-
|
|
277
|
-
|
|
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:
|
|
278
318
|
return True
|
|
279
319
|
return False
|
|
280
320
|
|
|
@@ -294,20 +334,17 @@ def _hostname_dot_hack(raw_query: str) -> str:
|
|
|
294
334
|
if isinstance(raw_query, list):
|
|
295
335
|
for i, name in enumerate(raw_query):
|
|
296
336
|
raw_query[i] = add_dot(name)
|
|
337
|
+
elif isinstance(raw_query, str):
|
|
338
|
+
raw_query = add_dot(raw_query)
|
|
297
339
|
|
|
298
340
|
return raw_query
|
|
299
341
|
|
|
300
342
|
|
|
301
|
-
def parse_glob(
|
|
302
|
-
query_groups
|
|
303
|
-
|
|
304
|
-
if
|
|
305
|
-
|
|
306
|
-
if glob_type not in query_groups:
|
|
307
|
-
raise Exception(f"unknown query type: '{glob_type}'")
|
|
308
|
-
if not param:
|
|
309
|
-
raise Exception(f"empty param for '{glob_type}'")
|
|
310
|
-
query_groups[glob_type].append(param)
|
|
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
|
|
311
348
|
else:
|
|
312
|
-
query_groups["name__ic"]
|
|
349
|
+
query_groups["name__ic"] = [_hostname_dot_hack(name) for name in names]
|
|
313
350
|
return query_groups
|
|
@@ -32,7 +32,7 @@ undo route-distinguisher *
|
|
|
32
32
|
dialog: Warning: All VPN targets of this EVPN instance and all EVPN address family-related configurations under BGP will be deleted. Continue? [Y/N]: ::: Y
|
|
33
33
|
|
|
34
34
|
save
|
|
35
|
-
dialog: Warning: The current configuration will be written to the device. Continue
|
|
35
|
+
dialog: /Warning: The current configuration will be written to the device. Continue\? \[Y/N\]:?/ ::: Y
|
|
36
36
|
dialog: Warning: The current configuration will be written to the device. Are you sure to continue? [Y/N]: ::: Y
|
|
37
37
|
dialog: Are you sure to continue?[Y/N] ::: Y
|
|
38
38
|
dialog: Are you sure to continue? [Y/N] ::: Y
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from typing import List, Union, Iterable, Optional
|
|
3
|
-
|
|
4
|
-
from annet.storage import Query
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@dataclass
|
|
8
|
-
class NetboxQuery(Query):
|
|
9
|
-
query: List[str]
|
|
10
|
-
|
|
11
|
-
@classmethod
|
|
12
|
-
def new(
|
|
13
|
-
cls, query: Union[str, Iterable[str]],
|
|
14
|
-
hosts_range: Optional[slice] = None,
|
|
15
|
-
) -> "NetboxQuery":
|
|
16
|
-
if hosts_range is not None:
|
|
17
|
-
raise ValueError("host_range is not supported")
|
|
18
|
-
return cls(query=list(query))
|
|
19
|
-
|
|
20
|
-
@property
|
|
21
|
-
def globs(self):
|
|
22
|
-
# We process every query host as a glob
|
|
23
|
-
return self.query
|
|
24
|
-
|
|
25
|
-
def is_empty(self) -> bool:
|
|
26
|
-
return len(self.query) == 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|