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

@@ -1,13 +1,16 @@
1
- from annet.annlib.netdev.views.dump import DumpableView
2
- from annet.storage import Query
1
+ import dataclasses
3
2
  from dataclasses import dataclass, fields
4
- from typing import List, Iterable, Optional, Any, Sequence
5
- from annet.storage import StorageProvider, Storage
6
- from annet.connectors import AdapterWithName
7
- from annet.storage import Device as DeviceCls
8
- from annet.annlib.netdev.views.hardware import vendor_to_hw, HardwareView
3
+ from typing import Any, Iterable, List, Optional, Sequence
4
+
9
5
  import yaml
10
6
 
7
+ from annet.annlib.netdev.views.dump import DumpableView
8
+ from annet.annlib.netdev.views.hardware import HardwareView
9
+ from annet.connectors import AdapterWithName
10
+ from annet.hardware import hardware_connector
11
+ from annet.storage import Device as DeviceProtocol
12
+ from annet.storage import Query, Storage, StorageProvider
13
+
11
14
 
12
15
  @dataclass
13
16
  class Interface(DumpableView):
@@ -23,29 +26,46 @@ class Interface(DumpableView):
23
26
  @dataclass
24
27
  class DeviceStorage:
25
28
  fqdn: str
26
- vendor: str
29
+
30
+ vendor: Optional[str] = None
31
+ hw_model: Optional[str] = None
32
+ sw_version: Optional[str] = None
33
+
27
34
  hostname: Optional[str] = None
28
35
  serial: Optional[str] = None
29
36
  id: Optional[str] = None
30
37
  interfaces: Optional[list[Interface]] = None
31
38
  storage: Optional[Storage] = None
32
39
 
40
+ hw: HardwareView = dataclasses.field(init=False)
41
+
33
42
  def __post_init__(self):
34
43
  if not self.id:
35
44
  self.id = self.fqdn
36
45
  if not self.hostname:
37
46
  self.hostname = self.fqdn.split(".")[0]
38
- hw = vendor_to_hw(self.vendor)
39
- if not hw:
40
- raise Exception("unknown vendor")
47
+
48
+ if self.hw_model:
49
+ hw = HardwareView(self.hw_model, self.sw_version)
50
+ if self.vendor and self.vendor != hw.vendor:
51
+ raise Exception(f"Vendor {self.vendor} is not vendor from hw model ({hw.vendor})")
52
+ else:
53
+ self.vendor = hw.vendor
54
+ else:
55
+ hw_provider = hardware_connector.get()
56
+ hw: HardwareView = hw_provider.vendor_to_hw(self.vendor)
57
+ if not hw:
58
+ raise Exception("unknown vendor")
59
+ self.hw_model = hw.model
41
60
  self.hw = hw
61
+
42
62
  if isinstance(self.interfaces, list):
43
63
  interfaces = []
44
64
  for iface in self.interfaces:
45
65
  try:
46
66
  interfaces.append(Interface(**iface))
47
67
  except Exception as e:
48
- raise Exception("unable to parse %s as Interface %s" % (iface, e))
68
+ raise Exception("unable to parse %s as Interface: %s" % (iface, e))
49
69
  self.interfaces = interfaces
50
70
 
51
71
  def set_storage(self, storage: Storage):
@@ -53,7 +73,7 @@ class DeviceStorage:
53
73
 
54
74
 
55
75
  @dataclass
56
- class Device(DeviceCls, DumpableView):
76
+ class Device(DeviceProtocol, DumpableView):
57
77
  dev: DeviceStorage
58
78
 
59
79
  def __hash__(self):
@@ -79,7 +99,7 @@ class Device(DeviceCls, DumpableView):
79
99
 
80
100
  @property
81
101
  def storage(self) -> Storage:
82
- return self
102
+ return self.dev.storage
83
103
 
84
104
  @property
85
105
  def hw(self) -> HardwareView:
@@ -91,7 +111,11 @@ class Device(DeviceCls, DumpableView):
91
111
 
92
112
  @property
93
113
  def neighbours_ids(self):
94
- pass
114
+ return []
115
+
116
+ @property
117
+ def neighbours_fqdns(self) -> list[str]:
118
+ return []
95
119
 
96
120
  def make_lag(self, lag: int, ports: Sequence[str], lag_min_links: Optional[int]) -> Interface:
97
121
  raise NotImplementedError
@@ -105,8 +129,8 @@ class Device(DeviceCls, DumpableView):
105
129
  def find_interface(self, name: str) -> Optional[Interface]:
106
130
  raise NotImplementedError
107
131
 
108
- def neighbours_fqdns(self) -> list[str]:
109
- return []
132
+ def flush_perf(self):
133
+ pass
110
134
 
111
135
 
112
136
  @dataclass
@@ -120,7 +144,7 @@ class Devices:
120
144
  try:
121
145
  devices.append(Device(dev=DeviceStorage(**dev)))
122
146
  except Exception as e:
123
- raise Exception("unable to parse %s as Device %s" % (dev, e))
147
+ raise Exception("unable to parse %s as Device: %s" % (dev, e))
124
148
  self.devices = devices
125
149
 
126
150
 
@@ -193,12 +217,12 @@ class FS(Storage):
193
217
  return [dev.fqdn for dev in result]
194
218
 
