annet 2.6.0__py3-none-any.whl → 3.1.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.

annet/api/__init__.py CHANGED
@@ -25,7 +25,6 @@ import annet.deploy_ui
25
25
  import annet.lib
26
26
  from annet.annlib import jsontools
27
27
  from annet.annlib.netdev.views.hardware import HardwareView
28
- from annet.annlib.rbparser.platform import VENDOR_REVERSES
29
28
  from annet.annlib.types import GeneratorType
30
29
  from contextlog import get_logger
31
30
 
@@ -48,6 +47,7 @@ from annet.parallel import Parallel, TaskResult
48
47
  from annet.reference import RefTracker
49
48
  from annet.storage import Device, get_storage
50
49
  from annet.types import Diff, ExitCode, OldNewResult, Op, PCDiff, PCDiffFile
50
+ from annet.vendors import registry_connector
51
51
 
52
52
  DEFAULT_INDENT = " "
53
53
 
@@ -138,16 +138,19 @@ def _read_device_config(path, hw):
138
138
  _logger = get_logger()
139
139
  _logger.debug("Reading %r ...", path)
140
140
  score = 1
141
+ vendor_registry = registry_connector.get()
141
142
 
142
143
  with open(path) as cfgdump_file:
143
144
  text = cfgdump_file.read()
144
145
  try:
145
146
  if not hw:
146
147
  hw, score = guess_hw(text)
148
+
147
149
  config = tabparser.parse_to_tree(
148
150
  text=text,
149
- splitter=tabparser.make_formatter(hw).split,
151
+ splitter=vendor_registry.match(hw).make_formatter().split,
150
152
  )
153
+
151
154
  return config, hw, score
152
155
  except tabparser.ParserError:
153
156
  _logger.exception("Parser error: %r", path)
@@ -156,7 +159,7 @@ def _read_device_config(path, hw):
156
159
 
157
160
  # =====
158
161
  def _format_patch_blocks(patch_tree, hw, indent):
159
- formatter = tabparser.make_formatter(hw, indent=indent)
162
+ formatter = registry_connector.get().match(hw).make_formatter(indent=indent)
160
163
  return formatter.patch(patch_tree)
161
164
 
162
165
 
@@ -395,9 +398,12 @@ class CliDeployerJob(DeployerJob):
395
398
  (diff_obj, patch_tree) = _diff_and_patch(device, old, new, acl_rules,
396
399
  res.filter_acl_rules, self.add_comments,
397
400
  do_commit=not self.args.dont_commit)
398
- cmds = tabparser.make_formatter(device.hw, indent="").cmd_paths(patch_tree)
401
+
402
+ formatter = registry_connector.get().match(device.hw).make_formatter(indent="")
403
+ cmds = formatter.cmd_paths(patch_tree)
399
404
  if not cmds:
400
405
  return
406
+
401
407
  self._has_diff = True
402
408
  self.diffs[device] = diff_obj
403
409
  self.cmd_lines.extend(["= %s " % device.hostname, ""])
@@ -550,7 +556,7 @@ class Deployer:
550
556
  dest_name = "= %s" % ", ".join([dev.hostname for dev in devices])
551
557
  diff_lines.extend([dest_name, ""])
552
558
 
553
- for line in tabparser.make_formatter(devices[0].hw).diff(diff_obj):
559
+ for line in registry_connector.get().match(devices[0].hw).make_formatter().diff(diff_obj):
554
560
  diff_lines.append(line)
555
561
  diff_lines.append("")
556
562
  return diff_lines
