annet 2.3.1__py3-none-any.whl → 2.5.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/adapters/netbox/common/adapter.py +60 -0
- annet/adapters/netbox/common/models.py +47 -55
- annet/adapters/netbox/common/status_client.py +7 -0
- annet/adapters/netbox/common/storage_base.py +239 -0
- annet/adapters/netbox/provider.py +20 -7
- annet/adapters/netbox/v24/models.py +295 -0
- annet/adapters/netbox/v24/storage.py +8 -2
- annet/adapters/netbox/v37/models.py +60 -0
- annet/adapters/netbox/v37/storage.py +70 -300
- annet/adapters/netbox/v41/__init__.py +0 -0
- annet/adapters/netbox/v41/models.py +78 -0
- annet/adapters/netbox/v41/storage.py +91 -0
- annet/adapters/netbox/v42/__init__.py +0 -0
- annet/adapters/netbox/v42/models.py +63 -0
- annet/adapters/netbox/v42/storage.py +91 -0
- annet/annlib/netdev/devdb/data/devdb.json +2 -0
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info}/METADATA +4 -3
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info}/RECORD +23 -13
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info}/WHEEL +1 -1
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info}/entry_points.txt +0 -0
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info/licenses}/AUTHORS +0 -0
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info/licenses}/LICENSE +0 -0
- {annet-2.3.1.dist-info → annet-2.5.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from ipaddress import ip_interface, IPv6Interface
|
|
4
|
+
from typing import List, Optional, Any, Dict, Sequence, Callable
|
|
5
|
+
|
|
6
|
+
from annet.annlib.netdev.views.dump import DumpableView
|
|
7
|
+
from annet.annlib.netdev.views.hardware import HardwareView, lag_name, svi_name
|
|
8
|
+
from annet.storage import Storage
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class Entity(DumpableView):
|
|
13
|
+
id: int
|
|
14
|
+
name: str
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def _dump__list_key(self):
|
|
18
|
+
return self.name
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Label:
|
|
23
|
+
value: str
|
|
24
|
+
label: str
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass
|
|
28
|
+
class IpFamily:
|
|
29
|
+
value: int
|
|
30
|
+
label: str
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class DeviceType:
|
|
35
|
+
id: int
|
|
36
|
+
manufacturer: Entity
|
|
37
|
+
model: str
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass
|
|
41
|
+
class DeviceIp(DumpableView):
|
|
42
|
+
id: int
|
|
43
|
+
display: str
|
|
44
|
+
address: str
|
|
45
|
+
family: int
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def _dump__list_key(self):
|
|
49
|
+
return self.address
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class Prefix(DumpableView):
|
|
54
|
+
id: int
|
|
55
|
+
prefix: str
|
|
56
|
+
site: Optional[Entity]
|
|
57
|
+
vrf: Optional[Entity]
|
|
58
|
+
tenant: Optional[Entity]
|
|
59
|
+
vlan: Optional[Entity]
|
|
60
|
+
role: Optional[Entity]
|
|
61
|
+
status: Label
|
|
62
|
+
is_pool: bool
|
|
63
|
+
custom_fields: dict[str, Any]
|
|
64
|
+
created: datetime
|
|
65
|
+
last_updated: datetime
|
|
66
|
+
description: Optional[str] = ""
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def _dump__list_key(self):
|
|
70
|
+
return self.prefix
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@dataclass
|
|
74
|
+
class IpAddress(DumpableView):
|
|
75
|
+
id: int
|
|
76
|
+
assigned_object_id: int
|
|
77
|
+
display: str
|
|
78
|
+
family: IpFamily
|
|
79
|
+
address: str
|
|
80
|
+
status: Label
|
|
81
|
+
tags: List[Entity]
|
|
82
|
+
created: datetime
|
|
83
|
+
last_updated: datetime
|
|
84
|
+
prefix: Optional[Prefix] = None
|
|
85
|
+
vrf: Optional[Entity] = None
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def _dump__list_key(self):
|
|
89
|
+
return self.address
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@dataclass
|
|
93
|
+
class InterfaceConnectedEndpoint(Entity):
|
|
94
|
+
device: Entity
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclass
|
|
98
|
+
class InterfaceType:
|
|
99
|
+
value: str
|
|
100
|
+
label: str
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class InterfaceMode:
|
|
105
|
+
value: str
|
|
106
|
+
label: str
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@dataclass
|
|
110
|
+
class InterfaceVlan(Entity):
|
|
111
|
+
vid: int
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class Interface(Entity):
|
|
116
|
+
device: Entity
|
|
117
|
+
enabled: bool
|
|
118
|
+
description: str
|
|
119
|
+
type: InterfaceType
|
|
120
|
+
connected_endpoints: Optional[list[InterfaceConnectedEndpoint]]
|
|
121
|
+
mode: Optional[InterfaceMode]
|
|
122
|
+
untagged_vlan: Optional[InterfaceVlan]
|
|
123
|
+
tagged_vlans: Optional[List[InterfaceVlan]]
|
|
124
|
+
display: str = ""
|
|
125
|
+
ip_addresses: List[IpAddress] = field(default_factory=list)
|
|
126
|
+
vrf: Optional[Entity] = None
|
|
127
|
+
mtu: int | None = None
|
|
128
|
+
lag: Entity | None = None
|
|
129
|
+
lag_min_links: int | None = None
|
|
130
|
+
|
|
131
|
+
def add_addr(self, address_mask: str, vrf: str | None) -> None:
|
|
132
|
+
addr = ip_interface(address_mask)
|
|
133
|
+
if vrf is None:
|
|
134
|
+
vrf_obj = None
|
|
135
|
+
else:
|
|
136
|
+
vrf_obj = Entity(id=0, name=vrf)
|
|
137
|
+
|
|
138
|
+
if isinstance(addr, IPv6Interface):
|
|
139
|
+
family = IpFamily(value=6, label="IPv6")
|
|
140
|
+
else:
|
|
141
|
+
family = IpFamily(value=4, label="IPv4")
|
|
142
|
+
|
|
143
|
+
for existing_addr in self.ip_addresses:
|
|
144
|
+
if existing_addr.address == address_mask and (
|
|
145
|
+
(existing_addr.vrf is None and vrf is None) or
|
|
146
|
+
(existing_addr.vrf is not None and existing_addr.vrf.name == vrf)
|
|
147
|
+
):
|
|
148
|
+
return
|
|
149
|
+
self.ip_addresses.append(IpAddress(
|
|
150
|
+
id=0,
|
|
151
|
+
display=address_mask,
|
|
152
|
+
address=address_mask,
|
|
153
|
+
vrf=vrf_obj,
|
|
154
|
+
prefix=None,
|
|
155
|
+
family=family,
|
|
156
|
+
created=datetime.now(timezone.utc),
|
|
157
|
+
last_updated=datetime.now(timezone.utc),
|
|
158
|
+
tags=[],
|
|
159
|
+
status=Label(value="active", label="Active"),
|
|
160
|
+
assigned_object_id=self.id,
|
|
161
|
+
))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@dataclass
|
|
165
|
+
class NetboxDevice(Entity):
|
|
166
|
+
url: str
|
|
167
|
+
storage: Storage
|
|
168
|
+
|
|
169
|
+
display: str
|
|
170
|
+
device_type: DeviceType
|
|
171
|
+
device_role: Entity
|
|
172
|
+
tenant: Optional[Entity]
|
|
173
|
+
platform: Optional[Entity]
|
|
174
|
+
serial: str
|
|
175
|
+
asset_tag: Optional[str]
|
|
176
|
+
site: Entity
|
|
177
|
+
rack: Optional[Entity]
|
|
178
|
+
position: Optional[float]
|
|
179
|
+
face: Optional[Label]
|
|
180
|
+
status: Label
|
|
181
|
+
primary_ip: Optional[DeviceIp]
|
|
182
|
+
primary_ip4: Optional[DeviceIp]
|
|
183
|
+
primary_ip6: Optional[DeviceIp]
|
|
184
|
+
tags: List[Entity]
|
|
185
|
+
custom_fields: Dict[str, Any]
|
|
186
|
+
created: datetime
|
|
187
|
+
last_updated: datetime
|
|
188
|
+
|
|
189
|
+
fqdn: str
|
|
190
|
+
hostname: str
|
|
191
|
+
hw: HardwareView
|
|
192
|
+
breed: str
|
|
193
|
+
|
|
194
|
+
interfaces: List[Interface]
|
|
195
|
+
|
|
196
|
+
@property
|
|
197
|
+
def neighbors(self) -> List["Entity"]:
|
|
198
|
+
return [
|
|
199
|
+
endpoint.device
|
|
200
|
+
for iface in self.interfaces
|
|
201
|
+
if iface.connected_endpoints
|
|
202
|
+
for endpoint in iface.connected_endpoints
|
|
203
|
+
if endpoint.device
|
|
204
|
+
]
|
|
205
|
+
|
|
206
|
+
# compat
|
|
207
|
+
@property
|
|
208
|
+
def neighbours_fqdns(self) -> list[str]:
|
|
209
|
+
return [dev.name for dev in self.neighbors]
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def neighbours_ids(self):
|
|
213
|
+
return [dev.id for dev in self.neighbors]
|
|
214
|
+
|
|
215
|
+
def __hash__(self):
|
|
216
|
+
return hash((self.id, type(self)))
|
|
217
|
+
|
|
218
|
+
def __eq__(self, other):
|
|
219
|
+
return type(self) is type(other) and self.url == other.url
|
|
220
|
+
|
|
221
|
+
def is_pc(self) -> bool:
|
|
222
|
+
custom_breed_pc = ("Mellanox", "NVIDIA", "Moxa", "Nebius")
|
|
223
|
+
return self.device_type.manufacturer.name in custom_breed_pc or self.breed == "pc"
|
|
224
|
+
|
|
225
|
+
def _make_interface(self, name: str, type: InterfaceType) -> Interface:
|
|
226
|
+
return Interface(
|
|
227
|
+
name=name,
|
|
228
|
+
device=self,
|
|
229
|
+
enabled=True,
|
|
230
|
+
description="",
|
|
231
|
+
type=type,
|
|
232
|
+
id=0,
|
|
233
|
+
vrf=None,
|
|
234
|
+
display=name,
|
|
235
|
+
untagged_vlan=None,
|
|
236
|
+
tagged_vlans=[],
|
|
237
|
+
ip_addresses=[],
|
|
238
|
+
connected_endpoints=[],
|
|
239
|
+
mode=None,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
def _lag_name(self, lag: int) -> str:
|
|
243
|
+
return lag_name(self.hw, lag)
|
|
244
|
+
|
|
245
|
+
def make_lag(self, lag: int, ports: Sequence[str], lag_min_links: int | None) -> Interface:
|
|
246
|
+
new_name = self._lag_name(lag)
|
|
247
|
+
for target_interface in self.interfaces:
|
|
248
|
+
if target_interface.name == new_name:
|
|
249
|
+
return target_interface
|
|
250
|
+
lag_interface = self._make_interface(
|
|
251
|
+
name=new_name,
|
|
252
|
+
type=InterfaceType(value="lag", label="Link Aggregation Group (LAG)"),
|
|
253
|
+
)
|
|
254
|
+
lag_interface.lag_min_links = lag_min_links
|
|
255
|
+
for interface in self.interfaces:
|
|
256
|
+
if interface.name in ports:
|
|
257
|
+
interface.lag = lag_interface
|
|
258
|
+
self.interfaces.append(lag_interface)
|
|
259
|
+
return lag_interface
|
|
260
|
+
|
|
261
|
+
def _svi_name(self, svi: int) -> str:
|
|
262
|
+
return svi_name(self.hw, svi)
|
|
263
|
+
|
|
264
|
+
def add_svi(self, svi: int) -> Interface:
|
|
265
|
+
name = self._svi_name(svi)
|
|
266
|
+
for interface in self.interfaces:
|
|
267
|
+
if interface.name == name:
|
|
268
|
+
return interface
|
|
269
|
+
interface = self._make_interface(
|
|
270
|
+
name=name,
|
|
271
|
+
type=InterfaceType("virtual", "Virtual")
|
|
272
|
+
)
|
|
273
|
+
self.interfaces.append(interface)
|
|
274
|
+
return interface
|
|
275
|
+
|
|
276
|
+
def _subif_name(self, interface: str, subif: int) -> str:
|
|
277
|
+
return f"{interface}.{subif}"
|
|
278
|
+
|
|
279
|
+
def add_subif(self, interface: str, subif: int) -> Interface:
|
|
280
|
+
name = self._subif_name(interface, subif)
|
|
281
|
+
for target_port in self.interfaces:
|
|
282
|
+
if target_port.name == name:
|
|
283
|
+
return target_port
|
|
284
|
+
target_port = self._make_interface(
|
|
285
|
+
name=name,
|
|
286
|
+
type=InterfaceType("virtual", "Virtual")
|
|
287
|
+
)
|
|
288
|
+
self.interfaces.append(target_port)
|
|
289
|
+
return target_port
|
|
290
|
+
|
|
291
|
+
def find_interface(self, name: str) -> Optional[Interface]:
|
|
292
|
+
for iface in self.interfaces:
|
|
293
|
+
if iface.name == name:
|
|
294
|
+
return iface
|
|
295
|
+
return None
|
|
@@ -4,7 +4,8 @@ from typing import Optional, List, Union
|
|
|
4
4
|
from annetbox.v24 import models as api_models
|
|
5
5
|
from annetbox.v24.client_sync import NetboxV24
|
|
6
6
|
|
|
7
|
-
from annet.adapters.netbox.common import
|
|
7
|
+
from annet.adapters.netbox.common.models import InterfaceType
|
|
8
|
+
from annet.adapters.netbox.v24 import models
|
|
8
9
|
from annet.adapters.netbox.common.manufacturer import (
|
|
9
10
|
get_hw, get_breed,
|
|
10
11
|
)
|
|
@@ -78,7 +79,6 @@ def extend_device(
|
|
|
78
79
|
hw=get_hw(manufacturer, model, platform_name),
|
|
79
80
|
breed=get_breed(manufacturer, model),
|
|
80
81
|
interfaces=[],
|
|
81
|
-
neighbours_ids=[],
|
|
82
82
|
storage=storage,
|
|
83
83
|
)
|
|
84
84
|
|
|
@@ -87,9 +87,15 @@ def extend_interface(interface: api_models.Interface) -> models.Interface:
|
|
|
87
87
|
return models.Interface(
|
|
88
88
|
id=interface.id,
|
|
89
89
|
name=interface.name,
|
|
90
|
+
description="",
|
|
90
91
|
device=interface.device,
|
|
91
92
|
enabled=interface.enabled,
|
|
92
93
|
display=interface.name,
|
|
94
|
+
type=InterfaceType("Unknown", "unknown"),
|
|
95
|
+
connected_endpoints=None,
|
|
96
|
+
mode=None,
|
|
97
|
+
untagged_vlan=None,
|
|
98
|
+
tagged_vlans=None,
|
|
93
99
|
ip_addresses=[],
|
|
94
100
|
)
|
|
95
101
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from datetime import datetime, timezone
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from annet.adapters.netbox.common.models import (
|
|
6
|
+
IpAddress, NetboxDevice, Entity, Prefix, InterfaceType, Interface, IpFamily, Label,
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class PrefixV37(Prefix):
|
|
12
|
+
site: Optional[Entity] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class IpAddressV37(IpAddress[PrefixV37]):
|
|
17
|
+
prefix: Optional[PrefixV37] = None
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class InterfaceV37(Interface[IpAddressV37]):
|
|
22
|
+
def _add_new_addr(self, address_mask: str, vrf: Entity | None, family: IpFamily) -> None:
|
|
23
|
+
self.ip_addresses.append(IpAddressV37(
|
|
24
|
+
id=0,
|
|
25
|
+
display=address_mask,
|
|
26
|
+
address=address_mask,
|
|
27
|
+
vrf=vrf,
|
|
28
|
+
prefix=None,
|
|
29
|
+
family=family,
|
|
30
|
+
created=datetime.now(timezone.utc),
|
|
31
|
+
last_updated=datetime.now(timezone.utc),
|
|
32
|
+
tags=[],
|
|
33
|
+
status=Label(value="active", label="Active"),
|
|
34
|
+
assigned_object_id=self.id,
|
|
35
|
+
))
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class NetboxDeviceV37(NetboxDevice[InterfaceV37]):
|
|
40
|
+
device_role: Entity
|
|
41
|
+
|
|
42
|
+
def __hash__(self):
|
|
43
|
+
return hash((self.id, type(self)))
|
|
44
|
+
|
|
45
|
+
def _make_interface(self, name: str, type: InterfaceType) -> InterfaceV37:
|
|
46
|
+
return InterfaceV37(
|
|
47
|
+
name=name,
|
|
48
|
+
device=self,
|
|
49
|
+
enabled=True,
|
|
50
|
+
description="",
|
|
51
|
+
type=type,
|
|
52
|
+
id=0,
|
|
53
|
+
vrf=None,
|
|
54
|
+
display=name,
|
|
55
|
+
untagged_vlan=None,
|
|
56
|
+
tagged_vlans=[],
|
|
57
|
+
ip_addresses=[],
|
|
58
|
+
connected_endpoints=[],
|
|
59
|
+
mode=None,
|
|
60
|
+
)
|