Merge ~bjornt/maas:move-fake-commissioning-data into maas:master
- Git
- lp:~bjornt/maas
- move-fake-commissioning-data
- Merge into master
Proposed by
Björn Tillenius
Status: | Merged |
---|---|
Approved by: | Björn Tillenius |
Approved revision: | d1c0008c387b7fdef9e541a6797f17e31124b620 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~bjornt/maas:move-fake-commissioning-data |
Merge into: | maas:master |
Diff against target: |
880 lines (+419/-414) 3 files modified
src/maasserver/rpc/tests/test_rackcontrollers.py (+1/-4) src/maasserver/testing/commissioning.py (+413/-0) src/metadataserver/builtin_scripts/tests/test_network.py (+5/-410) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alberto Donato (community) | Approve | ||
Review via email: mp+416011@code.launchpad.net |
Commit message
Move FakeCommissioni
This makes it easier to reuse it for other tests and for sampledata.
Description of the change
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | diff --git a/src/maasserver/rpc/tests/test_rackcontrollers.py b/src/maasserver/rpc/tests/test_rackcontrollers.py |
2 | index 141135d..8e9c1a7 100644 |
3 | --- a/src/maasserver/rpc/tests/test_rackcontrollers.py |
4 | +++ b/src/maasserver/rpc/tests/test_rackcontrollers.py |
5 | @@ -25,16 +25,13 @@ from maasserver.rpc.rackcontrollers import ( |
6 | update_last_image_sync, |
7 | update_state, |
8 | ) |
9 | +from maasserver.testing.commissioning import FakeCommissioningData, LXDAddress |
10 | from maasserver.testing.factory import factory |
11 | from maasserver.testing.testcase import MAASServerTestCase |
12 | from maasserver.utils.orm import reload_object |
13 | from maastesting.matchers import DocTestMatches, MockCalledOnceWith |
14 | from metadataserver.builtin_scripts import load_builtin_scripts |
15 | from metadataserver.builtin_scripts.network import update_node_interfaces |
16 | -from metadataserver.builtin_scripts.tests.test_network import ( |
17 | - FakeCommissioningData, |
18 | - LXDAddress, |
19 | -) |
20 | from provisioningserver.enum import CONTROLLER_INSTALL_TYPE |
21 | from provisioningserver.rpc.exceptions import NoSuchScope |
22 | |
23 | diff --git a/src/maasserver/testing/commissioning.py b/src/maasserver/testing/commissioning.py |
24 | new file mode 100644 |
25 | index 0000000..5969219 |
26 | --- /dev/null |
27 | +++ b/src/maasserver/testing/commissioning.py |
28 | @@ -0,0 +1,413 @@ |
29 | +import dataclasses |
30 | +import random |
31 | +from typing import List, Optional |
32 | + |
33 | +from maasserver.testing.factory import factory |
34 | +from provisioningserver.utils.network import ( |
35 | + annotate_with_default_monitored_interfaces, |
36 | + get_default_monitored_interfaces, |
37 | +) |
38 | + |
39 | +GB = 1000 * 1000 * 1000 |
40 | + |
41 | + |
42 | +@dataclasses.dataclass |
43 | +class LXDPartition: |
44 | + id: str |
45 | + read_only: bool = False |
46 | + |
47 | + |
48 | +@dataclasses.dataclass |
49 | +class LXDDisk: |
50 | + id: str |
51 | + size: int = 250 * GB |
52 | + partitions: List[LXDPartition] = dataclasses.field(default_factory=list) |
53 | + type: str = "sata" |
54 | + read_only: bool = False |
55 | + removable: bool = False |
56 | + rpm: int = 0 |
57 | + numa_node: int = 0 |
58 | + |
59 | + |
60 | +@dataclasses.dataclass |
61 | +class LXDVlan: |
62 | + lower_device: str |
63 | + vid: int |
64 | + |
65 | + |
66 | +@dataclasses.dataclass |
67 | +class LXDBridge: |
68 | + upper_devices: List[str] = dataclasses.field(default_factory=list) |
69 | + |
70 | + |
71 | +@dataclasses.dataclass |
72 | +class LXDBond: |
73 | + lower_devices: List[str] = dataclasses.field(default_factory=list) |
74 | + |
75 | + |
76 | +@dataclasses.dataclass |
77 | +class LXDAddress: |
78 | + |
79 | + address: str |
80 | + netmask: str |
81 | + family: str = "inet" |
82 | + scope: str = "global" |
83 | + |
84 | + |
85 | +@dataclasses.dataclass |
86 | +class LXDNetwork: |
87 | + name: str |
88 | + hwaddr: str |
89 | + type: str = "broadcast" |
90 | + state: str = "up" |
91 | + addresses: List[LXDAddress] = dataclasses.field(default_factory=list) |
92 | + vlan: Optional[LXDVlan] = None |
93 | + bridge: Optional[LXDBridge] = None |
94 | + bond: Optional[LXDBond] = None |
95 | + |
96 | + |
97 | +@dataclasses.dataclass |
98 | +class LXDNetworkPort: |
99 | + |
100 | + id: str |
101 | + port: int |
102 | + address: str = dataclasses.field(default_factory=factory.make_mac_address) |
103 | + protocol: str = "ethernet" |
104 | + supported_modes: List[str] = dataclasses.field( |
105 | + default_factory=lambda: ["10000baseT/Full"] |
106 | + ) |
107 | + supported_ports: List[str] = dataclasses.field( |
108 | + default_factory=lambda: ["fiber"] |
109 | + ) |
110 | + port_type: str = "fiber" |
111 | + transceiver_type: str = "internal" |
112 | + auto_negotiation: bool = True |
113 | + link_detected: bool = True |
114 | + link_speed: int = 10000 |
115 | + link_duplex: str = "full" |
116 | + |
117 | + |
118 | +@dataclasses.dataclass |
119 | +class LXDNetworkCard: |
120 | + |
121 | + pci_address: str |
122 | + vendor: str = "My Corporation" |
123 | + vendor_id: str = "1234" |
124 | + product: str = "My Gigabit Network Connection" |
125 | + product_id: str = "5678" |
126 | + firmware_version: str = "1.63, 0x800009fa" |
127 | + numa_node: int = 0 |
128 | + driver: str = "mydriver" |
129 | + driver_version: str = "1.2.3" |
130 | + ports: Optional[List[LXDNetworkPort]] = None |
131 | + |
132 | + |
133 | +class FakeCommissioningData: |
134 | + """Helper to generate commissioning output programtically. |
135 | + |
136 | + Instead of hardcoding the commissioning data, taking care of |
137 | + including all the possible keys and values, this class allows you to |
138 | + tell which interface you want the machine to have, and you only have |
139 | + to specify what's important for the tests. The helper will ensure |
140 | + that all the other keys are there with a sane value and in the right |
141 | + format. |
142 | + """ |
143 | + |
144 | + def __init__( |
145 | + self, |
146 | + cores=1, |
147 | + memory=2048, |
148 | + disks=None, |
149 | + api_extensions=None, |
150 | + api_version="1.0", |
151 | + ): |
152 | + self.cores = cores |
153 | + self.memory = memory |
154 | + if api_extensions is None: |
155 | + api_extensions = [ |
156 | + "resources", |
157 | + "resources_v2", |
158 | + "api_os", |
159 | + "resources_system", |
160 | + "resources_usb_pci", |
161 | + ] |
162 | + self.api_extensions = api_extensions |
163 | + self.api_version = api_version |
164 | + self.environment = { |
165 | + "kernel": "Linux", |
166 | + "kernel_architecture": "x86_64", |
167 | + "kernel_version": "5.4.0-67-generic", |
168 | + "os_name": "ubuntu", |
169 | + "os_version": "20.04", |
170 | + "server": "maas-machine-resources", |
171 | + "server_name": factory.make_name("host"), |
172 | + "server_version": "4.11", |
173 | + } |
174 | + self.address_annotations = {} |
175 | + self._allocated_pci_addresses = [] |
176 | + self.networks = {} |
177 | + self._network_cards = [] |
178 | + if disks is None: |
179 | + disks = [LXDDisk("sda")] |
180 | + self._disks = list(disks) |
181 | + self.hints = None |
182 | + |
183 | + def allocate_pci_address(self): |
184 | + prev_address = ( |
185 | + self._allocated_pci_addresses[-1] |
186 | + if self._allocated_pci_addresses |
187 | + else "0000:00:00.0" |
188 | + ) |
189 | + bus, device, func = prev_address.split(":") |
190 | + next_device = int(device, 16) + 1 |
191 | + self._allocated_pci_addresses.append( |
192 | + f"{bus}:{next_device:0>4x}:{func}" |
193 | + ) |
194 | + return self._allocated_pci_addresses[-1] |
195 | + |
196 | + def get_available_vid(self): |
197 | + available_vids = set(range(2, 4095)) |
198 | + used_vids = { |
199 | + network.vlan.vid |
200 | + for network in self.networks.values() |
201 | + if network.vlan is not None |
202 | + } |
203 | + available_vids = list(available_vids.difference(used_vids)) |
204 | + return random.choice(available_vids) |
205 | + |
206 | + def create_network_card(self): |
207 | + card = LXDNetworkCard(self.allocate_pci_address()) |
208 | + self._network_cards.append(card) |
209 | + return card |
210 | + |
211 | + def create_physical_network( |
212 | + self, |
213 | + name=None, |
214 | + mac_address=None, |
215 | + card=None, |
216 | + port=None, |
217 | + ): |
218 | + if card is None: |
219 | + card = self.create_network_card() |
220 | + if card.ports is None: |
221 | + card.ports = [] |
222 | + network = self.create_physical_network_without_nic(name, mac_address) |
223 | + if port is None: |
224 | + port = LXDNetworkPort( |
225 | + network.name, len(card.ports), address=network.hwaddr |
226 | + ) |
227 | + card.ports.append(port) |
228 | + return network |
229 | + |
230 | + def create_physical_network_without_nic( |
231 | + self, |
232 | + name=None, |
233 | + mac_address=None, |
234 | + ): |
235 | + if name is None: |
236 | + name = factory.make_string("eth") |
237 | + if mac_address is None: |
238 | + mac_address = factory.make_mac_address() |
239 | + network = LXDNetwork(name, mac_address) |
240 | + self.networks[name] = network |
241 | + return network |
242 | + |
243 | + def create_vlan_network( |
244 | + self, |
245 | + name=None, |
246 | + vid=None, |
247 | + mac_address=None, |
248 | + parent=None, |
249 | + ): |
250 | + if name is None: |
251 | + name = factory.make_string("vlan") |
252 | + if parent is None: |
253 | + parent = self.create_physical_network() |
254 | + if mac_address is None: |
255 | + mac_address = factory.make_mac_address() |
256 | + if vid is None: |
257 | + vid = self.get_available_vid() |
258 | + network = LXDNetwork( |
259 | + name, mac_address, vlan=LXDVlan(lower_device=parent.name, vid=vid) |
260 | + ) |
261 | + self.networks[name] = network |
262 | + return network |
263 | + |
264 | + def create_bridge_network( |
265 | + self, |
266 | + name=None, |
267 | + mac_address=None, |
268 | + parents=None, |
269 | + ): |
270 | + if name is None: |
271 | + name = factory.make_string("bridge") |
272 | + if parents is None: |
273 | + parents = [self.create_physical_network()] |
274 | + if mac_address is None: |
275 | + mac_address = factory.make_mac_address() |
276 | + network = LXDNetwork( |
277 | + name, |
278 | + mac_address, |
279 | + bridge=LXDBridge( |
280 | + upper_devices=[parent.name for parent in parents] |
281 | + ), |
282 | + ) |
283 | + self.networks[name] = network |
284 | + return network |
285 | + |
286 | + def create_bond_network( |
287 | + self, |
288 | + name=None, |
289 | + mac_address=None, |
290 | + parents=None, |
291 | + ): |
292 | + if name is None: |
293 | + name = factory.make_string("bond") |
294 | + if parents is None: |
295 | + parents = [self.create_physical_network()] |
296 | + if mac_address is None: |
297 | + mac_address = factory.make_mac_address() |
298 | + network = LXDNetwork( |
299 | + name, |
300 | + mac_address, |
301 | + bond=LXDBond(lower_devices=[parent.name for parent in parents]), |
302 | + ) |
303 | + self.networks[name] = network |
304 | + return network |
305 | + |
306 | + def render(self, include_extra=False): |
307 | + storage_resources = { |
308 | + "disks": [dataclasses.asdict(disk) for disk in self._disks], |
309 | + "total": len(self._disks), |
310 | + } |
311 | + network_resources = { |
312 | + "cards": [ |
313 | + dataclasses.asdict(card) for card in self._network_cards |
314 | + ], |
315 | + "total": len(self._network_cards), |
316 | + } |
317 | + for card in network_resources["cards"]: |
318 | + if card["ports"] is None: |
319 | + del card["ports"] |
320 | + networks = { |
321 | + name: dataclasses.asdict(network) |
322 | + for name, network in self.networks.items() |
323 | + } |
324 | + old_interfaces_data = self._generate_interfaces() |
325 | + data = { |
326 | + "api_extensions": self.api_extensions, |
327 | + "api_version": self.api_version, |
328 | + "environment": self.environment, |
329 | + "resources": { |
330 | + "cpu": { |
331 | + "architecture": self.environment["kernel_architecture"], |
332 | + "sockets": [ |
333 | + { |
334 | + "socket": 0, |
335 | + "cores": [], |
336 | + } |
337 | + ], |
338 | + }, |
339 | + "memory": { |
340 | + "hugepages_total": 0, |
341 | + "hugepages_used": 0, |
342 | + "hugepages_size": 0, |
343 | + "used": int(0.3 * self.memory * 1024 * 1024), |
344 | + "total": int(self.memory * 1024 * 1024), |
345 | + }, |
346 | + "gpu": {"cards": [], "total": 0}, |
347 | + "network": network_resources, |
348 | + "storage": storage_resources, |
349 | + }, |
350 | + "networks": networks, |
351 | + } |
352 | + for core_index in range(self.cores): |
353 | + data["resources"]["cpu"]["sockets"][0]["cores"].append( |
354 | + { |
355 | + "core": core_index, |
356 | + "threads": [ |
357 | + { |
358 | + "id": core_index, |
359 | + "thread": 0, |
360 | + "online": True, |
361 | + "numa_node": 0, |
362 | + }, |
363 | + ], |
364 | + "frequency": 1500, |
365 | + } |
366 | + ) |
367 | + if include_extra: |
368 | + data["network-extra"] = { |
369 | + "interfaces": old_interfaces_data, |
370 | + "monitored-interfaces": get_default_monitored_interfaces( |
371 | + old_interfaces_data |
372 | + ), |
373 | + "hints": self.hints, |
374 | + } |
375 | + return data |
376 | + |
377 | + def _generate_interfaces(self): |
378 | + # XXX: It would be good if this method could basically call |
379 | + # get_all_interfaces_definition(), passing in information it |
380 | + # needs. But considering the goal is to minimize information and |
381 | + # instead make use of the LXD data directly, it's probably worth |
382 | + # holding off until there's less information to render. |
383 | + interfaces = {} |
384 | + for name, network in self.networks.items(): |
385 | + if network.type != "broadcast": |
386 | + continue |
387 | + interface = { |
388 | + "mac_address": self._get_network_port_mac( |
389 | + name, network.hwaddr |
390 | + ), |
391 | + "links": [], |
392 | + "enabled": network.state == "up", |
393 | + "source": "machine-resources", |
394 | + } |
395 | + if network.vlan is not None: |
396 | + interface.update( |
397 | + { |
398 | + "type": "vlan", |
399 | + "parents": [network.vlan.lower_device], |
400 | + "vid": network.vlan.vid, |
401 | + } |
402 | + ) |
403 | + elif network.bridge is not None: |
404 | + interface.update( |
405 | + { |
406 | + "type": "bridge", |
407 | + "parents": list(network.bridge.upper_devices), |
408 | + } |
409 | + ) |
410 | + elif network.bond is not None: |
411 | + interface.update( |
412 | + { |
413 | + "type": "bond", |
414 | + "parents": list(network.bond.lower_devices), |
415 | + } |
416 | + ) |
417 | + else: |
418 | + interface.update({"type": "physical", "parents": []}) |
419 | + for address in network.addresses: |
420 | + link = { |
421 | + "address": f"{address.address}/{address.netmask}", |
422 | + "mode": "static", |
423 | + } |
424 | + address_annotation = self.address_annotations.get( |
425 | + address.address, {} |
426 | + ) |
427 | + link.update(address_annotation) |
428 | + interface["links"].append(link) |
429 | + interfaces[name] = interface |
430 | + annotate_with_default_monitored_interfaces(interfaces) |
431 | + return interfaces |
432 | + |
433 | + def _get_network_port_mac(self, port_name, default): |
434 | + for card in self._network_cards: |
435 | + if not card.ports: |
436 | + continue |
437 | + for port in card.ports: |
438 | + if port.id == port_name: |
439 | + return port.address |
440 | + |
441 | + return default |
442 | diff --git a/src/metadataserver/builtin_scripts/tests/test_network.py b/src/metadataserver/builtin_scripts/tests/test_network.py |
443 | index a2f1a6d..5e06062 100644 |
444 | --- a/src/metadataserver/builtin_scripts/tests/test_network.py |
445 | +++ b/src/metadataserver/builtin_scripts/tests/test_network.py |
446 | @@ -1,7 +1,5 @@ |
447 | -import dataclasses |
448 | import json |
449 | import random |
450 | -from typing import List, Optional |
451 | from unittest.mock import call |
452 | |
453 | from maasserver.enum import ( |
454 | @@ -22,6 +20,11 @@ from maasserver.models.interface import ( |
455 | from maasserver.models.staticipaddress import StaticIPAddress |
456 | from maasserver.models.subnet import Subnet |
457 | from maasserver.models.vlan import VLAN |
458 | +from maasserver.testing.commissioning import ( |
459 | + FakeCommissioningData, |
460 | + LXDAddress, |
461 | + LXDNetworkCard, |
462 | +) |
463 | from maasserver.testing.factory import factory |
464 | from maasserver.testing.testcase import ( |
465 | MAASServerTestCase, |
466 | @@ -37,414 +40,6 @@ from metadataserver.builtin_scripts.network import ( |
467 | from provisioningserver.refresh.node_info_scripts import ( |
468 | COMMISSIONING_OUTPUT_NAME, |
469 | ) |
470 | -from provisioningserver.utils.network import ( |
471 | - annotate_with_default_monitored_interfaces, |
472 | - get_default_monitored_interfaces, |
473 | -) |
474 | - |
475 | -GB = 1000 * 1000 * 1000 |
476 | - |
477 | - |
478 | -@dataclasses.dataclass |
479 | -class LXDPartition: |
480 | - id: str |
481 | - read_only: bool = False |
482 | - |
483 | - |
484 | -@dataclasses.dataclass |
485 | -class LXDDisk: |
486 | - id: str |
487 | - size: int = 250 * GB |
488 | - partitions: List[LXDPartition] = dataclasses.field(default_factory=list) |
489 | - type: str = "sata" |
490 | - read_only: bool = False |
491 | - removable: bool = False |
492 | - rpm: int = 0 |
493 | - numa_node: int = 0 |
494 | - |
495 | - |
496 | -@dataclasses.dataclass |
497 | -class LXDVlan: |
498 | - lower_device: str |
499 | - vid: int |
500 | - |
501 | - |
502 | -@dataclasses.dataclass |
503 | -class LXDBridge: |
504 | - upper_devices: List[str] = dataclasses.field(default_factory=list) |
505 | - |
506 | - |
507 | -@dataclasses.dataclass |
508 | -class LXDBond: |
509 | - lower_devices: List[str] = dataclasses.field(default_factory=list) |
510 | - |
511 | - |
512 | -@dataclasses.dataclass |
513 | -class LXDAddress: |
514 | - |
515 | - address: str |
516 | - netmask: str |
517 | - family: str = "inet" |
518 | - scope: str = "global" |
519 | - |
520 | - |
521 | -@dataclasses.dataclass |
522 | -class LXDNetwork: |
523 | - name: str |
524 | - hwaddr: str |
525 | - type: str = "broadcast" |
526 | - state: str = "up" |
527 | - addresses: List[LXDAddress] = dataclasses.field(default_factory=list) |
528 | - vlan: Optional[LXDVlan] = None |
529 | - bridge: Optional[LXDBridge] = None |
530 | - bond: Optional[LXDBond] = None |
531 | - |
532 | - |
533 | -@dataclasses.dataclass |
534 | -class LXDNetworkPort: |
535 | - |
536 | - id: str |
537 | - port: int |
538 | - address: str = dataclasses.field(default_factory=factory.make_mac_address) |
539 | - protocol: str = "ethernet" |
540 | - supported_modes: List[str] = dataclasses.field( |
541 | - default_factory=lambda: ["10000baseT/Full"] |
542 | - ) |
543 | - supported_ports: List[str] = dataclasses.field( |
544 | - default_factory=lambda: ["fiber"] |
545 | - ) |
546 | - port_type: str = "fiber" |
547 | - transceiver_type: str = "internal" |
548 | - auto_negotiation: bool = True |
549 | - link_detected: bool = True |
550 | - link_speed: int = 10000 |
551 | - link_duplex: str = "full" |
552 | - |
553 | - |
554 | -@dataclasses.dataclass |
555 | -class LXDNetworkCard: |
556 | - |
557 | - pci_address: str |
558 | - vendor: str = "My Corporation" |
559 | - vendor_id: str = "1234" |
560 | - product: str = "My Gigabit Network Connection" |
561 | - product_id: str = "5678" |
562 | - firmware_version: str = "1.63, 0x800009fa" |
563 | - numa_node: int = 0 |
564 | - driver: str = "mydriver" |
565 | - driver_version: str = "1.2.3" |
566 | - ports: Optional[List[LXDNetworkPort]] = None |
567 | - |
568 | - |
569 | -class FakeCommissioningData: |
570 | - """Helper to generate commissioning output programtically. |
571 | - |
572 | - Instead of hardcoding the commissioning data, taking care of |
573 | - including all the possible keys and values, this class allows you to |
574 | - tell which interface you want the machine to have, and you only have |
575 | - to specify what's important for the tests. The helper will ensure |
576 | - that all the other keys are there with a sane value and in the right |
577 | - format. |
578 | - """ |
579 | - |
580 | - def __init__( |
581 | - self, |
582 | - cores=1, |
583 | - memory=2048, |
584 | - disks=None, |
585 | - api_extensions=None, |
586 | - api_version="1.0", |
587 | - ): |
588 | - self.cores = cores |
589 | - self.memory = memory |
590 | - if api_extensions is None: |
591 | - api_extensions = [ |
592 | - "resources", |
593 | - "resources_v2", |
594 | - "api_os", |
595 | - "resources_system", |
596 | - "resources_usb_pci", |
597 | - ] |
598 | - self.api_extensions = api_extensions |
599 | - self.api_version = api_version |
600 | - self.environment = { |
601 | - "kernel": "Linux", |
602 | - "kernel_architecture": "x86_64", |
603 | - "kernel_version": "5.4.0-67-generic", |
604 | - "os_name": "ubuntu", |
605 | - "os_version": "20.04", |
606 | - "server": "maas-machine-resources", |
607 | - "server_name": factory.make_name("host"), |
608 | - "server_version": "4.11", |
609 | - } |
610 | - self.address_annotations = {} |
611 | - self._allocated_pci_addresses = [] |
612 | - self.networks = {} |
613 | - self._network_cards = [] |
614 | - if disks is None: |
615 | - disks = [LXDDisk("sda")] |
616 | - self._disks = list(disks) |
617 | - self.hints = None |
618 | - |
619 | - def allocate_pci_address(self): |
620 | - prev_address = ( |
621 | - self._allocated_pci_addresses[-1] |
622 | - if self._allocated_pci_addresses |
623 | - else "0000:00:00.0" |
624 | - ) |
625 | - bus, device, func = prev_address.split(":") |
626 | - next_device = int(device, 16) + 1 |
627 | - self._allocated_pci_addresses.append( |
628 | - f"{bus}:{next_device:0>4x}:{func}" |
629 | - ) |
630 | - return self._allocated_pci_addresses[-1] |
631 | - |
632 | - def get_available_vid(self): |
633 | - available_vids = set(range(2, 4095)) |
634 | - used_vids = { |
635 | - network.vlan.vid |
636 | - for network in self.networks.values() |
637 | - if network.vlan is not None |
638 | - } |
639 | - available_vids = list(available_vids.difference(used_vids)) |
640 | - return random.choice(available_vids) |
641 | - |
642 | - def create_network_card(self): |
643 | - card = LXDNetworkCard(self.allocate_pci_address()) |
644 | - self._network_cards.append(card) |
645 | - return card |
646 | - |
647 | - def create_physical_network( |
648 | - self, |
649 | - name=None, |
650 | - mac_address=None, |
651 | - card=None, |
652 | - port=None, |
653 | - ): |
654 | - if card is None: |
655 | - card = self.create_network_card() |
656 | - if card.ports is None: |
657 | - card.ports = [] |
658 | - network = self.create_physical_network_without_nic(name, mac_address) |
659 | - if port is None: |
660 | - port = LXDNetworkPort( |
661 | - network.name, len(card.ports), address=network.hwaddr |
662 | - ) |
663 | - card.ports.append(port) |
664 | - return network |
665 | - |
666 | - def create_physical_network_without_nic( |
667 | - self, |
668 | - name=None, |
669 | - mac_address=None, |
670 | - ): |
671 | - if name is None: |
672 | - name = factory.make_string("eth") |
673 | - if mac_address is None: |
674 | - mac_address = factory.make_mac_address() |
675 | - network = LXDNetwork(name, mac_address) |
676 | - self.networks[name] = network |
677 | - return network |
678 | - |
679 | - def create_vlan_network( |
680 | - self, |
681 | - name=None, |
682 | - vid=None, |
683 | - mac_address=None, |
684 | - parent=None, |
685 | - ): |
686 | - if name is None: |
687 | - name = factory.make_string("vlan") |
688 | - if parent is None: |
689 | - parent = self.create_physical_network() |
690 | - if mac_address is None: |
691 | - mac_address = factory.make_mac_address() |
692 | - if vid is None: |
693 | - vid = self.get_available_vid() |
694 | - network = LXDNetwork( |
695 | - name, mac_address, vlan=LXDVlan(lower_device=parent.name, vid=vid) |
696 | - ) |
697 | - self.networks[name] = network |
698 | - return network |
699 | - |
700 | - def create_bridge_network( |
701 | - self, |
702 | - name=None, |
703 | - mac_address=None, |
704 | - parents=None, |
705 | - ): |
706 | - if name is None: |
707 | - name = factory.make_string("bridge") |
708 | - if parents is None: |
709 | - parents = [self.create_physical_network()] |
710 | - if mac_address is None: |
711 | - mac_address = factory.make_mac_address() |
712 | - network = LXDNetwork( |
713 | - name, |
714 | - mac_address, |
715 | - bridge=LXDBridge( |
716 | - upper_devices=[parent.name for parent in parents] |
717 | - ), |
718 | - ) |
719 | - self.networks[name] = network |
720 | - return network |
721 | - |
722 | - def create_bond_network( |
723 | - self, |
724 | - name=None, |
725 | - mac_address=None, |
726 | - parents=None, |
727 | - ): |
728 | - if name is None: |
729 | - name = factory.make_string("bond") |
730 | - if parents is None: |
731 | - parents = [self.create_physical_network()] |
732 | - if mac_address is None: |
733 | - mac_address = factory.make_mac_address() |
734 | - network = LXDNetwork( |
735 | - name, |
736 | - mac_address, |
737 | - bond=LXDBond(lower_devices=[parent.name for parent in parents]), |
738 | - ) |
739 | - self.networks[name] = network |
740 | - return network |
741 | - |
742 | - def render(self, include_extra=False): |
743 | - storage_resources = { |
744 | - "disks": [dataclasses.asdict(disk) for disk in self._disks], |
745 | - "total": len(self._disks), |
746 | - } |
747 | - network_resources = { |
748 | - "cards": [ |
749 | - dataclasses.asdict(card) for card in self._network_cards |
750 | - ], |
751 | - "total": len(self._network_cards), |
752 | - } |
753 | - for card in network_resources["cards"]: |
754 | - if card["ports"] is None: |
755 | - del card["ports"] |
756 | - networks = { |
757 | - name: dataclasses.asdict(network) |
758 | - for name, network in self.networks.items() |
759 | - } |
760 | - old_interfaces_data = self._generate_interfaces() |
761 | - data = { |
762 | - "api_extensions": self.api_extensions, |
763 | - "api_version": self.api_version, |
764 | - "environment": self.environment, |
765 | - "resources": { |
766 | - "cpu": { |
767 | - "architecture": self.environment["kernel_architecture"], |
768 | - "sockets": [ |
769 | - { |
770 | - "socket": 0, |
771 | - "cores": [], |
772 | - } |
773 | - ], |
774 | - }, |
775 | - "memory": { |
776 | - "hugepages_total": 0, |
777 | - "hugepages_used": 0, |
778 | - "hugepages_size": 0, |
779 | - "used": int(0.3 * self.memory * 1024 * 1024), |
780 | - "total": int(self.memory * 1024 * 1024), |
781 | - }, |
782 | - "gpu": {"cards": [], "total": 0}, |
783 | - "network": network_resources, |
784 | - "storage": storage_resources, |
785 | - }, |
786 | - "networks": networks, |
787 | - } |
788 | - for core_index in range(self.cores): |
789 | - data["resources"]["cpu"]["sockets"][0]["cores"].append( |
790 | - { |
791 | - "core": core_index, |
792 | - "threads": [ |
793 | - { |
794 | - "id": core_index, |
795 | - "thread": 0, |
796 | - "online": True, |
797 | - "numa_node": 0, |
798 | - }, |
799 | - ], |
800 | - "frequency": 1500, |
801 | - } |
802 | - ) |
803 | - if include_extra: |
804 | - data["network-extra"] = { |
805 | - "interfaces": old_interfaces_data, |
806 | - "monitored-interfaces": get_default_monitored_interfaces( |
807 | - old_interfaces_data |
808 | - ), |
809 | - "hints": self.hints, |
810 | - } |
811 | - return data |
812 | - |
813 | - def _generate_interfaces(self): |
814 | - # XXX: It would be good if this method could basically call |
815 | - # get_all_interfaces_definition(), passing in information it |
816 | - # needs. But considering the goal is to minimize information and |
817 | - # instead make use of the LXD data directly, it's probably worth |
818 | - # holding off until there's less information to render. |
819 | - interfaces = {} |
820 | - for name, network in self.networks.items(): |
821 | - if network.type != "broadcast": |
822 | - continue |
823 | - interface = { |
824 | - "mac_address": self._get_network_port_mac( |
825 | - name, network.hwaddr |
826 | - ), |
827 | - "links": [], |
828 | - "enabled": network.state == "up", |
829 | - "source": "machine-resources", |
830 | - } |
831 | - if network.vlan is not None: |
832 | - interface.update( |
833 | - { |
834 | - "type": "vlan", |
835 | - "parents": [network.vlan.lower_device], |
836 | - "vid": network.vlan.vid, |
837 | - } |
838 | - ) |
839 | - elif network.bridge is not None: |
840 | - interface.update( |
841 | - { |
842 | - "type": "bridge", |
843 | - "parents": list(network.bridge.upper_devices), |
844 | - } |
845 | - ) |
846 | - elif network.bond is not None: |
847 | - interface.update( |
848 | - { |
849 | - "type": "bond", |
850 | - "parents": list(network.bond.lower_devices), |
851 | - } |
852 | - ) |
853 | - else: |
854 | - interface.update({"type": "physical", "parents": []}) |
855 | - for address in network.addresses: |
856 | - link = { |
857 | - "address": f"{address.address}/{address.netmask}", |
858 | - "mode": "static", |
859 | - } |
860 | - address_annotation = self.address_annotations.get( |
861 | - address.address, {} |
862 | - ) |
863 | - link.update(address_annotation) |
864 | - interface["links"].append(link) |
865 | - interfaces[name] = interface |
866 | - annotate_with_default_monitored_interfaces(interfaces) |
867 | - return interfaces |
868 | - |
869 | - def _get_network_port_mac(self, port_name, default): |
870 | - for card in self._network_cards: |
871 | - if not card.ports: |
872 | - continue |
873 | - for port in card.ports: |
874 | - if port.id == port_name: |
875 | - return port.address |
876 | - |
877 | - return default |
878 | |
879 | |
880 | class UpdateInterfacesMixin: |
+1