gnetcli-adapter 2.5.2__tar.gz → 2.7.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gnetcli_adapter
3
- Version: 2.5.2
3
+ Version: 2.7.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.90
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
+
@@ -7,7 +7,7 @@ name = "gnetcli_adapter"
7
7
  dependencies = [
8
8
  "exceptiongroup>=1.1.3; python_version<'3.11'",
9
9
  "annet>=3.2.0",
10
- "gnetclisdk>=1.0.90",
10
+ "gnetclisdk>=1.1.0",
11
11
  "pydantic_settings",
12
12
  "pydantic"
13
13
  ]
@@ -1,9 +1,12 @@
1
+ import abc
1
2
  import traceback
2
3
 
3
4
  import asyncio
4
- import json
5
- import subprocess
6
- import time
5
+ import base64
6
+ from collections.abc import AsyncIterator, Iterable
7
+ from contextlib import asynccontextmanager
8
+ import logging
9
+ from typing import Any, Dict, List, Optional, Tuple
7
10
 
8
11
  import annet.annlib.command
9
12
 
@@ -12,26 +15,21 @@ from annet.annlib.command import Command, CommandList
12
15
  from annet.annlib.netdev.views.hardware import HardwareView
13
16
  from annet.adapters.netbox.common.models import NetboxDevice
14
17
  from annet.rulebook import common
15
-
16
18
  from annet.connectors import AdapterWithConfig, AdapterWithName
17
- from typing import Dict, List, Any, Optional, Tuple
18
19
  from annet.storage import Device
19
-
20
- from gnetcli_adapter.progress_tracker import (
21
- ProgressTracker, ProgressBarTracker, FileProgressTracker, LogProgressTracker, CompositeTracker,
22
- )
23
- from gnetclisdk.client import Credentials, Gnetcli, HostParams, QA, File, GnetcliSessionCmd
20
+ from gnetclisdk.config import Config
21
+ from gnetclisdk.starter import DEFAULT_GNETCLI_SERVER_CONF, GnetcliStarter
22
+ from gnetclisdk.client import Credentials, Gnetcli, HostParams, QA, File
24
23
  from gnetclisdk.exceptions import EOFError
25
24
  import gnetclisdk.proto.server_pb2 as pb
26
25
  from pydantic import Field, field_validator, FieldValidationInfo
27
26
  from pydantic_core import PydanticUndefined
28
27
  from pydantic_settings import BaseSettings, SettingsConfigDict
29
- import base64
30
- import logging
31
- import threading
32
- import atexit
33
- import shutil
34
- import os.path
28
+
29
+ from gnetcli_adapter.progress_tracker import (
30
+ ProgressTracker, ProgressBarTracker, FileProgressTracker, LogProgressTracker, CompositeTracker,
31
+ )
32
+
35
33
  try:
