annet 0.16.35__tar.gz → 1.0.1__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.35/annet.egg-info → annet-1.0.1}/PKG-INFO +1 -1
- annet-1.0.1/annet/adapters/fetchers/stub/fetcher.py +25 -0
- {annet-0.16.35 → annet-1.0.1}/annet/api/__init__.py +32 -8
- annet-1.0.1/annet/deploy.py +223 -0
- annet-1.0.1/annet/deploy_ui.py +774 -0
- {annet-0.16.35 → annet-1.0.1}/annet/gen.py +5 -5
- {annet-0.16.35 → annet-1.0.1}/annet/lib.py +19 -3
- {annet-0.16.35 → annet-1.0.1/annet.egg-info}/PKG-INFO +1 -1
- {annet-0.16.35 → annet-1.0.1}/annet.egg-info/SOURCES.txt +1 -0
- annet-0.16.35/annet/adapters/fetchers/stub/fetcher.py +0 -19
- annet-0.16.35/annet/deploy.py +0 -529
- {annet-0.16.35 → annet-1.0.1}/AUTHORS +0 -0
- {annet-0.16.35 → annet-1.0.1}/LICENSE +0 -0
- {annet-0.16.35 → annet-1.0.1}/MANIFEST.in +0 -0
- {annet-0.16.35 → annet-1.0.1}/README.md +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/file/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/file/provider.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/client.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/models.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/query.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/common/storage_opts.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/provider.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/adapters/netbox/v37/storage.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annet.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/command.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/diff.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/errors.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/filter_acl.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/jsontools.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/lib.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/db.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/devdb/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/output.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/patching.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/acl.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/platform.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/rulebook/common.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/tabparser.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/annlib/types.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/argparse.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/bgp_models.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/cli.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/cli_args.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/configs/context.yml +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/configs/logging.yaml +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/connectors.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/diff.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/executor.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/filtering.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/base.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/common/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/common/initial.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/entire.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/exceptions.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/jsonfragment.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/partial.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/perf.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/ref.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/generators/result.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/hardware.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/implicit.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/basemodel.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/device_models.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/executor.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/match_args.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/models_converter.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/peer_models.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/port_processor.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/mesh/registry.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/output.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/parallel.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/patching.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/reference.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/action.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/condition.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/match_builder.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/policy.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/result.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/routemap.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl/statement_builder.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/aspath.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/community.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/entities.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/execute.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/policy.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rpl_generators/rd.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/arista/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/arista/iface.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/aruba/ap_env.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/aruba/misc.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/b4com/file.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/b4com/iface.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/cisco/iface.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/cisco/misc.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/common.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/deploying.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/iface.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/misc.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/nexus/iface.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/patching.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/ribbon/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/routeros/file.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/arista.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/arista.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/aruba.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/b4com.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/cisco.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/huawei.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/huawei.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/nexus.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/pc.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/pc.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/routeros.order +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/storage.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/tabparser.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/text_term_format.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/tracing.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet/types.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet.egg-info/dependency_links.txt +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet.egg-info/entry_points.txt +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet.egg-info/requires.txt +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet.egg-info/top_level.txt +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/example/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/example/lldp.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/rpl_example/generator.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/rpl_example/items.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-0.16.35 → annet-1.0.1}/requirements.txt +0 -0
- {annet-0.16.35 → annet-1.0.1}/setup.cfg +0 -0
- {annet-0.16.35 → annet-1.0.1}/setup.py +0 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from annet.deploy import Fetcher
|
|
2
|
+
from annet.connectors import AdapterWithConfig
|
|
3
|
+
from typing import Dict, List, Any
|
|
4
|
+
from annet.storage import Device
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class StubFetcher(Fetcher, AdapterWithConfig):
|
|
8
|
+
@classmethod
|
|
9
|
+
def with_config(cls, **kwargs: Dict[str, Any]) -> Fetcher:
|
|
10
|
+
return cls(**kwargs)
|
|
11
|
+
|
|
12
|
+
async def fetch_packages(self,
|
|
13
|
+
devices: list[Device],
|
|
14
|
+
processes: int = 1,
|
|
15
|
+
max_slots: int = 0,
|
|
16
|
+
) -> tuple[dict[Device, str], dict[Device, Any]]:
|
|
17
|
+
raise NotImplementedError()
|
|
18
|
+
|
|
19
|
+
async def fetch(self,
|
|
20
|
+
devices: list[Device],
|
|
21
|
+
files_to_download: dict[str, list[str]] | None = None,
|
|
22
|
+
processes: int = 1,
|
|
23
|
+
max_slots: int = 0,
|
|
24
|
+
):
|
|
25
|
+
raise NotImplementedError()
|
|
@@ -22,6 +22,8 @@ from typing import (
|
|
|
22
22
|
)
|
|
23
23
|
|
|
24
24
|
import colorama
|
|
25
|
+
import annet.deploy
|
|
26
|
+
import annet.deploy_ui
|
|
25
27
|
import annet.lib
|
|
26
28
|
from annet.annlib import jsontools
|
|
27
29
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
@@ -262,7 +264,7 @@ def patch(args: cli_args.ShowPatchOptions, loader: ann_gen.Loader):
|
|
|
262
264
|
global live_configs # pylint: disable=global-statement
|
|
263
265
|
if args.config == "running":
|
|
264
266
|
fetcher = annet.deploy.get_fetcher()
|
|
265
|
-
live_configs = fetcher.fetch(loader.devices, processes=args.parallel)
|
|
267
|
+
live_configs = annet.lib.do_async(fetcher.fetch(loader.devices, processes=args.parallel))
|
|
266
268
|
stdin = args.stdin(filter_acl=args.filter_acl, config=args.config)
|
|
267
269
|
|
|
268
270
|
filterer = filtering.filterer_connector.get()
|
|
@@ -567,7 +569,7 @@ class Deployer:
|
|
|
567
569
|
if not diff_obj:
|
|
568
570
|
self.empty_diff_hostnames.update(dev.hostname for dev in devices)
|
|
569
571
|
if not self.args.no_ask_deploy:
|
|
570
|
-
#
|
|
572
|
+
# разобьём список устройств на несколько линий
|
|
571
573
|
dest_name = ""
|
|
572
574
|
try:
|
|
573
575
|
_, term_columns_str = os.popen("stty size", "r").read().split()
|
|
@@ -596,18 +598,18 @@ class Deployer:
|
|
|
596
598
|
return diff_lines
|
|
597
599
|
|
|
598
600
|
def ask_deploy(self) -> str:
|
|
599
|
-
return self._ask("y", annet.
|
|
601
|
+
return self._ask("y", annet.deploy_ui.AskConfirm(
|
|
600
602
|
text="\n".join(self.diff_lines()),
|
|
601
603
|
alternative_text="\n".join(self.cmd_lines),
|
|
602
604
|
))
|
|
603
605
|
|
|
604
606
|
def ask_rollback(self) -> str:
|
|
605
|
-
return self._ask("n", annet.
|
|
607
|
+
return self._ask("n", annet.deploy_ui.AskConfirm(
|
|
606
608
|
text="Execute rollback?\n",
|
|
607
609
|
alternative_text="",
|
|
608
610
|
))
|
|
609
611
|
|
|
610
|
-
def _ask(self, default_ans: str, ask: annet.
|
|
612
|
+
def _ask(self, default_ans: str, ask: annet.deploy_ui.AskConfirm) -> str:
|
|
611
613
|
# если filter_acl из stdin то с ним уже не получится работать как с терминалом
|
|
612
614
|
ans = default_ans
|
|
613
615
|
if not self.args.no_ask_deploy:
|
|
@@ -666,11 +668,22 @@ def deploy(
|
|
|
666
668
|
filterer: Filterer,
|
|
667
669
|
fetcher: Fetcher,
|
|
668
670
|
deploy_driver: DeployDriver,
|
|
671
|
+
) -> ExitCode:
|
|
672
|
+
return annet.lib.do_async(adeploy(args, loader, deployer, filterer, fetcher, deploy_driver))
|
|
673
|
+
|
|
674
|
+
|
|
675
|
+
async def adeploy(
|
|
676
|
+
args: cli_args.DeployOptions,
|
|
677
|
+
loader: ann_gen.Loader,
|
|
678
|
+
deployer: Deployer,
|
|
679
|
+
filterer: Filterer,
|
|
680
|
+
fetcher: Fetcher,
|
|
681
|
+
deploy_driver: DeployDriver,
|
|
669
682
|
) -> ExitCode:
|
|
670
683
|
""" Сгенерировать конфиг для устройств и задеплоить его """
|
|
671
684
|
ret: ExitCode = 0
|
|
672
685
|
global live_configs # pylint: disable=global-statement
|
|
673
|
-
live_configs = fetcher.fetch(devices=loader.devices, processes=args.parallel)
|
|
686
|
+
live_configs = await fetcher.fetch(devices=loader.devices, processes=args.parallel)
|
|
674
687
|
pool = ann_gen.OldNewParallel(args, loader, filterer)
|
|
675
688
|
|
|
676
689
|
for res in pool.generated_configs(loader.devices):
|
|
@@ -687,7 +700,18 @@ def deploy(
|
|
|
687
700
|
ans = deployer.ask_deploy()
|
|
688
701
|
if ans != "y":
|
|
689
702
|
return 2 ** 2
|
|
690
|
-
|
|
703
|
+
progress_bar = None
|
|
704
|
+
if sys.stdout.isatty() and not args.no_progress:
|
|
705
|
+
progress_bar = annet.deploy_ui.ProgressBars(odict([(device.fqdn, {}) for device in deploy_cmds]))
|
|
706
|
+
progress_bar.init()
|
|
707
|
+
with progress_bar:
|
|
708
|
+
progress_bar.start_terminal_refresher()
|
|
709
|
+
result = await deploy_driver.bulk_deploy(deploy_cmds, args, progress_bar=progress_bar)
|
|
710
|
+
await progress_bar.wait_for_exit()
|
|
711
|
+
progress_bar.screen.clear()
|
|
712
|
+
progress_bar.stop_terminal_refresher()
|
|
713
|
+
else:
|
|
714
|
+
result = await deploy_driver.bulk_deploy(deploy_cmds, args)
|
|
691
715
|
|
|
692
716
|
rolled_back = False
|
|
693
717
|
rollback_cmds = {deployer.fqdn_to_device[x]: cc for x, cc in result.original_states.items() if cc}
|
|
@@ -695,7 +719,7 @@ def deploy(
|
|
|
695
719
|
ans = deployer.ask_rollback()
|
|
696
720
|
if rollback_cmds and ans == "y":
|
|
697
721
|
rolled_back = True
|
|
698
|
-
|
|
722
|
+
await deploy_driver.bulk_deploy(rollback_cmds, args)
|
|
699
723
|
|
|
700
724
|
if not args.no_check_diff and not rolled_back:
|
|
701
725
|
deployer.check_diff(result, loader)
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# pylint: disable=unused-argument
|
|
2
|
+
import abc
|
|
3
|
+
import itertools
|
|
4
|
+
from collections import namedtuple
|
|
5
|
+
from typing import Dict, List, Optional, Any, OrderedDict, Tuple, Type
|
|
6
|
+
|
|
7
|
+
from contextlog import get_logger
|
|
8
|
+
|
|
9
|
+
from annet.annlib.command import Command, Question, CommandList
|
|
10
|
+
from annet.annlib.netdev.views.hardware import HardwareView
|
|
11
|
+
from annet.annlib.rbparser.deploying import MakeMessageMatcher, Answer
|
|
12
|
+
from annet.cli_args import DeployOptions
|
|
13
|
+
from annet.connectors import Connector, get_connector_from_config
|
|
14
|
+
from annet.rulebook import get_rulebook, deploying
|
|
15
|
+
from annet.storage import Device
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
_DeployResultBase = namedtuple("_DeployResultBase", ("hostnames", "results", "durations", "original_states"))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProgressBar(abc.ABC):
|
|
22
|
+
@abc.abstractmethod
|
|
23
|
+
def set_content(self, tile_name: str, content: str):
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def set_progress(self,
|
|
28
|
+
tile_name: str,
|
|
29
|
+
iteration: int,
|
|
30
|
+
total: int,
|
|
31
|
+
prefix: str = "",
|
|
32
|
+
suffix: str = "",
|
|
33
|
+
fill: str = "",
|
|
34
|
+
error: bool = False,
|
|
35
|
+
):
|
|
36
|
+
...
|
|
37
|
+
|
|
38
|
+
@abc.abstractmethod
|
|
39
|
+
def set_exception(self, tile_name: str, cmd_exc: str, last_cmd: str, progress_max: int):
|
|
40
|
+
...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DeployResult(_DeployResultBase): # noqa: E302
|
|
44
|
+
def add_results(self, results: dict[str, tuple[list[str], list[Exception]]]) -> None:
|
|
45
|
+
for hostname, (excs, result) in results.items():
|
|
46
|
+
self.hostnames.append(hostname)
|
|
47
|
+
self.results[hostname] = excs
|
|
48
|
+
self.durations[hostname] = 0.0
|
|
49
|
+
self.original_states[hostname] = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class _FetcherConnector(Connector["Fetcher"]):
|
|
53
|
+
name = "Fetcher"
|
|
54
|
+
ep_name = "deploy_fetcher"
|
|
55
|
+
ep_by_group_only = "annet.connectors.fetcher"
|
|
56
|
+
|
|
57
|
+
def _get_default(self) -> Type["Fetcher"]:
|
|
58
|
+
# if entry points are broken, try to use direct import
|
|
59
|
+
import annet.adapters.fetchers.stub.fetcher as stub_fetcher
|
|
60
|
+
return stub_fetcher.StubFetcher
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class _DriverConnector(Connector["DeployDriver"]):
|
|
64
|
+
name = "DeployDriver"
|
|
65
|
+
ep_name = "deploy_driver"
|
|
66
|
+
ep_by_group_only = "annet.connectors.deployer"
|
|
67
|
+
|
|
68
|
+
def _get_default(self) -> Type["DeployDriver"]:
|
|
69
|
+
# if entry points are broken, try to use direct import
|
|
70
|
+
import annet.adapters.deployers.stub.deployer as stub_deployer
|
|
71
|
+
return stub_deployer.StubDeployDriver
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
fetcher_connector = _FetcherConnector()
|
|
75
|
+
driver_connector = _DriverConnector()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Fetcher(abc.ABC):
|
|
79
|
+
@abc.abstractmethod
|
|
80
|
+
async def fetch_packages(self,
|
|
81
|
+
devices: list[Device],
|
|
82
|
+
processes: int = 1,
|
|
83
|
+
max_slots: int = 0,
|
|
84
|
+
) -> tuple[dict[Device, str], dict[Device, Any]]:
|
|
85
|
+
pass
|
|
86
|
+
|
|
87
|
+
@abc.abstractmethod
|
|
88
|
+
async def fetch(self,
|
|
89
|
+
devices: list[Device],
|
|
90
|
+
files_to_download: dict[str, list[str]] | None = None,
|
|
91
|
+
processes: int = 1,
|
|
92
|
+
max_slots: int = 0,
|
|
93
|
+
):
|
|
94
|
+
pass
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def get_fetcher() -> Fetcher:
|
|
98
|
+
connectors = fetcher_connector.get_all()
|
|
99
|
+
fetcher, _ = get_connector_from_config("fetcher", connectors)
|
|
100
|
+
return fetcher
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
class DeployDriver(abc.ABC):
|
|
104
|
+
@abc.abstractmethod
|
|
105
|
+
async def bulk_deploy(self, deploy_cmds: dict, args: DeployOptions, progress_bar: ProgressBar | None = None) -> DeployResult:
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
@abc.abstractmethod
|
|
109
|
+
def apply_deploy_rulebook(self, hw: HardwareView, cmd_paths, do_finalize=True, do_commit=True):
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
@abc.abstractmethod
|
|
113
|
+
def build_configuration_cmdlist(self, hw: HardwareView, do_finalize=True, do_commit=True):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
@abc.abstractmethod
|
|
117
|
+
def build_exit_cmdlist(self, hw):
|
|
118
|
+
pass
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def get_deployer() -> DeployDriver:
|
|
122
|
+
connectors = driver_connector.get_all()
|
|
123
|
+
deployer, _ = get_connector_from_config("deployer", connectors)
|
|
124
|
+
return deployer
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ===
|
|
128
|
+
def scrub_config(text: str, breed: str) -> str:
|
|
129
|
+
return text
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def show_bulk_report(hostnames, results, durations, log_dir):
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class RulebookQuestionHandler:
|
|
137
|
+
def __init__(self, dialogs):
|
|
138
|
+
self._dialogs = dialogs
|
|
139
|
+
|
|
140
|
+
def __call__(self, dev: Connector, cmd: Command, match_content: bytes):
|
|
141
|
+
content = match_content.strip()
|
|
142
|
+
content = content.decode()
|
|
143
|
+
for matcher, answer in self._dialogs.items():
|
|
144
|
+
if matcher(content):
|
|
145
|
+
return Command(answer.text)
|
|
146
|
+
|
|
147
|
+
get_logger().info("no answer in rulebook. dialogs=%s match_content=%s", self._dialogs, match_content)
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def rb_question_to_question(q: MakeMessageMatcher, a: Answer) -> Question: # TODO: drop MakeMessageMatcher
|
|
152
|
+
if not a.send_nl:
|
|
153
|
+
raise Exception("not supported false send_nl")
|
|
154
|
+
text: str = q._text # pylint: disable=protected-access
|
|
155
|
+
is_regexp = False
|
|
156
|
+
if text.startswith("/") and text.endswith("/"):
|
|
157
|
+
is_regexp = True
|
|
158
|
+
text = text[1:-1]
|
|
159
|
+
res = Question(question=text, answer=a.text, is_regexp=is_regexp)
|
|
160
|
+
return res
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def make_cmd_params(rule: Dict[str, Any]) -> Dict[str, Any]:
|
|
164
|
+
if rule:
|
|
165
|
+
qa_handler = RulebookQuestionHandler(rule["attrs"]["dialogs"])
|
|
166
|
+
qa_list: List[Question] = []
|
|
167
|
+
for matcher, answer in qa_handler._dialogs.items(): # pylint: disable=protected-access
|
|
168
|
+
qa_list.append(rb_question_to_question(matcher, answer))
|
|
169
|
+
return {
|
|
170
|
+
"questions": qa_list,
|
|
171
|
+
"timeout": rule["attrs"]["timeout"],
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
"timeout": 30,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def make_apply_commands(rule: dict, hw: HardwareView, do_commit: bool, do_finalize: bool, path: Optional[str] = None):
|
|
179
|
+
apply_logic = rule["attrs"]["apply_logic"]
|
|
180
|
+
before, after = apply_logic(hw, do_commit=do_commit, do_finalize=do_finalize, path=path)
|
|
181
|
+
return before, after
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def fill_cmd_params(rules: OrderedDict, cmd: Command):
|
|
185
|
+
rule = deploying.match_deploy_rule(rules, (cmd.cmd,), {})
|
|
186
|
+
if rule:
|
|
187
|
+
cmd_params = make_cmd_params(rule)
|
|
188
|
+
cmd.questions = cmd_params.get("questions", None)
|
|
189
|
+
cmd.timeout = cmd_params["timeout"]
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def apply_deploy_rulebook(hw: HardwareView, cmd_paths, do_finalize=True, do_commit=True):
|
|
193
|
+
rules = get_rulebook(hw)["deploying"]
|
|
194
|
+
cmds_with_apply = []
|
|
195
|
+
for cmd_path, context in cmd_paths.items():
|
|
196
|
+
rule = deploying.match_deploy_rule(rules, cmd_path, context)
|
|
197
|
+
cmd_params = make_cmd_params(rule)
|
|
198
|
+
before, after = make_apply_commands(rule, hw, do_commit, do_finalize)
|
|
199
|
+
|
|
200
|
+
cmd = Command(cmd_path[-1], **cmd_params)
|
|
201
|
+
# XXX более чистый способ передавать-мета инфу о команде
|
|
202
|
+
cmd.level = len(cmd_path) - 1
|
|
203
|
+
cmds_with_apply.append((cmd, before, after))
|
|
204
|
+
|
|
205
|
+
def _key(item):
|
|
206
|
+
_cmd, before, after = item
|
|
207
|
+
return (tuple(cmd.cmd for cmd in before), tuple(cmd.cmd for cmd in after))
|
|
208
|
+
|
|
209
|
+
cmdlist = CommandList()
|
|
210
|
+
for _k, cmd_before_after in itertools.groupby(cmds_with_apply, key=_key):
|
|
211
|
+
cmd_before_after = list(cmd_before_after)
|
|
212
|
+
_, before, after = cmd_before_after[0]
|
|
213
|
+
for c in before:
|
|
214
|
+
c.level = 0
|
|
215
|
+
fill_cmd_params(rules, c)
|
|
216
|
+
cmdlist.add_cmd(c)
|
|
217
|
+
for cmd, _before, _after in cmd_before_after:
|
|
218
|
+
cmdlist.add_cmd(cmd)
|
|
219
|
+
for c in after:
|
|
220
|
+
c.level = 0
|
|
221
|
+
fill_cmd_params(rules, c)
|
|
222
|
+
cmdlist.add_cmd(c)
|
|
223
|
+
return cmdlist
|