195
219
  def make_devices(
196
- self,
197
- query: Query | list,
198
- preload_neighbors=False,
199
- use_mesh=None,
200
- preload_extra_fields=False,
201
- **kwargs,
220
+ self,
221
+ query: Query | list,
222
+ preload_neighbors=False,
223
+ use_mesh=None,
224
+ preload_extra_fields=False,
225
+ **kwargs,
202
226
  ) -> list[Device]:
203
227
  if isinstance(query, list):
204
228
  query = Query.new(query)
@@ -231,8 +255,7 @@ def filter_query(devices: list[Device], query: Query) -> list[Device]:
231
255
 
232
256
  def read_inventory(path: str, storage: Storage) -> Devices:
233
257
  with open(path, "r") as f:
234
- data = f.read()
235
- file_data = yaml.load(data, Loader=yaml.BaseLoader)
258
+ file_data = yaml.load(f, Loader=yaml.SafeLoader)
236
259
  res = dataclass_from_dict(Devices, file_data)
237
260
  for dev in res.devices:
238
261
  dev.dev.set_storage(storage)
@@ -4,32 +4,13 @@ from annet.annlib.netdev.views.hardware import HardwareView
4
4
 
5
5
  logger = getLogger(__name__)
6
6
 
7
- _VENDORS = {
8
- "cisco": "Cisco",
9
- "catalyst": "Cisco Catalyst",
10
- "nexus": "Cisco Nexus",
11
- "iosxr": "Cisco XR",
12
- "huawei": "Huawei",
13
- "optixtrans": "Huawei OptiXtrans",
14
- "juniper": "Juniper",
15
- "arista": "Arista",
16
- "pc": "PC",
17
- "nokia": "Nokia",
18
- "aruba": "Aruba",
19
- "routeros": "RouterOS",
20
- "ribbon": "Ribbon",
21
- "b4com": "B4com",
22
- "h3c": "H3C",
23
- }
24
-
25
7
 
26
8
  def get_hw(manufacturer: str, model: str, platform_name: str):
27
- # by some reason Netbox calls Mellanox SN as MSN, so we fix them here
9
+ # By some reason Netbox calls Mellanox SN as MSN, so we fix them here
28
10
  if manufacturer == "Mellanox" and model.startswith("MSN"):
29
11
  model = model.replace("MSN", "SN", 1)
30
- vendor = manufacturer + " " + model
31
- hw = HardwareView(_VENDORS.get(vendor.lower(), vendor), platform_name)
32
- return hw
12
+
13
+ return HardwareView(manufacturer + " " + model, platform_name)
33
14
 
34
15
 
35
16
  def get_breed(manufacturer: str, model: str):
@@ -5,8 +5,9 @@ from ipaddress import ip_interface, IPv6Interface
5
5
  from typing import List, Optional, Any, Dict, Sequence, TypeVar, Generic
6
6
 
7
7
  from annet.annlib.netdev.views.dump import DumpableView
8
- from annet.annlib.netdev.views.hardware import HardwareView, lag_name, svi_name
8
+ from annet.annlib.netdev.views.hardware import HardwareView, lag_name
9
9
  from annet.storage import Storage
10
+ from annet.vendors import registry_connector
10
11
 
11
12
 
12
13
  @dataclass
@@ -259,7 +260,7 @@ class NetboxDevice(Entity, Generic[_InterfaceT]):
259
260
  return lag_interface
260
261
 
261
262
  def _svi_name(self, svi: int) -> str:
262
- return svi_name(self.hw, svi)
263
+ return registry_connector.get().match(self.hw).svi_name(svi)
263
264
 
264
265
  def add_svi(self, svi: int) -> _InterfaceT:
265
266
  name = self._svi_name(svi)
@@ -1,11 +1,12 @@
1
1
  from dataclasses import dataclass, field
2
2
  from datetime import datetime, timezone
3
3
  from ipaddress import ip_interface, IPv6Interface
4
- from typing import List, Optional, Any, Dict, Sequence, Callable
4
+ from typing import List, Optional, Any, Dict, Sequence
5
5
 
6
6
  from annet.annlib.netdev.views.dump import DumpableView
7
- from annet.annlib.netdev.views.hardware import HardwareView, lag_name, svi_name
7
+ from annet.annlib.netdev.views.hardware import HardwareView, lag_name
8
8
  from annet.storage import Storage
9
+ from annet.vendors import registry_connector
9
10
 
10
11
 
11
12
  @dataclass
@@ -259,7 +260,7 @@ class NetboxDevice(Entity):
259
260
  return lag_interface
260
261
 
261
262
  def _svi_name(self, svi: int) -> str:
262
- return svi_name(self.hw, svi)
263
+ return registry_connector.get().match(self.hw).svi_name(svi)
263
264
 
264
265
  def add_svi(self, svi: int) -> Interface:
265
266
  name = self._svi_name(svi)
@@ -1,3 +1,4 @@
1
+ import functools
1
2
  from typing import Optional
2
3
 
3
4
  from annet.annlib.netdev.devdb import parse_hw_model
@@ -51,20 +52,31 @@ class HardwareLeaf(DumpableView):
51
52
 
52
53
 
53
54
  class HardwareView(HardwareLeaf):
54
- def __init__(self, hw_model, sw_version):
55
- (true_sequences, false_sequences) = parse_hw_model(hw_model or "")
55
+ def __init__(self, hw_model, sw_version: Optional[str] = None):
56
+ true_sequences, false_sequences = parse_hw_model(hw_model or "")
56
57
  super().__init__((), true_sequences, false_sequences)