@@ -786,14 +792,17 @@ def guess_hw(config_text: str):
786
792
  текста конфига и annet/rulebook/texts/*.rul"""
787
793
  scores = {}
788
794
  hw_provider = hardware_connector.get()
789
- for vendor in VENDOR_REVERSES:
795
+ vendor_registry = registry_connector.get()
796
+ for vendor in vendor_registry:
790
797
  hw = hw_provider.vendor_to_hw(vendor)
791
- fmtr = tabparser.make_formatter(hw)
792
798
  rb = rulebook.get_rulebook(hw)
799
+ fmtr = vendor_registry[vendor].make_formatter()
800
+
793
801
  try:
794
802
  config = tabparser.parse_to_tree(config_text, fmtr.split)
795
803
  except Exception:
796
804
  continue
805
+
797
806
  pre = patching.make_pre(patching.make_diff({}, config, rb, []))
798
807
  metric = _count_pre_score(pre)
799
808
  scores[metric] = hw
annet/diff.py CHANGED
@@ -21,8 +21,8 @@ from annet.cli_args import ShowDiffOptions
21
21
  from annet.connectors import CachedConnector
22
22
  from annet.output import output_driver_connector
23
23
  from annet.storage import Device
24
- from annet.tabparser import make_formatter
25
24
  from annet.types import Diff, PCDiff, PCDiffFile
25
+ from annet.vendors import registry_connector
26
26
 
27
27
  from .gen import Loader, old_new
28
28
 
@@ -159,7 +159,7 @@ def _transform_text_diff_for_collapsing(text_diff) -> List[str]:
159
159
 
160
160
 
161
161
  def _make_text_diff(device: Device, diff: Diff) -> List[str]:
162
- formatter = make_formatter(device.hw)
162
+ formatter = registry_connector.get().match(device.hw).make_formatter()
163
163
  res = formatter.diff(diff)
164
164
  return res
165
165
 
@@ -226,7 +226,7 @@ class FrrFileDiffer(UnifiedFileDiffer):
226
226
  indent = " "
227
227
  rb = rulebook.rulebook_provider_connector.get()
228
228
  rulebook_data = rb.get_rulebook(hw)
229
- formatter = tabparser.make_formatter(hw, indent=indent)
229
+ formatter = registry_connector.get().match(hw).make_formatter(indent=indent)
230
230
 
231
231
  old_tree = tabparser.parse_to_tree(old_text or "", splitter=formatter.split)
232
232
  new_tree = tabparser.parse_to_tree(new_text or "", splitter=formatter.split)
annet/gen.py CHANGED
@@ -29,7 +29,7 @@ from annet import generators, implicit, patching, tabparser, tracing
29
29
  from annet.annlib import jsontools
30
30
  from annet.annlib.rbparser.acl import compile_acl_text
31
31
  from annet.cli_args import DeployOptions, GenOptions, ShowGenOptions
32
- from annet.deploy import scrub_config, get_fetcher
32
+ from annet.deploy import get_fetcher, scrub_config
33
33
  from annet.filtering import Filterer
34
34
  from annet.generators import (
35
35
  BaseGenerator,
@@ -40,12 +40,12 @@ from annet.generators import (
40
40
  PartialGenerator,
41
41
  RefGenerator,
42
42
  )
43
- from annet.lib import merge_dicts, percentile, do_async
43
+ from annet.lib import do_async, merge_dicts, percentile
44
44
  from annet.output import output_driver_connector
45
- from annet.parallel import Parallel
46
- from annet.storage import Device, Storage, storage_connector
45
+ from annet.storage import Device, Storage
47
46
  from annet.tracing import tracing_connector
48
47
  from annet.types import OldNewResult
48
+ from annet.vendors import registry_connector
49
49
 
50
50
 
51
51
  # Вывод всех генераторов вместе.
@@ -162,7 +162,7 @@ def _old_new_per_device(ctx: OldNewDeviceContext, device: Device, filterer: Filt
162
162
  if ctx.config != "empty":
163
163
  old = tabparser.parse_to_tree(
164
164
  text=text,
165
- splitter=tabparser.make_formatter(device.hw).split,
165
+ splitter=registry_connector.get().match(device.hw).make_formatter().split,
166
166
  )
167
167
  if not old:
168
168
  res = generators.run_partial_initial(device)
@@ -638,7 +638,7 @@ def _existing_cfg_file_name(config_dir: str, device) -> Optional[str]:
638
638
 
639
639
 
640
640
  def format_config_blocks(config, hw, indent, _level=0):
641
- formatter = tabparser.make_formatter(hw, indent=indent)
641
+ formatter = registry_connector.get().match(hw).make_formatter(indent=indent)
642
642
  return formatter.join(config)
643
643
 
644
644
 
@@ -7,24 +7,15 @@ import os
7
7
  import re
8
8
  import textwrap
9
9
  from collections import OrderedDict as odict
10
- from typing import (
11
- FrozenSet,
12
- Iterable,
13
- List,
14
- Optional,
15
- Union,
16
- )
10
+ from typing import FrozenSet, Iterable, List, Optional, Union
17
11
 
18
- from annet.annlib.rbparser.acl import compile_acl_text
19
12
  from contextlog import get_logger
20
13
 
21
- from annet.storage import Device
22
-
23
14
  from annet import patching, tabparser, tracing
15
+ from annet.annlib.rbparser.acl import compile_acl_text
24
16
  from annet.cli_args import GenSelectOptions, ShowGeneratorsOptions
25
- from annet.lib import (
26
- get_context,
27
- )
17
+ from annet.lib import get_context
18
+ from annet.storage import Device
28
19
  from annet.tracing import tracing_connector
29
20
  from annet.types import (
30
21
  GeneratorEntireResult,
@@ -33,19 +24,20 @@ from annet.types import (
33
24
  GeneratorPartialRunArgs,
34
25
  GeneratorResult,
35
26
  )
36
- from .base import (
37
- BaseGenerator,
38
- TextGenerator as TextGenerator,
39
- ParamsList as ParamsList,
40
- )
41
- from .exceptions import NotSupportedDevice, GeneratorError
27
+ from annet.vendors import registry_connector
28
+
29
+ from .base import BaseGenerator
30
+ from .base import ParamsList as ParamsList
31
+ from .base import TextGenerator as TextGenerator
32
+ from .entire import Entire
33
+ from .exceptions import GeneratorError, NotSupportedDevice
42
34
  from .jsonfragment import JSONFragment
43
35
  from .partial import PartialGenerator
44
- from .entire import Entire
45
- from .ref import RefGenerator
46
36
  from .perf import GeneratorPerfMesurer
37
+ from .ref import RefGenerator
47
38
  from .result import RunGeneratorResult
48
39
 
40
+
49
41
  # =====
50
42
  DISABLED_TAG = "disable"
51
43
 
@@ -200,7 +192,8 @@ def _run_partial_generator(gen: "PartialGenerator", run_args: GeneratorPartialRu
200
192
  logger.error("Generator error in file '%s:%i'", filename, lineno)
201
193
  raise GeneratorError(f"{gen} on {device}") from err
202
194
 
203
- fmtr = tabparser.make_formatter(device.hw)
195
+ fmtr = registry_connector.get().match(device.hw).make_formatter()
196
+
204
197
  try:
205
198
  config = tabparser.parse_to_tree(text=output, splitter=fmtr.split)
206
199
  except tabparser.ParserError as err:
annet/hardware.py CHANGED
@@ -1,17 +1,11 @@
1
1
  import abc
2
2
  from typing import Any
3
3
 
4
- from annet.annlib.netdev.views.hardware import HardwareView, hw_to_vendor
5
-
4
+ from annet.annlib.netdev.views.hardware import HardwareView
5
+ from annet.vendors import registry_connector
6
6
  from annet.connectors import Connector
7
7
 
8
8
 
9
- try:
10
- from annet.annlib.netdev.views.hardware import vendor_to_hw
11
- except ImportError:
12
- from netdev.views.hardware import vendor_to_hw
13
-
14
-
15
9
  class _HardwareConnector(Connector["HarwareProvider"]):
16
10
  name = "Hardware"
17
11
  ep_name = "hardware"
@@ -31,7 +25,7 @@ class HarwareProvider(abc.ABC):
31
25
  pass
32
26
 
33
27
  @abc.abstractmethod
34
- def hw_to_vendor(self, hw: Any) -> str:
28
+ def hw_to_vendor(self, hw: Any) -> str | None:
35
29
  pass
36
30
 
37
31
 
@@ -40,7 +34,9 @@ class AnnetHardwareProvider(HarwareProvider):
40
34
  return HardwareView(hw_model, sw_version)
41
35
 
42
36
  def vendor_to_hw(self, vendor: str) -> HardwareView:
43
- return vendor_to_hw(vendor)
37
+ return registry_connector.get().get(vendor.lower()).hardware
44
38
 
45
- def hw_to_vendor(self, hw: HardwareView) -> str:
46
- return hw_to_vendor(hw)
39
+ def hw_to_vendor(self, hw: HardwareView) -> str | None:
40
+ if vendor := registry_connector.get().match(hw, None):
41
+ return vendor.NAME
42
+ return None
@@ -5,11 +5,12 @@ from typing import Iterable, Union
5
5
 
6
6
  from annet.annlib.lib import mako_render
7
7
  from annet.annlib.rbparser.ordering import compile_ordering_text
8
- from annet.annlib.rbparser.platform import VENDOR_REVERSES, VENDOR_ALIASES
8
+ from annet.annlib.rbparser.platform import VENDOR_ALIASES
9
9
 
10
10
  from annet.connectors import CachedConnector
11
11
  from annet.rulebook.deploying import compile_deploying_text
12
12
  from annet.rulebook.patching import compile_patching_text
13
+ from annet.vendors import registry_connector
13
14
 
14
15
 
15
16
  class RulebookProvider(ABC):
@@ -63,7 +64,7 @@ class DefaultRulebookProvider(RulebookProvider):
63
64
  if hw in self._rulebook_cache:
64
65
  return self._rulebook_cache[hw]
65
66
 
66
- assert hw.vendor in VENDOR_REVERSES, "Unknown vendor: %s" % (hw.vendor)
67
+ assert hw.vendor in registry_connector.get(), "Unknown vendor: %s" % (hw.vendor)
67
68
  rul_vendor_name = VENDOR_ALIASES.get(hw.vendor, hw.vendor)
68
69
  patching = compile_patching_text(self._render_rul(rul_vendor_name + ".rul", hw), rul_vendor_name)
69
70
 
@@ -2,8 +2,9 @@ import functools
2
2
  from collections import OrderedDict as odict
3
3
  from collections import namedtuple
4
4
 
5
- from annet.annlib.rbparser import platform, syntax
5
+ from annet.annlib.rbparser import syntax
6
6
  from annet.annlib.rbparser.deploying import compile_messages
7
+ from annet.vendors import registry_connector
7
8
  from valkit.common import valid_bool, valid_number, valid_string_list
8
9
  from valkit.python import valid_object_path
9
10
 
@@ -38,7 +39,7 @@ def compile_deploying_text(text, vendor):
38
39
  "default": [],
39
40
  }
40
41
  }),
41
- reverse_prefix=platform.VENDOR_REVERSES[vendor],
42
+ reverse_prefix=registry_connector.get()[vendor].reverse,
42
43
  )
43
44
 
44
45
 
@@ -1,10 +1,10 @@
1
+ import functools
1
2
  import re
2
3
  from collections import OrderedDict as odict
3
- import functools
4
4
 
5
5
  from annet.annlib.lib import jun_activate, jun_is_inactive, merge_dicts
6
+ from annet.annlib.tabparser import JuniperFormatter
6
7
  from annet.annlib.types import Op
7
- from annet.tabparser import JuniperFormatter
8
8
  from annet.rulebook import common
9
9
 
10
10
 
@@ -1,14 +1,13 @@
1
1
  import functools
2
2
  import re
3
3
  from collections import OrderedDict as odict
4
-
5
4
  from annet.annlib.rbparser import platform, syntax
5
+ from annet.vendors import registry_connector
6
6
  from valkit.common import valid_bool, valid_string_list
7
7
  from valkit.python import valid_object_path
8
8
 
9
9
  from .common import import_rulebook_function
10
10
 
11
-
12
11
  DEFAULT_PATCH_LOGIC = "common.default"
13
12
  ORDERED_PATCH_LOGIC = "common.ordered"
14
13
  REWRITE_PATCH_LOGIC = "common.rewrite"
@@ -19,6 +18,7 @@ MULTILINE_DIFF_LOGIC = "common.multiline_diff"
19
18
  # =====
20
19
  @functools.lru_cache()
21
20
  def compile_patching_text(text, vendor):
21
+
22
22
  return _compile_patching(
23
23
  tree=syntax.parse_text(text, params_scheme={
24
24
  "global": {
@@ -31,7 +31,7 @@ def compile_patching_text(text, vendor):
31
31
  },
32
32
  "diff_logic": {
33
33
  "validator": valid_object_path,
34
- "default": platform.VENDOR_DIFF[vendor],
34
+ "default": registry_connector.get()[vendor].diff(False),
35
35
  },
36
36
  "comment": {
37
37
  "validator": valid_string_list,
@@ -62,7 +62,7 @@ def compile_patching_text(text, vendor):
62
62
  "default": False,
63
63
  },
64
64
  }),
65
- reverse_prefix=platform.VENDOR_REVERSES[vendor],
65
+ reverse_prefix=registry_connector.get()[vendor].reverse,
66
66
  vendor=vendor,
67
67
  )
68
68
 
@@ -86,7 +86,7 @@ def _compile_patching(tree, reverse_prefix, vendor):
86
86
  }
87
87
  else:
88
88
  if attrs["params"]["ordered"]:
89
- attrs["params"]["diff_logic"] = platform.VENDOR_DIFF_ORDERED[vendor]
89
+ attrs["params"]["diff_logic"] = registry_connector.get()[vendor].diff(True)
90
90
  attrs["params"]["logic"] = ORDERED_PATCH_LOGIC
91
91
  elif attrs["params"]["rewrite"]:
92
92
  attrs["params"]["diff_logic"] = REWRITE_DIFF_LOGIC
@@ -0,0 +1,33 @@
1
+ from typing import Callable
2
+
3
+ from annet.connectors import Connector
4
+ from annet.vendors.base import AbstractVendor
5
+
6
+ from .library import (
7
+ arista,
8
+ aruba,
9
+ b4com,
10
+ cisco,
11
+ h3c,
12
+ huawei,
13
+ iosxr,
14
+ juniper,
15
+ nexus,
16
+ nokia,
17
+ optixtrans,
18
+ pc,
19
+ ribbon,
20
+ routeros,
21
+ )
22
+ from .registry import Registry, registry
23
+
24
+
25
+ class _RegistryConnector(Connector[Registry]):
26
+ name = "Registry"
27
+ ep_name = "vendors"
28
+
29
+ def _get_default(self) -> Callable[[], Registry]:
30
+ return lambda: registry
31
+
32
+
33
+ registry_connector = _RegistryConnector()
annet/vendors/base.py ADDED
@@ -0,0 +1,38 @@
1
+ import abc
2
+ from typing import ClassVar
3
+
4
+ from annet.annlib.netdev.views.hardware import HardwareView
5
+ from annet.annlib.tabparser import CommonFormatter
6
+
7
+
8
+ class AbstractVendor(abc.ABC):
9
+ NAME: ClassVar[str]
10
+
11
+ @abc.abstractmethod
12
+ def match(self) -> list[str]:
13
+ raise NotImplementedError
14
+
15
+ @property
16
+ @abc.abstractmethod
17
+ def reverse(self) -> str:
18
+ raise NotImplementedError
19
+
20
+ def diff(self, order: bool) -> str:
21
+ return "common.ordered_diff" if order else "common.default_diff"
22
+
23
+ @property
24
+ @abc.abstractmethod
25
+ def exit(self) -> str:
26
+ raise NotImplementedError
27
+
28
+ @property
29
+ @abc.abstractmethod
30
+ def hardware(self) -> HardwareView:
31
+ raise NotImplementedError
32
+
33
+ def svi_name(self, num: int) -> str:
34
+ return f"vlan{num}"
35
+
36
+ @abc.abstractmethod
37
+ def make_formatter(self, **kwargs) -> CommonFormatter:
38
+ raise NotImplementedError
@@ -0,0 +1,93 @@
1
+ import enum
2
+ from operator import itemgetter
3
+
4
+ from annet.annlib.netdev.views.hardware import HardwareView
5
+ from annet.annlib.tabparser import CommonFormatter
6
+
7
+ from .base import AbstractVendor
8
+
9
+
10
+ _SENTINEL = enum.Enum("_SENTINEL", "sentinel")
11
+ sentinel = _SENTINEL.sentinel
12
+
13
+
14
+ class GenericVendor(AbstractVendor):
15
+ def match(self) -> list[str]:
16
+ return []
17
+
18
+ @property
19
+ def reverse(self) -> str:
20
+ return "-"
21
+
22
+ @property
23
+ def hardware(self) -> HardwareView:
24
+ return HardwareView("")
25
+
26
+ def make_formatter(self, **kwargs) -> CommonFormatter:
27
+ return CommonFormatter(**kwargs)
28
+
29
+ @property
30
+ def exit(self) -> str:
31
+ return ""
32
+
33
+
34
+ GENERIC_VENDOR = GenericVendor()
35
+
36
+
37
+ class Registry:
38
+ def __init__(self):
39
+ self.vendors: dict[str, AbstractVendor] = {}
40
+ self._matchers = {}
41
+
42
+ def register(self, cls: type[AbstractVendor]) -> type[AbstractVendor]:
43
+ if not cls.NAME:
44
+ raise RuntimeError(f"{cls.__name__} has empty NAME field")
45
+ if cls.NAME in self.vendors:
46
+ raise RuntimeError(f"{cls.__name__} with name {cls.NAME} already registered")
47
+ self.vendors[cls.NAME] = cls()
48
+
49
+ return cls
50
+
51
+ def __add__(self, other: "Registry"):
52
+ self.vendors = dict(**other.vendors, **self.vendors)
53
+
54
+ def __getitem__(self, item) -> AbstractVendor:
55
+ if item in self.vendors:
56
+ return self.vendors[item]
57
+ raise RuntimeError(f"Unknown vendor {item}")
58
+
59
+ def match(
60
+ self,
61
+ hw: HardwareView | str,
62
+ default: _SENTINEL | AbstractVendor | None = sentinel
63
+ ) -> AbstractVendor | None:
64
+ if isinstance(hw, str):
65
+ hw = HardwareView(hw, "")
66
+
67
+ matched: list[tuple[AbstractVendor, int]] = []
68
+ for name, vendor in self.vendors.items():
69
+ for item in vendor.match():
70
+ if hw.match(item):
71
+ matched.append((vendor, item.count(".")))
72
+
73
+ if matched:
74
+ return next(iter(sorted(matched, key=itemgetter(1), reverse=True)))[0]
75
+ if default is sentinel:
76
+ return GENERIC_VENDOR
77
+ return default
78
+
79
+ def get(self, item: str, default: _SENTINEL | AbstractVendor | None = sentinel) -> AbstractVendor | None:
80
+ if item in self:
81
+ return self[item]
82
+ if default is sentinel:
83
+ return GENERIC_VENDOR
84
+ return default
85
+
86
+ def __contains__(self, item):
87
+ return item in self.vendors
88
+
89
+ def __iter__(self):
90
+ return iter(self.vendors)
91
+
92
+
93
+ registry = Registry()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annet
3
- Version: 2.6.0
3
+ Version: 3.1.0
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT