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/__init__.py +5 -4
- annet/adapters/file/provider.py +49 -26
- annet/adapters/netbox/common/manufacturer.py +3 -22
- annet/adapters/netbox/common/models.py +3 -2
- annet/adapters/netbox/v24/models.py +4 -3
- annet/annlib/netdev/views/hardware.py +15 -70
- annet/annlib/patching.py +9 -6
- annet/annlib/rbparser/acl.py +3 -2
- annet/annlib/rbparser/ordering.py +3 -2
- annet/annlib/rbparser/platform.py +0 -66
- annet/annlib/rulebook/common.py +21 -14
- annet/annlib/tabparser.py +0 -18
- annet/api/__init__.py +16 -7
- annet/diff.py +3 -3
- annet/gen.py +6 -6
- annet/generators/__init__.py +15 -22
- annet/hardware.py +8 -12
- annet/rulebook/__init__.py +3 -2
- annet/rulebook/deploying.py +3 -2
- annet/rulebook/juniper/__init__.py +2 -2
- annet/rulebook/patching.py +5 -5
- annet/vendors/__init__.py +33 -0
- annet/vendors/base.py +38 -0
- annet/vendors/registry.py +93 -0
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/METADATA +1 -1
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/RECORD +33 -30
- annet_generators/example/__init__.py +6 -1
- annet_generators/example/hostname.py +113 -0
- annet/tabparser.py +0 -53
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/WHEEL +0 -0
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/entry_points.txt +0 -0
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/licenses/AUTHORS +0 -0
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/licenses/LICENSE +0 -0
- {annet-2.6.0.dist-info → annet-3.1.0.dist-info}/top_level.txt +0 -0
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=
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
43
|
+
from annet.lib import do_async, merge_dicts, percentile
|
|
44
44
|
from annet.output import output_driver_connector
|
|
45
|
-
from annet.
|
|
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=
|
|
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 =
|
|
641
|
+
formatter = registry_connector.get().match(hw).make_formatter(indent=indent)
|
|
642
642
|
return formatter.join(config)
|
|
643
643
|
|
|
644
644
|
|
annet/generators/__init__.py
CHANGED
|
@@ -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
|
-
|
|
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 .
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
from .
|
|
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 =
|
|
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
|
|
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
|
|
37
|
+
return registry_connector.get().get(vendor.lower()).hardware
|
|
44
38
|
|
|
45
|
-
def hw_to_vendor(self, hw: HardwareView) -> str:
|
|
46
|
-
|
|
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
|
annet/rulebook/__init__.py
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
annet/rulebook/deploying.py
CHANGED
|
@@ -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
|
|
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=
|
|
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
|
|
annet/rulebook/patching.py
CHANGED
|
@@ -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":
|
|
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=
|
|
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"] =
|
|
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()
|