57
58
  self.model = hw_model or ""
58
59
  self._soft = sw_version or ""
59
60
 
60
61
  @property
61
62
  def vendor(self) -> Optional[str]:
62
- return hw_to_vendor(self)
63
+ from annet.hardware import hardware_connector
64
+ return hardware_connector.get().hw_to_vendor(self)
63
65
 
64
66
  @property
65
67
  def soft(self) -> str:
66
68
  return self._soft
67
69
 
70
+ @soft.setter
71
+ def soft(self, value: str) -> None:
72
+ self._soft = value
73
+
74
+ def match(self, expr: str) -> bool:
75
+ dev_path = expr.split(".")
76
+ if dev_path and dev_path[0] == "hw":
77
+ dev_path = dev_path[1:]
78
+ return bool(functools.reduce(getattr, dev_path, self))
79
+
68
80
  def __hash__(self):
69
81
  return hash(self.model)
70
82
 
@@ -72,62 +84,6 @@ class HardwareView(HardwareLeaf):
72
84
  return self.model == other.model
73
85
 
74
86
 
75
- def hw_to_vendor(hw: HardwareView) -> Optional[str]:
76
- if hw.Nexus:
77
- return "nexus"
78
- elif hw.Cisco.XR or hw.Cisco.ASR or hw.Cisco.XRV:
79
- return "iosxr"
80
- elif hw.Cisco:
81
- return "cisco"
82
- elif hw.OptiXtrans:
83
- return "optixtrans"
84
- elif hw.Huawei:
85
- return "huawei"
86
- elif hw.Juniper:
87
- return "juniper"
88
- elif hw.Arista:
89
- return "arista"
90
- elif hw.PC:
91
- return "pc"
92
- elif hw.Nokia:
93
- return "nokia"
94
- elif hw.RouterOS:
95
- return "routeros"
96
- elif hw.Aruba:
97
- return "aruba"
98
- elif hw.Ribbon:
99
- return "ribbon"
100
- elif hw.H3C:
101
- return "h3c"
102
- elif hw.B4com:
103
- return "b4com"
104
- return None
105
-
106
-
107
- def vendor_to_hw(vendor):
108
- hw = HardwareView(
109
- {
110
- "cisco": "Cisco",
111
- "catalyst": "Cisco Catalyst",
112
- "nexus": "Cisco Nexus",
113
- "iosxr": "Cisco XR",
114
- "huawei": "Huawei",
115
- "optixtrans": "Huawei OptiXtrans",
116
- "juniper": "Juniper",
117
- "arista": "Arista",
118
- "pc": "PC",
119
- "nokia": "Nokia",
120
- "aruba": "Aruba",
121
- "routeros": "RouterOS",
122
- "ribbon": "Ribbon",
123
- "h3c": "H3C",
124
- "b4com": "B4com",
125
- }.get(vendor.lower(), vendor),
126
- None,
127
- )
128
- return hw
129
-
130
-
131
87
  def lag_name(hw: HardwareView, nlagg: int) -> str:
132
88
  if hw.Huawei:
133
89
  return f"Eth-Trunk{nlagg}"
@@ -148,14 +104,3 @@ def lag_name(hw: HardwareView, nlagg: int) -> str:
148
104
  if hw.Nokia:
149
105
  return f"lagg-{nlagg}"
150
106
  raise NotImplementedError(hw)
151
-
152
-
153
- def svi_name(hw: HardwareView, num: int) -> str:
154
- if hw.Juniper:
155
- return f"irb.{num}"
156
- elif hw.Huawei:
157
- return f"Vlanif{num}"
158
- elif hw.Arista or hw.Cisco:
159
- return f"Vlan{num}"
160
- else:
161
- return f"vlan{num}"
annet/annlib/patching.py CHANGED
@@ -19,6 +19,7 @@ from .rulebook.common import call_diff_logic
19
19
  from .rulebook.common import default as common_default
20
20
  from .tabparser import CommonFormatter
21
21
  from .types import Diff, Op
22
+ from ..vendors import registry_connector
22
23
 
23
24
 
24
25
  # =====
@@ -224,14 +225,15 @@ class Orderer:
224
225
  return (f_order or 0), cmd_direct, odict(children), f_rule
225
226
 
226
227
  def order_config(self, config):
227
- if self.vendor not in platform.VENDOR_REVERSES:
228
+ if self.vendor not in registry_connector.get():
228
229
  return config
229
-
230
- ordered = []
231
- reverse_prefix = platform.VENDOR_REVERSES[self.vendor]
232
230
  if not config:
233
231
  return odict()
234
- for (row, children) in config.items():
232
+
233
+ ordered = []
234
+ reverse_prefix = registry_connector.get()[self.vendor].reverse
235
+
236
+ for row, children in config.items():
235
237
  cmd_direct = not row.startswith(reverse_prefix)
236
238
  (order, direct, rb, _) = self.get_order(row, cmd_direct)
237
239
  child_orderer = Orderer(rb, self.vendor)
@@ -5,7 +5,8 @@ from typing import Any, Callable, List, Optional
5
5
  from valkit import add_validator_magic
6
6
  from valkit.common import valid_bool, valid_number, valid_string_list
7
7
 
8
- from . import platform, syntax
8
+ from annet.vendors import registry_connector
9
+ from . import syntax
9
10
 
10
11
 
