annet 0.15.2__tar.gz → 0.15.3__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.15.2/annet.egg-info → annet-0.15.3}/PKG-INFO +1 -1
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/storage_opts.py +5 -4
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/provider.py +11 -1
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/tabparser.py +3 -2
- {annet-0.15.2 → annet-0.15.3}/annet/api/__init__.py +5 -5
- {annet-0.15.2 → annet-0.15.3}/annet/cli.py +5 -5
- annet-0.15.3/annet/connectors.py +143 -0
- {annet-0.15.2 → annet-0.15.3}/annet/deploy.py +21 -28
- {annet-0.15.2 → annet-0.15.3}/annet/filtering.py +1 -0
- {annet-0.15.2 → annet-0.15.3}/annet/gen.py +4 -4
- {annet-0.15.2 → annet-0.15.3}/annet/hardware.py +1 -0
- {annet-0.15.2 → annet-0.15.3}/annet/output.py +1 -0
- {annet-0.15.2 → annet-0.15.3}/annet/storage.py +6 -17
- {annet-0.15.2 → annet-0.15.3/annet.egg-info}/PKG-INFO +1 -1
- {annet-0.15.2 → annet-0.15.3}/annet.egg-info/entry_points.txt +3 -0
- {annet-0.15.2 → annet-0.15.3}/setup.py +3 -0
- annet-0.15.2/annet/connectors.py +0 -78
- {annet-0.15.2 → annet-0.15.3}/AUTHORS +0 -0
- {annet-0.15.2 → annet-0.15.3}/LICENSE +0 -0
- {annet-0.15.2 → annet-0.15.3}/MANIFEST.in +0 -0
- {annet-0.15.2 → annet-0.15.3}/README.md +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/client.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/models.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/query.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annet.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/command.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/diff.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/errors.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/filter_acl.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/jsontools.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/lib.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/db.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/output.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/patching.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/acl.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/platform.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/rulebook/common.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/annlib/types.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/argparse.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/cli_args.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/configs/context.yml +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/configs/logging.yaml +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/diff.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/executor.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/base.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/common/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/common/initial.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/entire.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/exceptions.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/jsonfragment.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/partial.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/perf.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/ref.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/generators/result.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/implicit.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/lib.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/parallel.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/patching.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/reference.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/arista/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/arista/iface.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/aruba/misc.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/cisco/iface.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/cisco/misc.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/common.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/deploying.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/iface.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/misc.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/nexus/iface.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/patching.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/ribbon/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/arista.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/arista.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/aruba.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/cisco.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/huawei.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/nexus.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/pc.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/pc.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/routeros.order +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/tabparser.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/text_term_format.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/tracing.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet/types.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet.egg-info/SOURCES.txt +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet.egg-info/dependency_links.txt +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet.egg-info/requires.txt +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet.egg-info/top_level.txt +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet_generators/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet_generators/example/__init__.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/annet_generators/example/lldp.py +0 -0
- {annet-0.15.2 → annet-0.15.3}/requirements.txt +0 -0
- {annet-0.15.2 → annet-0.15.3}/setup.cfg +0 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
+
DEFAULT_URL = "http://localhost"
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
class NetboxStorageOpts:
|
|
6
8
|
def __init__(self, url: str, token: str):
|
|
@@ -9,7 +11,6 @@ class NetboxStorageOpts:
|
|
|
9
11
|
|
|
10
12
|
@classmethod
|
|
11
13
|
def parse_params(cls, conf_params: dict[str, str] | None, cli_opts: Any):
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
14
|
+
url = os.getenv("NETBOX_URL") or conf_params.get("url") or DEFAULT_URL
|
|
15
|
+
token = os.getenv("NETBOX_TOKEN", "").strip() or conf_params.get("token") or ""
|
|
16
|
+
return cls(url=url, token=token)
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional
|
|
2
|
+
|
|
1
3
|
from dataclass_rest.exceptions import ClientError
|
|
2
4
|
|
|
3
5
|
from annet.storage import StorageProvider, Storage
|
|
6
|
+
from annet.connectors import AdapterWithName, AdapterWithConfig, T
|
|
4
7
|
from .common.status_client import NetboxStatusClient
|
|
5
8
|
from .common.storage_opts import NetboxStorageOpts
|
|
6
9
|
from .common.query import NetboxQuery
|
|
@@ -23,7 +26,14 @@ def storage_factory(opts: NetboxStorageOpts) -> Storage:
|
|
|
23
26
|
raise ValueError(f"Unsupported version: {status.netbox_version}")
|
|
24
27
|
|
|
25
28
|
|
|
26
|
-
class NetboxProvider(StorageProvider):
|
|
29
|
+
class NetboxProvider(StorageProvider, AdapterWithName, AdapterWithConfig):
|
|
30
|
+
def __init__(self, url: Optional[str] = None, token: Optional[str] = None):
|
|
31
|
+
self.url = url
|
|
32
|
+
self.token = token
|
|
33
|
+
|
|
34
|
+
def with_config(self, **kwargs: Dict[str, Any]) -> T:
|
|
35
|
+
return NetboxProvider(**kwargs)
|
|
36
|
+
|
|
27
37
|
def storage(self):
|
|
28
38
|
return storage_factory
|
|
29
39
|
|
|
@@ -490,10 +490,11 @@ class RosFormatter(CommonFormatter):
|
|
|
490
490
|
rows.append((row, None, row_context))
|
|
491
491
|
|
|
492
492
|
prev_prow = None
|
|
493
|
+
prev_prow_context = {}
|
|
493
494
|
for sub_config, row_group in itertools.groupby(rows, lambda x: x[1]):
|
|
494
495
|
if sub_config is None:
|
|
495
496
|
if prev_prow:
|
|
496
|
-
yield prev_prow
|
|
497
|
+
yield prev_prow, prev_prow_context
|
|
497
498
|
yield BlockBegin, None
|
|
498
499
|
for row, _, row_context in row_group:
|
|
499
500
|
yield row, row_context
|
|
@@ -502,7 +503,7 @@ class RosFormatter(CommonFormatter):
|
|
|
502
503
|
else:
|
|
503
504
|
for row, _, row_context in row_group:
|
|
504
505
|
if context and context.parent and context.parent.row:
|
|
505
|
-
prev_prow = context.parent.
|
|
506
|
+
prev_prow, prev_prow_context = context.parent.current
|
|
506
507
|
prow = f"{context.parent.row} {row}"
|
|
507
508
|
else:
|
|
508
509
|
prow = row
|
|
@@ -218,8 +218,8 @@ def log_host_progress_cb(pool: Parallel, task_result: TaskResult):
|
|
|
218
218
|
stacklevel=2,
|
|
219
219
|
)
|
|
220
220
|
args = cast(cli_args.QueryOptions, pool.args[0])
|
|
221
|
-
connector
|
|
222
|
-
storage_opts = connector.opts().parse_params(
|
|
221
|
+
connector = get_storage()
|
|
222
|
+
storage_opts = connector.opts().parse_params({}, args)
|
|
223
223
|
with connector.storage()(storage_opts) as storage:
|
|
224
224
|
fqdns = storage.resolve_fdnds_by_query(args.query)
|
|
225
225
|
PoolProgressLogger(device_fqdns=fqdns)(pool, task_result)
|
|
@@ -260,7 +260,7 @@ def patch(args: cli_args.ShowPatchOptions, loader: ann_gen.Loader):
|
|
|
260
260
|
""" Сгенерировать патч для устройств """
|
|
261
261
|
global live_configs # pylint: disable=global-statement
|
|
262
262
|
if args.config == "running":
|
|
263
|
-
fetcher = annet.deploy.
|
|
263
|
+
fetcher = annet.deploy.get_fetcher()
|
|
264
264
|
live_configs = fetcher.fetch(loader.devices, processes=args.parallel)
|
|
265
265
|
stdin = args.stdin(filter_acl=args.filter_acl, config=args.config)
|
|
266
266
|
|
|
@@ -434,7 +434,7 @@ class CliDeployerJob(DeployerJob):
|
|
|
434
434
|
self.cmd_lines.extend(["= %s " % device.hostname, ""])
|
|
435
435
|
self.cmd_lines.extend(map(itemgetter(-1), cmds))
|
|
436
436
|
self.cmd_lines.append("")
|
|
437
|
-
deployer_driver = annet.deploy.
|
|
437
|
+
deployer_driver = annet.deploy.get_deployer()
|
|
438
438
|
self.deploy_cmds[device] = deployer_driver.apply_deploy_rulebook(
|
|
439
439
|
device.hw, cmds,
|
|
440
440
|
do_commit=not self.args.dont_commit
|
|
@@ -494,7 +494,7 @@ class PCDeployerJob(DeployerJob):
|
|
|
494
494
|
"generator_types": generator_types,
|
|
495
495
|
}
|
|
496
496
|
self.diffs[device] = upload_files
|
|
497
|
-
deployer_driver = annet.deploy.
|
|
497
|
+
deployer_driver = annet.deploy.get_deployer()
|
|
498
498
|
before, after = deployer_driver.build_configuration_cmdlist(device.hw)
|
|
499
499
|
for cmd in deployer_driver.build_exit_cmdlist(device.hw):
|
|
500
500
|
after.add_cmd(cmd)
|
|
@@ -15,7 +15,7 @@ import yaml
|
|
|
15
15
|
from contextlog import get_logger
|
|
16
16
|
from valkit.python import valid_logging_level
|
|
17
17
|
|
|
18
|
-
from annet.deploy import
|
|
18
|
+
from annet.deploy import get_fetcher, get_deployer
|
|
19
19
|
from annet import api, cli_args, filtering, generators
|
|
20
20
|
from annet.api import collapse_texts, Deployer
|
|
21
21
|
from annet.argparse import ArgParser, subcommand
|
|
@@ -68,8 +68,8 @@ def get_loader(gen_args: cli_args.GenOptions, args: cli_args.QueryOptionsBase):
|
|
|
68
68
|
exit_stack = ExitStack()
|
|
69
69
|
storages = []
|
|
70
70
|
with exit_stack:
|
|
71
|
-
connector,
|
|
72
|
-
storage_opts = connector.opts().parse_params(
|
|
71
|
+
connector, conf_params = get_storage()
|
|
72
|
+
storage_opts = connector.opts().parse_params(conf_params, args)
|
|
73
73
|
storages.append(exit_stack.enter_context(connector.storage()(storage_opts)))
|
|
74
74
|
yield Loader(*storages, args=gen_args, no_empty_warning=args.query.is_empty())
|
|
75
75
|
|
|
@@ -233,8 +233,8 @@ def deploy(args: cli_args.DeployOptions):
|
|
|
233
233
|
|
|
234
234
|
deployer = Deployer(args)
|
|
235
235
|
filterer = filtering.filterer_connector.get()
|
|
236
|
-
fetcher =
|
|
237
|
-
deploy_driver =
|
|
236
|
+
fetcher = get_fetcher()
|
|
237
|
+
deploy_driver = get_deployer()
|
|
238
238
|
|
|
239
239
|
with get_loader(args, args) as loader:
|
|
240
240
|
return api.deploy(
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from functools import cached_property
|
|
4
|
+
from importlib.metadata import entry_points
|
|
5
|
+
from typing import Generic, Optional, Type, TypeVar, List, Dict, Any, Tuple
|
|
6
|
+
import warnings
|
|
7
|
+
from annet.lib import get_context
|
|
8
|
+
|
|
9
|
+
T = TypeVar("T")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Connector(ABC, Generic[T]):
|
|
13
|
+
name: str
|
|
14
|
+
# legacy
|
|
15
|
+
ep_name: str
|
|
16
|
+
ep_group: str = "annet.connectors"
|
|
17
|
+
# right way just to use ep groups
|
|
18
|
+
ep_by_group_only: str = ""
|
|
19
|
+
_classes: Optional[List[Type[T]]] = None
|
|
20
|
+
|
|
21
|
+
def _get_default(self) -> Type[T]:
|
|
22
|
+
raise RuntimeError(f"{self.name} is not set")
|
|
23
|
+
|
|
24
|
+
@cached_property
|
|
25
|
+
def _entry_point(self) -> List[Type[T]]:
|
|
26
|
+
ep = load_entry_point(self.ep_group, self.ep_name)
|
|
27
|
+
if self.ep_by_group_only:
|
|
28
|
+
ep.extend(load_entry_point_new(self.ep_by_group_only))
|
|
29
|
+
return ep
|
|
30
|
+
|
|
31
|
+
def get(self, *args, **kwargs) -> T:
|
|
32
|
+
"""
|
|
33
|
+
Returns connector. If more than one is registered returns random and throw warning
|
|
34
|
+
"""
|
|
35
|
+
if self._classes is None:
|
|
36
|
+
self._classes = self._entry_point or [self._get_default()]
|
|
37
|
+
if not self._classes:
|
|
38
|
+
raise Exception(f"Not found registered class for group={self.ep_group}")
|
|
39
|
+
if len(self._classes) > 1:
|
|
40
|
+
warnings.warn(f"Multiple classes are registered with the group={self.ep_group} but "
|
|
41
|
+
f"{[cls for cls in self._classes]}", UserWarning)
|
|
42
|
+
res = self._classes[0]
|
|
43
|
+
return res(*args, **kwargs)
|
|
44
|
+
|
|
45
|
+
def get_all(self, *args, **kwargs) -> List[T]:
|
|
46
|
+
if self._classes is None:
|
|
47
|
+
self._classes = self._entry_point or [self._get_default()]
|
|
48
|
+
|
|
49
|
+
return [cls(*args, **kwargs) for cls in self._classes]
|
|
50
|
+
|
|
51
|
+
def set(self, cls: Type[T]):
|
|
52
|
+
if self._classes is not None:
|
|
53
|
+
raise RuntimeError(f"Cannot reinitialize value of {self.name}")
|
|
54
|
+
self._classes = [cls]
|
|
55
|
+
|
|
56
|
+
def set_all(self, classes: List[Type[T]]):
|
|
57
|
+
if self._classes is not None:
|
|
58
|
+
raise RuntimeError(f"Cannot reinitialize value of {self.name}")
|
|
59
|
+
self._classes = list(classes)
|
|
60
|
+
|
|
61
|
+
def is_default(self) -> bool:
|
|
62
|
+
return self._classes is self._entry_point is None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class CachedConnector(Connector[T], ABC):
|
|
66
|
+
_cache: Optional[T] = None
|
|
67
|
+
|
|
68
|
+
def get(self, *args, **kwargs) -> T:
|
|
69
|
+
assert not (args or kwargs), "Arguments forwarding is not allowed for cached connectors"
|
|
70
|
+
if self._cache is None:
|
|
71
|
+
self._cache = super().get()
|
|
72
|
+
return self._cache
|
|
73
|
+
|
|
74
|
+
def set(self, cls: Type[T]):
|
|
75
|
+
super().set(cls)
|
|
76
|
+
self._cache = None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def load_entry_point(group: str, name: str):
|
|
80
|
+
if sys.version_info < (3, 10):
|
|
81
|
+
ep = [item for item in entry_points().get(group, []) if item.name == name]
|
|
82
|
+
else:
|
|
83
|
+
ep = entry_points(group=group, name=name) # pylint: disable=unexpected-keyword-arg
|
|
84
|
+
if not ep:
|
|
85
|
+
return []
|
|
86
|
+
return [item.load() for item in ep]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_entry_point_new(group: str) -> List:
|
|
90
|
+
if sys.version_info < (3, 10):
|
|
91
|
+
ep = [item for item in entry_points().get(group, [])]
|
|
92
|
+
else:
|
|
93
|
+
ep = entry_points(group=group) # pylint: disable=unexpected-keyword-arg
|
|
94
|
+
if not ep:
|
|
95
|
+
return []
|
|
96
|
+
return [item.load() for item in ep]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class AdapterWithConfig(ABC, Generic[T]):
|
|
100
|
+
@abstractmethod
|
|
101
|
+
def with_config(self, **kwargs: Dict[str, Any]) -> T:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class AdapterWithName(ABC):
|
|
106
|
+
@abstractmethod
|
|
107
|
+
def name(self) -> str:
|
|
108
|
+
pass
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def get_connector_from_config(config_key: str, connectors: List[Connector]) -> Tuple[Connector, Dict[str, Any]]:
|
|
112
|
+
seen: list[str] = []
|
|
113
|
+
if not connectors:
|
|
114
|
+
raise Exception("empty connectors")
|
|
115
|
+
connector = connectors[0] # default
|
|
116
|
+
connector_params: Dict[str, Any] = {}
|
|
117
|
+
if context_storage := get_context().get(config_key):
|
|
118
|
+
adapter_name = context_storage.get("adapter", None)
|
|
119
|
+
connector_params = context_storage.get("params", {})
|
|
120
|
+
if adapter_name:
|
|
121
|
+
for con in connectors:
|
|
122
|
+
con_name = connector.__class__.__name__
|
|
123
|
+
if isinstance(con, AdapterWithName):
|
|
124
|
+
con_name = con.name()
|
|
125
|
+
seen.append(con_name)
|
|
126
|
+
if adapter_name == con_name:
|
|
127
|
+
connector = con
|
|
128
|
+
break
|
|
129
|
+
else:
|
|
130
|
+
raise Exception("unknown %s %s: seen %s" % (config_key, adapter_name, seen))
|
|
131
|
+
else:
|
|
132
|
+
connector = connectors[0]
|
|
133
|
+
if len(connectors) > 1:
|
|
134
|
+
warnings.warn(f"Please specify adapter for '{config_key}'. Found more than one classes {connectors}", UserWarning)
|
|
135
|
+
else:
|
|
136
|
+
connector = connectors[0]
|
|
137
|
+
if len(connectors) > 1:
|
|
138
|
+
warnings.warn(f"Please specify '{config_key}'. Found more than one classes {connectors}", UserWarning)
|
|
139
|
+
if isinstance(connector, AdapterWithConfig):
|
|
140
|
+
connector = connector.with_config(**connector_params)
|
|
141
|
+
# return connector_params only for storage
|
|
142
|
+
# TODO: switch storage interface to AdapterWithConfig
|
|
143
|
+
return connector, connector_params
|
|
@@ -6,7 +6,7 @@ import itertools
|
|
|
6
6
|
import re
|
|
7
7
|
from collections import namedtuple
|
|
8
8
|
from contextlib import contextmanager
|
|
9
|
-
from typing import Dict, List, Optional,
|
|
9
|
+
from typing import Dict, List, Optional, Any, OrderedDict, Tuple, Type
|
|
10
10
|
|
|
11
11
|
from contextlog import get_logger
|
|
12
12
|
|
|
@@ -15,7 +15,7 @@ from annet.annlib.command import Command, Question, CommandList
|
|
|
15
15
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
16
16
|
from annet.annlib.rbparser.deploying import MakeMessageMatcher, Answer
|
|
17
17
|
from annet.cli_args import DeployOptions
|
|
18
|
-
from annet.connectors import Connector
|
|
18
|
+
from annet.connectors import Connector, get_connector_from_config
|
|
19
19
|
from annet.output import TextArgs
|
|
20
20
|
from annet.rulebook import get_rulebook, deploying
|
|
21
21
|
from annet.storage import Device
|
|
@@ -39,17 +39,23 @@ class DeployResult(_DeployResultBase): # noqa: E302
|
|
|
39
39
|
class _FetcherConnector(Connector["Fetcher"]):
|
|
40
40
|
name = "Fetcher"
|
|
41
41
|
ep_name = "deploy_fetcher"
|
|
42
|
+
ep_by_group_only = "annet.connectors.fetcher"
|
|
42
43
|
|
|
43
44
|
def _get_default(self) -> Type["Fetcher"]:
|
|
44
|
-
|
|
45
|
+
# if entry points are broken, try to use direct import
|
|
46
|
+
import annet.adapters.fetchers.stub.fetcher as stub_fetcher
|
|
47
|
+
return stub_fetcher.StubFetcher
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
class _DriverConnector(Connector["DeployDriver"]):
|
|
48
51
|
name = "DeployDriver"
|
|
49
52
|
ep_name = "deploy_driver"
|
|
53
|
+
ep_by_group_only = "annet.connectors.deployer"
|
|
50
54
|
|
|
51
55
|
def _get_default(self) -> Type["DeployDriver"]:
|
|
52
|
-
|
|
56
|
+
# if entry points are broken, try to use direct import
|
|
57
|
+
import annet.adapters.deployers.stub.deployer as stub_deployer
|
|
58
|
+
return stub_deployer.StubDeployDriver
|
|
53
59
|
|
|
54
60
|
|
|
55
61
|
fetcher_connector = _FetcherConnector()
|
|
@@ -59,7 +65,7 @@ driver_connector = _DriverConnector()
|
|
|
59
65
|
class Fetcher(abc.ABC):
|
|
60
66
|
@abc.abstractmethod
|
|
61
67
|
def fetch_packages(self, devices: List[Device],
|
|
62
|
-
processes: int = 1, max_slots: int = 0):
|
|
68
|
+
processes: int = 1, max_slots: int = 0) -> Tuple[Dict[Device, str], Dict[Device, Any]]:
|
|
63
69
|
pass
|
|
64
70
|
|
|
65
71
|
@abc.abstractmethod
|
|
@@ -69,15 +75,10 @@ class Fetcher(abc.ABC):
|
|
|
69
75
|
pass
|
|
70
76
|
|
|
71
77
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
def fetch(self, devices: List[Device],
|
|
78
|
-
files_to_download: Dict[str, List[str]] = None,
|
|
79
|
-
processes: int = 1, max_slots: int = 0):
|
|
80
|
-
raise NotImplementedError()
|
|
78
|
+
def get_fetcher() -> Fetcher:
|
|
79
|
+
connectors = fetcher_connector.get_all()
|
|
80
|
+
fetcher, _ = get_connector_from_config("fetcher", connectors)
|
|
81
|
+
return fetcher
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
class DeployDriver(abc.ABC):
|
|
@@ -86,11 +87,11 @@ class DeployDriver(abc.ABC):
|
|
|
86
87
|
pass
|
|
87
88
|
|
|
88
89
|
@abc.abstractmethod
|
|
89
|
-
def apply_deploy_rulebook(self, hw, cmd_paths, do_finalize=True, do_commit=True):
|
|
90
|
+
def apply_deploy_rulebook(self, hw: HardwareView, cmd_paths, do_finalize=True, do_commit=True):
|
|
90
91
|
pass
|
|
91
92
|
|
|
92
93
|
@abc.abstractmethod
|
|
93
|
-
def build_configuration_cmdlist(self, hw, do_finalize=True, do_commit=True):
|
|
94
|
+
def build_configuration_cmdlist(self, hw: HardwareView, do_finalize=True, do_commit=True):
|
|
94
95
|
pass
|
|
95
96
|
|
|
96
97
|
@abc.abstractmethod
|
|
@@ -98,18 +99,10 @@ class DeployDriver(abc.ABC):
|
|
|
98
99
|
pass
|
|
99
100
|
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def apply_deploy_rulebook(self, hw, cmd_paths, do_finalize=True, do_commit=True):
|
|
106
|
-
NotImplementedError()
|
|
107
|
-
|
|
108
|
-
def build_configuration_cmdlist(self, hw, do_finalize=True, do_commit=True):
|
|
109
|
-
NotImplementedError()
|
|
110
|
-
|
|
111
|
-
def build_exit_cmdlist(self, hw):
|
|
112
|
-
raise NotImplementedError()
|
|
102
|
+
def get_deployer() -> DeployDriver:
|
|
103
|
+
connectors = fetcher_connector.get_all()
|
|
104
|
+
deployer, _ = get_connector_from_config("deployer", connectors)
|
|
105
|
+
return deployer
|
|
113
106
|
|
|
114
107
|
|
|
115
108
|
# ===
|
|
@@ -30,7 +30,7 @@ from annet.annlib import jsontools
|
|
|
30
30
|
from annet.annlib.rbparser import platform
|
|
31
31
|
from annet.annlib.rbparser.acl import compile_acl_text
|
|
32
32
|
from annet.cli_args import DeployOptions, GenOptions, ShowGenOptions
|
|
33
|
-
from annet.deploy import
|
|
33
|
+
from annet.deploy import scrub_config, get_fetcher
|
|
34
34
|
from annet.filtering import Filterer
|
|
35
35
|
from annet.generators import (
|
|
36
36
|
BaseGenerator,
|
|
@@ -407,7 +407,7 @@ def old_new(
|
|
|
407
407
|
if do_files_download and config == "running":
|
|
408
408
|
files_to_download = _get_files_to_download(devices, gens)
|
|
409
409
|
devices_with_files = [device for device in devices if device in files_to_download]
|
|
410
|
-
fetcher =
|
|
410
|
+
fetcher = get_fetcher()
|
|
411
411
|
fetched_packages, failed_packages = fetcher.fetch_packages(devices_with_files)
|
|
412
412
|
|
|
413
413
|
ctx = OldNewDeviceContext(
|
|
@@ -808,7 +808,7 @@ def _old_resolve_running(config: str, devices: List[Device]) -> Tuple[Dict[Devic
|
|
|
808
808
|
global live_configs # pylint: disable=global-statement
|
|
809
809
|
if live_configs is None:
|
|
810
810
|
# предварительно прочесть все конфиги прямо по ssh
|
|
811
|
-
fetcher =
|
|
811
|
+
fetcher = get_fetcher()
|
|
812
812
|
running, failed_running = fetcher.fetch(devices)
|
|
813
813
|
else:
|
|
814
814
|
running, failed_running = live_configs # pylint: disable=unpacking-non-sequence
|
|
@@ -826,7 +826,7 @@ def _old_resolve_files(config: str,
|
|
|
826
826
|
files_to_download = _get_files_to_download(devices, gens)
|
|
827
827
|
devices_with_files = [device for device in devices if device in files_to_download]
|
|
828
828
|
if devices_with_files:
|
|
829
|
-
fetcher =
|
|
829
|
+
fetcher = get_fetcher()
|
|
830
830
|
downloaded_files, failed_files = fetcher.fetch(devices_with_files,
|
|
831
831
|
files_to_download=files_to_download)
|
|
832
832
|
return downloaded_files, failed_files
|
|
@@ -30,6 +30,7 @@ BLACKBOX_FILENAME = "config.cfg"
|
|
|
30
30
|
class _DriverConnector(Connector["OutputDriver"]):
|
|
31
31
|
name = "OutputDriver"
|
|
32
32
|
ep_name = "output"
|
|
33
|
+
ep_by_group_only = "annet.connectors.output"
|
|
33
34
|
|
|
34
35
|
def _get_default(self) -> Type["OutputDriver"]:
|
|
35
36
|
return OutputDriverBasic
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
from typing import Any, Iterable, Optional, Type, Union, Protocol, Dict
|
|
3
|
-
from annet.connectors import Connector,
|
|
3
|
+
from annet.connectors import Connector, get_connector_from_config
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class _StorageConnector(Connector["StorageProvider"]):
|
|
7
|
-
name = "Storage"
|
|
8
|
-
ep_name = "storage"
|
|
7
|
+
name = "Storage" # legacy
|
|
8
|
+
ep_name = "storage" # legacy
|
|
9
|
+
ep_by_group_only = "annet.connectors.storage"
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
storage_connector = _StorageConnector()
|
|
@@ -128,18 +129,6 @@ class Device(Protocol):
|
|
|
128
129
|
pass
|
|
129
130
|
|
|
130
131
|
|
|
131
|
-
def get_storage() ->
|
|
132
|
+
def get_storage() -> tuple[StorageProvider, Dict[str, Any]]:
|
|
132
133
|
connectors = storage_connector.get_all()
|
|
133
|
-
|
|
134
|
-
if context_storage := get_context().get("storage"):
|
|
135
|
-
for connector in connectors:
|
|
136
|
-
con_name = connector.name()
|
|
137
|
-
seen.append(con_name)
|
|
138
|
-
if "adapter" not in context_storage:
|
|
139
|
-
raise Exception("adapter is not set in %s" % context_storage)
|
|
140
|
-
if context_storage["adapter"] == con_name:
|
|
141
|
-
return connector, context_storage.get("params", {})
|
|
142
|
-
else:
|
|
143
|
-
raise Exception("unknown storage %s: seen %s" % (context_storage["adapter"], seen))
|
|
144
|
-
else:
|
|
145
|
-
return connectors[0], {}
|
|
134
|
+
return get_connector_from_config("storage", connectors)
|
|
@@ -39,6 +39,9 @@ if __name__ == "__main__":
|
|
|
39
39
|
"annet.connectors": [
|
|
40
40
|
"storage = annet.adapters.netbox.provider:NetboxProvider",
|
|
41
41
|
],
|
|
42
|
+
"annet.connectors.storage": [
|
|
43
|
+
"file = annet.adapters.file.provider:Provider",
|
|
44
|
+
],
|
|
42
45
|
},
|
|
43
46
|
python_requires=">=3.8",
|
|
44
47
|
install_requires=requirements(),
|
annet-0.15.2/annet/connectors.py
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
from abc import ABC
|
|
3
|
-
from functools import cached_property
|
|
4
|
-
from importlib.metadata import entry_points
|
|
5
|
-
from typing import Generic, Optional, Type, TypeVar, List
|
|
6
|
-
from annet.lib import get_context
|
|
7
|
-
|
|
8
|
-
T = TypeVar("T")
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class Connector(ABC, Generic[T]):
|
|
12
|
-
name: str
|
|
13
|
-
ep_name: str
|
|
14
|
-
ep_group: str = "annet.connectors"
|
|
15
|
-
_classes: Optional[List[Type[T]]] = None
|
|
16
|
-
|
|
17
|
-
def _get_default(self) -> Type[T]:
|
|
18
|
-
raise RuntimeError(f"{self.name} is not set")
|
|
19
|
-
|
|
20
|
-
@cached_property
|
|
21
|
-
def _entry_point(self) -> List[Type[T]]:
|
|
22
|
-
return load_entry_point(self.ep_group, self.ep_name)
|
|
23
|
-
|
|
24
|
-
def get(self, *args, **kwargs) -> T:
|
|
25
|
-
if self._classes is None:
|
|
26
|
-
self._classes = self._entry_point or [self._get_default()]
|
|
27
|
-
if len(self._classes) > 1:
|
|
28
|
-
raise RuntimeError(
|
|
29
|
-
f"Multiple classes are registered with the same "
|
|
30
|
-
f"group={self.ep_group} and name={self.ep_name}: "
|
|
31
|
-
f"{[cls for cls in self._classes]}",
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
res = self._classes[0]
|
|
35
|
-
return res(*args, **kwargs)
|
|
36
|
-
|
|
37
|
-
def get_all(self, *args, **kwargs) -> List[T]:
|
|
38
|
-
if self._classes is None:
|
|
39
|
-
self._classes = self._entry_point or [self._get_default()]
|
|
40
|
-
|
|
41
|
-
return [cls(*args, **kwargs) for cls in self._classes]
|
|
42
|
-
|
|
43
|
-
def set(self, cls: Type[T]):
|
|
44
|
-
if self._classes is not None:
|
|
45
|
-
raise RuntimeError(f"Cannot reinitialize value of {self.name}")
|
|
46
|
-
self._classes = [cls]
|
|
47
|
-
|
|
48
|
-
def set_all(self, classes: List[Type[T]]):
|
|
49
|
-
if self._classes is not None:
|
|
50
|
-
raise RuntimeError(f"Cannot reinitialize value of {self.name}")
|
|
51
|
-
self._classes = list(classes)
|
|
52
|
-
|
|
53
|
-
def is_default(self) -> bool:
|
|
54
|
-
return self._classes is self._entry_point is None
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class CachedConnector(Connector[T], ABC):
|
|
58
|
-
_cache: Optional[T] = None
|
|
59
|
-
|
|
60
|
-
def get(self, *args, **kwargs) -> T:
|
|
61
|
-
assert not (args or kwargs), "Arguments forwarding is not allowed for cached connectors"
|
|
62
|
-
if self._cache is None:
|
|
63
|
-
self._cache = super().get()
|
|
64
|
-
return self._cache
|
|
65
|
-
|
|
66
|
-
def set(self, cls: Type[T]):
|
|
67
|
-
super().set(cls)
|
|
68
|
-
self._cache = None
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
def load_entry_point(group: str, name: str):
|
|
72
|
-
if sys.version_info < (3, 10):
|
|
73
|
-
ep = [item for item in entry_points().get(group, []) if item.name == name]
|
|
74
|
-
else:
|
|
75
|
-
ep = entry_points(group=group, name=name) # pylint: disable=unexpected-keyword-arg
|
|
76
|
-
if not ep:
|
|
77
|
-
return None
|
|
78
|
-
return [item.load() for item in ep]
|
|
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
|