annet 3.1.4__py3-none-any.whl → 3.3.0__py3-none-any.whl

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.

@@ -3,6 +3,7 @@ from datetime import datetime, timezone
3
3
  from typing import Optional
4
4
  from annet.adapters.netbox.common.models import InterfaceType, IpFamily, Label, Prefix, Entity
5
5
  from annet.adapters.netbox.v41.models import InterfaceV41, IpAddressV41, NetboxDeviceV41
6
+ import annetbox.v42.models
6
7
 
7
8
 
8
9
  @dataclass
@@ -61,3 +62,8 @@ class NetboxDeviceV42(NetboxDeviceV41):
61
62
  connected_endpoints=[],
62
63
  mode=None,
63
64
  )
65
+
66
+
67
+ # should be unified in case of netbox update
68
+ Vrf = annetbox.v42.models.Vrf
69
+ Vlan = annetbox.v42.models.Vlan
@@ -1,4 +1,6 @@
1
1
  import ssl
2
+ from typing import Optional
3
+
2
4
  from adaptix import P
3
5
  from adaptix.conversion import get_converter, link, link_constant, link_function
4
6
  from annetbox.v42 import client_sync
@@ -6,7 +8,8 @@ from annetbox.v42 import models as api_models
6
8
 
7
9
  from annet.adapters.netbox.common.adapter import NetboxAdapter, get_device_breed, get_device_hw
8
10
  from annet.adapters.netbox.common.storage_base import BaseNetboxStorage
9
- from annet.adapters.netbox.v42.models import InterfaceV42, NetboxDeviceV42, PrefixV42, IpAddressV42
11
+ from annet.adapters.netbox.common.storage_opts import NetboxStorageOpts
12
+ from annet.adapters.netbox.v42.models import InterfaceV42, NetboxDeviceV42, PrefixV42, IpAddressV42, Vlan, Vrf
10
13
  from annet.storage import Storage
11
14
 
12
15
 
