annet 2.4.0__py3-none-any.whl → 2.5.1__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.

@@ -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 models
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
+ )