11
12
  # =====
@@ -13,7 +14,7 @@ from . import platform, syntax
13
14
  def compile_acl_text(text, vendor, allow_ignore=False):
14
15
  return _compile_acl(
15
16
  trees=[syntax.parse_text(text, _PARAMS_SCHEME)],
16
- reverse_prefix=platform.VENDOR_REVERSES[vendor],
17
+ reverse_prefix=registry_connector.get()[vendor].reverse,
17
18
  allow_ignore=allow_ignore,
18
19
  vendor=vendor,
19
20
  )
@@ -4,7 +4,8 @@ from collections import OrderedDict as odict
4
4
 
5
5
  from valkit.common import valid_bool, valid_string_list
6
6
 
7
- from . import platform, syntax
7
+ from annet.vendors import registry_connector
8
+ from . import syntax
8
9
 
9
10
 
10
11
  # =====
@@ -25,7 +26,7 @@ def compile_ordering_text(text, vendor):
25
26
  "default": None,
26
27
  }
27
28
  }),
28
- reverse_prefix=platform.VENDOR_REVERSES[vendor],
29
+ reverse_prefix=registry_connector.get()[vendor].reverse,
29
30
  )
30
31
 
31
32
 
annet/api/__init__.py CHANGED
@@ -48,6 +48,7 @@ from annet.parallel import Parallel, TaskResult
48
48
  from annet.reference import RefTracker
49
49
  from annet.storage import Device, get_storage
50
50
  from annet.types import Diff, ExitCode, OldNewResult, Op, PCDiff, PCDiffFile
51
+ from annet.vendors import registry_connector
51
52
 
52
53
  DEFAULT_INDENT = " "
53
54
 