@@ -79,8 +82,21 @@ class NetboxV42Adapter(NetboxAdapter[NetboxDeviceV42, InterfaceV42, IpAddressV42
79
82
  def list_ipprefixes(self, prefixes: list[str]) -> list[PrefixV42]:
80
83
  return self.convert_ip_prefixes(self.netbox.ipam_all_prefixes(prefix=prefixes).results)
81
84
 
85
+ def list_all_vrfs(self) -> list[Vrf]:
86
+ return self.netbox.ipam_all_vrfs().results
87
+
88
+ def list_all_vlans(self) -> list[Vlan]:
89
+ return self.netbox.ipam_all_vlans().results
90
+
82
91
 
83
92
  class NetboxStorageV42(BaseNetboxStorage[NetboxDeviceV42, InterfaceV42, IpAddressV42, PrefixV42]):
93
+ netbox: NetboxV42Adapter
94
+
95
+ def __init__(self, opts: Optional[NetboxStorageOpts] = None):
96
+ super().__init__(opts)
97
+ self._all_vlans: list[Vlan] | None = None
98
+ self._all_vrfs: list[Vrf] | None = None
99
+
84
100
  def _init_adapter(
85
101
  self,
86
102
  url: str,
@@ -89,3 +105,13 @@ class NetboxStorageV42(BaseNetboxStorage[NetboxDeviceV42, InterfaceV42, IpAddres
89
105
  threads: int,
90
106
  ) -> NetboxAdapter[NetboxDeviceV42, InterfaceV42, IpAddressV42, PrefixV42]:
91
107
  return NetboxV42Adapter(self, url, token, ssl_context, threads)
108
+
109
+ def resolve_all_vlans(self) -> list[Vlan]:
110
+ if self._all_vlans is None:
111
+ self._all_vlans = self.netbox.list_all_vlans()
112
+ return self._all_vlans
113
+
114
+ def resolve_all_vrfs(self) -> list[Vrf]:
115
+ if self._all_vrfs is None:
116
+ self._all_vrfs = self.netbox.list_all_vrfs()
117
+ return self._all_vrfs
@@ -2,9 +2,9 @@ import os
2
2
  import typing
3
3
  from collections import OrderedDict as odict
4
4
 
5
- from annet.annlib.command import CommandList, Command
6
-
5
+ from annet.annlib.command import Command, CommandList
7
6
  from annet.types import Op
7
+ from annet.vendors import registry_connector
8
8
 
9
9
 
10
10
  # =====
@@ -287,87 +287,5 @@ class ApplyItem(typing.NamedTuple):
287
287
  after: CommandList
288
288
 
289
289
 
290
- def apply(hw, do_commit, do_finalize, **_):
291
- before, after = CommandList(), CommandList()
292
- if hw.Huawei:
293
- before.add_cmd(Command("system-view"))
294
- if do_commit and (hw.Huawei.CE or hw.Huawei.NE):
295
- after.add_cmd(Command("commit"))
296
- after.add_cmd(Command("q"))
297
- if do_finalize:
298
- after.add_cmd(Command("save", timeout=20))
299
- elif hw.Arista:
300
- before.add_cmd(Command("conf s"))
301
- if do_commit:
302
- after.add_cmd(Command("commit"))
303
- else:
304
- after.add_cmd(Command("abort")) # просто exit оставит висеть configure session
305
- if do_finalize:
306
- after.add_cmd(Command("write memory"))
307
- elif hw.ASR or hw.XRV or hw.XR:
308
- # коммит сам сохраняет изменения в стартап do_finalize не применим
309
- before.add_cmd(Command("configure exclusive"))
310
- if do_commit:
311
- after.add_cmd(Command("commit"))
312
- after.add_cmd(Command("exit"))
313
- elif hw.Cisco or hw.Nexus:
314
- # классический ios и nxos не умеет коммиты
315
- before.add_cmd(Command("conf t"))
316
- after.add_cmd(Command("exit"))
317
- if do_finalize:
318
- after.add_cmd(Command("copy running-config startup-config", timeout=40))
319
- elif hw.Juniper:
320
- # коммит сам сохраняет изменения в стартап do_finalize не применим
321
- before.add_cmd(Command("configure exclusive"))
322
- if do_commit:
323
- after.add_cmd(Command("commit", timeout=30))
324
- after.add_cmd(Command("exit"))
325
- elif hw.PC:
326
- if hw.soft.startswith(("Cumulus", "SwitchDev")):
327
- if os.environ.get("ETCKEEPER_CHECK", False):
328
- before.add_cmd(Command("etckeeper check"))
329
- elif hw.Nokia:
330
- # коммит сам сохраняет изменения в стартап do_finalize не применим
331
- before.add_cmd(Command("configure private"))
332
- if do_commit:
333
- after.add_cmd(Command("commit"))
334
- elif hw.RouterOS:
335
- # FIXME: пока не удалось победить \x1b[c после включения safe mode
336
- # if len(cmds) > 99:
337
- # raise Exception("RouterOS does not support more 100 actions in safe mode")
338
- # before.add_cmd(RosDevice.SAFE_MODE)
339
- pass
340
- # after.add_cmd(RosDevice.SAFE_MODE)
341
- elif hw.Aruba:
342
- before.add_cmd(Command("conf t"))
343
- after.add_cmd(Command("end"))
344
- if do_commit:
345
- after.add_cmd(Command("commit apply"))
346
- if do_finalize:
347
- after.add_cmd(Command("write memory"))
348
- elif hw.Ribbon:
349
- # коммит сам сохраняет изменения в стартап do_finalize не применим
350
- before.add_cmd(Command("configure exclusive"))
351
- if do_commit:
352
- after.add_cmd(Command("commit", timeout=30))
353
- after.add_cmd(Command("exit"))
354
- elif hw.B4com.CS2148P:
355
- before.add_cmd(Command("conf t"))
356
- after.add_cmd(Command("end"))
357
- if do_finalize:
358
- after.add_cmd(Command("write", timeout=40))
359
- elif hw.B4com:
360
- before.add_cmd(Command("conf t"))
361
- if do_commit:
362
- after.add_cmd(Command("commit"))
363
- after.add_cmd(Command("end"))
364
- if do_finalize:
365
- after.add_cmd(Command("write", timeout=40))
366
- elif hw.H3C:
367
- before.add_cmd(Command("system-view"))
368
- if do_finalize:
369
- after.add_cmd(Command("save force", timeout=90))
370
- else:
371
- raise Exception("unknown hw %s" % hw)
372
-
373
- return before, after
290
+ def apply(hw, do_commit, do_finalize, path):
291
+ return registry_connector.get().match(hw).apply(hw, do_commit, do_finalize, path)
@@ -157,7 +157,7 @@ def run_partial_generators(
157
157
 
158
158
 
159
159
  @tracing.function(name="run_partial_generator")
160
- def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> Optional[GeneratorPartialResult]:
160
+ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRunArgs) -> GeneratorPartialResult | None:
161
161
  logger = get_logger(generator=_make_generator_ctx(gen))
162
162
  device = run_args.device
163
163
  output = ""
@@ -207,7 +207,9 @@ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRu
207
207
 
208
208
  if run_args.use_acl:
209
209
  try:
210
- with tracing_connector.get().start_as_current_span("apply_acl", tracer_name=__name__, min_duration="0.01") as acl_span:
210
+ with tracing_connector.get().start_as_current_span(
211
+ "apply_acl", tracer_name=__name__, min_duration="0.01"
212
+ ) as acl_span:
211
213
  tracing_connector.get().set_device_attributes(acl_span, run_args.device)
212
214
  config = patching.apply_acl(
213
215
  config=config,
@@ -265,8 +267,8 @@ def check_entire_generators_required_packages(gens, device_packages: FrozenSet[s
265
267
 
266
268
  @tracing.function
267
269
  def run_file_generators(
268
- gens: Iterable[Union["JSONFragment", "Entire"]],
269
- device: "Device",
270
+ gens: Iterable[Union["JSONFragment", "Entire"]],
271
+ device: "Device",
270
272
  ) -> RunGeneratorResult:
271
273
  """Run generators that generate files or file parts."""
272
274
  ret = RunGeneratorResult()
@@ -295,21 +297,22 @@ def run_file_generators(
295
297
  @tracing.function(min_duration="0.5")
296
298
  def _run_entire_generator(gen: "Entire", device: "Device") -> Optional[GeneratorResult]:
297
299
  logger = get_logger(generator=_make_generator_ctx(gen))
298
- if not gen.supports_device(device):
299
- logger.info("generator %s is not supported for device %s", gen, device.hostname)
300
- return
301
-
302
300
  span = tracing_connector.get().get_current_span()
303
301
  if span:
304
302
  tracing_connector.get().set_device_attributes(span, device)
305
303
  tracing_connector.get().set_dimensions_attributes(span, gen, device)
306
304
 
307
- path = gen.path(device)
308
- if not path:
309
- raise RuntimeError("entire generator should return non-empty path")
310
-
311
- logger.info("Generating ENTIRE ...")
312
305
  with GeneratorPerfMesurer(gen, trace_min_duration="0.5") as pm:
306
+ if not gen.supports_device(device):
307
+ logger.debug("generator %s is not supported for device %s", gen, device.hostname)
308
+ return
309
+
310
+ path = gen.path(device)
311
+ if not path:
312
+ raise RuntimeError("entire generator should return non-empty path")
313
+
314
+ logger.info("Generating ENTIRE ...")
315
+
313
316
  output = gen(device)
314
317
 
315
318
  return GeneratorEntireResult(
@@ -329,35 +332,37 @@ def _make_generator_ctx(gen):
329
332
 
330
333
 
331
334
  def _run_json_fragment_generator(
332
- gen: "JSONFragment",
333
- device: "Device",
335
+ gen: "JSONFragment",
336
+ device: "Device",
334
337
  ) -> Optional[GeneratorResult]:
335
338
  logger = get_logger(generator=_make_generator_ctx(gen))
336
- if not gen.supports_device(device):
337
- logger.info("generator %s is not supported for device %s", gen, device.hostname)
338
- return
339
-
340
- path = gen.path(device)
341
- if not path:
342
- raise RuntimeError("json fragment generator should return non-empty path")
343
-
344
- acl_item_or_list_of_items = gen.acl(device)
345
- safe_acl_item_or_list_of_items = gen.acl_safe(device)
346
- if not acl_item_or_list_of_items:
347
- raise RuntimeError("json fragment generator should return non-empty acl")
348
- if isinstance(acl_item_or_list_of_items, list):
349
- acl = acl_item_or_list_of_items
350
- else:
351
- acl = [acl_item_or_list_of_items]
352
- if isinstance(safe_acl_item_or_list_of_items, list):
353
- acl_safe = safe_acl_item_or_list_of_items
354
- else:
355
- acl_safe = [safe_acl_item_or_list_of_items]
356
-
357
- logger.info("Generating JSON_FRAGMENT ...")
339
+
358
340
  with GeneratorPerfMesurer(gen) as pm:
341
+ if not gen.supports_device(device):
342
+ logger.info("generator %s is not supported for device %s", gen, device.hostname)
343
+ return
344
+
345
+ path = gen.path(device)
346
+ if not path:
347
+ raise RuntimeError("json fragment generator should return non-empty path")
348
+
349
+ acl_item_or_list_of_items = gen.acl(device)
350
+ safe_acl_item_or_list_of_items = gen.acl_safe(device)
351
+ if not acl_item_or_list_of_items:
352
+ raise RuntimeError("json fragment generator should return non-empty acl")
353
+ if isinstance(acl_item_or_list_of_items, list):
354
+ acl = acl_item_or_list_of_items
355
+ else:
356
+ acl = [acl_item_or_list_of_items]
357
+ if isinstance(safe_acl_item_or_list_of_items, list):
358
+ acl_safe = safe_acl_item_or_list_of_items
359
+ else:
360
+ acl_safe = [safe_acl_item_or_list_of_items]
361
+
362
+ logger.info("Generating JSON_FRAGMENT ...")
363
+
359
364
  config = gen(device)
360
- reload_cmds = gen.get_reload_cmds(device)
365
+ reload_cmds = gen.get_reload_cmds(device)
361
366
  return GeneratorJSONFragmentResult(
362
367
  name=gen.__class__.__name__,
363
368
  tags=gen.TAGS,
@@ -407,7 +412,8 @@ def _load_gen_module(module_path: str):
407
412
  except ModuleNotFoundError as e:
408
413
  try: # maybe it's a path to module
409
414
  module_abs_path = os.path.abspath(module_path)
410
- module = importlib.machinery.SourceFileLoader(re.sub(r"[./]", "_", module_abs_path).strip("_"), module_abs_path).load_module()
415
+ module = importlib.machinery.SourceFileLoader(re.sub(r"[./]", "_", module_abs_path).strip("_"),
416
+ module_abs_path).load_module()
411
417
  except ModuleNotFoundError:
412
418
  raise e
413
419
  return module
@@ -3,19 +3,10 @@ from __future__ import annotations
3
3
  import pkgutil
4
4
  import re
5
5
  import types
6
- from typing import (
7
- FrozenSet,
8
- Iterable,
9
- List,
10
- Optional,
11
- Set,
12
- Union,
13
- )
14
-
15
- from annet.lib import (
16
- flatten,
17
- mako_render,
18
- )
6
+ from typing import FrozenSet, Iterable, List, Optional, Set, Union
7
+
8
+ from annet.lib import flatten, mako_render
9
+
19
10
  from .base import BaseGenerator, _filter_str
20
11
  from .exceptions import NotSupportedDevice
21
12
 
@@ -39,8 +30,7 @@ class Entire(BaseGenerator):
39
30
  def run(self, device) -> Union[None, str, Iterable[Union[str, tuple]]]:
40
31
  raise NotImplementedError
41
32
 
42
- def reload(self, device) -> Optional[
43
- str]: # pylint: disable=unused-argument
33
+ def reload(self, device) -> Optional[str]: # pylint: disable=unused-argument
44
34
  return
45
35
 
46
36
  def get_reload_cmds(self, device) -> str:
annet/generators/perf.py CHANGED
@@ -1,27 +1,19 @@
1
- from __future__ import annotations
2
-
3
1
  import time
4
- from typing import (
5
- Optional,
6
- Union,
7
- )
2
+ from typing import Optional, Union
8
3
 
9
4
  from annet import tracing
10
5
  from annet.tracing import tracing_connector
11
- from annet.types import (
12
- GeneratorPartialRunArgs,
13
- GeneratorPerf,
14
- )
15
- from .entire import Entire
16
- from .partial import PartialGenerator
6
+ from annet.types import GeneratorPartialRunArgs, GeneratorPerf
7
+
8
+ from .base import BaseGenerator
17
9
 
18
10
 
19
11
  class GeneratorPerfMesurer:
20
12
  def __init__(
21
- self,
22
- gen: Union[PartialGenerator, Entire],
23
- run_args: Optional[GeneratorPartialRunArgs] = None,
24
- trace_min_duration: tracing.MinDurationT = None
13
+ self,
14
+ gen: BaseGenerator,
15
+ run_args: Optional[GeneratorPartialRunArgs] = None,
16
+ trace_min_duration: tracing.MinDurationT = None
25
17
  ):
26
18
  self._gen = gen
27
19
  self._run_args = run_args
@@ -44,8 +36,7 @@ class GeneratorPerfMesurer:
44
36
  self._span = self._span_ctx.__enter__() # pylint: disable=unnecessary-dunder-call
45
37
 
46
38
  if self._span:
47
- self._span.set_attributes(
48
- {"generator.class": self._gen.__class__.__name__})
39
+ self._span.set_attributes({"generator.class": self._gen.__class__.__name__})
49
40
  if self._run_args:
50
41
  tracing_connector.get().set_device_attributes(
51
42
  self._span, self._run_args.device,
annet/vendors/base.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import abc
2
2
  from typing import ClassVar
3
3
 
4
+ from annet.annlib.command import CommandList
4
5
  from annet.annlib.netdev.views.hardware import HardwareView
5
6
  from annet.annlib.tabparser import CommonFormatter
6
7
 
@@ -12,6 +13,11 @@ class AbstractVendor(abc.ABC):
12
13
  def match(self) -> list[str]:
13
14
  raise NotImplementedError
14
15
 
16
+ def apply(
17
+ self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str
18
+ ) -> tuple[CommandList, CommandList]:
19
+ return CommandList(), CommandList()
20
+
15
21
  @property
16
22
  @abc.abstractmethod
17
23
  def reverse(self) -> str:
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import AristaFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,19 @@ from annet.vendors.registry import registry
8
9
  class AristaVendor(AbstractVendor):
9
10
  NAME = "arista"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("conf s"))
16
+ if do_commit:
17
+ after.add_cmd(Command("commit"))
18
+ else:
19
+ after.add_cmd(Command("abort")) # просто exit оставит висеть configure session
20
+ if do_finalize:
21
+ after.add_cmd(Command("write memory"))
22
+
23
+ return before, after
24
+
11
25
  def match(self) -> list[str]:
12
26
  return ["Arista"]
13
27
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import ArubaFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,18 @@ from annet.vendors.registry import registry
8
9
  class ArubaVendor(AbstractVendor):
9
10
  NAME = "aruba"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("conf t"))
16
+ after.add_cmd(Command("end"))
17
+ if do_commit:
18
+ after.add_cmd(Command("commit apply"))
19
+ if do_finalize:
20
+ after.add_cmd(Command("write memory"))
21
+
22
+ return before, after
23
+
11
24
  def match(self) -> list[str]:
12
25
  return ["Aruba"]
13
26
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import B4comFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,24 @@ from annet.vendors.registry import registry
8
9
  class B4ComVendor(AbstractVendor):
9
10
  NAME = "b4com"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ if hw.B4com.CS2148P:
16
+ before.add_cmd(Command("conf t"))
17
+ after.add_cmd(Command("end"))
18
+ if do_finalize:
19
+ after.add_cmd(Command("write", timeout=40))
20
+ else:
21
+ before.add_cmd(Command("conf t"))
22
+ if do_commit:
23
+ after.add_cmd(Command("commit"))
24
+ after.add_cmd(Command("end"))
25
+ if do_finalize:
26
+ after.add_cmd(Command("write", timeout=40))
27
+
28
+ return before, after
29
+
11
30
  def match(self) -> list[str]:
12
31
  return ["B4com"]
13
32
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import CiscoFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,16 @@ from annet.vendors.registry import registry
8
9
  class CiscoVendor(AbstractVendor):
9
10
  NAME = "cisco"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("conf t"))
16
+ after.add_cmd(Command("exit"))
17
+ if do_finalize:
18
+ after.add_cmd(Command("copy running-config startup-config", timeout=40))
19
+
20
+ return before, after
21
+
11
22
  def match(self) -> list[str]:
12
23
  return ["Cisco"]
13
24
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import HuaweiFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,15 @@ from annet.vendors.registry import registry
8
9
  class H3CVendor(AbstractVendor):
9
10
  NAME = "h3c"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("system-view"))
16
+ if do_finalize:
17
+ after.add_cmd(Command("save force", timeout=90))
18
+
19
+ return before, after
20
+
11
21
  def match(self) -> list[str]:
12
22
  return ["H3C"]
13
23
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import HuaweiFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,18 @@ from annet.vendors.registry import registry
8
9
  class HuaweiVendor(AbstractVendor):
9
10
  NAME = "huawei"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("system-view"))
16
+ if do_commit and (hw.Huawei.CE or hw.Huawei.NE):
17
+ after.add_cmd(Command("commit"))
18
+ after.add_cmd(Command("q"))
19
+ if do_finalize:
20
+ after.add_cmd(Command("save", timeout=20))
21
+
22
+ return before, after
23
+
11
24
  def match(self) -> list[str]:
12
25
  return ["Huawei"]
13
26
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import AsrFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,16 @@ from annet.vendors.registry import registry
8
9
  class IosXrVendor(AbstractVendor):
9
10
  NAME = "iosxr"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("configure exclusive"))
16
+ if do_commit:
17
+ after.add_cmd(Command("commit"))
18
+ after.add_cmd(Command("exit"))
19
+
20
+ return before, after
21
+
11
22
  def match(self) -> list[str]:
12
23
  return ["Cisco.ASR", "Cisco.XR", "Cisco.XRV"]
13
24
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import JuniperFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,16 @@ from annet.vendors.registry import registry
8
9
  class JuniperVendor(AbstractVendor):
9
10
  NAME = "juniper"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("configure exclusive"))
16
+ if do_commit:
17
+ after.add_cmd(Command("commit", timeout=30))
18
+ after.add_cmd(Command("exit"))
19
+
20
+ return before, after
21
+
11
22
  def match(self) -> list[str]:
12
23
  return ["Juniper"]
13
24
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import NexusFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,16 @@ from annet.vendors.registry import registry
8
9
  class NexusVendor(AbstractVendor):
9
10
  NAME = "nexus"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("conf t"))
16
+ after.add_cmd(Command("exit"))
17
+ if do_finalize:
18
+ after.add_cmd(Command("copy running-config startup-config", timeout=40))
19
+
20
+ return before, after
21
+
11
22
  def match(self) -> list[str]:
12
23
  return ["Cisco.Nexus"]
13
24
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import NokiaFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,15 @@ from annet.vendors.registry import registry
8
9
  class NokiaVendor(AbstractVendor):
9
10
  NAME = "nokia"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("configure private"))
16
+ if do_commit:
17
+ after.add_cmd(Command("commit"))
18
+
19
+ return before, after
20
+
11
21
  def match(self) -> list[str]:
12
22
  return ["Nokia"]
13
23
 
@@ -1,19 +1,16 @@
1
1
  from annet.annlib.netdev.views.hardware import HardwareView
2
2
  from annet.annlib.tabparser import OptixtransFormatter
3
- from annet.vendors.base import AbstractVendor
4
3
  from annet.vendors.registry import registry
5
4
 
5
+ from .huawei import HuaweiVendor
6
+
6
7
 
7
8
  @registry.register
8
- class OptixTransVendor(AbstractVendor):
9
+ class OptixTransVendor(HuaweiVendor):
9
10
  NAME = "optixtrans"
10
11
 
11
12
  def match(self) -> list[str]:
12
- return ["OptiXtrans"]
13
-
14
- @property
15
- def reverse(self) -> str:
16
- return "undo"
13
+ return ["Huawei.OptiXtrans"]
17
14
 
18
15
  @property
19
16
  def hardware(self) -> HardwareView:
@@ -22,6 +19,5 @@ class OptixTransVendor(AbstractVendor):
22
19
  def make_formatter(self, **kwargs) -> OptixtransFormatter:
23
20
  return OptixtransFormatter(**kwargs)
24
21
 
25
- @property
26
- def exit(self) -> str:
27
- return "quit"
22
+ def svi_name(self, num: int) -> str:
23
+ return f"vlan{num}"
@@ -1,3 +1,6 @@
1
+ import os
2
+
3
+ from annet.annlib.command import Command, CommandList
1
4
  from annet.annlib.netdev.views.hardware import HardwareView
2
5
  from annet.annlib.tabparser import CommonFormatter
3
6
  from annet.vendors.base import AbstractVendor
@@ -15,6 +18,15 @@ class PCVendor(AbstractVendor):
15
18
  def reverse(self) -> str:
16
19
  return "-"
17
20
 
21
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
22
+ before, after = CommandList(), CommandList()
23
+
24
+ if hw.soft.startswith(("Cumulus", "SwitchDev")):
25
+ if os.environ.get("ETCKEEPER_CHECK", False):
26
+ before.add_cmd(Command("etckeeper check"))
27
+
28
+ return before, after
29
+
18
30
  @property
19
31
  def hardware(self) -> HardwareView:
20
32
  return HardwareView("PC")
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import RibbonFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,16 @@ from annet.vendors.registry import registry
8
9
  class RibbonVendor(AbstractVendor):
9
10
  NAME = "ribbon"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ before.add_cmd(Command("configure exclusive"))
16
+ if do_commit:
17
+ after.add_cmd(Command("commit", timeout=30))
18
+ after.add_cmd(Command("exit"))
19
+
20
+ return before, after
21
+
11
22
  def match(self) -> list[str]:
12
23
  return ["Ribbon"]
13
24
 
@@ -1,3 +1,4 @@
1
+ from annet.annlib.command import Command, CommandList
1
2
  from annet.annlib.netdev.views.hardware import HardwareView
2
3
  from annet.annlib.tabparser import RosFormatter
3
4
  from annet.vendors.base import AbstractVendor
@@ -8,6 +9,18 @@ from annet.vendors.registry import registry
8
9
  class RouterOSVendor(AbstractVendor):
9
10
  NAME = "routeros"
10
11
 
12
+ def apply(self, hw: HardwareView, do_commit: bool, do_finalize: bool, path: str) -> tuple[CommandList, CommandList]:
13
+ before, after = CommandList(), CommandList()
14
+
15
+ # FIXME: пока не удалось победить \x1b[c после включения safe mode
16
+ # if len(cmds) > 99:
17
+ # raise Exception("RouterOS does not support more 100 actions in safe mode")
18
+ # before.add_cmd(RosDevice.SAFE_MODE)
19
+ pass
20
+ # after.add_cmd(RosDevice.SAFE_MODE)
21
+
22
+ return before, after
23
+
11
24
  def match(self) -> list[str]:
12
25
  return ["RouterOS"]
13
26
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annet
3
- Version: 3.1.4
3
+ Version: 3.3.0
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -21,7 +21,7 @@ Requires-Dist: yarl>=1.8.2
21
21
  Requires-Dist: adaptix==3.0.0b7
22
22
  Requires-Dist: dataclass-rest==0.4
23
23
  Provides-Extra: netbox
24
- Requires-Dist: annetbox[sync]>=0.3.0; extra == "netbox"
24
+ Requires-Dist: annetbox[sync]>=0.4.0; extra == "netbox"
25
25
  Dynamic: home-page
26
26
  Dynamic: license
27
27
  Dynamic: license-file
@@ -49,8 +49,8 @@ annet/adapters/netbox/v41/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
49
49
  annet/adapters/netbox/v41/models.py,sha256=nunqgxffobE2C3_g1bkHXWffStBdAJxHfx0eAbvnWAU,2068
50
50
  annet/adapters/netbox/v41/storage.py,sha256=h6e7V4Od5o7P3Ssr_vUQ1IBjsxdnug4YGvG1BokwKuk,3795
51
51
  annet/adapters/netbox/v42/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
52
- annet/adapters/netbox/v42/models.py,sha256=xo5TB6VE0KNvQwUB_zasl2bDbNuKCCjcsHB3P-JVLgk,1784
53
- annet/adapters/netbox/v42/storage.py,sha256=atiThgkDGVdJStvFCDn4JtPZfEAhUNbw5IH_W-ijFDg,3795
52
+ annet/adapters/netbox/v42/models.py,sha256=UWrnRh1k3a048XsVmqh50dF91dunrolIJzmTLT0QExw,1920
53
+ annet/adapters/netbox/v42/storage.py,sha256=iMhdrdAAYZXS_VHoqi_5azFB3BVmp4daK4TVCI9vhxM,4666
54
54
  annet/annlib/__init__.py,sha256=fT1l4xV5fqqg8HPw9HqmZVN2qwS8i6X1aIm2zGDjxKY,252
55
55
  annet/annlib/command.py,sha256=uuBddMQphtn8P5MO5kzIa8_QrtMns-k05VeKv1bcAuA,1043
56
56
  annet/annlib/diff.py,sha256=MZ6eQAU3cadQp8KaSE6uAYFtcfMDCIe_eNuVROnYkCk,4496
@@ -76,17 +76,17 @@ annet/annlib/rbparser/ordering.py,sha256=XknggB92N4IbhHBZZHL0StwCCITt8mkho4D4Nu5
76
76
  annet/annlib/rbparser/platform.py,sha256=d1jFH8hGMOf_qiveKE4H-c0OfNnzwmt2VYpSiSOn9qc,42
77
77
  annet/annlib/rbparser/syntax.py,sha256=iZ7Y-4QQBw4L3UtjEh54qisiRDhobl7HZxFNdP8mi54,3577
78
78
  annet/annlib/rulebook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
- annet/annlib/rulebook/common.py,sha256=h2x-NWnOboED4jUHdywbB0lcl1wbI1hmlVF4n2l87OU,17016
79
+ annet/annlib/rulebook/common.py,sha256=2ZMu7Z47Ip7d8yMjihDNKKhwl9aV5U4IeCcyXM4CjeA,13527
80
80
  annet/api/__init__.py,sha256=EMGUgt3pTi2MDF8DfXlS8PGX2zT0iGLY01R6QlQHvGg,33305
81
81
  annet/configs/context.yml,sha256=RVLrKLIHpCty7AGwOnmqf7Uu0iZQCn-AjYhophDJer8,259
82
82
  annet/configs/logging.yaml,sha256=EUagfir99QqA73Scc3k7sfQccbU3E1SvEQdyhLFtCl4,997
83
- annet/generators/__init__.py,sha256=wJbNLI1bMppE71tnjJrqNGtbtLZ_gdRd-HytdZPzBiY,16451
83
+ annet/generators/__init__.py,sha256=p71npgfOVie1jtbkGd_Uw_sD1STl6-HdddcfcPOBMVg,16632
84
84
  annet/generators/base.py,sha256=rgQLcQBPZm4ecbKmRhVOpBR-GFJAiVfdb_y5f2-LUR8,3670
85
- annet/generators/entire.py,sha256=mfnKtn5GcHiqjGjuhETkOVm-0ppMELAPsmg5jQrYTZc,2922
85
+ annet/generators/entire.py,sha256=jFTbx9BKoiXMstq_QeesRH2yi01LHyYHxgCtvvwBmeM,2872
86
86
  annet/generators/exceptions.py,sha256=GPXTBgn2xZ3Ev_bdOPlfCLGRC1kQHe_dEq88S-uyi5s,151
87
87
  annet/generators/jsonfragment.py,sha256=Cl43t9_OtNWRxesk3B69h60KvD37tiK9W7sLLmppAT4,4379
88
88
  annet/generators/partial.py,sha256=XI01KDA--XwjSEU33SOQCCJZRXFq5boRz1uJA8lVA1g,3502
89
- annet/generators/perf.py,sha256=K72ivUuXbNXrsHrLeKWhGmczGYWsB7kUDdDzqOX6j3c,2370
89
+ annet/generators/perf.py,sha256=IaAcfEVtX7UNO11VOCXzp-FPj_tOx_CDQ34HGThCn4c,2225
90
90
  annet/generators/ref.py,sha256=QVdeL8po1D0kBsVLOpCjFR81D8yNTk-kaQj5WUM4hng,438
91
91
  annet/generators/result.py,sha256=zMAvGOYQU803bGy6datZduHLgrEqK2Zba_Jcf9Qn9p0,4976
92
92
  annet/generators/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -180,25 +180,25 @@ annet/rulebook/texts/ribbon.rul,sha256=609LyLTDCtXPVQNAzqS-VEyCpW3byHP8TCMJLFMn5
180
180
  annet/rulebook/texts/routeros.order,sha256=M71uy_hf0KAjLNS3zZY3uih4m2xLUcu26FEoVVjC6k0,905
181
181
  annet/rulebook/texts/routeros.rul,sha256=ipfxjj0mjFef6IsUlupqx4BY_Je_OUb8u_U1019O1DE,1203
182
182
  annet/vendors/__init__.py,sha256=gQcDFlKeWDZB6vxJ_MdPWEoE-C5dg-YgXvgGkuV9YLw,569
183
- annet/vendors/base.py,sha256=KLf_lv-Vm3uPH_Kuovdr1V257EALmAS_gYhKEL196FA,919
183
+ annet/vendors/base.py,sha256=J9Bz7pDdBE7nCceHm2t9EzZ0MJChEugKLWaoOKZlP9Q,1144
184
184
  annet/vendors/registry.py,sha256=zJUj5vJ105kD7xvxt84MYNTNYZI08AKCNTCmE0YtMzo,2539
185
185
  annet/vendors/library/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
- annet/vendors/library/arista.py,sha256=SF6RxoOsQwp7MUmwnAS0V8HrmHkGKDdL5jj3pPOmVLo,732
187
- annet/vendors/library/aruba.py,sha256=coAEyEsHkILvcIx6IZeBywJWXLgcRPffu56AYQrTedY,768
188
- annet/vendors/library/b4com.py,sha256=jRemG0mur0cItmGLmjrqDKt5GDsJ56nIyB6JCsenWhA,655
189
- annet/vendors/library/cisco.py,sha256=ZwBKBWVJS1DmUl1DnklyxQr85kEg7wef75emOFahIZc,725
190
- annet/vendors/library/h3c.py,sha256=x1QwQqcjD4AUTtJI5CJ9g90N14piNFUuaJ9uAorST3k,652
191
- annet/vendors/library/huawei.py,sha256=cw69jgFfce4l2Lv_fs2x54b30LBdkjhsuwg8RglFCJ8,736
192
- annet/vendors/library/iosxr.py,sha256=prC4WZct6Z-msFd14lTcr6zkL0HMlz7apG3Kry8VpNU,752
193
- annet/vendors/library/juniper.py,sha256=1rVdNtOmAd96F_jMSfxKJqmcVhvXcjGxPg6zJ72qK-Q,855
194
- annet/vendors/library/nexus.py,sha256=OAAfJB4OON34iEJb1jIl_v7aIwcK10yxcPOV_l1E6AI,667
195
- annet/vendors/library/nokia.py,sha256=GWZ6f2L_mSy9RMsRR-8FJKXFA7x9pj_tODy2CD7fxXw,771
196
- annet/vendors/library/optixtrans.py,sha256=425akCUYKdeM7qV1ZUvfRjho_bWoWhHotoo0j5plGmY,699
197
- annet/vendors/library/pc.py,sha256=UOfaBtlFUGiAF9qsjlhgWNDJz31SqlMdtyYqoYtn6GM,641
198
- annet/vendors/library/ribbon.py,sha256=3DJBZ5wOoi0mkfqLpNeRJIkThVuRqSiJ_67G7eoKZzs,782
199
- annet/vendors/library/routeros.py,sha256=pvMG3MJpc4-pUBYnEzOXWfK0vqM7l0SlxwlGywaPovk,661
200
- annet-3.1.4.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
201
- annet-3.1.4.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
186
+ annet/vendors/library/arista.py,sha256=tp01vFmPDa2H3-3MSHzJ0iWVwe3kI_IKNViHa0VWB8M,1301
187
+ annet/vendors/library/aruba.py,sha256=vGRUlbe48LRZs67o19wLOLnbDW-GDbAASWwsPuAzUro,1256
188
+ annet/vendors/library/b4com.py,sha256=GMgOLd_JT-96gWb4KKGlZcaJ5Cfbp_cwXtYbwrPimNM,1389
189
+ annet/vendors/library/cisco.py,sha256=ccKLOqxPQPFKUxb-SMdYR6-CCcILRvUB8shXZa7y_VU,1175
190
+ annet/vendors/library/h3c.py,sha256=-gY4DKAMqMcoMcVCWORxM945SfI9r8zekPXFqqKi52o,1044
191
+ annet/vendors/library/huawei.py,sha256=rDI3ScgJ7Cq8SlnGTZsZT8JcM8PxjyeGsQUMA5Xo4i8,1260
192
+ annet/vendors/library/iosxr.py,sha256=5QUN0ERGhBvXRA6G2FC7fHeE-GNuk58Wog28sqzFU2k,1173
193
+ annet/vendors/library/juniper.py,sha256=1Zih2jPdq34ZUZpjUX5nZKxOq4eiqgypaVqkmTmvTbc,1288
194
+ annet/vendors/library/nexus.py,sha256=GW3fWz0W10V9AuW17FFDM9vVUoCQgUVIGDufJaU6B2w,1117
195
+ annet/vendors/library/nokia.py,sha256=IPAwBux11cM2YdEK_b9dal7NAdmcMAo9HCLbWxjdZzo,1151
196
+ annet/vendors/library/optixtrans.py,sha256=xNurVzIrr7cefVUCzhxI-7xnCnayk1kIyRfU8Ug5t8M,631
197
+ annet/vendors/library/pc.py,sha256=4lPTvtvjGDCUuZ6yDyi-U4X9HPZRtj_CoYZM38jxwzo,1085
198
+ annet/vendors/library/ribbon.py,sha256=55tlhY8weGTE9x-CDpFUMvEX6pg0KnrXnd-P8bxceGk,1215
199
+ annet/vendors/library/routeros.py,sha256=0Hi-tDBjgBwfewiZi0EAnXkgGGHlOc_uwpY8xd9p3TM,1253
200
+ annet-3.3.0.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
201
+ annet-3.3.0.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
202
202
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
203
203
  annet_generators/example/__init__.py,sha256=OJ77uj8axc-FIyIu_Xdcnzmde3oQW5mk5qbODkhuVc8,355
204
204
  annet_generators/example/hostname.py,sha256=RloLzNVetEoWPLITzfJ13Nk3CC0yi-cZB1RTd6dnuhI,2541
@@ -211,8 +211,8 @@ annet_generators/rpl_example/generator.py,sha256=EWah19gOH8G-QyNyWqxCqdRi0BK7GbM
211
211
  annet_generators/rpl_example/items.py,sha256=d99HSXDHFjZq511EvGhIqRTWK3F4ZsCWfdUqFYQcyhE,772
212
212
  annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
213
213
  annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
214
- annet-3.1.4.dist-info/METADATA,sha256=Q7ZpCjkgFIsvMzZDWHS4rr9JIsPjumv3CQDbHr8iFdc,815
215
- annet-3.1.4.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
216
- annet-3.1.4.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
217
- annet-3.1.4.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
218
- annet-3.1.4.dist-info/RECORD,,
214
+ annet-3.3.0.dist-info/METADATA,sha256=5C_NTl6Czb2mm3dWoXxyGwGlcuGcdN3kAVYBBWD6Ie0,815
215
+ annet-3.3.0.dist-info/WHEEL,sha256=ck4Vq1_RXyvS4Jt6SI0Vz6fyVs4GWg7AINwpsaGEgPE,91
216
+ annet-3.3.0.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
217
+ annet-3.3.0.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
218
+ annet-3.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.0.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5