gnetcli-adapter 2.5.2__tar.gz → 2.6.0__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.
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/PKG-INFO +45 -2
- gnetcli_adapter-2.6.0/README.md +45 -0
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/pyproject.toml +1 -1
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/src/gnetcli_adapter/gnetcli_adapter.py +103 -152
- gnetcli_adapter-2.5.2/README.md +0 -2
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/LICENSE +0 -0
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/src/gnetcli_adapter/__init__.py +0 -0
- {gnetcli_adapter-2.5.2 → gnetcli_adapter-2.6.0}/src/gnetcli_adapter/progress_tracker.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gnetcli_adapter
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.6.0
|
|
4
4
|
Summary: Gnetcli-server adapter for Annet
|
|
5
5
|
Author-email: Aleksandr Balezin <gescheit12@gmail.com>
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
14
14
|
License-File: LICENSE
|
|
15
15
|
Requires-Dist: exceptiongroup>=1.1.3; python_version<'3.11'
|
|
16
16
|
Requires-Dist: annet>=3.2.0
|
|
17
|
-
Requires-Dist: gnetclisdk>=1.0
|
|
17
|
+
Requires-Dist: gnetclisdk>=1.1.0
|
|
18
18
|
Requires-Dist: pydantic_settings
|
|
19
19
|
Requires-Dist: pydantic
|
|
20
20
|
Requires-Dist: bandit[toml]==1.7.5 ; extra == "test"
|
|
@@ -42,3 +42,46 @@ Provides-Extra: test
|
|
|
42
42
|
# gnetcli_adapter
|
|
43
43
|
This package provides deployer and fetcher adapters for Annet
|
|
44
44
|
|
|
45
|
+
# Examples
|
|
46
|
+
|
|
47
|
+
## Using specified login and password
|
|
48
|
+
|
|
49
|
+
cat ~/.annet/context.yml
|
|
50
|
+
```yaml
|
|
51
|
+
fetcher:
|
|
52
|
+
default:
|
|
53
|
+
adapter: gnetcli
|
|
54
|
+
params: &gnetcli
|
|
55
|
+
dev_login: mylogin
|
|
56
|
+
dev_password: mypassword
|
|
57
|
+
deployer:
|
|
58
|
+
default:
|
|
59
|
+
adapter: gnetcli
|
|
60
|
+
params:
|
|
61
|
+
<<: *gnetcli
|
|
62
|
+
...
|
|
63
|
+
context:
|
|
64
|
+
default:
|
|
65
|
+
fetcher: default
|
|
66
|
+
deployer: default
|
|
67
|
+
selected_context: default
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Using tunnel through master SSH-connection
|
|
71
|
+
|
|
72
|
+
https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing
|
|
73
|
+
|
|
74
|
+
cat ~/.ssh/context.yml
|
|
75
|
+
```
|
|
76
|
+
Host myhost*
|
|
77
|
+
ProxyJump mybastion
|
|
78
|
+
|
|
79
|
+
Host mybastion
|
|
80
|
+
ControlMaster auto
|
|
81
|
+
ControlPath ~/.ssh/mastersockets/%r@%h:%p
|
|
82
|
+
ControlPersist 120m
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`~/.annet/context.yml` the same because gnetcli read .ssh/config by default.
|
|
86
|
+
|
|
87
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# gnetcli_adapter
|
|
2
|
+
This package provides deployer and fetcher adapters for Annet
|
|
3
|
+
|
|
4
|
+
# Examples
|
|
5
|
+
|
|
6
|
+
## Using specified login and password
|
|
7
|
+
|
|
8
|
+
cat ~/.annet/context.yml
|
|
9
|
+
```yaml
|
|
10
|
+
fetcher:
|
|
11
|
+
default:
|
|
12
|
+
adapter: gnetcli
|
|
13
|
+
params: &gnetcli
|
|
14
|
+
dev_login: mylogin
|
|
15
|
+
dev_password: mypassword
|
|
16
|
+
deployer:
|
|
17
|
+
default:
|
|
18
|
+
adapter: gnetcli
|
|
19
|
+
params:
|
|
20
|
+
<<: *gnetcli
|
|
21
|
+
...
|
|
22
|
+
context:
|
|
23
|
+
default:
|
|
24
|
+
fetcher: default
|
|
25
|
+
deployer: default
|
|
26
|
+
selected_context: default
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Using tunnel through master SSH-connection
|
|
30
|
+
|
|
31
|
+
https://en.wikibooks.org/wiki/OpenSSH/Cookbook/Multiplexing
|
|
32
|
+
|
|
33
|
+
cat ~/.ssh/context.yml
|
|
34
|
+
```
|
|
35
|
+
Host myhost*
|
|
36
|
+
ProxyJump mybastion
|
|
37
|
+
|
|
38
|
+
Host mybastion
|
|
39
|
+
ControlMaster auto
|
|
40
|
+
ControlPath ~/.ssh/mastersockets/%r@%h:%p
|
|
41
|
+
ControlPersist 120m
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`~/.annet/context.yml` the same because gnetcli read .ssh/config by default.
|
|
45
|
+
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import traceback
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
4
|
+
import base64
|
|
5
|
+
from collections.abc import AsyncIterator, Iterable
|
|
6
|
+
from contextlib import asynccontextmanager
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
7
9
|
|
|
8
10
|
import annet.annlib.command
|
|
9
11
|
|
|
@@ -12,26 +14,21 @@ from annet.annlib.command import Command, CommandList
|
|
|
12
14
|
from annet.annlib.netdev.views.hardware import HardwareView
|
|
13
15
|
from annet.adapters.netbox.common.models import NetboxDevice
|
|
14
16
|
from annet.rulebook import common
|
|
15
|
-
|
|
16
17
|
from annet.connectors import AdapterWithConfig, AdapterWithName
|
|
17
|
-
from typing import Dict, List, Any, Optional, Tuple
|
|
18
18
|
from annet.storage import Device
|
|
19
|
-
|
|
20
|
-
from
|
|
21
|
-
|
|
22
|
-
)
|
|
23
|
-
from gnetclisdk.client import Credentials, Gnetcli, HostParams, QA, File, GnetcliSessionCmd
|
|
19
|
+
from gnetclisdk.config import Config
|
|
20
|
+
from gnetclisdk.starter import DEFAULT_GNETCLI_SERVER_CONF, GnetcliStarter
|
|
21
|
+
from gnetclisdk.client import Credentials, Gnetcli, HostParams, QA, File
|
|
24
22
|
from gnetclisdk.exceptions import EOFError
|
|
25
23
|
import gnetclisdk.proto.server_pb2 as pb
|
|
26
24
|
from pydantic import Field, field_validator, FieldValidationInfo
|
|
27
25
|
from pydantic_core import PydanticUndefined
|
|
28
26
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
29
|
-
|
|
30
|
-
import
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
import os.path
|
|
27
|
+
|
|
28
|
+
from gnetcli_adapter.progress_tracker import (
|
|
29
|
+
ProgressTracker, ProgressBarTracker, FileProgressTracker, LogProgressTracker, CompositeTracker,
|
|
30
|
+
)
|
|
31
|
+
|
|
35
32
|
try:
|
|
36
33
|
from builtins import ( # type: ignore[attr-defined, unused-ignore]
|
|
37
34
|
ExceptionGroup,
|
|
@@ -55,23 +52,9 @@ breed_to_device = {
|
|
|
55
52
|
"vrp55": "huawei",
|
|
56
53
|
}
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
logging:
|
|
60
|
-
level: debug
|
|
61
|
-
json: true
|
|
62
|
-
port: 0
|
|
63
|
-
dev_auth:
|
|
64
|
-
use_agent: true
|
|
65
|
-
ssh_config: true
|
|
66
|
-
|
|
67
|
-
"""
|
|
68
|
-
|
|
69
|
-
_local_gnetcli: Optional[threading.Thread] = None
|
|
70
|
-
_local_gnetcli_p: Optional[subprocess.Popen] = None
|
|
71
|
-
_local_gnetcli_url: Optional[str] = None
|
|
72
|
-
LOG_FORMAT = "%(asctime)s - l:%(lineno)d - %(funcName)s() - %(levelname)s - %(message)s"
|
|
73
|
-
DATE_FMT = "%Y-%m-%d %H:%M:%S"
|
|
55
|
+
|
|
74
56
|
DEFAULT_GNETCLI_SERVER_PATH = "gnetcli_server"
|
|
57
|
+
|
|
75
58
|
_logger = logging.getLogger(__name__)
|
|
76
59
|
|
|
77
60
|
|
|
@@ -80,7 +63,7 @@ class AppSettings(BaseSettings):
|
|
|
80
63
|
|
|
81
64
|
url: Optional[str] = None
|
|
82
65
|
server_path: str = DEFAULT_GNETCLI_SERVER_PATH
|
|
83
|
-
server_conf:
|
|
66
|
+
server_conf: Config = DEFAULT_GNETCLI_SERVER_CONF
|
|
84
67
|
insecure_grpc: bool = Field(default=True)
|
|
85
68
|
login: Optional[str] = None
|
|
86
69
|
password: Optional[str] = None
|
|
@@ -128,29 +111,6 @@ async def get_config(breed: str) -> List[str]:
|
|
|
128
111
|
raise Exception("unknown breed %r" % breed)
|
|
129
112
|
|
|
130
113
|
|
|
131
|
-
def check_gnetcli_server(server_path: str, config: str = DEFAULT_GNETCLI_SERVER_CONF):
|
|
132
|
-
global _local_gnetcli
|
|
133
|
-
if not _local_gnetcli:
|
|
134
|
-
t = threading.Thread(target=run_gnetcli_server,
|
|
135
|
-
kwargs={"server_path": server_path, "config": config})
|
|
136
|
-
t.daemon = True
|
|
137
|
-
t.start()
|
|
138
|
-
time.sleep(1)
|
|
139
|
-
_local_gnetcli = t
|
|
140
|
-
if _local_gnetcli_p is None:
|
|
141
|
-
raise Exception("server failed")
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
def cleanup():
|
|
145
|
-
if _local_gnetcli_p is not None:
|
|
146
|
-
_local_gnetcli_p.terminate()
|
|
147
|
-
time.sleep(0.05)
|
|
148
|
-
_local_gnetcli_p.kill()
|
|
149
|
-
time.sleep(0.05)
|
|
150
|
-
|
|
151
|
-
atexit.register(cleanup)
|
|
152
|
-
|
|
153
|
-
|
|
154
114
|
async def gather_with_concurrency(n: int, *coros: list[asyncio.Task]):
|
|
155
115
|
if n == 0:
|
|
156
116
|
return await asyncio.gather(*coros, return_exceptions=True)
|
|
@@ -183,52 +143,6 @@ def get_device_ip(dev: Device) -> Optional[str]:
|
|
|
183
143
|
return None
|
|
184
144
|
|
|
185
145
|
|
|
186
|
-
def run_gnetcli_server(server_path: str, config: str = DEFAULT_GNETCLI_SERVER_CONF):
|
|
187
|
-
global _local_gnetcli_p
|
|
188
|
-
global _local_gnetcli_url
|
|
189
|
-
abs_path: Optional[str] = shutil.which(server_path)
|
|
190
|
-
if not abs_path and server_path == DEFAULT_GNETCLI_SERVER_PATH:
|
|
191
|
-
abs_path = shutil.which(os.path.expanduser("~/go/bin/gnetcli_server"))
|
|
192
|
-
gnetcli_abs_path: str = abs_path or server_path
|
|
193
|
-
_logger.info("starting gnetcli server %s", gnetcli_abs_path)
|
|
194
|
-
try:
|
|
195
|
-
proc = subprocess.Popen(
|
|
196
|
-
[gnetcli_abs_path, "--conf-file", "-"],
|
|
197
|
-
stdout=subprocess.DEVNULL, # we do not read stdout
|
|
198
|
-
stderr=subprocess.PIPE,
|
|
199
|
-
stdin=subprocess.PIPE,
|
|
200
|
-
bufsize=1,
|
|
201
|
-
universal_newlines=True,
|
|
202
|
-
)
|
|
203
|
-
except Exception as e:
|
|
204
|
-
logging.exception("server exec error %s", e)
|
|
205
|
-
raise
|
|
206
|
-
proc.stdin.write(config)
|
|
207
|
-
proc.stdin.close()
|
|
208
|
-
_local_gnetcli_p = proc
|
|
209
|
-
while True:
|
|
210
|
-
output = proc.stderr.readline()
|
|
211
|
-
if output == "" and proc.poll() is not None:
|
|
212
|
-
break
|
|
213
|
-
if output:
|
|
214
|
-
_logger.debug("gnetcli output: %s", output.strip())
|
|
215
|
-
if _local_gnetcli_url is None:
|
|
216
|
-
try:
|
|
217
|
-
data = json.loads(output)
|
|
218
|
-
except Exception:
|
|
219
|
-
pass
|
|
220
|
-
else:
|
|
221
|
-
if data.get("msg") == "init tcp socket":
|
|
222
|
-
_local_gnetcli_url = data.get("address")
|
|
223
|
-
if data.get("msg") == "init unix socket":
|
|
224
|
-
_local_gnetcli_url = "unix:" + data.get("path")
|
|
225
|
-
if data.get("level") == "panic":
|
|
226
|
-
_logger.error("gnetcli error %s", data)
|
|
227
|
-
_logger.debug("set gnetcli server exit code %s", proc.returncode)
|
|
228
|
-
rc = proc.poll()
|
|
229
|
-
return rc
|
|
230
|
-
|
|
231
|
-
|
|
232
146
|
class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
233
147
|
def __init__(
|
|
234
148
|
self,
|
|
@@ -239,7 +153,7 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
239
153
|
dev_password: Optional[str] = None,
|
|
240
154
|
ssh_agent_enabled: bool = True,
|
|
241
155
|
server_path: Optional[str] = None,
|
|
242
|
-
server_conf:
|
|
156
|
+
server_conf: Config = DEFAULT_GNETCLI_SERVER_CONF,
|
|
243
157
|
):
|
|
244
158
|
conf_args = {
|
|
245
159
|
"login": login,
|
|
@@ -252,7 +166,6 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
252
166
|
"ssh_agent_enabled": ssh_agent_enabled,
|
|
253
167
|
}
|
|
254
168
|
self.conf = AppSettings(**{k: v for k,v in conf_args.items() if v is not None})
|
|
255
|
-
self.api = make_api(self.conf)
|
|
256
169
|
|
|
257
170
|
@classmethod
|
|
258
171
|
def name(cls) -> str:
|
|
@@ -268,11 +181,25 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
268
181
|
# TODO: implement fetch_packages
|
|
269
182
|
return {}, {}
|
|
270
183
|
|
|
271
|
-
async def fetch(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
184
|
+
async def fetch(
|
|
185
|
+
self,
|
|
186
|
+
devices: List[Device],
|
|
187
|
+
files_to_download: Optional[Dict[Device, List[str]]] = None,
|
|
188
|
+
processes: int = 1,
|
|
189
|
+
max_slots: int = 0,
|
|
190
|
+
):
|
|
191
|
+
if not devices:
|
|
192
|
+
return {}, {}
|
|
193
|
+
async with make_api(self.conf) as api:
|
|
194
|
+
return await self._fetch(api, devices, files_to_download, processes, max_slots)
|
|
195
|
+
|
|
196
|
+
async def _fetch(
|
|
197
|
+
self,
|
|
198
|
+
api: Gnetcli,
|
|
199
|
+
devices: List[Device],
|
|
200
|
+
files_to_download: Optional[Dict[Device, List[str]]] = None,
|
|
201
|
+
processes: int = 1,
|
|
202
|
+
max_slots: int = 0,
|
|
276
203
|
):
|
|
277
204
|
running = {}
|
|
278
205
|
failed_running = {}
|
|
@@ -282,12 +209,12 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
282
209
|
if files_to_download or device.is_pc():
|
|
283
210
|
if files_to_download:
|
|
284
211
|
files = files_to_download.get(device, [])
|
|
285
|
-
task = self.adownload_dev(device=device, files=files)
|
|
212
|
+
task = self.adownload_dev(api=api, device=device, files=files)
|
|
286
213
|
tasks[task] = device
|
|
287
214
|
else:
|
|
288
215
|
running[device] = {}
|
|
289
216
|
else:
|
|
290
|
-
task = self.afetch_dev(device=device)
|
|
217
|
+
task = self.afetch_dev(api=api, device=device)
|
|
291
218
|
tasks[task] = device
|
|
292
219
|
|
|
293
220
|
if not tasks:
|
|
@@ -306,14 +233,14 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
306
233
|
running[device] = dev_res
|
|
307
234
|
return running, failed_running
|
|
308
235
|
|
|
309
|
-
async def afetch_dev(self, device: Device) -> str:
|
|
236
|
+
async def afetch_dev(self, api: Gnetcli, device: Device) -> str:
|
|
310
237
|
cmds = await get_config(breed=device.breed)
|
|
311
238
|
# map annet breed to gnetcli device type
|
|
312
239
|
gnetcli_device = breed_to_device.get(device.breed, device.breed)
|
|
313
240
|
dev_result = []
|
|
314
241
|
ip = get_device_ip(device)
|
|
315
242
|
for cmd in cmds:
|
|
316
|
-
res = await
|
|
243
|
+
res = await api.cmd(
|
|
317
244
|
hostname=device.fqdn,
|
|
318
245
|
cmd=cmd,
|
|
319
246
|
host_params=HostParams(
|
|
@@ -327,10 +254,10 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
|
|
|
327
254
|
dev_result.append(res.out)
|
|
328
255
|
return b"\n".join(dev_result).decode()
|
|
329
256
|
|
|
330
|
-
async def adownload_dev(self, device: Device, files: List[str]) -> Dict[str, str]:
|
|
257
|
+
async def adownload_dev(self, api: Gnetcli, device: Device, files: List[str]) -> Dict[str, str]:
|
|
331
258
|
gnetcli_device = breed_to_device.get(device.breed, device.breed)
|
|
332
259
|
ip = get_device_ip(device)
|
|
333
|
-
downloaded = await
|
|
260
|
+
downloaded = await api.download(
|
|
334
261
|
hostname=device.fqdn,
|
|
335
262
|
paths=files,
|
|
336
263
|
host_params=HostParams(
|
|
@@ -355,27 +282,15 @@ def parse_annet_qa(qa: list[annet.annlib.command.Question]) -> list[QA]:
|
|
|
355
282
|
return res
|
|
356
283
|
|
|
357
284
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
raise Exception("gnetcli server died with code %s" % _local_gnetcli_p.returncode)
|
|
368
|
-
gnetcli_url = _local_gnetcli_url
|
|
369
|
-
else:
|
|
370
|
-
gnetcli_url = conf.url
|
|
371
|
-
auth_token = conf.make_server_credentials()
|
|
372
|
-
api = Gnetcli(
|
|
373
|
-
server=gnetcli_url,
|
|
374
|
-
auth_token=auth_token,
|
|
375
|
-
insecure_grpc=conf.insecure_grpc,
|
|
376
|
-
user_agent="annet",
|
|
377
|
-
)
|
|
378
|
-
return api
|
|
285
|
+
@asynccontextmanager
|
|
286
|
+
async def make_api(conf: AppSettings) -> AsyncIterator[Gnetcli]:
|
|
287
|
+
async with GnetcliStarter(conf.server_path, conf.server_conf) as gnetcli_url:
|
|
288
|
+
yield Gnetcli(
|
|
289
|
+
server=gnetcli_url,
|
|
290
|
+
auth_token= conf.make_server_credentials(),
|
|
291
|
+
insecure_grpc=conf.insecure_grpc,
|
|
292
|
+
user_agent="annet",
|
|
293
|
+
)
|
|
379
294
|
|
|
380
295
|
|
|
381
296
|
class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
@@ -388,7 +303,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
388
303
|
dev_password: Optional[str] = None,
|
|
389
304
|
ssh_agent_enabled: bool = True,
|
|
390
305
|
server_path: Optional[str] = None,
|
|
391
|
-
server_conf: Optional[
|
|
306
|
+
server_conf: Optional[Config] = DEFAULT_GNETCLI_SERVER_CONF,
|
|
392
307
|
logs_dir: Optional[str] = None,
|
|
393
308
|
):
|
|
394
309
|
conf_args = {
|
|
@@ -402,7 +317,6 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
402
317
|
"ssh_agent_enabled": ssh_agent_enabled,
|
|
403
318
|
}
|
|
404
319
|
self.conf = AppSettings(**{k: v for k,v in conf_args.items() if v is not None})
|
|
405
|
-
self.api = make_api(self.conf)
|
|
406
320
|
self.logs_dir = logs_dir
|
|
407
321
|
|
|
408
322
|
@classmethod
|
|
@@ -413,7 +327,29 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
413
327
|
def with_config(cls, **kwargs: Any) -> DeployDriver:
|
|
414
328
|
return cls(**kwargs)
|
|
415
329
|
|
|
416
|
-
async def bulk_deploy(
|
|
330
|
+
async def bulk_deploy(
|
|
331
|
+
self,
|
|
332
|
+
deploy_cmds: dict[Device, CommandList],
|
|
333
|
+
args: DeployOptions,
|
|
334
|
+
progress_bar: ProgressBar | None = None,
|
|
335
|
+
) -> DeployResult:
|
|
336
|
+
if not deploy_cmds:
|
|
337
|
+
return DeployResult(hostnames=[], results={}, durations={}, original_states={})
|
|
338
|
+
async with make_api(self.conf) as api:
|
|
339
|
+
return await self._bulk_deploy(
|
|
340
|
+
api=api,
|
|
341
|
+
deploy_cmds=deploy_cmds,
|
|
342
|
+
args=args,
|
|
343
|
+
progress_bar=progress_bar,
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
async def _bulk_deploy(
|
|
347
|
+
self,
|
|
348
|
+
api: Gnetcli,
|
|
349
|
+
deploy_cmds: dict[Device, CommandList],
|
|
350
|
+
args: DeployOptions,
|
|
351
|
+
progress_bar: ProgressBar | None = None,
|
|
352
|
+
) -> DeployResult:
|
|
417
353
|
if progress_bar:
|
|
418
354
|
for host, cmds in deploy_cmds.items():
|
|
419
355
|
progress_bar.set_progress(host.fqdn, 0, len(cmds))
|
|
@@ -427,11 +363,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
427
363
|
pass
|
|
428
364
|
deploy_items = deploy_cmds.items()
|
|
429
365
|
if max_parallel == 1:
|
|
430
|
-
result = await self.serial_deploy(deploy_items, args, progress_bar)
|
|
366
|
+
result = await self.serial_deploy(api, deploy_items, args, progress_bar)
|
|
431
367
|
else:
|
|
432
368
|
result = await gather_with_concurrency(
|
|
433
369
|
max_parallel,
|
|
434
|
-
*[self.deploy(device, cmds, args, progress_bar) for device, cmds in deploy_items],
|
|
370
|
+
*[self.deploy(api, device, cmds, args, progress_bar) for device, cmds in deploy_items],
|
|
435
371
|
)
|
|
436
372
|
res = DeployResult(hostnames=[], results={}, durations={}, original_states={})
|
|
437
373
|
res.add_results(results={dev.fqdn: dev_res for (dev, _), dev_res in zip(deploy_items, result)})
|
|
@@ -452,7 +388,9 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
452
388
|
return reload_cmds
|
|
453
389
|
|
|
454
390
|
def _get_total(
|
|
455
|
-
|
|
391
|
+
self,
|
|
392
|
+
command_groups: list[tuple[str, CommandList]],
|
|
393
|
+
files: dict[str, File],
|
|
456
394
|
) -> int:
|
|
457
395
|
run_cmds = 0
|
|
458
396
|
for _, cmds in command_groups:
|
|
@@ -461,7 +399,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
461
399
|
run_cmds += 1
|
|
462
400
|
return run_cmds
|
|
463
401
|
|
|
464
|
-
def _init_progress_tracker(
|
|
402
|
+
def _init_progress_tracker(
|
|
403
|
+
self,
|
|
404
|
+
device: Device,
|
|
405
|
+
progress_bar: ProgressBar | None,
|
|
406
|
+
) -> ProgressTracker:
|
|
465
407
|
tracker = CompositeTracker(LogProgressTracker(device))
|
|
466
408
|
if progress_bar:
|
|
467
409
|
tracker.add_tracker(ProgressBarTracker(device, progress_bar))
|
|
@@ -469,7 +411,13 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
469
411
|
tracker.add_tracker(FileProgressTracker(device, self.logs_dir))
|
|
470
412
|
return tracker
|
|
471
413
|
|
|
472
|
-
async def serial_deploy(
|
|
414
|
+
async def serial_deploy(
|
|
415
|
+
self,
|
|
416
|
+
api: Gnetcli,
|
|
417
|
+
deploy_items: Iterable[tuple[Device, CommandList]],
|
|
418
|
+
args: DeployOptions,
|
|
419
|
+
progress_bar: ProgressBar | None = None,
|
|
420
|
+
):
|
|
473
421
|
res = {}
|
|
474
422
|
for device, cmds in deploy_items:
|
|
475
423
|
if progress_bar:
|
|
@@ -481,16 +429,17 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
481
429
|
break
|
|
482
430
|
except Exception:
|
|
483
431
|
pass
|
|
484
|
-
dev_res = await self.deploy(device, cmds, args, progress_bar)
|
|
432
|
+
dev_res = await self.deploy(api, device, cmds, args, progress_bar)
|
|
485
433
|
res[device] = dev_res
|
|
486
434
|
return res
|
|
487
435
|
|
|
488
436
|
async def deploy(
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
437
|
+
self,
|
|
438
|
+
api: Gnetcli,
|
|
439
|
+
device: Device,
|
|
440
|
+
cmds: CommandList,
|
|
441
|
+
args: DeployOptions,
|
|
442
|
+
progress_bar: ProgressBar | None = None,
|
|
494
443
|
) -> Exception | None:
|
|
495
444
|
gnetcli_device = breed_to_device.get(device.breed, device.breed)
|
|
496
445
|
ip = get_device_ip(device)
|
|
@@ -515,6 +464,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
515
464
|
tracker.set_total(self._get_total(command_groups, files))
|
|
516
465
|
try:
|
|
517
466
|
seen_exc, results = await self._deploy(
|
|
467
|
+
api=api,
|
|
518
468
|
device=device,
|
|
519
469
|
host_params=host_params,
|
|
520
470
|
command_groups=command_groups,
|
|
@@ -537,6 +487,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
537
487
|
|
|
538
488
|
async def _deploy(
|
|
539
489
|
self,
|
|
490
|
+
api: Gnetcli,
|
|
540
491
|
device: Device,
|
|
541
492
|
host_params: HostParams,
|
|
542
493
|
command_groups: list[tuple[str, CommandList]],
|
|
@@ -547,11 +498,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
|
|
|
547
498
|
results = []
|
|
548
499
|
if files:
|
|
549
500
|
tracker.upload_files(list(files))
|
|
550
|
-
await
|
|
501
|
+
await api.upload(hostname=device.fqdn, files=files, host_params=host_params)
|
|
551
502
|
tracker.files_uploaded()
|
|
552
503
|
|
|
553
504
|
old_group_name = ""
|
|
554
|
-
async with
|
|
505
|
+
async with api.cmd_session(hostname=device.fqdn) as session:
|
|
555
506
|
for group_number, (group_name, cmdlist) in enumerate(command_groups):
|
|
556
507
|
if group_name != old_group_name:
|
|
557
508
|
tracker.start_group(group_name)
|
gnetcli_adapter-2.5.2/README.md
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|