@@ -786,7 +787,7 @@ def guess_hw(config_text: str):
786
787
  текста конфига и annet/rulebook/texts/*.rul"""
787
788
  scores = {}
788
789
  hw_provider = hardware_connector.get()
789
- for vendor in VENDOR_REVERSES:
790
+ for vendor in registry_connector.get():
790
791
  hw = hw_provider.vendor_to_hw(vendor)
791
792
  fmtr = tabparser.make_formatter(hw)
792
793
  rb = rulebook.get_rulebook(hw)
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
@@ -10,6 +10,7 @@ from annet.annlib.rbparser.platform import VENDOR_REVERSES, VENDOR_ALIASES
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
 
@@ -3,12 +3,12 @@ import re
3
3
  from collections import OrderedDict as odict
4
4
 
5
5
  from annet.annlib.rbparser import platform, syntax
6
+ from annet.vendors import registry_connector
6
7
  from valkit.common import valid_bool, valid_string_list
7
8
  from valkit.python import valid_object_path
8
9
 
9
10
  from .common import import_rulebook_function
10
11
 
11
-
12
12
  DEFAULT_PATCH_LOGIC = "common.default"
13
13
  ORDERED_PATCH_LOGIC = "common.ordered"
14
14
  REWRITE_PATCH_LOGIC = "common.rewrite"
@@ -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
 
@@ -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,25 @@
1
+ import abc
2
+ from typing import ClassVar
3
+
4
+ from annet.annlib.netdev.views.hardware import HardwareView
5
+
6
+
7
+ class AbstractVendor(abc.ABC):
8
+ NAME: ClassVar[str]
9
+
10
+ @abc.abstractmethod
11
+ def match(self) -> list[str]:
12
+ raise NotImplementedError
13
+
14
+ @property
15
+ @abc.abstractmethod
16
+ def reverse(self) -> str:
17
+ raise NotImplementedError
18
+
19
+ @property
20
+ @abc.abstractmethod
21
+ def hardware(self) -> HardwareView:
22
+ raise NotImplementedError
23
+
24
+ def svi_name(self, num: int) -> str:
25
+ return f"vlan{num}"
@@ -0,0 +1,84 @@
1
+ import enum
2
+ from operator import itemgetter
3
+
4
+ from annet.annlib.netdev.views.hardware import HardwareView
5
+
6
+ from .base import AbstractVendor
7
+
8
+ _SENTINEL = enum.Enum("_SENTINEL", "sentinel")
9
+ sentinel = _SENTINEL.sentinel
10
+
11
+
12
+ class GenericVendor(AbstractVendor):
13
+ def match(self) -> list[str]:
14
+ return []
15
+
16
+ @property
17
+ def reverse(self) -> str:
18
+ return "-"
19
+
20
+ @property
21
+ def hardware(self) -> HardwareView:
22
+ return HardwareView("")
23
+
24
+
25
+ GENERIC_VENDOR = GenericVendor()
26
+
27
+
28
+ class Registry:
29
+ def __init__(self):
30
+ self.vendors: dict[str, AbstractVendor] = {}
31
+ self._matchers = {}
32
+
33
+ def register(self, cls: type[AbstractVendor]) -> type[AbstractVendor]:
34
+ if not cls.NAME:
35
+ raise RuntimeError(f"{cls.__name__} has empty NAME field")
36
+ if cls.NAME in self.vendors:
37
+ raise RuntimeError(f"{cls.__name__} with name {cls.NAME} already registered")
38
+ self.vendors[cls.NAME] = cls()
39
+
40
+ return cls
41
+
42
+ def __add__(self, other: "Registry"):
43
+ self.vendors = dict(**other.vendors, **self.vendors)
44
+
45
+ def __getitem__(self, item) -> AbstractVendor:
46
+ if item in self.vendors:
47
+ return self.vendors[item]
48
+ raise RuntimeError(f"Unknown vendor {item}")
49
+
50
+ def match(
51
+ self,
52
+ hw: HardwareView | str,
53
+ default: _SENTINEL | AbstractVendor | None = sentinel
54
+ ) -> AbstractVendor | None:
55
+ if isinstance(hw, str):
56
+ hw = HardwareView(hw, "")
57
+
58
+ matched: list[tuple[AbstractVendor, int]] = []
59
+ for name, vendor in self.vendors.items():
60
+ for item in vendor.match():
61
+ if hw.match(item):
62
+ matched.append((vendor, item.count(".")))
63
+
64
+ if matched:
65
+ return next(iter(sorted(matched, key=itemgetter(1), reverse=True)))[0]
66
+ if default is sentinel:
67
+ return GENERIC_VENDOR
68
+ return default
69
+
70
+ def get(self, item: str, default: _SENTINEL | AbstractVendor | None = sentinel) -> AbstractVendor | None:
71
+ if item in self:
72
+ return self[item]
73
+ if default is sentinel:
74
+ return GENERIC_VENDOR
75
+ return default
76
+
77
+ def __contains__(self, item):
78
+ return item in self.vendors
79
+
80
+ def __iter__(self):
81
+ return iter(self.vendors)
82
+
83
+
84
+ registry = Registry()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: annet
3
- Version: 2.6.0
3
+ Version: 3.0.0
4
4
  Summary: annet
5
5
  Home-page: https://github.com/annetutil/annet
6
6
  License: MIT
@@ -11,7 +11,7 @@ annet/diff.py,sha256=vz51az7RrXdYXsPbNMi9Y2awbjNRPQSFl6zzvE36Kx4,9441
11
11
  annet/executor.py,sha256=lcKI-EbYqeCiBNpL729kSltduzxbAzOkQ1L_QK7tNv8,5112
12
12
  annet/filtering.py,sha256=ZtqxPsKdV9reZoRxtQyBg22BqyMqd-2SotYcxZ-68AQ,903
13
13
  annet/gen.py,sha256=m76bL6rVbusNV_uVHrHnA3D7TUvvLtckXx7hlr5HGA0,31897
14
- annet/hardware.py,sha256=_iR28dWiPtt6ZYdk-qg1sxazkSRJE3ukqKB-fFFfQak,1141
14
+ annet/hardware.py,sha256=e7RWc5yPdEDT5hxNqcqWv9g9DRWIgwAieA2iT4FdPqc,1158
15
15
  annet/implicit.py,sha256=G6EwZbrtUp089qRAwh96hminp236-1pJbeKAedoEafg,6056
16
16
  annet/lib.py,sha256=4N4X6jCCrig5rk7Ua4AofrV9zK9jhzkBq57fLsfBJjw,4812
17
17
  annet/output.py,sha256=se8EpyNS9f9kPOlOaAV0ht4DjzDoBr8F2UafiezLPYw,7743
@@ -28,20 +28,20 @@ annet/adapters/fetchers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
28
28
  annet/adapters/fetchers/stub/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
29
  annet/adapters/fetchers/stub/fetcher.py,sha256=FzpkIzPJBQVuNNSdXKoGzkALmIGMgo2gmGLXzYWdDYA,891
30
30
  annet/adapters/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
- annet/adapters/file/provider.py,sha256=KyHuA5w9E4T3Lxeko8b5M7jRmu7yhTJVLqLkas4ue1A,6654
31
+ annet/adapters/file/provider.py,sha256=3hNt0QQg46SVymLQ4Bh9G4VNYyhnB7gV5yu5OiIJpZE,7307
32
32
  annet/adapters/netbox/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  annet/adapters/netbox/provider.py,sha256=SrxW_uBLvMTqtRiYXreL6RrZK4MpxVguF2ITnYOnVHU,2226
34
34
  annet/adapters/netbox/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
35
  annet/adapters/netbox/common/adapter.py,sha256=-YcO3371D8hupNXKf5f4-FdFSjTto-7VKDvz29Gid38,2016
36
36
  annet/adapters/netbox/common/client.py,sha256=PaxHG4W9H8_uunIwMBNYkLq4eQJYoO6p6gY-ciQs7Nc,2563
37
- annet/adapters/netbox/common/manufacturer.py,sha256=A3g9RrCpzYQQbdZ3OGYBalDn-OI7LlBnkXvMM757ftI,1770
38
- annet/adapters/netbox/common/models.py,sha256=b-xAQb1vtCLqafMl4npwNEznPT_QghlOWe3Yc7JkQ5c,7620
37
+ annet/adapters/netbox/common/manufacturer.py,sha256=n1RxJSGqXilKcGSqwIc7d2jD1NCaUXG5kQcOO93WTxw,1319
38
+ annet/adapters/netbox/common/models.py,sha256=IS9q5ZhrDhp74sjl5exSQu6lVOrQCX_I_RRtA9uCcdg,7686
39
39
  annet/adapters/netbox/common/query.py,sha256=kbNQSZwkjFeDArHwA8INHUauxCxYElXtNh58pZipWdo,1867
40
40
  annet/adapters/netbox/common/status_client.py,sha256=POaqiQJ0jPcqUQH-X_fWHVnKB7TBYveNriaT0eNTlfI,769
41
41
  annet/adapters/netbox/common/storage_base.py,sha256=3dn42CWTEZVBig4ABhy5BMQQ3YGb28InncWZoGvrgKk,8768
42
42
  annet/adapters/netbox/common/storage_opts.py,sha256=wfv1spElomwgVYMCgGth3SWVF0RsRgtUgq9GpFL9hJs,1520
43
43
  annet/adapters/netbox/v24/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
- annet/adapters/netbox/v24/models.py,sha256=TAZUYXf1TlOrzgitWZaaLvW0tB143RgnFb1wA2X9lc8,7549
44
+ annet/adapters/netbox/v24/models.py,sha256=RH2ooUPHOtHT0q1ZQE7v23FgmcsGenzzSgJyft13o1k,7605
45
45
  annet/adapters/netbox/v24/storage.py,sha256=zptzrW4aZUv_exuGw0DqQPm_kXZR3DwL4zRHYL6twTk,6219
46
46
  annet/adapters/netbox/v37/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
47
  annet/adapters/netbox/v37/models.py,sha256=VqI1bT5E5pFIaDJWxWMU6bioNyrXKJuNq-_J8YIJIbw,1613
@@ -60,7 +60,7 @@ annet/annlib/filter_acl.py,sha256=mxDga-3SXqpPEsdQypTq0ScqUqP625lxX-DmJalEc3s,71
60
60
  annet/annlib/jsontools.py,sha256=BS7s4rI8R9c_y3zz0zYl1l6con65oQ0MvfsC1dsXZts,5535
61
61
  annet/annlib/lib.py,sha256=lxmYrbeablFMhFtvFmADVpVShSFi9TN4gNWaBs_Ygm0,14952
62
62
  annet/annlib/output.py,sha256=_SjJ6G6bejvnTKqNHw6xeio0FT9oO3OIkLaOC3cEga4,7569
63
- annet/annlib/patching.py,sha256=IZYW4kydEzBmRi_PZ8Lk0g7hx-sSYl2wjd6lDaI0D4k,21435
63
+ annet/annlib/patching.py,sha256=1Ku5dNCQS9nlsMEr7tt7o1uGBsBeqfVFw4NY8aWYvnc,21483
64
64
  annet/annlib/tabparser.py,sha256=aXwY0glVQ4__Sg1FFXiSPghyoNQRrMUQ7XCjMq__3lM,32016
65
65
  annet/annlib/types.py,sha256=VHU0CBADfYmO0xzB_c5f-mcuU3dUumuJczQnqGlib9M,852
66
66
  annet/annlib/netdev/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -69,16 +69,16 @@ annet/annlib/netdev/devdb/__init__.py,sha256=aKYjjLbJebdKBjnGDpVLQdSqrV2JL24spGm
69
69
  annet/annlib/netdev/devdb/data/devdb.json,sha256=t-j7SjEqSWNPhYV3cPQCSpODc3VGcRKJze9fgj4L9Uw,6710
70
70
  annet/annlib/netdev/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  annet/annlib/netdev/views/dump.py,sha256=rIlyvnA3uM8bB_7oq1nS2KDxTp6dQh2hz-FbNhYIpOU,4630
72
- annet/annlib/netdev/views/hardware.py,sha256=jybt3BjEKbhSIU6A4MvA2X2-i3BqWcllvMAX_EKLGMU,4396
72
+ annet/annlib/netdev/views/hardware.py,sha256=XvHtRn29SROKtxqMyr86oc1ItUKy36N98ppfuJQL8PY,3235
73
73
  annet/annlib/rbparser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
- annet/annlib/rbparser/acl.py,sha256=RR8yPt6t96_IiyuKVbeZ-3x32cyhBAT2wC1y99oWBO8,3931
74
+ annet/annlib/rbparser/acl.py,sha256=I_1VUZQgF-s4dYTxyEDxQ-GkAP2MeXonzzsc2xROpTM,3974
75
75
  annet/annlib/rbparser/deploying.py,sha256=ACT8QNhDAhJx3ZKuGh2nYBOrpdka2qEKuLDxvQAGKLk,1649
76
- annet/annlib/rbparser/ordering.py,sha256=SmN_22pIJSIkmyT1-HSjWsqid7UJ0DgkqyQu7IO3bS4,2142
76
+ annet/annlib/rbparser/ordering.py,sha256=XknggB92N4IbhHBZZHL0StwCCITt8mkho4D4Nu5Kb4k,2185
77
77
  annet/annlib/rbparser/platform.py,sha256=vNoK25ACNQuDRv5I9uDfmNi8Y8yi10XWSbWIeKEVUgs,1672
78
78
  annet/annlib/rbparser/syntax.py,sha256=iZ7Y-4QQBw4L3UtjEh54qisiRDhobl7HZxFNdP8mi54,3577
79
79
  annet/annlib/rulebook/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
80
  annet/annlib/rulebook/common.py,sha256=-E6ATuFkSjNhyK0hiGd-iap5Ch5up2MNuVCbobWfmeU,16649
81
- annet/api/__init__.py,sha256=DU88rY4sQLUoBE9g0WxgqF6GdL7CIUAak01Ejuf63qc,33098
81
+ annet/api/__init__.py,sha256=KYtCj2Kgj2Qt1eqhk8kpSz4QgXToDdK2Okvtac3UD-w,33152
82
82
  annet/configs/context.yml,sha256=RVLrKLIHpCty7AGwOnmqf7Uu0iZQCn-AjYhophDJer8,259
83
83
  annet/configs/logging.yaml,sha256=EUagfir99QqA73Scc3k7sfQccbU3E1SvEQdyhLFtCl4,997
84
84
  annet/generators/__init__.py,sha256=9MmstB59DlHGnY7GP36nyTiAdKtHdsJTyOYc0cxwm5U,16398
@@ -118,10 +118,10 @@ annet/rpl_generators/execute.py,sha256=wS6e6fwcPWywsHB0gBMqZ17eF0s4YOBgDgwPB_cr5
118
118
  annet/rpl_generators/policy.py,sha256=j_2EtAsaxldhZZT4kRXKZacuAsGnHr3JSc2BMxr_Pow,37174
119
119
  annet/rpl_generators/prefix_lists.py,sha256=5jM5Xj0dtx5xF9ap-TgExs0ofRAzm0LN2j3aL5Ub_yc,4778
120
120
  annet/rpl_generators/rd.py,sha256=YGXgx1D2D0-pixgspXJzA6NvW8lx3AmHMxIY2l5rraI,1457
121
- annet/rulebook/__init__.py,sha256=oafL5HC8QHdkO9CH2q_fxohPMxOgjn-dNQa5kPjuqsA,3942
121
+ annet/rulebook/__init__.py,sha256=WtDrO9BrbrF1o06zOTVrEMGuTgDtv7MfF8B29pRW77U,3996
122
122
  annet/rulebook/common.py,sha256=zK1s2c5lc5HQbIlMUQ4HARQudXSgOYiZ_Sxc2I_tHqg,721
123
- annet/rulebook/deploying.py,sha256=XV0XQvc3YvwM8SOgOQlc-fCW4bnjQg_1CTZkTwMp14A,2972
124
- annet/rulebook/patching.py,sha256=ve_D-9lnWs6Qb_NwqOLUWX-b_tI_L3pwQ7cgUPnqndY,4970
123
+ annet/rulebook/deploying.py,sha256=9CMeOUw5L1OWdrccSRfpJyH9H_jH7xWNU1JldviBNrk,3015
124
+ annet/rulebook/patching.py,sha256=4JQE_qmdHb0Eggj9ytp99P1Z59uW5ZeUrPJIOdfgi_0,5022
125
125
  annet/rulebook/arista/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
126
  annet/rulebook/arista/aaa.py,sha256=7fBTwBnz9SOqYbwG8GBeEQTJG0e4uC4HkuZJeMG-kAY,2250
127
127
  annet/rulebook/arista/iface.py,sha256=bgj6a3r__-OE6VyYbSfnD6ps2QJKyX028W7IFJww-UY,720
@@ -180,10 +180,14 @@ annet/rulebook/texts/ribbon.deploy,sha256=hCq2XeAcwaYanyQ8lTdnez6Pgq-gRqpNuR8dAl
180
180
  annet/rulebook/texts/ribbon.rul,sha256=609LyLTDCtXPVQNAzqS-VEyCpW3byHP8TCMJLFMn5cc,2108
181
181
  annet/rulebook/texts/routeros.order,sha256=M71uy_hf0KAjLNS3zZY3uih4m2xLUcu26FEoVVjC6k0,905
182
182
  annet/rulebook/texts/routeros.rul,sha256=ipfxjj0mjFef6IsUlupqx4BY_Je_OUb8u_U1019O1DE,1203
183
- annet-2.6.0.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
184
- annet-2.6.0.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
183
+ annet/vendors/__init__.py,sha256=gQcDFlKeWDZB6vxJ_MdPWEoE-C5dg-YgXvgGkuV9YLw,569
184
+ annet/vendors/base.py,sha256=ll7WKL9KtHiNl9Cs6pQs4bJH6c6JRhyHIM7wdgkysdU,536
185
+ annet/vendors/registry.py,sha256=hav8ONavMAM7FCHgF-0_NKC_ntNAtcTicjxqXNtbovw,2326
186
+ annet-3.0.0.dist-info/licenses/AUTHORS,sha256=rh3w5P6gEgqmuC-bw-HB68vBCr-yIBFhVL0PG4hguLs,878
187
+ annet-3.0.0.dist-info/licenses/LICENSE,sha256=yPxl7dno02Pw7gAcFPIFONzx_gapwDoPXsIsh6Y7lC0,1079
185
188
  annet_generators/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
186
- annet_generators/example/__init__.py,sha256=1z030I00c9KeqW0ntkT1IRLYKD5LZAHYjiNHrOLen1Q,221
189
+ annet_generators/example/__init__.py,sha256=OJ77uj8axc-FIyIu_Xdcnzmde3oQW5mk5qbODkhuVc8,355
190
+ annet_generators/example/hostname.py,sha256=RloLzNVetEoWPLITzfJ13Nk3CC0yi-cZB1RTd6dnuhI,2541
187
191
  annet_generators/example/lldp.py,sha256=24bGvShxbio-JxUdaehyPRu31LhH9YwSwFDrWVRn6yo,2100
188
192
  annet_generators/mesh_example/__init__.py,sha256=NfNWgXn1TNiWI6A5tmU6Y-4QV2i33td0Qs3re0MNNMo,218
189
193
  annet_generators/mesh_example/bgp.py,sha256=jzyDndSSGYyYBquDnLlR-7P5lzmUKcSyYCml3VsoMC0,1385
@@ -193,8 +197,8 @@ annet_generators/rpl_example/generator.py,sha256=EWah19gOH8G-QyNyWqxCqdRi0BK7GbM
193
197
  annet_generators/rpl_example/items.py,sha256=d99HSXDHFjZq511EvGhIqRTWK3F4ZsCWfdUqFYQcyhE,772
194
198
  annet_generators/rpl_example/mesh.py,sha256=z_WgfDZZ4xnyh3cSf75igyH09hGvtexEVwy1gCD_DzA,288
195
199
  annet_generators/rpl_example/route_policy.py,sha256=z6nPb0VDeQtKD1NIg9sFvmUxBD5tVs2frfNIuKdM-5c,2318
196
- annet-2.6.0.dist-info/METADATA,sha256=5dAbgYBs9h-co3Rqk177VqRH5wNcEOEh15ZNaaTBS7w,815
197
- annet-2.6.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
198
- annet-2.6.0.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
199
- annet-2.6.0.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
200
- annet-2.6.0.dist-info/RECORD,,
200
+ annet-3.0.0.dist-info/METADATA,sha256=x8H3f6rBvrh1jfXlkoWpqA5LiH7ZB6mnoc04Yhb6o6I,815
201
+ annet-3.0.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
202
+ annet-3.0.0.dist-info/entry_points.txt,sha256=5lIaDGlGi3l6QQ2ry2jZaqViP5Lvt8AmsegdD0Uznck,192
203
+ annet-3.0.0.dist-info/top_level.txt,sha256=QsoTZBsUtwp_FEcmRwuN8QITBmLOZFqjssRfKilGbP8,23
204
+ annet-3.0.0.dist-info/RECORD,,
@@ -1,10 +1,15 @@
1
+ import itertools
1
2
  from typing import List
2
3
 
3
4
  from annet.generators import BaseGenerator
4
5
  from annet.storage import Storage
5
6
 
6
7
  from . import lldp
8
+ from . import hostname
7
9
 
8
10
 
9
11
  def get_generators(store: Storage) -> List[BaseGenerator]:
10
- return lldp.get_generators(store)
12
+ return list(itertools.chain.from_iterable([
13
+ hostname.get_generators(store),
14
+ lldp.get_generators(store),
15
+ ]))
@@ -0,0 +1,113 @@
1
+ from typing import List
2
+
3
+ from annet.generators import BaseGenerator, PartialGenerator
4
+ from annet.storage import Storage
5
+
6
+
7
+ class Hostname(PartialGenerator):
8
+ TAGS = ["hostname"]
9
+
10
+ def acl_huawei(self, _):
11
+ return """
12
+ sysname
13
+ header
14
+ """
15
+
16
+ def acl_safe_huawei(self, _):
17
+ return """
18
+ sysname
19
+ header
20
+ """
21
+
22
+ def run_huawei(self, device):
23
+ yield "sysname", device.hostname
24
+ yield 'header login information "%s"' % device.hostname
25
+
26
+ def acl_cisco(self, _):
27
+ return """
28
+ hostname
29
+ banner login
30
+ """
31
+
32
+ def acl_safe_cisco(self, _):
33
+ return """
34
+ hostname
35
+ banner login
36
+ """
37
+
38
+ def run_cisco(self, device):
39
+ if device.hw.Cisco.AIR and device.tags["Lightweight WiFi точка"]:
40
+ return
41
+ elif device.hw.Catalyst:
42
+ yield "banner login ^C%s^C" % device.hostname
43
+ yield "hostname", device.hostname
44
+
45
+ def acl_arista(self, _):
46
+ return """
47
+ hostname
48
+ """
49
+
50
+ def acl_safe_arista(self, _):
51
+ return """
52
+ hostname
53
+ """
54
+
55
+ def run_arista(self, device):
56
+ yield "hostname", device.hostname
57
+
58
+ acl_nexus = acl_arista
59
+ run_nexus = run_arista
60
+
61
+ def acl_juniper(self, _):
62
+ return """
63
+ system %cant_delete
64
+ host-name
65
+ """
66
+
67
+ def acl_safe_juniper(self, _):
68
+ return """
69
+ system %cant_delete
70
+ host-name
71
+ """
72
+
73
+ def run_juniper(self, device):
74
+ with self.block("system"):
75
+ yield "host-name", device.hostname
76
+
77
+ acl_ribbon = acl_juniper
78
+ acl_safe_ribbon = acl_safe_juniper
79
+ run_ribbon = run_juniper
80
+
81
+ def acl_nokia(self, _):
82
+ return """
83
+ system
84
+ name
85
+ """
86
+
87
+ def acl_safe_nokia(self, _):
88
+ return """
89
+ system
90
+ name
91
+ """
92
+
93
+ def run_nokia(self, device):
94
+ with self.block("system"):
95
+ yield "name", self.literal(device.hostname)
96
+
97
+ def acl_routeros(self, _):
98
+ return """
99
+ system %cant_delete
100
+ identity %cant_delete
101
+ set ~ %cant_delete
102
+ """
103
+
104
+ def run_routeros(self, device):
105
+ with self.block("system"):
106
+ with self.block("identity"):
107
+ yield f"set name={device.hostname}"
108
+
109
+
110
+ def get_generators(store: Storage) -> List[BaseGenerator]:
111
+ return [
112
+ Hostname(store),
113
+ ]
File without changes