36
34
  from builtins import ( # type: ignore[attr-defined, unused-ignore]
37
35
  ExceptionGroup,
@@ -55,23 +53,9 @@ breed_to_device = {
55
53
  "vrp55": "huawei",
56
54
  }
57
55
 
58
- DEFAULT_GNETCLI_SERVER_CONF = """
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"
56
+
74
57
  DEFAULT_GNETCLI_SERVER_PATH = "gnetcli_server"
58
+
75
59
  _logger = logging.getLogger(__name__)
76
60
 
77
61
 
@@ -80,7 +64,7 @@ class AppSettings(BaseSettings):
80
64
 
81
65
  url: Optional[str] = None
82
66
  server_path: str = DEFAULT_GNETCLI_SERVER_PATH
83
- server_conf: str = DEFAULT_GNETCLI_SERVER_CONF
67
+ server_conf: Config = DEFAULT_GNETCLI_SERVER_CONF
84
68
  insecure_grpc: bool = Field(default=True)
85
69
  login: Optional[str] = None
86
70
  password: Optional[str] = None
@@ -128,29 +112,6 @@ async def get_config(breed: str) -> List[str]:
128
112
  raise Exception("unknown breed %r" % breed)
129
113
 
130
114
 
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
115
  async def gather_with_concurrency(n: int, *coros: list[asyncio.Task]):
155
116
  if n == 0:
156
117
  return await asyncio.gather(*coros, return_exceptions=True)
@@ -182,54 +143,21 @@ def get_device_ip(dev: Device) -> Optional[str]:
182
143
  _logger.warning("get device ip error: %s", e)
183
144
  return None
184
145
 
146
+ class ApiMaker(metaclass=abc.ABCMeta):
147
+ conf: AppSettings
148
+
149
+ @asynccontextmanager
150
+ async def make_api(self) -> AsyncIterator[Gnetcli]:
151
+ async with GnetcliStarter(self.conf.server_path, self.conf.server_conf) as gnetcli_url:
152
+ yield Gnetcli(
153
+ server=gnetcli_url,
154
+ auth_token=self.conf.make_server_credentials(),
155
+ insecure_grpc=self.conf.insecure_grpc,
156
+ user_agent="annet",
157
+ )
185
158
 
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
- class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
159
+
160
+ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName, ApiMaker):
233
161
  def __init__(
234
162
  self,
235
163
  url: Optional[str] = None,
@@ -239,7 +167,7 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
239
167
  dev_password: Optional[str] = None,
240
168
  ssh_agent_enabled: bool = True,
241
169
  server_path: Optional[str] = None,
242
- server_conf: str = DEFAULT_GNETCLI_SERVER_CONF,
170
+ server_conf: Config = DEFAULT_GNETCLI_SERVER_CONF,
243
171
  ):
244
172
  conf_args = {
245
173
  "login": login,
@@ -252,7 +180,6 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
252
180
  "ssh_agent_enabled": ssh_agent_enabled,
253
181
  }
254
182
  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
183
 
257
184
  @classmethod
258
185
  def name(cls) -> str:
@@ -268,11 +195,25 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
268
195
  # TODO: implement fetch_packages
269
196
  return {}, {}
270
197
 
271
- async def fetch(self,
272
- devices: List[Device],
273
- files_to_download: Optional[Dict[Device, List[str]]] = None,
274
- processes: int = 1,
275
- max_slots: int = 0,
198
+ async def fetch(
199
+ self,
200
+ devices: List[Device],
201
+ files_to_download: Optional[Dict[Device, List[str]]] = None,
202
+ processes: int = 1,
203
+ max_slots: int = 0,
204
+ ):
205
+ if not devices:
206
+ return {}, {}
207
+ async with self.make_api() as api:
208
+ return await self._fetch(api, devices, files_to_download, processes, max_slots)
209
+
210
+ async def _fetch(
211
+ self,
212
+ api: Gnetcli,
213
+ devices: List[Device],
214
+ files_to_download: Optional[Dict[Device, List[str]]] = None,
215
+ processes: int = 1,
216
+ max_slots: int = 0,
276
217
  ):
277
218
  running = {}
278
219
  failed_running = {}
@@ -282,12 +223,12 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
282
223
  if files_to_download or device.is_pc():
283
224
  if files_to_download:
284
225
  files = files_to_download.get(device, [])
285
- task = self.adownload_dev(device=device, files=files)
226
+ task = self.adownload_dev(api=api, device=device, files=files)
286
227
  tasks[task] = device
287
228
  else:
288
229
  running[device] = {}
289
230
  else:
290
- task = self.afetch_dev(device=device)
231
+ task = self.afetch_dev(api=api, device=device)
291
232
  tasks[task] = device
292
233
 
293
234
  if not tasks:
@@ -306,14 +247,14 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
306
247
  running[device] = dev_res
307
248
  return running, failed_running
308
249
 
309
- async def afetch_dev(self, device: Device) -> str:
250
+ async def afetch_dev(self, api: Gnetcli, device: Device) -> str:
310
251
  cmds = await get_config(breed=device.breed)
311
252
  # map annet breed to gnetcli device type
312
253
  gnetcli_device = breed_to_device.get(device.breed, device.breed)
313
254
  dev_result = []
314
255
  ip = get_device_ip(device)
315
256
  for cmd in cmds:
316
- res = await self.api.cmd(
257
+ res = await api.cmd(
317
258
  hostname=device.fqdn,
318
259
  cmd=cmd,
319
260
  host_params=HostParams(
@@ -327,10 +268,10 @@ class GnetcliFetcher(Fetcher, AdapterWithConfig, AdapterWithName):
327
268
  dev_result.append(res.out)
328
269
  return b"\n".join(dev_result).decode()
329
270
 
330
- async def adownload_dev(self, device: Device, files: List[str]) -> Dict[str, str]:
271
+ async def adownload_dev(self, api: Gnetcli, device: Device, files: List[str]) -> Dict[str, str]:
331
272
  gnetcli_device = breed_to_device.get(device.breed, device.breed)
332
273
  ip = get_device_ip(device)
333
- downloaded = await self.api.download(
274
+ downloaded = await api.download(
334
275
  hostname=device.fqdn,
335
276
  paths=files,
336
277
  host_params=HostParams(
@@ -355,30 +296,7 @@ def parse_annet_qa(qa: list[annet.annlib.command.Question]) -> list[QA]:
355
296
  return res
356
297
 
357
298
 
358
- def make_api(conf: AppSettings) -> Gnetcli:
359
- gnetcli_url = _local_gnetcli_url
360
- if not conf.url:
361
- check_gnetcli_server(server_path=conf.server_path, config=conf.server_conf)
362
- if _local_gnetcli_url is None:
363
- _logger.info("waiting for _local_gnetcli_url appears")
364
- start = time.monotonic()
365
- while time.monotonic() - start < 5:
366
- if _local_gnetcli_p is not None and _local_gnetcli_p.returncode is not None:
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
379
-
380
-
381
- class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
299
+ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName, ApiMaker):
382
300
  def __init__(
383
301
  self,
384
302
  url: Optional[str] = None,
@@ -388,7 +306,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
388
306
  dev_password: Optional[str] = None,
389
307
  ssh_agent_enabled: bool = True,
390
308
  server_path: Optional[str] = None,
391
- server_conf: Optional[str] = DEFAULT_GNETCLI_SERVER_CONF,
309
+ server_conf: Optional[Config] = DEFAULT_GNETCLI_SERVER_CONF,
392
310
  logs_dir: Optional[str] = None,
393
311
  ):
394
312
  conf_args = {
@@ -402,7 +320,6 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
402
320
  "ssh_agent_enabled": ssh_agent_enabled,
403
321
  }
404
322
  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
323
  self.logs_dir = logs_dir
407
324
 
408
325
  @classmethod
@@ -413,7 +330,29 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
413
330
  def with_config(cls, **kwargs: Any) -> DeployDriver:
414
331
  return cls(**kwargs)
415
332
 
416
- async def bulk_deploy(self, deploy_cmds: dict[Device, CommandList], args: DeployOptions, progress_bar: ProgressBar | None = None) -> DeployResult:
333
+ async def bulk_deploy(
334
+ self,
335
+ deploy_cmds: dict[Device, CommandList],
336
+ args: DeployOptions,
337
+ progress_bar: ProgressBar | None = None,
338
+ ) -> DeployResult:
339
+ if not deploy_cmds:
340
+ return DeployResult(hostnames=[], results={}, durations={}, original_states={})
341
+ async with self.make_api() as api:
342
+ return await self._bulk_deploy(
343
+ api=api,
344
+ deploy_cmds=deploy_cmds,
345
+ args=args,
346
+ progress_bar=progress_bar,
347
+ )
348
+
349
+ async def _bulk_deploy(
350
+ self,
351
+ api: Gnetcli,
352
+ deploy_cmds: dict[Device, CommandList],
353
+ args: DeployOptions,
354
+ progress_bar: ProgressBar | None = None,
355
+ ) -> DeployResult:
417
356
  if progress_bar:
418
357
  for host, cmds in deploy_cmds.items():
419
358
  progress_bar.set_progress(host.fqdn, 0, len(cmds))
@@ -427,11 +366,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
427
366
  pass
428
367
  deploy_items = deploy_cmds.items()
429
368
  if max_parallel == 1:
430
- result = await self.serial_deploy(deploy_items, args, progress_bar)
369
+ result = await self.serial_deploy(api, deploy_items, args, progress_bar)
431
370
  else:
432
371
  result = await gather_with_concurrency(
433
372
  max_parallel,
434
- *[self.deploy(device, cmds, args, progress_bar) for device, cmds in deploy_items],
373
+ *[self.deploy(api, device, cmds, args, progress_bar) for device, cmds in deploy_items],
435
374
  )
436
375
  res = DeployResult(hostnames=[], results={}, durations={}, original_states={})
437
376
  res.add_results(results={dev.fqdn: dev_res for (dev, _), dev_res in zip(deploy_items, result)})
@@ -452,7 +391,9 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
452
391
  return reload_cmds
453
392
 
454
393
  def _get_total(
455
- self, command_groups: list[tuple[str, CommandList]], files: dict[str, File],
394
+ self,
395
+ command_groups: list[tuple[str, CommandList]],
396
+ files: dict[str, File],
456
397
  ) -> int:
457
398
  run_cmds = 0
458
399
  for _, cmds in command_groups:
@@ -461,7 +402,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
461
402
  run_cmds += 1
462
403
  return run_cmds
463
404
 
464
- def _init_progress_tracker(self, device: Device, progress_bar: ProgressBar | None) -> ProgressTracker:
405
+ def _init_progress_tracker(
406
+ self,
407
+ device: Device,
408
+ progress_bar: ProgressBar | None,
409
+ ) -> ProgressTracker:
465
410
  tracker = CompositeTracker(LogProgressTracker(device))
466
411
  if progress_bar:
467
412
  tracker.add_tracker(ProgressBarTracker(device, progress_bar))
@@ -469,7 +414,13 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
469
414
  tracker.add_tracker(FileProgressTracker(device, self.logs_dir))
470
415
  return tracker
471
416
 
472
- async def serial_deploy(self, deploy_items, args, progress_bar: ProgressBar | None = None):
417
+ async def serial_deploy(
418
+ self,
419
+ api: Gnetcli,
420
+ deploy_items: Iterable[tuple[Device, CommandList]],
421
+ args: DeployOptions,
422
+ progress_bar: ProgressBar | None = None,
423
+ ):
473
424
  res = {}
474
425
  for device, cmds in deploy_items:
475
426
  if progress_bar:
@@ -481,16 +432,17 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
481
432
  break
482
433
  except Exception:
483
434
  pass
484
- dev_res = await self.deploy(device, cmds, args, progress_bar)
435
+ dev_res = await self.deploy(api, device, cmds, args, progress_bar)
485
436
  res[device] = dev_res
486
437
  return res
487
438
 
488
439
  async def deploy(
489
- self,
490
- device: Device,
491
- cmds: CommandList,
492
- args: DeployOptions,
493
- progress_bar: ProgressBar | None = None,
440
+ self,
441
+ api: Gnetcli,
442
+ device: Device,
443
+ cmds: CommandList,
444
+ args: DeployOptions,
445
+ progress_bar: ProgressBar | None = None,
494
446
  ) -> Exception | None:
495
447
  gnetcli_device = breed_to_device.get(device.breed, device.breed)
496
448
  ip = get_device_ip(device)
@@ -515,6 +467,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
515
467
  tracker.set_total(self._get_total(command_groups, files))
516
468
  try:
517
469
  seen_exc, results = await self._deploy(
470
+ api=api,
518
471
  device=device,
519
472
  host_params=host_params,
520
473
  command_groups=command_groups,
@@ -537,6 +490,7 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
537
490
 
538
491
  async def _deploy(
539
492
  self,
493
+ api: Gnetcli,
540
494
  device: Device,
541
495
  host_params: HostParams,
542
496
  command_groups: list[tuple[str, CommandList]],
@@ -547,11 +501,11 @@ class GnetcliDeployer(DeployDriver, AdapterWithConfig, AdapterWithName):
547
501
  results = []
548
502
  if files:
549
503
  tracker.upload_files(list(files))
550
- await self.api.upload(hostname=device.fqdn, files=files, host_params=host_params)
504
+ await api.upload(hostname=device.fqdn, files=files, host_params=host_params)
551
505
  tracker.files_uploaded()
552
506
 
553
507
  old_group_name = ""
554
- async with self.api.cmd_session(hostname=device.fqdn) as session:
508
+ async with api.cmd_session(hostname=device.fqdn) as session:
555
509
  for group_number, (group_name, cmdlist) in enumerate(command_groups):
556
510
  if group_name != old_group_name:
557
511
  tracker.start_group(group_name)
@@ -1,2 +0,0 @@
1
- # gnetcli_adapter
2
- This package provides deployer and fetcher adapters for Annet
File without changes