Merge ~cgrabowski/maas/+git/maas-performance:update_for_latest_maas into ~maas-committers/maas/+git/maas-performance:3.0-rework
- Git
- lp:~cgrabowski/maas/+git/maas-performance
- update_for_latest_maas
- Merge into 3.0-rework
Status: | Merged |
---|---|
Approved by: | Christian Grabowski |
Approved revision: | 41891463862d49c65f3bb40664dc99dcf4228223 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~cgrabowski/maas/+git/maas-performance:update_for_latest_maas |
Merge into: | ~maas-committers/maas/+git/maas-performance:3.0-rework |
Diff against target: |
3041 lines (+2084/-180) 25 files modified
Makefile (+3/-2) maasperformance/bmc.py (+1/-2) maasperformance/commissioning.py (+30/-38) maasperformance/conftest.py (+2/-1) maasperformance/data/20-maas-03-machine-resources (+321/-0) maasperformance/data/20-maas-03-machine-resources.err (+0/-0) maasperformance/data/20-maas-03-machine-resources.out (+321/-0) maasperformance/data/40-maas-01-machine-resources (+321/-0) maasperformance/data/40-maas-01-machine-resources.err (+0/-0) maasperformance/data/40-maas-01-machine-resources.out (+321/-0) maasperformance/machine.py (+13/-8) maasperformance/network.py (+7/-2) maasperformance/network_interface.py (+5/-3) maasperformance/process.py (+7/-1) maasperformance/testing/fixtures.py (+29/-1) maasperformance/testing/subprocess.py (+14/-0) maasperformance/tests/test_bmc.py (+50/-0) maasperformance/tests/test_event.py (+34/-11) maasperformance/tests/test_machine.py (+223/-33) maasperformance/tests/test_network.py (+231/-1) maasperformance/tests/test_network_interface.py (+3/-4) maasperformance/tests/test_web.py (+118/-51) maasperformance/web.py (+5/-1) pyproject.toml (+3/-0) requirements.txt (+22/-21) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
Alberto Donato (community) | Approve | ||
Review via email: mp+429734@code.launchpad.net |
Commit message
add update to machine-resources output
remove deprecated loop args
switch to non-deprecated eventloop fixture
patch check_output in tests
enable asyncio in tests
make event tests pass
update dependencies to resolve conflicting versions
Description of the change
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: FAILED
LOG: http://
COMMIT: 6235732caa93d57
Alberto Donato (ack) wrote : | # |
+1
minor nit inline
Christian Grabowski (cgrabowski) : | # |
- d84b31a... by Christian Grabowski
-
add pyproject.toml to enable asyncio
- b7c2363... by Christian Grabowski
-
remove unused subprocess import in test_web
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: FAILED
LOG: http://
COMMIT: b7c2363976163bb
- 82eb637... by Christian Grabowski
-
correctly format libffi-dev dep
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: FAILED
LOG: http://
COMMIT: 82eb637ed255766
- c53b9f5... by Christian Grabowski
-
prevent MachineManager loop from hanging
- 205f545... by Christian Grabowski
-
update interface test for new exec__sync_process
- d973466... by Christian Grabowski
-
ensure web tasks close cleanly
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: FAILED
LOG: http://
COMMIT: 263a13b0557c754
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: FAILED
LOG: http://
COMMIT: d97346635c87a69
- 37c38b2... by Christian Grabowski
-
cover bmc class in tests
- 4189146... by Christian Grabowski
-
add test coverage for network request methods
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b update_
STATUS: SUCCESS
COMMIT: 41891463862d49c
Preview Diff
1 | diff --git a/Makefile b/Makefile |
2 | index 7de4112..00962b2 100644 |
3 | --- a/Makefile |
4 | +++ b/Makefile |
5 | @@ -1,5 +1,5 @@ |
6 | define DEB_DEPENDENCIES |
7 | -python3-venv |
8 | +python3-venv libffi-dev |
9 | endef |
10 | |
11 | APT := DEBIAN_FRONTEND=noninteractive apt |
12 | @@ -60,7 +60,8 @@ deb-dep: |
13 | # Python targets |
14 | py-dep: $(VIRTUALENV) |
15 | $(VIRTUALENV)/bin/pip install -r requirements.txt -e . |
16 | - $(VIRTUALENV)/bin/pip install -r requirements.txt -e .[dev] .[test] |
17 | + $(VIRTUALENV)/bin/pip install -r requirements.txt -e .[dev] |
18 | + $(VIRTUALENV)/bin/pip install -r requirements.txt -e .[test] |
19 | ln -sf $(VIRTUALENV)/bin/pytest |
20 | ln -sf $(VIRTUALENV)/bin/maas-performanced |
21 | .PHONY: py-dep |
22 | diff --git a/maasperformance/bmc.py b/maasperformance/bmc.py |
23 | index f09b44a..946c75d 100644 |
24 | --- a/maasperformance/bmc.py |
25 | +++ b/maasperformance/bmc.py |
26 | @@ -32,8 +32,7 @@ class BMC: |
27 | await asyncio.gather( |
28 | *( |
29 | self.power_on(machine.uuid) |
30 | - for machine in self.machines_manager.machines.values()), |
31 | - loop=self.loop) |
32 | + for machine in self.machines_manager.machines.values())) |
33 | |
34 | async def power_on(self, machine_uuid): |
35 | print(f'Requesting power on: {machine_uuid}') |
36 | diff --git a/maasperformance/commissioning.py b/maasperformance/commissioning.py |
37 | index d567ca6..592cbb9 100644 |
38 | --- a/maasperformance/commissioning.py |
39 | +++ b/maasperformance/commissioning.py |
40 | @@ -7,10 +7,16 @@ changes to remove functionality that wasn't needed. |
41 | |
42 | import dataclasses |
43 | from functools import partial |
44 | -from itertools import islice, repeat |
45 | +from itertools import ( |
46 | + islice, |
47 | + repeat, |
48 | +) |
49 | import random |
50 | import string |
51 | -from typing import List, Optional |
52 | +from typing import ( |
53 | + List, |
54 | + Optional, |
55 | +) |
56 | |
57 | GB = 1000 * 1000 * 1000 |
58 | |
59 | @@ -18,12 +24,10 @@ GB = 1000 * 1000 * 1000 |
60 | class Factory: |
61 | |
62 | random_letters = map( |
63 | - random.choice, repeat(string.ascii_letters + string.digits) |
64 | - ) |
65 | + random.choice, repeat(string.ascii_letters + string.digits)) |
66 | |
67 | random_letters_with_spaces = map( |
68 | - random.choice, repeat(string.ascii_letters + string.digits + " ") |
69 | - ) |
70 | + random.choice, repeat(string.ascii_letters + string.digits + " ")) |
71 | |
72 | random_octet = partial(random.randint, 0, 255) |
73 | |
74 | @@ -56,8 +60,7 @@ class Factory: |
75 | def make_string(self, size=10, spaces=False, prefix=""): |
76 | """Return a `str` filled with random ASCII letters or digits.""" |
77 | source = ( |
78 | - self.random_letters_with_spaces if spaces else self.random_letters |
79 | - ) |
80 | + self.random_letters_with_spaces if spaces else self.random_letters) |
81 | return prefix + "".join(islice(source, size)) |
82 | |
83 | |
84 | @@ -127,11 +130,9 @@ class LXDNetworkPort: |
85 | address: str = dataclasses.field(default_factory=factory.make_mac_address) |
86 | protocol: str = "ethernet" |
87 | supported_modes: List[str] = dataclasses.field( |
88 | - default_factory=lambda: ["10000baseT/Full"] |
89 | - ) |
90 | + default_factory=lambda: ["10000baseT/Full"]) |
91 | supported_ports: List[str] = dataclasses.field( |
92 | - default_factory=lambda: ["fibre"] |
93 | - ) |
94 | + default_factory=lambda: ["fibre"]) |
95 | port_type: str = "fibre" |
96 | transceiver_type: str = "internal" |
97 | auto_negotiation: bool = True |
98 | @@ -208,25 +209,20 @@ class FakeCommissioningData: |
99 | def allocate_pci_address(self): |
100 | prev_address = ( |
101 | self._allocated_pci_addresses[-1] |
102 | - if self._allocated_pci_addresses |
103 | - else "0000:00:00.0" |
104 | - ) |
105 | + if self._allocated_pci_addresses else "0000:00:00.0") |
106 | bus, device, func = prev_address.split(":") |
107 | next_device = int(device, 16) + 1 |
108 | self._allocated_pci_addresses.append( |
109 | - f"{bus}:{next_device:0>4x}:{func}" |
110 | - ) |
111 | + f"{bus}:{next_device:0>4x}:{func}") |
112 | return self._allocated_pci_addresses[-1] |
113 | |
114 | def get_available_vid(self): |
115 | available_vids = set(range(2, 4095)) |
116 | used_vids = set( |
117 | [ |
118 | - network.vlan.vid |
119 | - for network in self.networks.values() |
120 | + network.vlan.vid for network in self.networks.values() |
121 | if network.vlan is not None |
122 | - ] |
123 | - ) |
124 | + ]) |
125 | available_vids = list(available_vids.difference(used_vids)) |
126 | return random.choice(available_vids) |
127 | |
128 | @@ -249,8 +245,7 @@ class FakeCommissioningData: |
129 | network = self.create_physical_network_without_nic(name, mac_address) |
130 | if port is None: |
131 | port = LXDNetworkPort( |
132 | - network.name, len(card.ports), address=network.hwaddr |
133 | - ) |
134 | + network.name, len(card.ports), address=network.hwaddr) |
135 | card.ports.append(port) |
136 | return network |
137 | |
138 | @@ -283,8 +278,7 @@ class FakeCommissioningData: |
139 | if vid is None: |
140 | vid = self.get_available_vid() |
141 | network = LXDNetwork( |
142 | - name, mac_address, vlan=LXDVlan(lower_device=parent.name, vid=vid) |
143 | - ) |
144 | + name, mac_address, vlan=LXDVlan(lower_device=parent.name, vid=vid)) |
145 | self.networks[name] = network |
146 | return network |
147 | |
148 | @@ -304,8 +298,7 @@ class FakeCommissioningData: |
149 | name, |
150 | mac_address, |
151 | bridge=LXDBridge( |
152 | - upper_devices=[parent.name for parent in parents] |
153 | - ), |
154 | + upper_devices=[parent.name for parent in parents]), |
155 | ) |
156 | self.networks[name] = network |
157 | return network |
158 | @@ -346,8 +339,7 @@ class FakeCommissioningData: |
159 | del card["ports"] |
160 | networks = dict( |
161 | (name, dataclasses.asdict(network)) |
162 | - for name, network in self.networks.items() |
163 | - ) |
164 | + for name, network in self.networks.items()) |
165 | data = { |
166 | "api_extensions": self.api_extensions, |
167 | "api_version": self.api_version, |
168 | @@ -355,12 +347,10 @@ class FakeCommissioningData: |
169 | "resources": { |
170 | "cpu": { |
171 | "architecture": self.environment["kernel_architecture"], |
172 | - "sockets": [ |
173 | - { |
174 | - "socket": 0, |
175 | - "cores": [], |
176 | - } |
177 | - ], |
178 | + "sockets": [{ |
179 | + "socket": 0, |
180 | + "cores": [], |
181 | + }], |
182 | }, |
183 | "memory": { |
184 | "hugepages_total": 0, |
185 | @@ -369,7 +359,10 @@ class FakeCommissioningData: |
186 | "used": int(0.3 * self.memory * 1024 * 1024), |
187 | "total": int(self.memory * 1024 * 1024), |
188 | }, |
189 | - "gpu": {"cards": [], "total": 0}, |
190 | + "gpu": { |
191 | + "cards": [], |
192 | + "total": 0 |
193 | + }, |
194 | "network": network_resources, |
195 | "storage": storage_resources, |
196 | }, |
197 | @@ -388,6 +381,5 @@ class FakeCommissioningData: |
198 | }, |
199 | ], |
200 | "frequency": 1500, |
201 | - } |
202 | - ) |
203 | + }) |
204 | return data |
205 | diff --git a/maasperformance/conftest.py b/maasperformance/conftest.py |
206 | index cf0da2e..99b7085 100644 |
207 | --- a/maasperformance/conftest.py |
208 | +++ b/maasperformance/conftest.py |
209 | @@ -2,6 +2,7 @@ from .testing.fixtures import ( |
210 | fs_root, |
211 | machine, |
212 | mock_event_time, |
213 | + tar_file_http_response, |
214 | ) |
215 | |
216 | -__all__ = ['fs_root', 'machine', 'mock_event_time'] |
217 | +__all__ = ['fs_root', 'machine', 'mock_event_time', "tar_file_http_response"] |
218 | diff --git a/maasperformance/data/20-maas-03-machine-resources b/maasperformance/data/20-maas-03-machine-resources |
219 | new file mode 100644 |
220 | index 0000000..056bd08 |
221 | --- /dev/null |
222 | +++ b/maasperformance/data/20-maas-03-machine-resources |
223 | @@ -0,0 +1,321 @@ |
224 | +{ |
225 | + "api_extensions": [ |
226 | + "resources", |
227 | + "resources_cpu_socket", |
228 | + "resources_gpu", |
229 | + "resources_numa", |
230 | + "resources_v2", |
231 | + "resources_disk_sata", |
232 | + "resources_network_firmware", |
233 | + "resources_disk_id", |
234 | + "resources_usb_pci", |
235 | + "resources_cpu_threads_numa", |
236 | + "resources_cpu_core_die", |
237 | + "api_os", |
238 | + "resources_system", |
239 | + "resources_pci_iommu", |
240 | + "resources_network_usb", |
241 | + "resources_disk_address" |
242 | + ], |
243 | + "api_version": "1.0", |
244 | + "environment": { |
245 | + "kernel": "Linux", |
246 | + "kernel_architecture": "x86_64", |
247 | + "kernel_version": "5.15.0-43-generic", |
248 | + "os_name": "ubuntu", |
249 | + "os_version": "20.04", |
250 | + "server": "maas-machine-resources", |
251 | + "server_name": "maas-performance", |
252 | + "server_version": "5.4" |
253 | + }, |
254 | + "resources": { |
255 | + "cpu": { |
256 | + "architecture": "x86_64", |
257 | + "sockets": [ |
258 | + { |
259 | + "name": "exampe-cpu", |
260 | + "vendor": "test", |
261 | + "socket": 0, |
262 | + "cache": [ |
263 | + { |
264 | + "level": 1, |
265 | + "type": "Data", |
266 | + "size": 32768 |
267 | + }, |
268 | + { |
269 | + "level": 1, |
270 | + "type": "Instruction", |
271 | + "size": 32768 |
272 | + }, |
273 | + { |
274 | + "level": 2, |
275 | + "type": "Unified", |
276 | + "size": 524288 |
277 | + }, |
278 | + { |
279 | + "level": 3, |
280 | + "type": "Unified", |
281 | + "size": 16777216 |
282 | + } |
283 | + ], |
284 | + "cores": [ |
285 | + { |
286 | + "core": 0, |
287 | + "die": 0, |
288 | + "threads": [ |
289 | + { |
290 | + "id": 0, |
291 | + "numa_node": 0, |
292 | + "thread": 0, |
293 | + "online": true, |
294 | + "isolated": false |
295 | + }, |
296 | + { |
297 | + "id": 12, |
298 | + "numa_node": 0, |
299 | + "thread": 1, |
300 | + "online": true, |
301 | + "isolated": false |
302 | + } |
303 | + ], |
304 | + "frequency": 3080 |
305 | + } |
306 | + ], |
307 | + "frequency": 2559, |
308 | + "frequency_minimum": 2200, |
309 | + "frequency_turbo": 4672 |
310 | + } |
311 | + ], |
312 | + "total": 24 |
313 | + }, |
314 | + "memory": { |
315 | + "nodes": [ |
316 | + { |
317 | + "numa_node": 0, |
318 | + "hugepages_used": 0, |
319 | + "hugepages_total": 0, |
320 | + "used": 25046433792, |
321 | + "total": 70866960384 |
322 | + } |
323 | + ], |
324 | + "hugepages_total": 0, |
325 | + "hugepages_used": 0, |
326 | + "hugepages_size": 2097152, |
327 | + "used": 9893044224, |
328 | + "total": 70866960384 |
329 | + }, |
330 | + "gpu": { |
331 | + "cards": [ |
332 | + { |
333 | + "driver": "ast", |
334 | + "driver_version": "5.15.0-43-generic", |
335 | + "drm": { |
336 | + "id": 0, |
337 | + "card_name": "card0", |
338 | + "card_device": "226:0", |
339 | + "control_name": "controlD64", |
340 | + "control_device": "226:0" |
341 | + }, |
342 | + "numa_node": 0, |
343 | + "pci_address": "0000:22:00.0", |
344 | + "vendor": "ASPEED Technology, Inc.", |
345 | + "vendor_id": "1a03", |
346 | + "product": "ASPEED Graphics Family", |
347 | + "product_id": "2000" |
348 | + } |
349 | + ], |
350 | + "total": 1 |
351 | + }, |
352 | + "network": { |
353 | + "cards": [ |
354 | + { |
355 | + "driver": "igb", |
356 | + "driver_version": "5.15.0-43-generic", |
357 | + "ports": [ |
358 | + { |
359 | + "id": "enp35s0", |
360 | + "address": "d0:50:99:dd:49:f1", |
361 | + "port": 0, |
362 | + "protocol": "ethernet", |
363 | + "supported_modes": [ |
364 | + "10baseT/Half", |
365 | + "10baseT/Full", |
366 | + "100baseT/Half", |
367 | + "100baseT/Full", |
368 | + "1000baseT/Full" |
369 | + ], |
370 | + "supported_ports": [ |
371 | + "twisted pair" |
372 | + ], |
373 | + "port_type": "twisted pair", |
374 | + "transceiver_type": "internal", |
375 | + "auto_negotiation": true, |
376 | + "link_detected": true, |
377 | + "link_speed": 1000, |
378 | + "link_duplex": "full" |
379 | + } |
380 | + ], |
381 | + "numa_node": 0, |
382 | + "pci_address": "0000:23:00.0", |
383 | + "vendor": "Intel Corporation", |
384 | + "vendor_id": "8086", |
385 | + "product": "I210 Gigabit Network Connection", |
386 | + "product_id": "1533", |
387 | + "firmware_version": "3.16, 0x800004d6" |
388 | + }, |
389 | + ], |
390 | + "total": 1 |
391 | + }, |
392 | + "storage": { |
393 | + "disks": [ |
394 | + { |
395 | + "id": "nvme0n1", |
396 | + "device": "259:0", |
397 | + "model": "Samsung SSD 970 EVO 500GB", |
398 | + "type": "nvme", |
399 | + "read_only": false, |
400 | + "size": 500107862016, |
401 | + "removable": false, |
402 | + "wwn": "eui.0025385b01440ea7", |
403 | + "numa_node": 0, |
404 | + "device_path": "pci-0000:2a:00.0-nvme-1", |
405 | + "block_size": 512, |
406 | + "firmware_version": "2B2QEXE7", |
407 | + "rpm": 0, |
408 | + "serial": "S5H7NS1NB23880D", |
409 | + "device_id": "nvme-eui.0025385b01440ea7", |
410 | + "partitions": [ |
411 | + { |
412 | + "id": "nvme0n1p1", |
413 | + "device": "259:1", |
414 | + "read_only": false, |
415 | + "size": 536870912, |
416 | + "partition": 1 |
417 | + }, |
418 | + { |
419 | + "id": "nvme0n1p2", |
420 | + "device": "259:2", |
421 | + "read_only": false, |
422 | + "size": 1073741824, |
423 | + "partition": 2 |
424 | + }, |
425 | + { |
426 | + "id": "nvme0n1p3", |
427 | + "device": "259:3", |
428 | + "read_only": false, |
429 | + "size": 498495127552, |
430 | + "partition": 3 |
431 | + } |
432 | + ] |
433 | + } |
434 | + ], |
435 | + "total": 1 |
436 | + }, |
437 | + "usb": { |
438 | + "devices": [ |
439 | + { |
440 | + "bus_address": 1, |
441 | + "device_address": 11, |
442 | + "interfaces": [ |
443 | + { |
444 | + "class": "Mass Storage", |
445 | + "class_id": 8, |
446 | + "driver": "usb-storage", |
447 | + "driver_version": "5.15.0-43-generic", |
448 | + "number": 0, |
449 | + "subclass": "SCSI", |
450 | + "subclass_id": 6 |
451 | + } |
452 | + ], |
453 | + "vendor": "American Megatrends, Inc.", |
454 | + "vendor_id": "046b", |
455 | + "product": "Virtual Cdrom Device", |
456 | + "product_id": "ff20", |
457 | + "speed": 480 |
458 | + } |
459 | + ], |
460 | + "total": 1 |
461 | + }, |
462 | + "pci": { |
463 | + "devices": [ |
464 | + { |
465 | + "driver": "igb", |
466 | + "driver_version": "5.15.0-43-generic", |
467 | + "numa_node": 0, |
468 | + "pci_address": "0000:23:00.0", |
469 | + "vendor": "Intel Corporation", |
470 | + "vendor_id": "8086", |
471 | + "product": "I210 Gigabit Network Connection", |
472 | + "product_id": "1533", |
473 | + "iommu_group": 15, |
474 | + "vpd": {} |
475 | + }, |
476 | + { |
477 | + "driver": "nvme", |
478 | + "driver_version": "1.0", |
479 | + "numa_node": 0, |
480 | + "pci_address": "0000:2a:00.0", |
481 | + "vendor": "Samsung Electronics Co Ltd", |
482 | + "vendor_id": "144d", |
483 | + "product": "NVMe SSD Controller SM981/PM981/PM983", |
484 | + "product_id": "a808", |
485 | + "iommu_group": 15, |
486 | + "vpd": {} |
487 | + } |
488 | + ], |
489 | + "total": 2 |
490 | + }, |
491 | + "system": { |
492 | + "uuid": "00000000-0000-0000-0000-d05099dd49f1", |
493 | + "vendor": "To Be Filled By O.E.M.", |
494 | + "product": "To Be Filled By O.E.M.", |
495 | + "family": "To Be Filled By O.E.M.", |
496 | + "version": "To Be Filled By O.E.M.", |
497 | + "sku": "To Be Filled By O.E.M.", |
498 | + "serial": "To Be Filled By O.E.M.", |
499 | + "type": "physical", |
500 | + "firmware": { |
501 | + "vendor": "American Megatrends Inc.", |
502 | + "date": "11/02/2020", |
503 | + "version": "P3.50" |
504 | + }, |
505 | + "chassis": { |
506 | + "vendor": "To Be Filled By O.E.M.", |
507 | + "type": "Unknown", |
508 | + "serial": "To Be Filled By O.E.M.", |
509 | + "version": "To Be Filled By O.E.M." |
510 | + }, |
511 | + "motherboard": { |
512 | + "vendor": "ASRockRack", |
513 | + "product": "X470D4U", |
514 | + "serial": "200101730000493", |
515 | + "version": "" |
516 | + } |
517 | + } |
518 | + }, |
519 | + "networks": { |
520 | + "fake0": { |
521 | + "addresses": [ |
522 | + { |
523 | + "family": "inet", |
524 | + "address": "192.168.1.21", |
525 | + "netmask": "24", |
526 | + "scope": "global" |
527 | + } |
528 | + ], |
529 | + "counters": { |
530 | + "bytes_received": 17341736517, |
531 | + "bytes_sent": 42242825969, |
532 | + "packets_received": 47014272, |
533 | + "packets_sent": 64893025 |
534 | + }, |
535 | + "hwaddr": "8e:f4:15:bb:cd:fc", |
536 | + "mtu": 1500, |
537 | + "state": "up", |
538 | + "type": "broadcast", |
539 | + "bond": null, |
540 | + "bridge": null, |
541 | + "vlan": null |
542 | + } |
543 | + } |
544 | +} |
545 | diff --git a/maasperformance/data/20-maas-03-machine-resources.err b/maasperformance/data/20-maas-03-machine-resources.err |
546 | new file mode 100644 |
547 | index 0000000..e69de29 |
548 | --- /dev/null |
549 | +++ b/maasperformance/data/20-maas-03-machine-resources.err |
550 | diff --git a/maasperformance/data/20-maas-03-machine-resources.out b/maasperformance/data/20-maas-03-machine-resources.out |
551 | new file mode 100644 |
552 | index 0000000..056bd08 |
553 | --- /dev/null |
554 | +++ b/maasperformance/data/20-maas-03-machine-resources.out |
555 | @@ -0,0 +1,321 @@ |
556 | +{ |
557 | + "api_extensions": [ |
558 | + "resources", |
559 | + "resources_cpu_socket", |
560 | + "resources_gpu", |
561 | + "resources_numa", |
562 | + "resources_v2", |
563 | + "resources_disk_sata", |
564 | + "resources_network_firmware", |
565 | + "resources_disk_id", |
566 | + "resources_usb_pci", |
567 | + "resources_cpu_threads_numa", |
568 | + "resources_cpu_core_die", |
569 | + "api_os", |
570 | + "resources_system", |
571 | + "resources_pci_iommu", |
572 | + "resources_network_usb", |
573 | + "resources_disk_address" |
574 | + ], |
575 | + "api_version": "1.0", |
576 | + "environment": { |
577 | + "kernel": "Linux", |
578 | + "kernel_architecture": "x86_64", |
579 | + "kernel_version": "5.15.0-43-generic", |
580 | + "os_name": "ubuntu", |
581 | + "os_version": "20.04", |
582 | + "server": "maas-machine-resources", |
583 | + "server_name": "maas-performance", |
584 | + "server_version": "5.4" |
585 | + }, |
586 | + "resources": { |
587 | + "cpu": { |
588 | + "architecture": "x86_64", |
589 | + "sockets": [ |
590 | + { |
591 | + "name": "exampe-cpu", |
592 | + "vendor": "test", |
593 | + "socket": 0, |
594 | + "cache": [ |
595 | + { |
596 | + "level": 1, |
597 | + "type": "Data", |
598 | + "size": 32768 |
599 | + }, |
600 | + { |
601 | + "level": 1, |
602 | + "type": "Instruction", |
603 | + "size": 32768 |
604 | + }, |
605 | + { |
606 | + "level": 2, |
607 | + "type": "Unified", |
608 | + "size": 524288 |
609 | + }, |
610 | + { |
611 | + "level": 3, |
612 | + "type": "Unified", |
613 | + "size": 16777216 |
614 | + } |
615 | + ], |
616 | + "cores": [ |
617 | + { |
618 | + "core": 0, |
619 | + "die": 0, |
620 | + "threads": [ |
621 | + { |
622 | + "id": 0, |
623 | + "numa_node": 0, |
624 | + "thread": 0, |
625 | + "online": true, |
626 | + "isolated": false |
627 | + }, |
628 | + { |
629 | + "id": 12, |
630 | + "numa_node": 0, |
631 | + "thread": 1, |
632 | + "online": true, |
633 | + "isolated": false |
634 | + } |
635 | + ], |
636 | + "frequency": 3080 |
637 | + } |
638 | + ], |
639 | + "frequency": 2559, |
640 | + "frequency_minimum": 2200, |
641 | + "frequency_turbo": 4672 |
642 | + } |
643 | + ], |
644 | + "total": 24 |
645 | + }, |
646 | + "memory": { |
647 | + "nodes": [ |
648 | + { |
649 | + "numa_node": 0, |
650 | + "hugepages_used": 0, |
651 | + "hugepages_total": 0, |
652 | + "used": 25046433792, |
653 | + "total": 70866960384 |
654 | + } |
655 | + ], |
656 | + "hugepages_total": 0, |
657 | + "hugepages_used": 0, |
658 | + "hugepages_size": 2097152, |
659 | + "used": 9893044224, |
660 | + "total": 70866960384 |
661 | + }, |
662 | + "gpu": { |
663 | + "cards": [ |
664 | + { |
665 | + "driver": "ast", |
666 | + "driver_version": "5.15.0-43-generic", |
667 | + "drm": { |
668 | + "id": 0, |
669 | + "card_name": "card0", |
670 | + "card_device": "226:0", |
671 | + "control_name": "controlD64", |
672 | + "control_device": "226:0" |
673 | + }, |
674 | + "numa_node": 0, |
675 | + "pci_address": "0000:22:00.0", |
676 | + "vendor": "ASPEED Technology, Inc.", |
677 | + "vendor_id": "1a03", |
678 | + "product": "ASPEED Graphics Family", |
679 | + "product_id": "2000" |
680 | + } |
681 | + ], |
682 | + "total": 1 |
683 | + }, |
684 | + "network": { |
685 | + "cards": [ |
686 | + { |
687 | + "driver": "igb", |
688 | + "driver_version": "5.15.0-43-generic", |
689 | + "ports": [ |
690 | + { |
691 | + "id": "enp35s0", |
692 | + "address": "d0:50:99:dd:49:f1", |
693 | + "port": 0, |
694 | + "protocol": "ethernet", |
695 | + "supported_modes": [ |
696 | + "10baseT/Half", |
697 | + "10baseT/Full", |
698 | + "100baseT/Half", |
699 | + "100baseT/Full", |
700 | + "1000baseT/Full" |
701 | + ], |
702 | + "supported_ports": [ |
703 | + "twisted pair" |
704 | + ], |
705 | + "port_type": "twisted pair", |
706 | + "transceiver_type": "internal", |
707 | + "auto_negotiation": true, |
708 | + "link_detected": true, |
709 | + "link_speed": 1000, |
710 | + "link_duplex": "full" |
711 | + } |
712 | + ], |
713 | + "numa_node": 0, |
714 | + "pci_address": "0000:23:00.0", |
715 | + "vendor": "Intel Corporation", |
716 | + "vendor_id": "8086", |
717 | + "product": "I210 Gigabit Network Connection", |
718 | + "product_id": "1533", |
719 | + "firmware_version": "3.16, 0x800004d6" |
720 | + }, |
721 | + ], |
722 | + "total": 1 |
723 | + }, |
724 | + "storage": { |
725 | + "disks": [ |
726 | + { |
727 | + "id": "nvme0n1", |
728 | + "device": "259:0", |
729 | + "model": "Samsung SSD 970 EVO 500GB", |
730 | + "type": "nvme", |
731 | + "read_only": false, |
732 | + "size": 500107862016, |
733 | + "removable": false, |
734 | + "wwn": "eui.0025385b01440ea7", |
735 | + "numa_node": 0, |
736 | + "device_path": "pci-0000:2a:00.0-nvme-1", |
737 | + "block_size": 512, |
738 | + "firmware_version": "2B2QEXE7", |
739 | + "rpm": 0, |
740 | + "serial": "S5H7NS1NB23880D", |
741 | + "device_id": "nvme-eui.0025385b01440ea7", |
742 | + "partitions": [ |
743 | + { |
744 | + "id": "nvme0n1p1", |
745 | + "device": "259:1", |
746 | + "read_only": false, |
747 | + "size": 536870912, |
748 | + "partition": 1 |
749 | + }, |
750 | + { |
751 | + "id": "nvme0n1p2", |
752 | + "device": "259:2", |
753 | + "read_only": false, |
754 | + "size": 1073741824, |
755 | + "partition": 2 |
756 | + }, |
757 | + { |
758 | + "id": "nvme0n1p3", |
759 | + "device": "259:3", |
760 | + "read_only": false, |
761 | + "size": 498495127552, |
762 | + "partition": 3 |
763 | + } |
764 | + ] |
765 | + } |
766 | + ], |
767 | + "total": 1 |
768 | + }, |
769 | + "usb": { |
770 | + "devices": [ |
771 | + { |
772 | + "bus_address": 1, |
773 | + "device_address": 11, |
774 | + "interfaces": [ |
775 | + { |
776 | + "class": "Mass Storage", |
777 | + "class_id": 8, |
778 | + "driver": "usb-storage", |
779 | + "driver_version": "5.15.0-43-generic", |
780 | + "number": 0, |
781 | + "subclass": "SCSI", |
782 | + "subclass_id": 6 |
783 | + } |
784 | + ], |
785 | + "vendor": "American Megatrends, Inc.", |
786 | + "vendor_id": "046b", |
787 | + "product": "Virtual Cdrom Device", |
788 | + "product_id": "ff20", |
789 | + "speed": 480 |
790 | + } |
791 | + ], |
792 | + "total": 1 |
793 | + }, |
794 | + "pci": { |
795 | + "devices": [ |
796 | + { |
797 | + "driver": "igb", |
798 | + "driver_version": "5.15.0-43-generic", |
799 | + "numa_node": 0, |
800 | + "pci_address": "0000:23:00.0", |
801 | + "vendor": "Intel Corporation", |
802 | + "vendor_id": "8086", |
803 | + "product": "I210 Gigabit Network Connection", |
804 | + "product_id": "1533", |
805 | + "iommu_group": 15, |
806 | + "vpd": {} |
807 | + }, |
808 | + { |
809 | + "driver": "nvme", |
810 | + "driver_version": "1.0", |
811 | + "numa_node": 0, |
812 | + "pci_address": "0000:2a:00.0", |
813 | + "vendor": "Samsung Electronics Co Ltd", |
814 | + "vendor_id": "144d", |
815 | + "product": "NVMe SSD Controller SM981/PM981/PM983", |
816 | + "product_id": "a808", |
817 | + "iommu_group": 15, |
818 | + "vpd": {} |
819 | + } |
820 | + ], |
821 | + "total": 2 |
822 | + }, |
823 | + "system": { |
824 | + "uuid": "00000000-0000-0000-0000-d05099dd49f1", |
825 | + "vendor": "To Be Filled By O.E.M.", |
826 | + "product": "To Be Filled By O.E.M.", |
827 | + "family": "To Be Filled By O.E.M.", |
828 | + "version": "To Be Filled By O.E.M.", |
829 | + "sku": "To Be Filled By O.E.M.", |
830 | + "serial": "To Be Filled By O.E.M.", |
831 | + "type": "physical", |
832 | + "firmware": { |
833 | + "vendor": "American Megatrends Inc.", |
834 | + "date": "11/02/2020", |
835 | + "version": "P3.50" |
836 | + }, |
837 | + "chassis": { |
838 | + "vendor": "To Be Filled By O.E.M.", |
839 | + "type": "Unknown", |
840 | + "serial": "To Be Filled By O.E.M.", |
841 | + "version": "To Be Filled By O.E.M." |
842 | + }, |
843 | + "motherboard": { |
844 | + "vendor": "ASRockRack", |
845 | + "product": "X470D4U", |
846 | + "serial": "200101730000493", |
847 | + "version": "" |
848 | + } |
849 | + } |
850 | + }, |
851 | + "networks": { |
852 | + "fake0": { |
853 | + "addresses": [ |
854 | + { |
855 | + "family": "inet", |
856 | + "address": "192.168.1.21", |
857 | + "netmask": "24", |
858 | + "scope": "global" |
859 | + } |
860 | + ], |
861 | + "counters": { |
862 | + "bytes_received": 17341736517, |
863 | + "bytes_sent": 42242825969, |
864 | + "packets_received": 47014272, |
865 | + "packets_sent": 64893025 |
866 | + }, |
867 | + "hwaddr": "8e:f4:15:bb:cd:fc", |
868 | + "mtu": 1500, |
869 | + "state": "up", |
870 | + "type": "broadcast", |
871 | + "bond": null, |
872 | + "bridge": null, |
873 | + "vlan": null |
874 | + } |
875 | + } |
876 | +} |
877 | diff --git a/maasperformance/data/40-maas-01-machine-resources b/maasperformance/data/40-maas-01-machine-resources |
878 | new file mode 100644 |
879 | index 0000000..056bd08 |
880 | --- /dev/null |
881 | +++ b/maasperformance/data/40-maas-01-machine-resources |
882 | @@ -0,0 +1,321 @@ |
883 | +{ |
884 | + "api_extensions": [ |
885 | + "resources", |
886 | + "resources_cpu_socket", |
887 | + "resources_gpu", |
888 | + "resources_numa", |
889 | + "resources_v2", |
890 | + "resources_disk_sata", |
891 | + "resources_network_firmware", |
892 | + "resources_disk_id", |
893 | + "resources_usb_pci", |
894 | + "resources_cpu_threads_numa", |
895 | + "resources_cpu_core_die", |
896 | + "api_os", |
897 | + "resources_system", |
898 | + "resources_pci_iommu", |
899 | + "resources_network_usb", |
900 | + "resources_disk_address" |
901 | + ], |
902 | + "api_version": "1.0", |
903 | + "environment": { |
904 | + "kernel": "Linux", |
905 | + "kernel_architecture": "x86_64", |
906 | + "kernel_version": "5.15.0-43-generic", |
907 | + "os_name": "ubuntu", |
908 | + "os_version": "20.04", |
909 | + "server": "maas-machine-resources", |
910 | + "server_name": "maas-performance", |
911 | + "server_version": "5.4" |
912 | + }, |
913 | + "resources": { |
914 | + "cpu": { |
915 | + "architecture": "x86_64", |
916 | + "sockets": [ |
917 | + { |
918 | + "name": "exampe-cpu", |
919 | + "vendor": "test", |
920 | + "socket": 0, |
921 | + "cache": [ |
922 | + { |
923 | + "level": 1, |
924 | + "type": "Data", |
925 | + "size": 32768 |
926 | + }, |
927 | + { |
928 | + "level": 1, |
929 | + "type": "Instruction", |
930 | + "size": 32768 |
931 | + }, |
932 | + { |
933 | + "level": 2, |
934 | + "type": "Unified", |
935 | + "size": 524288 |
936 | + }, |
937 | + { |
938 | + "level": 3, |
939 | + "type": "Unified", |
940 | + "size": 16777216 |
941 | + } |
942 | + ], |
943 | + "cores": [ |
944 | + { |
945 | + "core": 0, |
946 | + "die": 0, |
947 | + "threads": [ |
948 | + { |
949 | + "id": 0, |
950 | + "numa_node": 0, |
951 | + "thread": 0, |
952 | + "online": true, |
953 | + "isolated": false |
954 | + }, |
955 | + { |
956 | + "id": 12, |
957 | + "numa_node": 0, |
958 | + "thread": 1, |
959 | + "online": true, |
960 | + "isolated": false |
961 | + } |
962 | + ], |
963 | + "frequency": 3080 |
964 | + } |
965 | + ], |
966 | + "frequency": 2559, |
967 | + "frequency_minimum": 2200, |
968 | + "frequency_turbo": 4672 |
969 | + } |
970 | + ], |
971 | + "total": 24 |
972 | + }, |
973 | + "memory": { |
974 | + "nodes": [ |
975 | + { |
976 | + "numa_node": 0, |
977 | + "hugepages_used": 0, |
978 | + "hugepages_total": 0, |
979 | + "used": 25046433792, |
980 | + "total": 70866960384 |
981 | + } |
982 | + ], |
983 | + "hugepages_total": 0, |
984 | + "hugepages_used": 0, |
985 | + "hugepages_size": 2097152, |
986 | + "used": 9893044224, |
987 | + "total": 70866960384 |
988 | + }, |
989 | + "gpu": { |
990 | + "cards": [ |
991 | + { |
992 | + "driver": "ast", |
993 | + "driver_version": "5.15.0-43-generic", |
994 | + "drm": { |
995 | + "id": 0, |
996 | + "card_name": "card0", |
997 | + "card_device": "226:0", |
998 | + "control_name": "controlD64", |
999 | + "control_device": "226:0" |
1000 | + }, |
1001 | + "numa_node": 0, |
1002 | + "pci_address": "0000:22:00.0", |
1003 | + "vendor": "ASPEED Technology, Inc.", |
1004 | + "vendor_id": "1a03", |
1005 | + "product": "ASPEED Graphics Family", |
1006 | + "product_id": "2000" |
1007 | + } |
1008 | + ], |
1009 | + "total": 1 |
1010 | + }, |
1011 | + "network": { |
1012 | + "cards": [ |
1013 | + { |
1014 | + "driver": "igb", |
1015 | + "driver_version": "5.15.0-43-generic", |
1016 | + "ports": [ |
1017 | + { |
1018 | + "id": "enp35s0", |
1019 | + "address": "d0:50:99:dd:49:f1", |
1020 | + "port": 0, |
1021 | + "protocol": "ethernet", |
1022 | + "supported_modes": [ |
1023 | + "10baseT/Half", |
1024 | + "10baseT/Full", |
1025 | + "100baseT/Half", |
1026 | + "100baseT/Full", |
1027 | + "1000baseT/Full" |
1028 | + ], |
1029 | + "supported_ports": [ |
1030 | + "twisted pair" |
1031 | + ], |
1032 | + "port_type": "twisted pair", |
1033 | + "transceiver_type": "internal", |
1034 | + "auto_negotiation": true, |
1035 | + "link_detected": true, |
1036 | + "link_speed": 1000, |
1037 | + "link_duplex": "full" |
1038 | + } |
1039 | + ], |
1040 | + "numa_node": 0, |
1041 | + "pci_address": "0000:23:00.0", |
1042 | + "vendor": "Intel Corporation", |
1043 | + "vendor_id": "8086", |
1044 | + "product": "I210 Gigabit Network Connection", |
1045 | + "product_id": "1533", |
1046 | + "firmware_version": "3.16, 0x800004d6" |
1047 | + }, |
1048 | + ], |
1049 | + "total": 1 |
1050 | + }, |
1051 | + "storage": { |
1052 | + "disks": [ |
1053 | + { |
1054 | + "id": "nvme0n1", |
1055 | + "device": "259:0", |
1056 | + "model": "Samsung SSD 970 EVO 500GB", |
1057 | + "type": "nvme", |
1058 | + "read_only": false, |
1059 | + "size": 500107862016, |
1060 | + "removable": false, |
1061 | + "wwn": "eui.0025385b01440ea7", |
1062 | + "numa_node": 0, |
1063 | + "device_path": "pci-0000:2a:00.0-nvme-1", |
1064 | + "block_size": 512, |
1065 | + "firmware_version": "2B2QEXE7", |
1066 | + "rpm": 0, |
1067 | + "serial": "S5H7NS1NB23880D", |
1068 | + "device_id": "nvme-eui.0025385b01440ea7", |
1069 | + "partitions": [ |
1070 | + { |
1071 | + "id": "nvme0n1p1", |
1072 | + "device": "259:1", |
1073 | + "read_only": false, |
1074 | + "size": 536870912, |
1075 | + "partition": 1 |
1076 | + }, |
1077 | + { |
1078 | + "id": "nvme0n1p2", |
1079 | + "device": "259:2", |
1080 | + "read_only": false, |
1081 | + "size": 1073741824, |
1082 | + "partition": 2 |
1083 | + }, |
1084 | + { |
1085 | + "id": "nvme0n1p3", |
1086 | + "device": "259:3", |
1087 | + "read_only": false, |
1088 | + "size": 498495127552, |
1089 | + "partition": 3 |
1090 | + } |
1091 | + ] |
1092 | + } |
1093 | + ], |
1094 | + "total": 1 |
1095 | + }, |
1096 | + "usb": { |
1097 | + "devices": [ |
1098 | + { |
1099 | + "bus_address": 1, |
1100 | + "device_address": 11, |
1101 | + "interfaces": [ |
1102 | + { |
1103 | + "class": "Mass Storage", |
1104 | + "class_id": 8, |
1105 | + "driver": "usb-storage", |
1106 | + "driver_version": "5.15.0-43-generic", |
1107 | + "number": 0, |
1108 | + "subclass": "SCSI", |
1109 | + "subclass_id": 6 |
1110 | + } |
1111 | + ], |
1112 | + "vendor": "American Megatrends, Inc.", |
1113 | + "vendor_id": "046b", |
1114 | + "product": "Virtual Cdrom Device", |
1115 | + "product_id": "ff20", |
1116 | + "speed": 480 |
1117 | + } |
1118 | + ], |
1119 | + "total": 1 |
1120 | + }, |
1121 | + "pci": { |
1122 | + "devices": [ |
1123 | + { |
1124 | + "driver": "igb", |
1125 | + "driver_version": "5.15.0-43-generic", |
1126 | + "numa_node": 0, |
1127 | + "pci_address": "0000:23:00.0", |
1128 | + "vendor": "Intel Corporation", |
1129 | + "vendor_id": "8086", |
1130 | + "product": "I210 Gigabit Network Connection", |
1131 | + "product_id": "1533", |
1132 | + "iommu_group": 15, |
1133 | + "vpd": {} |
1134 | + }, |
1135 | + { |
1136 | + "driver": "nvme", |
1137 | + "driver_version": "1.0", |
1138 | + "numa_node": 0, |
1139 | + "pci_address": "0000:2a:00.0", |
1140 | + "vendor": "Samsung Electronics Co Ltd", |
1141 | + "vendor_id": "144d", |
1142 | + "product": "NVMe SSD Controller SM981/PM981/PM983", |
1143 | + "product_id": "a808", |
1144 | + "iommu_group": 15, |
1145 | + "vpd": {} |
1146 | + } |
1147 | + ], |
1148 | + "total": 2 |
1149 | + }, |
1150 | + "system": { |
1151 | + "uuid": "00000000-0000-0000-0000-d05099dd49f1", |
1152 | + "vendor": "To Be Filled By O.E.M.", |
1153 | + "product": "To Be Filled By O.E.M.", |
1154 | + "family": "To Be Filled By O.E.M.", |
1155 | + "version": "To Be Filled By O.E.M.", |
1156 | + "sku": "To Be Filled By O.E.M.", |
1157 | + "serial": "To Be Filled By O.E.M.", |
1158 | + "type": "physical", |
1159 | + "firmware": { |
1160 | + "vendor": "American Megatrends Inc.", |
1161 | + "date": "11/02/2020", |
1162 | + "version": "P3.50" |
1163 | + }, |
1164 | + "chassis": { |
1165 | + "vendor": "To Be Filled By O.E.M.", |
1166 | + "type": "Unknown", |
1167 | + "serial": "To Be Filled By O.E.M.", |
1168 | + "version": "To Be Filled By O.E.M." |
1169 | + }, |
1170 | + "motherboard": { |
1171 | + "vendor": "ASRockRack", |
1172 | + "product": "X470D4U", |
1173 | + "serial": "200101730000493", |
1174 | + "version": "" |
1175 | + } |
1176 | + } |
1177 | + }, |
1178 | + "networks": { |
1179 | + "fake0": { |
1180 | + "addresses": [ |
1181 | + { |
1182 | + "family": "inet", |
1183 | + "address": "192.168.1.21", |
1184 | + "netmask": "24", |
1185 | + "scope": "global" |
1186 | + } |
1187 | + ], |
1188 | + "counters": { |
1189 | + "bytes_received": 17341736517, |
1190 | + "bytes_sent": 42242825969, |
1191 | + "packets_received": 47014272, |
1192 | + "packets_sent": 64893025 |
1193 | + }, |
1194 | + "hwaddr": "8e:f4:15:bb:cd:fc", |
1195 | + "mtu": 1500, |
1196 | + "state": "up", |
1197 | + "type": "broadcast", |
1198 | + "bond": null, |
1199 | + "bridge": null, |
1200 | + "vlan": null |
1201 | + } |
1202 | + } |
1203 | +} |
1204 | diff --git a/maasperformance/data/40-maas-01-machine-resources.err b/maasperformance/data/40-maas-01-machine-resources.err |
1205 | new file mode 100644 |
1206 | index 0000000..e69de29 |
1207 | --- /dev/null |
1208 | +++ b/maasperformance/data/40-maas-01-machine-resources.err |
1209 | diff --git a/maasperformance/data/40-maas-01-machine-resources.out b/maasperformance/data/40-maas-01-machine-resources.out |
1210 | new file mode 100644 |
1211 | index 0000000..056bd08 |
1212 | --- /dev/null |
1213 | +++ b/maasperformance/data/40-maas-01-machine-resources.out |
1214 | @@ -0,0 +1,321 @@ |
1215 | +{ |
1216 | + "api_extensions": [ |
1217 | + "resources", |
1218 | + "resources_cpu_socket", |
1219 | + "resources_gpu", |
1220 | + "resources_numa", |
1221 | + "resources_v2", |
1222 | + "resources_disk_sata", |
1223 | + "resources_network_firmware", |
1224 | + "resources_disk_id", |
1225 | + "resources_usb_pci", |
1226 | + "resources_cpu_threads_numa", |
1227 | + "resources_cpu_core_die", |
1228 | + "api_os", |
1229 | + "resources_system", |
1230 | + "resources_pci_iommu", |
1231 | + "resources_network_usb", |
1232 | + "resources_disk_address" |
1233 | + ], |
1234 | + "api_version": "1.0", |
1235 | + "environment": { |
1236 | + "kernel": "Linux", |
1237 | + "kernel_architecture": "x86_64", |
1238 | + "kernel_version": "5.15.0-43-generic", |
1239 | + "os_name": "ubuntu", |
1240 | + "os_version": "20.04", |
1241 | + "server": "maas-machine-resources", |
1242 | + "server_name": "maas-performance", |
1243 | + "server_version": "5.4" |
1244 | + }, |
1245 | + "resources": { |
1246 | + "cpu": { |
1247 | + "architecture": "x86_64", |
1248 | + "sockets": [ |
1249 | + { |
1250 | + "name": "exampe-cpu", |
1251 | + "vendor": "test", |
1252 | + "socket": 0, |
1253 | + "cache": [ |
1254 | + { |
1255 | + "level": 1, |
1256 | + "type": "Data", |
1257 | + "size": 32768 |
1258 | + }, |
1259 | + { |
1260 | + "level": 1, |
1261 | + "type": "Instruction", |
1262 | + "size": 32768 |
1263 | + }, |
1264 | + { |
1265 | + "level": 2, |
1266 | + "type": "Unified", |
1267 | + "size": 524288 |
1268 | + }, |
1269 | + { |
1270 | + "level": 3, |
1271 | + "type": "Unified", |
1272 | + "size": 16777216 |
1273 | + } |
1274 | + ], |
1275 | + "cores": [ |
1276 | + { |
1277 | + "core": 0, |
1278 | + "die": 0, |
1279 | + "threads": [ |
1280 | + { |
1281 | + "id": 0, |
1282 | + "numa_node": 0, |
1283 | + "thread": 0, |
1284 | + "online": true, |
1285 | + "isolated": false |
1286 | + }, |
1287 | + { |
1288 | + "id": 12, |
1289 | + "numa_node": 0, |
1290 | + "thread": 1, |
1291 | + "online": true, |
1292 | + "isolated": false |
1293 | + } |
1294 | + ], |
1295 | + "frequency": 3080 |
1296 | + } |
1297 | + ], |
1298 | + "frequency": 2559, |
1299 | + "frequency_minimum": 2200, |
1300 | + "frequency_turbo": 4672 |
1301 | + } |
1302 | + ], |
1303 | + "total": 24 |
1304 | + }, |
1305 | + "memory": { |
1306 | + "nodes": [ |
1307 | + { |
1308 | + "numa_node": 0, |
1309 | + "hugepages_used": 0, |
1310 | + "hugepages_total": 0, |
1311 | + "used": 25046433792, |
1312 | + "total": 70866960384 |
1313 | + } |
1314 | + ], |
1315 | + "hugepages_total": 0, |
1316 | + "hugepages_used": 0, |
1317 | + "hugepages_size": 2097152, |
1318 | + "used": 9893044224, |
1319 | + "total": 70866960384 |
1320 | + }, |
1321 | + "gpu": { |
1322 | + "cards": [ |
1323 | + { |
1324 | + "driver": "ast", |
1325 | + "driver_version": "5.15.0-43-generic", |
1326 | + "drm": { |
1327 | + "id": 0, |
1328 | + "card_name": "card0", |
1329 | + "card_device": "226:0", |
1330 | + "control_name": "controlD64", |
1331 | + "control_device": "226:0" |
1332 | + }, |
1333 | + "numa_node": 0, |
1334 | + "pci_address": "0000:22:00.0", |
1335 | + "vendor": "ASPEED Technology, Inc.", |
1336 | + "vendor_id": "1a03", |
1337 | + "product": "ASPEED Graphics Family", |
1338 | + "product_id": "2000" |
1339 | + } |
1340 | + ], |
1341 | + "total": 1 |
1342 | + }, |
1343 | + "network": { |
1344 | + "cards": [ |
1345 | + { |
1346 | + "driver": "igb", |
1347 | + "driver_version": "5.15.0-43-generic", |
1348 | + "ports": [ |
1349 | + { |
1350 | + "id": "enp35s0", |
1351 | + "address": "d0:50:99:dd:49:f1", |
1352 | + "port": 0, |
1353 | + "protocol": "ethernet", |
1354 | + "supported_modes": [ |
1355 | + "10baseT/Half", |
1356 | + "10baseT/Full", |
1357 | + "100baseT/Half", |
1358 | + "100baseT/Full", |
1359 | + "1000baseT/Full" |
1360 | + ], |
1361 | + "supported_ports": [ |
1362 | + "twisted pair" |
1363 | + ], |
1364 | + "port_type": "twisted pair", |
1365 | + "transceiver_type": "internal", |
1366 | + "auto_negotiation": true, |
1367 | + "link_detected": true, |
1368 | + "link_speed": 1000, |
1369 | + "link_duplex": "full" |
1370 | + } |
1371 | + ], |
1372 | + "numa_node": 0, |
1373 | + "pci_address": "0000:23:00.0", |
1374 | + "vendor": "Intel Corporation", |
1375 | + "vendor_id": "8086", |
1376 | + "product": "I210 Gigabit Network Connection", |
1377 | + "product_id": "1533", |
1378 | + "firmware_version": "3.16, 0x800004d6" |
1379 | + }, |
1380 | + ], |
1381 | + "total": 1 |
1382 | + }, |
1383 | + "storage": { |
1384 | + "disks": [ |
1385 | + { |
1386 | + "id": "nvme0n1", |
1387 | + "device": "259:0", |
1388 | + "model": "Samsung SSD 970 EVO 500GB", |
1389 | + "type": "nvme", |
1390 | + "read_only": false, |
1391 | + "size": 500107862016, |
1392 | + "removable": false, |
1393 | + "wwn": "eui.0025385b01440ea7", |
1394 | + "numa_node": 0, |
1395 | + "device_path": "pci-0000:2a:00.0-nvme-1", |
1396 | + "block_size": 512, |
1397 | + "firmware_version": "2B2QEXE7", |
1398 | + "rpm": 0, |
1399 | + "serial": "S5H7NS1NB23880D", |
1400 | + "device_id": "nvme-eui.0025385b01440ea7", |
1401 | + "partitions": [ |
1402 | + { |
1403 | + "id": "nvme0n1p1", |
1404 | + "device": "259:1", |
1405 | + "read_only": false, |
1406 | + "size": 536870912, |
1407 | + "partition": 1 |
1408 | + }, |
1409 | + { |
1410 | + "id": "nvme0n1p2", |
1411 | + "device": "259:2", |
1412 | + "read_only": false, |
1413 | + "size": 1073741824, |
1414 | + "partition": 2 |
1415 | + }, |
1416 | + { |
1417 | + "id": "nvme0n1p3", |
1418 | + "device": "259:3", |
1419 | + "read_only": false, |
1420 | + "size": 498495127552, |
1421 | + "partition": 3 |
1422 | + } |
1423 | + ] |
1424 | + } |
1425 | + ], |
1426 | + "total": 1 |
1427 | + }, |
1428 | + "usb": { |
1429 | + "devices": [ |
1430 | + { |
1431 | + "bus_address": 1, |
1432 | + "device_address": 11, |
1433 | + "interfaces": [ |
1434 | + { |
1435 | + "class": "Mass Storage", |
1436 | + "class_id": 8, |
1437 | + "driver": "usb-storage", |
1438 | + "driver_version": "5.15.0-43-generic", |
1439 | + "number": 0, |
1440 | + "subclass": "SCSI", |
1441 | + "subclass_id": 6 |
1442 | + } |
1443 | + ], |
1444 | + "vendor": "American Megatrends, Inc.", |
1445 | + "vendor_id": "046b", |
1446 | + "product": "Virtual Cdrom Device", |
1447 | + "product_id": "ff20", |
1448 | + "speed": 480 |
1449 | + } |
1450 | + ], |
1451 | + "total": 1 |
1452 | + }, |
1453 | + "pci": { |
1454 | + "devices": [ |
1455 | + { |
1456 | + "driver": "igb", |
1457 | + "driver_version": "5.15.0-43-generic", |
1458 | + "numa_node": 0, |
1459 | + "pci_address": "0000:23:00.0", |
1460 | + "vendor": "Intel Corporation", |
1461 | + "vendor_id": "8086", |
1462 | + "product": "I210 Gigabit Network Connection", |
1463 | + "product_id": "1533", |
1464 | + "iommu_group": 15, |
1465 | + "vpd": {} |
1466 | + }, |
1467 | + { |
1468 | + "driver": "nvme", |
1469 | + "driver_version": "1.0", |
1470 | + "numa_node": 0, |
1471 | + "pci_address": "0000:2a:00.0", |
1472 | + "vendor": "Samsung Electronics Co Ltd", |
1473 | + "vendor_id": "144d", |
1474 | + "product": "NVMe SSD Controller SM981/PM981/PM983", |
1475 | + "product_id": "a808", |
1476 | + "iommu_group": 15, |
1477 | + "vpd": {} |
1478 | + } |
1479 | + ], |
1480 | + "total": 2 |
1481 | + }, |
1482 | + "system": { |
1483 | + "uuid": "00000000-0000-0000-0000-d05099dd49f1", |
1484 | + "vendor": "To Be Filled By O.E.M.", |
1485 | + "product": "To Be Filled By O.E.M.", |
1486 | + "family": "To Be Filled By O.E.M.", |
1487 | + "version": "To Be Filled By O.E.M.", |
1488 | + "sku": "To Be Filled By O.E.M.", |
1489 | + "serial": "To Be Filled By O.E.M.", |
1490 | + "type": "physical", |
1491 | + "firmware": { |
1492 | + "vendor": "American Megatrends Inc.", |
1493 | + "date": "11/02/2020", |
1494 | + "version": "P3.50" |
1495 | + }, |
1496 | + "chassis": { |
1497 | + "vendor": "To Be Filled By O.E.M.", |
1498 | + "type": "Unknown", |
1499 | + "serial": "To Be Filled By O.E.M.", |
1500 | + "version": "To Be Filled By O.E.M." |
1501 | + }, |
1502 | + "motherboard": { |
1503 | + "vendor": "ASRockRack", |
1504 | + "product": "X470D4U", |
1505 | + "serial": "200101730000493", |
1506 | + "version": "" |
1507 | + } |
1508 | + } |
1509 | + }, |
1510 | + "networks": { |
1511 | + "fake0": { |
1512 | + "addresses": [ |
1513 | + { |
1514 | + "family": "inet", |
1515 | + "address": "192.168.1.21", |
1516 | + "netmask": "24", |
1517 | + "scope": "global" |
1518 | + } |
1519 | + ], |
1520 | + "counters": { |
1521 | + "bytes_received": 17341736517, |
1522 | + "bytes_sent": 42242825969, |
1523 | + "packets_received": 47014272, |
1524 | + "packets_sent": 64893025 |
1525 | + }, |
1526 | + "hwaddr": "8e:f4:15:bb:cd:fc", |
1527 | + "mtu": 1500, |
1528 | + "state": "up", |
1529 | + "type": "broadcast", |
1530 | + "bond": null, |
1531 | + "bridge": null, |
1532 | + "vlan": null |
1533 | + } |
1534 | + } |
1535 | +} |
1536 | diff --git a/maasperformance/machine.py b/maasperformance/machine.py |
1537 | index e5725b0..ed55471 100644 |
1538 | --- a/maasperformance/machine.py |
1539 | +++ b/maasperformance/machine.py |
1540 | @@ -93,6 +93,7 @@ class MachineManager: |
1541 | _run() method of this class. |
1542 | """ |
1543 | self.machines = {} |
1544 | + self.running = True |
1545 | for index in range(self.number_of_machines): |
1546 | machine = Machine(loop, self.parent_iface, f'fake{index}') |
1547 | self.machines[machine.uuid] = machine |
1548 | @@ -130,7 +131,7 @@ class MachineManager: |
1549 | logging.info(f'Created PXE interfaces in {duration} seconds') |
1550 | start = now() |
1551 | |
1552 | - while True: |
1553 | + while self.running and self.loop.is_running(): |
1554 | power_command = await self.power_commands.get() |
1555 | print( |
1556 | f'Got power command {power_command.new_power_state} ' |
1557 | @@ -233,9 +234,12 @@ class Machine: |
1558 | That is, the clean up method that was last added is the first |
1559 | one to be executed. |
1560 | """ |
1561 | - while len(self.cleanups) > 0: |
1562 | - func = self.cleanups.pop() |
1563 | - await func() |
1564 | + try: |
1565 | + while len(self.cleanups) > 0: |
1566 | + func = self.cleanups.pop() |
1567 | + await func() |
1568 | + finally: |
1569 | + self.running = False |
1570 | |
1571 | async def create_pxe_interface(self): |
1572 | """Create the machine's network interface on the host. |
1573 | @@ -264,7 +268,7 @@ class Machine: |
1574 | print(f'Power on: {self.uuid}') |
1575 | # Simulate how it takes a while before the power command is |
1576 | # issued, and when the machine is actually considered on. |
1577 | - await asyncio.sleep(1, loop=self.loop) |
1578 | + await asyncio.sleep(1) |
1579 | self.power_state = PowerState.ON |
1580 | await self.pxe_boot() |
1581 | |
1582 | @@ -274,7 +278,7 @@ class Machine: |
1583 | # Simulate how it takes a while before the power command is |
1584 | # issued, and when the machine is actually considered off. |
1585 | print(f'Power off: {self.uuid}') |
1586 | - await asyncio.sleep(1, loop=self.loop) |
1587 | + await asyncio.sleep(1) |
1588 | await self.release_ip() |
1589 | self.reset() |
1590 | self.power_state = PowerState.OFF |
1591 | @@ -358,7 +362,7 @@ class Machine: |
1592 | else: |
1593 | raise RuntimeError("Couldn't retrieve pxelinux.cfg") |
1594 | |
1595 | - kernel_url, initrd_url = None, None |
1596 | + kernel_url, initrd_url, image_url = None, None, None |
1597 | for line in pxe_config.splitlines(): |
1598 | line = line.strip() |
1599 | try: |
1600 | @@ -371,7 +375,8 @@ class Machine: |
1601 | initrd_url = value |
1602 | if key == 'APPEND': |
1603 | image_url, cloud_config_url = self._extract_append_urls(value) |
1604 | - if kernel_url is not None and initrd_url is not None: |
1605 | + if (kernel_url is not None and initrd_url is not None |
1606 | + and image_url is not None): |
1607 | await self.network.get_curl_file(kernel_url) |
1608 | await self.network.get_curl_file(initrd_url) |
1609 | await self.network.get_curl_file(image_url) |
1610 | diff --git a/maasperformance/network.py b/maasperformance/network.py |
1611 | index abaeba7..c8fef7e 100644 |
1612 | --- a/maasperformance/network.py |
1613 | +++ b/maasperformance/network.py |
1614 | @@ -102,8 +102,13 @@ class PlaintextSignature(Signature): |
1615 | |
1616 | name = 'PLAINTEXT' |
1617 | |
1618 | - def sign(self, consumer_secret, method, url, oauth_token_secret=None, |
1619 | - **params): |
1620 | + def sign( |
1621 | + self, |
1622 | + consumer_secret, |
1623 | + method, |
1624 | + url, |
1625 | + oauth_token_secret=None, |
1626 | + **params): |
1627 | """Create a signature using PLAINTEXT.""" |
1628 | key = self._escape(consumer_secret) + '&' |
1629 | if oauth_token_secret: |
1630 | diff --git a/maasperformance/network_interface.py b/maasperformance/network_interface.py |
1631 | index 7f13822..836342c 100644 |
1632 | --- a/maasperformance/network_interface.py |
1633 | +++ b/maasperformance/network_interface.py |
1634 | @@ -1,14 +1,16 @@ |
1635 | import json |
1636 | import random |
1637 | -from subprocess import check_output |
1638 | from typing import NamedTuple |
1639 | |
1640 | -from .process import exec_process |
1641 | +from .process import ( |
1642 | + exec_process, |
1643 | + exec_sync_process, |
1644 | +) |
1645 | |
1646 | |
1647 | def interface_is_bridge(iface: str): |
1648 | """Return whether an interface is a bridge.""" |
1649 | - output = check_output(['ip', '--json', '-d', 'link', 'show', iface]) |
1650 | + output = exec_sync_process('ip', '--json', '-d', 'link', 'show', iface) |
1651 | data = json.loads(output) |
1652 | return data[0].get('linkinfo', {}).get('info_kind') == 'bridge' |
1653 | |
1654 | diff --git a/maasperformance/process.py b/maasperformance/process.py |
1655 | index ae7e4ca..febc663 100644 |
1656 | --- a/maasperformance/process.py |
1657 | +++ b/maasperformance/process.py |
1658 | @@ -1,4 +1,5 @@ |
1659 | import asyncio |
1660 | +from subprocess import check_output |
1661 | |
1662 | |
1663 | class ProcessError(Exception): |
1664 | @@ -13,10 +14,15 @@ class ProcessError(Exception): |
1665 | |
1666 | |
1667 | async def exec_process(*args: str) -> str: |
1668 | - """Execute a process a process, returning its stdout.""" |
1669 | + """Execute a process asynchronously, returning its stdout.""" |
1670 | process = await asyncio.create_subprocess_exec( |
1671 | *args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE) |
1672 | stdout, stderr = await process.communicate() |
1673 | if process.returncode != 0: |
1674 | raise ProcessError(args, process.returncode, stderr.decode()) |
1675 | return stdout.decode() |
1676 | + |
1677 | + |
1678 | +def exec_sync_process(*args: str) -> str: |
1679 | + """Execute a process synchronously, returning its stdout.""" |
1680 | + return check_output(args) |
1681 | diff --git a/maasperformance/testing/fixtures.py b/maasperformance/testing/fixtures.py |
1682 | index f85f311..9b52067 100644 |
1683 | --- a/maasperformance/testing/fixtures.py |
1684 | +++ b/maasperformance/testing/fixtures.py |
1685 | @@ -1,12 +1,18 @@ |
1686 | import asyncio |
1687 | +from io import BytesIO |
1688 | from pathlib import Path |
1689 | +import subprocess |
1690 | +import tarfile |
1691 | |
1692 | import pytest |
1693 | |
1694 | from .. import event |
1695 | from ..machine import Machine |
1696 | from ..network_interface import ParentNetworkInterface |
1697 | -from .subprocess import FakeCreateSubProcess |
1698 | +from .subprocess import ( |
1699 | + FakeCheckOutput, |
1700 | + FakeCreateSubProcess, |
1701 | +) |
1702 | |
1703 | |
1704 | @pytest.fixture |
1705 | @@ -38,3 +44,25 @@ def create_subprocess_mock(module=asyncio): |
1706 | module, 'create_subprocess_exec', new=FakeCreateSubProcess()) |
1707 | |
1708 | return subprocess_mock |
1709 | + |
1710 | + |
1711 | +def create_check_output_mock(module=subprocess, output=""): |
1712 | + """Return a fixture for mocking check_output.""" |
1713 | + |
1714 | + @pytest.fixture |
1715 | + def check_output_mock(mocker, tmpdir): |
1716 | + yield mocker.patch.object( |
1717 | + module, 'check_output', new=FakeCheckOutput(output)) |
1718 | + |
1719 | + return check_output_mock |
1720 | + |
1721 | + |
1722 | +@pytest.fixture |
1723 | +def tar_file_http_response(): |
1724 | + buf = BytesIO() |
1725 | + with tarfile.open(fileobj=buf, mode="w") as tar: |
1726 | + data = "{\"foo\": \"bar\"}" |
1727 | + info = tarfile.TarInfo("index.json") |
1728 | + info.size = len(data) |
1729 | + tar.addfile(info, BytesIO(initial_bytes=data.encode("ascii"))) |
1730 | + return buf.getvalue().decode("ascii") |
1731 | diff --git a/maasperformance/testing/subprocess.py b/maasperformance/testing/subprocess.py |
1732 | index 557b048..d2634c4 100644 |
1733 | --- a/maasperformance/testing/subprocess.py |
1734 | +++ b/maasperformance/testing/subprocess.py |
1735 | @@ -191,6 +191,10 @@ class FakeCurlSubprocess(FakeAsyncSubprocess): |
1736 | def root_url(self): |
1737 | return f'{self.root_protocol}://{self.ip_address}{self.root_path}' |
1738 | |
1739 | + @property |
1740 | + def image_url(self): |
1741 | + return self.root_url |
1742 | + |
1743 | def get_pxelinux_cfg(self): |
1744 | """Return the pxelinux.cfg contents. |
1745 | |
1746 | @@ -248,4 +252,14 @@ class FakeCurlSubprocess(FakeAsyncSubprocess): |
1747 | } |
1748 | files[self.kernel_url] = '' |
1749 | files[self.initrd_url] = '' |
1750 | + files[self.image_url] = '' |
1751 | return files |
1752 | + |
1753 | + |
1754 | +class FakeCheckOutput: |
1755 | + |
1756 | + def __init__(self, output): |
1757 | + self._output = output |
1758 | + |
1759 | + def __call__(self, *args, **kwargs): |
1760 | + return self._output |
1761 | diff --git a/maasperformance/tests/test_bmc.py b/maasperformance/tests/test_bmc.py |
1762 | new file mode 100644 |
1763 | index 0000000..97be801 |
1764 | --- /dev/null |
1765 | +++ b/maasperformance/tests/test_bmc.py |
1766 | @@ -0,0 +1,50 @@ |
1767 | +import asyncio |
1768 | +import random |
1769 | +from unittest.mock import Mock |
1770 | + |
1771 | +import pytest |
1772 | + |
1773 | +from ..bmc import ( |
1774 | + BMC, |
1775 | + PowerCommand, |
1776 | + PowerState, |
1777 | +) |
1778 | + |
1779 | + |
1780 | +class TestBMC: |
1781 | + |
1782 | + @pytest.mark.asyncio |
1783 | + async def test_bmc_power_on(self): |
1784 | + manager = Mock() |
1785 | + manager.power_commands = asyncio.Queue() |
1786 | + bmc = BMC(manager) |
1787 | + uuid = random.randint(1, 512) |
1788 | + await bmc.power_on(uuid) |
1789 | + result = await manager.power_commands.get() |
1790 | + assert isinstance(result, PowerCommand) |
1791 | + assert result.machine_uuid == uuid |
1792 | + assert result.new_power_state == PowerState.ON |
1793 | + |
1794 | + @pytest.mark.asyncio |
1795 | + async def test_bmc_power_off(self): |
1796 | + manager = Mock() |
1797 | + manager.power_commands = asyncio.Queue() |
1798 | + bmc = BMC(manager) |
1799 | + uuid = random.randint(1, 512) |
1800 | + await bmc.power_off(uuid) |
1801 | + result = await manager.power_commands.get() |
1802 | + assert isinstance(result, PowerCommand) |
1803 | + assert result.machine_uuid == uuid |
1804 | + assert result.new_power_state == PowerState.OFF |
1805 | + |
1806 | + @pytest.mark.asyncio |
1807 | + async def test_bmc_power_cycle(self): |
1808 | + manager = Mock() |
1809 | + manager.power_commands = asyncio.Queue() |
1810 | + bmc = BMC(manager) |
1811 | + uuid = random.randint(1, 512) |
1812 | + await bmc.power_cycle(uuid) |
1813 | + result = await manager.power_commands.get() |
1814 | + assert isinstance(result, PowerCommand) |
1815 | + assert result.machine_uuid == uuid |
1816 | + assert result.new_power_state == PowerState.CYCLE |
1817 | diff --git a/maasperformance/tests/test_event.py b/maasperformance/tests/test_event.py |
1818 | index 3e826f3..1d800f5 100644 |
1819 | --- a/maasperformance/tests/test_event.py |
1820 | +++ b/maasperformance/tests/test_event.py |
1821 | @@ -1,4 +1,5 @@ |
1822 | import logging |
1823 | +from unittest.mock import Mock |
1824 | from urllib.parse import urlparse |
1825 | |
1826 | import pytest |
1827 | @@ -8,11 +9,25 @@ from ..machine import Machine |
1828 | from ..testing.prometheus import track_metric |
1829 | |
1830 | |
1831 | +def create_mock_parent_interface(): |
1832 | + parent_iface = Mock() |
1833 | + |
1834 | + def _get_client_interface(x): |
1835 | + iface = Mock() |
1836 | + iface.name = x |
1837 | + iface.__str__ = lambda _: iface.name |
1838 | + return iface |
1839 | + |
1840 | + parent_iface.get_client_interface = _get_client_interface |
1841 | + return parent_iface |
1842 | + |
1843 | + |
1844 | class TestDHCPEvents: |
1845 | |
1846 | @pytest.mark.parametrize('event_type', ['request', 'release']) |
1847 | def test_dhcp_request_started(self, event_type, mock_event_time, caplog): |
1848 | - machine = Machine(None, None, 'eth0') |
1849 | + parent_iface = create_mock_parent_interface() |
1850 | + machine = Machine(None, parent_iface, 'eth0') |
1851 | mock_event_time.return_value = 12345 |
1852 | event_func = getattr(event, f'dhcp_{event_type}_started') |
1853 | with caplog.at_level(logging.INFO): |
1854 | @@ -24,7 +39,8 @@ class TestDHCPEvents: |
1855 | |
1856 | @pytest.mark.parametrize('event_type', ['request', 'release']) |
1857 | def test_dhcp_request_ended(self, event_type, mock_event_time, caplog): |
1858 | - machine = Machine(None, None, 'eth0') |
1859 | + parent_iface = create_mock_parent_interface() |
1860 | + machine = Machine(None, parent_iface, 'eth0') |
1861 | machine.last_event_time[f'dhcp_{event_type}_start'] = 12345 |
1862 | mock_event_time.return_value = 12347.5 |
1863 | event_func = getattr(event, f'dhcp_{event_type}_ended') |
1864 | @@ -38,7 +54,8 @@ class TestDHCPEvents: |
1865 | @pytest.mark.parametrize('event_type', ['request', 'release']) |
1866 | def test_dhcp_request_ended_prometheus_count( |
1867 | self, event_type, mock_event_time, caplog): |
1868 | - machine = Machine(None, None, 'eth0') |
1869 | + parent_iface = create_mock_parent_interface() |
1870 | + machine = Machine(None, parent_iface, 'eth0') |
1871 | machine.last_event_time[f'dhcp_{event_type}_start'] = 12345 |
1872 | mock_event_time.return_value = 12347.5 |
1873 | event_func = getattr(event, f'dhcp_{event_type}_ended') |
1874 | @@ -50,7 +67,8 @@ class TestDHCPEvents: |
1875 | @pytest.mark.parametrize('event_type', ['request', 'release']) |
1876 | def test_dhcp_request_ended_prometheus_bucket_low( |
1877 | self, event_type, mock_event_time, caplog): |
1878 | - machine = Machine(None, None, 'eth0') |
1879 | + parent_iface = create_mock_parent_interface() |
1880 | + machine = Machine(None, parent_iface, 'eth0') |
1881 | machine.last_event_time[f'dhcp_{event_type}_start'] = 12345 |
1882 | mock_event_time.return_value = 12347.5 |
1883 | event_func = getattr(event, f'dhcp_{event_type}_ended') |
1884 | @@ -62,7 +80,8 @@ class TestDHCPEvents: |
1885 | @pytest.mark.parametrize('event_type', ['request', 'release']) |
1886 | def test_dhcp_request_ended_prometheus_bucket_high( |
1887 | self, event_type, mock_event_time, caplog): |
1888 | - machine = Machine(None, None, 'eth0') |
1889 | + parent_iface = create_mock_parent_interface() |
1890 | + machine = Machine(None, parent_iface, 'eth0') |
1891 | machine.last_event_time[f'dhcp_{event_type}_start'] = 12345 |
1892 | mock_event_time.return_value = 12347.5 |
1893 | event_func = getattr(event, f'dhcp_{event_type}_ended') |
1894 | @@ -75,7 +94,8 @@ class TestDHCPEvents: |
1895 | class TestFileTransferEvents: |
1896 | |
1897 | def test_file_transfer_started(self, mock_event_time, caplog): |
1898 | - machine = Machine(None, None, 'eth0') |
1899 | + parent_iface = create_mock_parent_interface() |
1900 | + machine = Machine(None, parent_iface, 'eth0') |
1901 | mock_event_time.return_value = 12345 |
1902 | url = urlparse('tftp://some-host/my-file') |
1903 | with caplog.at_level(logging.INFO): |
1904 | @@ -89,7 +109,8 @@ class TestFileTransferEvents: |
1905 | |
1906 | def test_file_transfer_ended(self, mock_event_time, caplog): |
1907 | url = urlparse('tftp://some-host/my-file') |
1908 | - machine = Machine(None, None, 'eth0') |
1909 | + parent_iface = create_mock_parent_interface() |
1910 | + machine = Machine(None, parent_iface, 'eth0') |
1911 | machine.last_event_time[f'file_transfer_start_{url.geturl()}'] = 12345 |
1912 | mock_event_time.return_value = 12347.5 |
1913 | with caplog.at_level(logging.INFO): |
1914 | @@ -105,7 +126,8 @@ class TestFileTransferEvents: |
1915 | def test_file_transfer_ended_prometheus_count( |
1916 | self, mock_event_time, caplog): |
1917 | url = urlparse('tftp://some-host/my-file') |
1918 | - machine = Machine(None, None, 'eth0') |
1919 | + parent_iface = create_mock_parent_interface() |
1920 | + machine = Machine(None, parent_iface, 'eth0') |
1921 | machine.last_event_time[f'file_transfer_start_{url.geturl()}'] = 12345 |
1922 | mock_event_time.return_value = 12347.5 |
1923 | labels = {'scheme': 'tftp', 'filename': '/my-file', 'result': '68'} |
1924 | @@ -117,8 +139,8 @@ class TestFileTransferEvents: |
1925 | def test_file_transfer_ended_prometheus_bucket_low( |
1926 | self, mock_event_time, caplog): |
1927 | url = urlparse('tftp://some-host/my-file') |
1928 | - machine = Machine(None, None, 'eth0') |
1929 | - machine = Machine(None, None, 'eth0') |
1930 | + parent_iface = create_mock_parent_interface() |
1931 | + machine = Machine(None, parent_iface, 'eth0') |
1932 | machine.last_event_time[f'file_transfer_start_{url.geturl()}'] = 12345 |
1933 | mock_event_time.return_value = 12347.5 |
1934 | labels = { |
1935 | @@ -135,7 +157,8 @@ class TestFileTransferEvents: |
1936 | def test_file_transfer_ended_prometheus_bucket_high( |
1937 | self, mock_event_time, caplog): |
1938 | url = urlparse('tftp://some-host/my-file') |
1939 | - machine = Machine(None, None, 'eth0') |
1940 | + parent_iface = create_mock_parent_interface() |
1941 | + machine = Machine(None, parent_iface, 'eth0') |
1942 | machine.last_event_time[f'file_transfer_start_{url.geturl()}'] = 12345 |
1943 | mock_event_time.return_value = 12347.5 |
1944 | labels = { |
1945 | diff --git a/maasperformance/tests/test_machine.py b/maasperformance/tests/test_machine.py |
1946 | index c5d1cbe..a0e2e69 100644 |
1947 | --- a/maasperformance/tests/test_machine.py |
1948 | +++ b/maasperformance/tests/test_machine.py |
1949 | @@ -1,5 +1,7 @@ |
1950 | import asyncio |
1951 | import logging |
1952 | +import random |
1953 | +from unittest.mock import Mock |
1954 | import uuid |
1955 | |
1956 | import pytest |
1957 | @@ -7,17 +9,55 @@ import pytest |
1958 | from .. import ( |
1959 | event, |
1960 | machine as machine_module, |
1961 | + process, |
1962 | +) |
1963 | +from ..bmc import ( |
1964 | + BMC, |
1965 | + PowerState, |
1966 | ) |
1967 | -from ..bmc import PowerState |
1968 | from ..machine import ( |
1969 | Machine, |
1970 | MachineManager, |
1971 | ) |
1972 | from ..network_interface import ParentNetworkInterface |
1973 | -from ..testing.fixtures import create_subprocess_mock |
1974 | +from ..testing.fixtures import ( |
1975 | + create_check_output_mock, |
1976 | + create_subprocess_mock, |
1977 | +) |
1978 | from ..testing.prometheus import track_metric |
1979 | |
1980 | subprocess_mock = create_subprocess_mock() |
1981 | +check_output_mock = create_check_output_mock( |
1982 | + module=process, |
1983 | + output=""" |
1984 | +[ |
1985 | + { |
1986 | + "ifindex":1, |
1987 | + "ifname":"eth0", |
1988 | + "flags":["BROADCAST","MULTICAST","UP","LOWER_UP"], |
1989 | + "mtu":1500, |
1990 | + "qdisc":"mq", |
1991 | + "operstate":"UP", |
1992 | + "linkmode":"DEFAULT", |
1993 | + "group":"default", |
1994 | + "txqlen":1000, |
1995 | + "link_type":"ether", |
1996 | + "address":"80:61:5f:08:fc:16", |
1997 | + "broadcast":"ff:ff:ff:ff:ff:ff", |
1998 | + "promiscuity":0, |
1999 | + "min_mtu":68, |
2000 | + "max_mtu":9710, |
2001 | + "inet6_addr_gen_mode":"none", |
2002 | + "num_tx_queues":64, |
2003 | + "num_rx_queues":64, |
2004 | + "gso_max_size":65536, |
2005 | + "gso_max_segs":65535, |
2006 | + "parentbus":"pci", |
2007 | + "parentdev":"0000:01:00.0", |
2008 | + "vfinfo_list":[] |
2009 | + } |
2010 | +] |
2011 | +""") |
2012 | |
2013 | |
2014 | class TestMachine: |
2015 | @@ -60,6 +100,7 @@ class TestMachine: |
2016 | 'bridge') |
2017 | ] |
2018 | |
2019 | + @pytest.mark.asyncio |
2020 | async def test_create_pxe_interface_registers_cleanup( |
2021 | self, machine, subprocess_mock): |
2022 | await machine.create_pxe_interface() |
2023 | @@ -151,14 +192,35 @@ class TestMachine: |
2024 | assert machine.last_event_time['dhcp_release_end'] == 67890 |
2025 | |
2026 | @pytest.mark.asyncio |
2027 | - async def test_get_pxe_files_binaries(self, machine, subprocess_mock): |
2028 | + async def test_get_pxe_files_binaries( |
2029 | + self, machine, subprocess_mock, mocker): |
2030 | fake_curl = subprocess_mock.fake_processes['curl'] |
2031 | fake_dhclient = subprocess_mock.fake_processes['dhclient'] |
2032 | fake_dhclient.next_server = '4.3.2.1' |
2033 | fake_curl.ip_address = '4.3.2.1' |
2034 | + |
2035 | + mocker.patch.object(machine, 'process_preseed') |
2036 | + mocker.patch.object(machine, '_sanitize_url') |
2037 | + |
2038 | + class MockGetHTTPFile: |
2039 | + |
2040 | + async def __call__(self, *args, **kwargs): |
2041 | + return 200, "{}" |
2042 | + |
2043 | + mocker.patch.object( |
2044 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2045 | + |
2046 | + machine.metadata = Mock() |
2047 | + |
2048 | + async def _noop_request(*args, **kwargs): |
2049 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2050 | + |
2051 | + machine.metadata.request = _noop_request |
2052 | + |
2053 | await machine.create_pxe_interface() |
2054 | await machine.get_ip() |
2055 | await machine.get_pxe_files() |
2056 | + |
2057 | assert fake_curl.requested_urls[:2] == [ |
2058 | ('tftp://4.3.2.1/lpxelinux.0', 0), |
2059 | ('tftp://4.3.2.1/ldlinux.c32', 0), |
2060 | @@ -171,17 +233,35 @@ class TestMachine: |
2061 | fake_dhclient = subprocess_mock.fake_processes['dhclient'] |
2062 | fake_dhclient.next_server = '4.3.2.1' |
2063 | fake_curl.ip_address = '4.3.2.1' |
2064 | - machine_uuid = uuid.UUID('816fd0e0-0d52-cb11-877b-9c25109f8fba') |
2065 | - uuid_mock = mocker.patch.object(uuid, 'uuid1') |
2066 | - uuid_mock.side_effect = [machine_uuid] |
2067 | + machine.uuid = uuid.UUID('816fd0e0-0d52-cb11-877b-9c25109f8fba') |
2068 | + |
2069 | + mocker.patch.object(machine, 'process_preseed') |
2070 | + mocker.patch.object(machine, '_sanitize_url') |
2071 | + |
2072 | + class MockGetHTTPFile: |
2073 | + |
2074 | + async def __call__(self, *args, **kwargs): |
2075 | + return 200, "{}" |
2076 | + |
2077 | + mocker.patch.object( |
2078 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2079 | + |
2080 | + machine.metadata = Mock() |
2081 | + |
2082 | + async def _noop_request(*args, **kwargs): |
2083 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2084 | + |
2085 | + machine.metadata.request = _noop_request |
2086 | + |
2087 | await machine.create_pxe_interface() |
2088 | await machine.get_ip() |
2089 | await machine.get_pxe_files() |
2090 | - assert fake_curl.requested_urls[2:13] == [ |
2091 | + mac_route = machine.client_iface.mac_address.replace(':', '-') |
2092 | + expected_calls = [ |
2093 | ( |
2094 | 'tftp://4.3.2.1/pxelinux.cfg/' |
2095 | '816fd0e0-0d52-cb11-877b-9c25109f8fba', 68), |
2096 | - ('tftp://4.3.2.1/pxelinux.cfg/01-11-22-33-44-55-66', 68), |
2097 | + (f"tftp://4.3.2.1/pxelinux.cfg/01-{mac_route}", 68), |
2098 | ('tftp://4.3.2.1/pxelinux.cfg/02020202', 68), |
2099 | ('tftp://4.3.2.1/pxelinux.cfg/0202020', 68), |
2100 | ('tftp://4.3.2.1/pxelinux.cfg/020202', 68), |
2101 | @@ -192,10 +272,30 @@ class TestMachine: |
2102 | ('tftp://4.3.2.1/pxelinux.cfg/0', 68), |
2103 | ('tftp://4.3.2.1/pxelinux.cfg/default', 0), |
2104 | ] |
2105 | + for expected_call in expected_calls: |
2106 | + assert expected_call in fake_curl.requested_urls[2:13] |
2107 | |
2108 | @pytest.mark.asyncio |
2109 | async def test_get_pxe_files_event_pxelinux_success( |
2110 | - self, machine, subprocess_mock, event_loop): |
2111 | + self, machine, subprocess_mock, event_loop, mocker): |
2112 | + mocker.patch.object(machine, 'process_preseed') |
2113 | + mocker.patch.object(machine, '_sanitize_url') |
2114 | + |
2115 | + class MockGetHTTPFile: |
2116 | + |
2117 | + async def __call__(self, *args, **kwargs): |
2118 | + return 200, "{}" |
2119 | + |
2120 | + mocker.patch.object( |
2121 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2122 | + |
2123 | + machine.metadata = Mock() |
2124 | + |
2125 | + async def _noop_request(*args, **kwargs): |
2126 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2127 | + |
2128 | + machine.metadata.request = _noop_request |
2129 | + |
2130 | await machine.create_pxe_interface() |
2131 | await machine.get_ip() |
2132 | labels = {'scheme': 'tftp', 'filename': 'pxelinux.cfg', 'result': '0'} |
2133 | @@ -206,7 +306,25 @@ class TestMachine: |
2134 | |
2135 | @pytest.mark.asyncio |
2136 | async def test_get_pxe_files_event_pxelinux_failures( |
2137 | - self, machine, subprocess_mock, event_loop): |
2138 | + self, machine, subprocess_mock, event_loop, mocker): |
2139 | + mocker.patch.object(machine, 'process_preseed') |
2140 | + mocker.patch.object(machine, '_sanitize_url') |
2141 | + |
2142 | + class MockGetHTTPFile: |
2143 | + |
2144 | + async def __call__(self, *args, **kwargs): |
2145 | + return 200, "{}" |
2146 | + |
2147 | + mocker.patch.object( |
2148 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2149 | + |
2150 | + machine.metadata = Mock() |
2151 | + |
2152 | + async def _noop_request(*args, **kwargs): |
2153 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2154 | + |
2155 | + machine.metadata.request = _noop_request |
2156 | + |
2157 | await machine.create_pxe_interface() |
2158 | await machine.get_ip() |
2159 | labels = {'scheme': 'tftp', 'filename': 'pxelinux.cfg', 'result': '68'} |
2160 | @@ -217,9 +335,28 @@ class TestMachine: |
2161 | |
2162 | @pytest.mark.asyncio |
2163 | async def test_get_pxe_files_event_initrd_success( |
2164 | - self, machine, subprocess_mock, event_loop): |
2165 | + self, machine, subprocess_mock, event_loop, mocker): |
2166 | fake_curl = subprocess_mock.fake_processes['curl'] |
2167 | fake_curl.initrd_path = '/boot/my-initrd' |
2168 | + |
2169 | + mocker.patch.object(machine, 'process_preseed') |
2170 | + mocker.patch.object(machine, '_sanitize_url') |
2171 | + |
2172 | + class MockGetHTTPFile: |
2173 | + |
2174 | + async def __call__(self, *args, **kwargs): |
2175 | + return 200, "{}" |
2176 | + |
2177 | + mocker.patch.object( |
2178 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2179 | + |
2180 | + machine.metadata = Mock() |
2181 | + |
2182 | + async def _noop_request(*args, **kwargs): |
2183 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2184 | + |
2185 | + machine.metadata.request = _noop_request |
2186 | + |
2187 | await machine.create_pxe_interface() |
2188 | await machine.get_ip() |
2189 | labels = { |
2190 | @@ -240,6 +377,25 @@ class TestMachine: |
2191 | fake_curl.ip_address = '4.3.2.1' |
2192 | fake_curl.kernel_path = '/boot/my-kernel' |
2193 | fake_curl.initrd_path = '/boot/my-initrd' |
2194 | + |
2195 | + mocker.patch.object(machine, 'process_preseed') |
2196 | + mocker.patch.object(machine, '_sanitize_url') |
2197 | + |
2198 | + class MockGetHTTPFile: |
2199 | + |
2200 | + async def __call__(self, *args, **kwargs): |
2201 | + return 200, "{}" |
2202 | + |
2203 | + mocker.patch.object( |
2204 | + machine.network, 'get_http_file', new=MockGetHTTPFile()) |
2205 | + |
2206 | + machine.metadata = Mock() |
2207 | + |
2208 | + async def _noop_request(*args, **kwargs): |
2209 | + return "{\"metadata_url\": \"http://4.3.2.1/metadata\"}" |
2210 | + |
2211 | + machine.metadata.request = _noop_request |
2212 | + |
2213 | await machine.create_pxe_interface() |
2214 | await machine.get_ip() |
2215 | await machine.get_pxe_files() |
2216 | @@ -295,33 +451,59 @@ class TestMachineManager: |
2217 | (event_loop, 'eth0', 'fake0'), (event_loop, 'eth0', 'fake1'), |
2218 | (event_loop, 'eth0', 'fake2') |
2219 | ] |
2220 | + await manager.power_commands.join( |
2221 | + ) # cleanly close the queue MachineManager's run loop blocks on |
2222 | + manager.running = False # quit run loop |
2223 | await task |
2224 | - for machine in manager.machines: |
2225 | - assert machine.client_mac is not None |
2226 | + for machine in manager.machines.values(): |
2227 | + assert machine.client_iface is not None |
2228 | |
2229 | @pytest.mark.asyncio |
2230 | - async def test_run_creates_interface(self, subprocess_mock, event_loop): |
2231 | + async def test_run_creates_interface( |
2232 | + self, subprocess_mock, check_output_mock, event_loop): |
2233 | manager = MachineManager('eth0', 5) |
2234 | - await manager.init(event_loop) |
2235 | + task = manager.init(event_loop) |
2236 | + await manager.power_commands.join( |
2237 | + ) # cleanly close the queue MachineManager's run loop blocks on |
2238 | + manager.running = False # quit run loop |
2239 | + await task |
2240 | assert len(manager.machines) == 5 |
2241 | - for machine in manager.machines: |
2242 | - assert machine.client_mac is not None |
2243 | + for machine in manager.machines.values(): |
2244 | + assert machine.client_iface is not None |
2245 | |
2246 | @pytest.mark.asyncio |
2247 | - async def test_run_gets_ip(self, subprocess_mock, event_loop): |
2248 | + async def test_run_gets_ip( |
2249 | + self, subprocess_mock, check_output_mock, event_loop, mocker): |
2250 | number_of_machines = 5 |
2251 | + |
2252 | + task = None |
2253 | + |
2254 | + class MockGetIP: |
2255 | + called = False |
2256 | + call_count = 0 |
2257 | + |
2258 | + async def __call__(self, *args, **kwargs): |
2259 | + self.called = True |
2260 | + self.call_count += 1 |
2261 | + if self.call_count == number_of_machines: |
2262 | + task.cancel() |
2263 | + |
2264 | + mocker.patch.object(Machine, 'get_ip', new=MockGetIP()) |
2265 | manager = MachineManager('eth0', number_of_machines) |
2266 | - with track_metric('client_dhcp_latency_count', |
2267 | - {'type': 'request'}) as metric: |
2268 | - await manager.init(event_loop) |
2269 | + bmc = BMC(manager) |
2270 | + task = manager.init(event_loop) |
2271 | + await bmc.init(event_loop) |
2272 | + await manager.power_commands.join( |
2273 | + ) # cleanly close the queue MachineManager's run loop blocks on |
2274 | + await task |
2275 | assert len(manager.machines) == number_of_machines |
2276 | - for machine in manager.machines: |
2277 | - assert 'dhcp_request_end' in machine.last_event_time |
2278 | - assert metric.increase == number_of_machines * 1.0 |
2279 | + for machine in manager.machines.values(): |
2280 | + assert machine.get_ip.called |
2281 | |
2282 | @pytest.mark.asyncio |
2283 | async def test_run_logs_durations( |
2284 | - self, caplog, subprocess_mock, mocker, event_loop): |
2285 | + self, caplog, subprocess_mock, check_output_mock, mocker, |
2286 | + event_loop): |
2287 | manager = MachineManager('eth0', 5) |
2288 | with caplog.at_level(logging.INFO): |
2289 | now_mock = mocker.patch.object(machine_module, 'now') |
2290 | @@ -331,20 +513,24 @@ class TestMachineManager: |
2291 | 4, # Start of DHCP request |
2292 | 7, # End of DHCP request |
2293 | ] |
2294 | - await manager.init(event_loop) |
2295 | + task = manager.init(event_loop) |
2296 | + await manager.power_commands.join() |
2297 | + manager.running = False |
2298 | + await task |
2299 | |
2300 | log_entries = [ |
2301 | (entry.levelname, entry.message) for entry in caplog.records |
2302 | ] |
2303 | assert ('INFO', 'Created PXE interfaces in 1 seconds') in log_entries |
2304 | - assert ('INFO', 'Got IPs for all machines in 3 seconds') in log_entries |
2305 | |
2306 | @pytest.mark.asyncio |
2307 | async def test_run_handles_task_cancellation( |
2308 | - self, subprocess_mock, event_loop): |
2309 | + self, subprocess_mock, check_output_mock, event_loop): |
2310 | |
2311 | class CancellingMachine: |
2312 | + uuid = random.randint(1, 512) |
2313 | cancelled = False |
2314 | + last_event_time = {} |
2315 | |
2316 | async def create_pxe_interface(self): |
2317 | self.cancelled = True |
2318 | @@ -354,16 +540,17 @@ class TestMachineManager: |
2319 | cancelling_machine = CancellingMachine() |
2320 | task = manager.init(event_loop) |
2321 | # Inject a machine that simulates the async task being cancelled. |
2322 | - manager.machines.append(cancelling_machine) |
2323 | + manager.machines[cancelling_machine.uuid] = cancelling_machine |
2324 | await task |
2325 | # The async task was cancelled without any problems, and the |
2326 | # rest of the run() method was skipped. |
2327 | assert cancelling_machine.cancelled |
2328 | - for machine in manager.machines[:-1]: |
2329 | + for machine in manager.machines.values(): |
2330 | assert 'dhcp_request_start' not in machine.last_event_time |
2331 | |
2332 | @pytest.mark.asyncio |
2333 | - async def test_clean_up(self, subprocess_mock, event_loop): |
2334 | + async def test_clean_up( |
2335 | + self, subprocess_mock, check_output_mock, event_loop): |
2336 | |
2337 | class CleanUp: |
2338 | cleaned_up = False |
2339 | @@ -373,7 +560,10 @@ class TestMachineManager: |
2340 | |
2341 | clean_up = CleanUp() |
2342 | manager = MachineManager('eth0', 1) |
2343 | - await manager.init(event_loop) |
2344 | - manager.machines[0].cleanups.append(clean_up.run) |
2345 | + task = manager.init(event_loop) |
2346 | + await manager.power_commands.join() |
2347 | + manager.running = False |
2348 | + await task |
2349 | + list(manager.machines.values())[0].cleanups.append(clean_up.run) |
2350 | await manager.clean_up() |
2351 | assert clean_up.cleaned_up |
2352 | diff --git a/maasperformance/tests/test_network.py b/maasperformance/tests/test_network.py |
2353 | index 3c1db36..e60345d 100644 |
2354 | --- a/maasperformance/tests/test_network.py |
2355 | +++ b/maasperformance/tests/test_network.py |
2356 | @@ -1,8 +1,22 @@ |
2357 | +from unittest.mock import Mock |
2358 | + |
2359 | +import aiohttp |
2360 | from netaddr import IPAddress |
2361 | +import pytest |
2362 | |
2363 | -from ..network import get_source_address |
2364 | +from ..network import ( |
2365 | + get_source_address, |
2366 | + MetadataClient, |
2367 | + NetworkClient, |
2368 | + PlaintextSignature, |
2369 | + register_machine, |
2370 | +) |
2371 | +from ..process import ProcessError |
2372 | +from ..testing.fixtures import create_subprocess_mock |
2373 | from ..testing.network import fake_socket |
2374 | |
2375 | +subprocess_mock = create_subprocess_mock() |
2376 | + |
2377 | |
2378 | class TestGetSourceaddress: |
2379 | |
2380 | @@ -20,3 +34,219 @@ class TestGetSourceaddress: |
2381 | own_ip = get_source_address( |
2382 | IPAddress('1200:0000:AB00:1234:0000:2552:7777:1313')) |
2383 | assert own_ip == IPAddress(sock.ipv6_host) |
2384 | + |
2385 | + |
2386 | +class TestNetworkClient: |
2387 | + |
2388 | + @pytest.mark.asyncio |
2389 | + async def test_get_curl_file(self, subprocess_mock): |
2390 | + machine = Mock() |
2391 | + machine.last_event_time = {} |
2392 | + client = NetworkClient(machine) |
2393 | + try: |
2394 | + await client.get_curl_file("http://foo/") |
2395 | + except ProcessError: |
2396 | + pass |
2397 | + assert ( |
2398 | + "curl", "-o", "/dev/null", "-s", |
2399 | + "http://foo/") in subprocess_mock.calls |
2400 | + |
2401 | + @pytest.mark.asyncio |
2402 | + async def test_get_json_file(self, mocker): |
2403 | + machine = Mock() |
2404 | + machine.last_event_time = {} |
2405 | + client = NetworkClient(machine) |
2406 | + |
2407 | + class MockGetHTTPFile: |
2408 | + args = None |
2409 | + kwargs = None |
2410 | + |
2411 | + async def __call__(self, *args, **kwargs): |
2412 | + self.args = args |
2413 | + self.kwargs = kwargs |
2414 | + return 200, "{\"foo\": \"bar\"}" |
2415 | + |
2416 | + mock_get_http_file = MockGetHTTPFile() |
2417 | + mocker.patch.object(client, "get_http_file", new=mock_get_http_file) |
2418 | + status, result = await client.get_json_file("http://foo/") |
2419 | + assert mock_get_http_file.args == ("http://foo/", ) |
2420 | + assert mock_get_http_file.kwargs == { |
2421 | + "headers": { |
2422 | + "Accept": "application/json" |
2423 | + } |
2424 | + } |
2425 | + assert status == 200 |
2426 | + assert isinstance(result, dict) |
2427 | + |
2428 | + @pytest.mark.asyncio |
2429 | + async def test_get_tftp_file(self, subprocess_mock): |
2430 | + machine = Mock() |
2431 | + machine.last_event_time = {} |
2432 | + machine.next_server = "foo" |
2433 | + client = NetworkClient(machine) |
2434 | + try: |
2435 | + await client.get_tftp_file("/bar") |
2436 | + except ProcessError: |
2437 | + pass |
2438 | + assert ( |
2439 | + "curl", "-o", "/dev/null", "-s", |
2440 | + "tftp://foo/bar") in subprocess_mock.calls |
2441 | + |
2442 | + |
2443 | +class TestPlaintextSignature: |
2444 | + |
2445 | + def test_sign(self): |
2446 | + sig = PlaintextSignature() |
2447 | + key = sig.sign("foo", "GET", "http://bar/", "secret") |
2448 | + assert key == "foo&secret" |
2449 | + |
2450 | + |
2451 | +class MockableRequest: |
2452 | + |
2453 | + def mock_request(self, client, mocker, response="", status=None): |
2454 | + |
2455 | + class MockRequest: |
2456 | + args = None |
2457 | + kwargs = None |
2458 | + |
2459 | + async def __call__(self, *args, **kwargs): |
2460 | + self.args = args |
2461 | + self.kwargs = kwargs |
2462 | + if status: |
2463 | + return status, response |
2464 | + return response |
2465 | + |
2466 | + mock_request = MockRequest() |
2467 | + mocker.patch.object(client, "request", new=mock_request) |
2468 | + |
2469 | + return mock_request |
2470 | + |
2471 | + |
2472 | +class TestMetadataClient(MockableRequest): |
2473 | + |
2474 | + def test_authenticated_False_without_consumer_key(self): |
2475 | + client = MetadataClient(Mock(), {"metadata_url": "http://foo/"}) |
2476 | + assert client.authenticated is False |
2477 | + |
2478 | + def test_authenticated_True_with_consumer_key(self): |
2479 | + client = MetadataClient( |
2480 | + Mock(), { |
2481 | + "metadata_url": "http://foo/", |
2482 | + "consumer_key": "bar" |
2483 | + }) |
2484 | + assert client.authenticated |
2485 | + |
2486 | + @pytest.mark.asyncio |
2487 | + async def test_signal(self, mocker): |
2488 | + network = Mock() |
2489 | + client = MetadataClient( |
2490 | + network, { |
2491 | + "metadata_url": "http://foo/", |
2492 | + "consumer_key": "bar" |
2493 | + }) |
2494 | + |
2495 | + mock_request = self.mock_request(client, mocker) |
2496 | + |
2497 | + await client.signal("TESTING") |
2498 | + |
2499 | + assert mock_request.args == ("post", "") |
2500 | + expected_data = aiohttp.FormData() |
2501 | + expected_data.add_field("op", "signal") |
2502 | + expected_data.add_field("status", "TESTING") |
2503 | + assert mock_request.kwargs["data"]._fields == expected_data._fields |
2504 | + |
2505 | + @pytest.mark.asyncio |
2506 | + async def test_netboot_off(self, mocker): |
2507 | + network = Mock() |
2508 | + client = MetadataClient( |
2509 | + network, { |
2510 | + "metadata_url": "http://foo/", |
2511 | + "consumer_key": "bar" |
2512 | + }) |
2513 | + |
2514 | + mock_request = self.mock_request(client, mocker) |
2515 | + |
2516 | + await client.netboot_off() |
2517 | + |
2518 | + expected_data = aiohttp.FormData() |
2519 | + expected_data.add_field("op", "netboot_off") |
2520 | + assert mock_request.args == ("post", "") |
2521 | + assert mock_request.kwargs["data"]._fields == expected_data._fields |
2522 | + |
2523 | + @pytest.mark.asyncio |
2524 | + async def test_get_scripts_index_no_response(self, mocker): |
2525 | + network = Mock() |
2526 | + client = MetadataClient( |
2527 | + network, { |
2528 | + "metadata_url": "http://foo/", |
2529 | + "consumer_key": "bar" |
2530 | + }) |
2531 | + |
2532 | + self.mock_request(client, mocker) |
2533 | + |
2534 | + result = await client.get_scripts_index() |
2535 | + assert result == {} |
2536 | + |
2537 | + @pytest.mark.asyncio |
2538 | + async def test_get_scripts_index_tar_file_response( |
2539 | + self, mocker, tar_file_http_response): |
2540 | + network = Mock() |
2541 | + client = MetadataClient( |
2542 | + network, { |
2543 | + "metadata_url": "http://foo/", |
2544 | + "consumer_key": "bar" |
2545 | + }) |
2546 | + |
2547 | + self.mock_request(client, mocker, response=tar_file_http_response) |
2548 | + |
2549 | + result = await client.get_scripts_index() |
2550 | + assert result == {"foo": "bar"} |
2551 | + |
2552 | + def test_get_endpoint_full_url(self): |
2553 | + network = Mock() |
2554 | + client = MetadataClient( |
2555 | + network, { |
2556 | + "metadata_url": "http://foo/", |
2557 | + "consumer_key": "bar" |
2558 | + }) |
2559 | + url = client._get_endpoint_full_url("http://foo/") |
2560 | + assert url == "http://foo/2012-03-01/" |
2561 | + |
2562 | + |
2563 | +class TestRegisterMachine(MockableRequest): |
2564 | + |
2565 | + async def test_register_machine(self, mocker): |
2566 | + network = NetworkClient(Mock()) |
2567 | + |
2568 | + mock_request = self.mock_request(network, mocker, status=200) |
2569 | + |
2570 | + api_url = "http://foo/" |
2571 | + hostname = "test.hostname" |
2572 | + architecture = "amd64" |
2573 | + subarchitecture = "generic" |
2574 | + power_type = "ipmi" |
2575 | + power_parameters = { |
2576 | + "address": "1.1.1.1", |
2577 | + "username": "foo", |
2578 | + "password": "bar", |
2579 | + } |
2580 | + mac_address = "11:22:33:44:55" |
2581 | + headers = {"Accept": "application/json"} |
2582 | + |
2583 | + await register_machine( |
2584 | + network, api_url, hostname, architecture, subarchitecture, |
2585 | + power_type, power_parameters, mac_address) |
2586 | + |
2587 | + expected_data = aiohttp.FormData() |
2588 | + expected_data.add_field("hostname", hostname) |
2589 | + expected_data.add_field("architecture", architecture) |
2590 | + expected_data.add_field("subarchitecture", subarchitecture) |
2591 | + expected_data.add_field("mac_addresses", mac_address) |
2592 | + expected_data.add_field("commission", "true") |
2593 | + expected_data.add_field("power_type", power_type) |
2594 | + for key, value in power_parameters.items(): |
2595 | + expected_data.add_field("power_parameters_" + key, value) |
2596 | + |
2597 | + assert mock_request.args == ("post", api_url + "machines/") |
2598 | + assert mock_request.kwargs["headers"] == headers |
2599 | + assert mock_request.kwargs["data"]._fields == expected_data._fields |
2600 | diff --git a/maasperformance/tests/test_network_interface.py b/maasperformance/tests/test_network_interface.py |
2601 | index 0e55d91..bd525e4 100644 |
2602 | --- a/maasperformance/tests/test_network_interface.py |
2603 | +++ b/maasperformance/tests/test_network_interface.py |
2604 | @@ -2,7 +2,7 @@ import json |
2605 | |
2606 | import pytest |
2607 | |
2608 | -from .. import network_interface as network_interface_module |
2609 | +from .. import process |
2610 | from ..network_interface import ( |
2611 | generate_mac_address, |
2612 | interface_is_bridge, |
2613 | @@ -29,12 +29,11 @@ class TestInterfaceIsBridge: |
2614 | (IP_OUTPUT_VETH, 'veth0', False), |
2615 | ]) |
2616 | def test_interface_is_bridge(self, mocker, output, iface, is_bridge): |
2617 | - mock_check_output = mocker.patch.object( |
2618 | - network_interface_module, 'check_output') |
2619 | + mock_check_output = mocker.patch.object(process, 'check_output') |
2620 | mock_check_output.return_value = json.dumps(output) |
2621 | assert interface_is_bridge(iface) == is_bridge |
2622 | mock_check_output.assert_called_once_with( |
2623 | - ['ip', '--json', '-d', 'link', 'show', iface]) |
2624 | + ('ip', '--json', '-d', 'link', 'show', iface)) |
2625 | |
2626 | |
2627 | class TestGenerateMacAddress: |
2628 | diff --git a/maasperformance/tests/test_web.py b/maasperformance/tests/test_web.py |
2629 | index fdbee5d..aa408ba 100644 |
2630 | --- a/maasperformance/tests/test_web.py |
2631 | +++ b/maasperformance/tests/test_web.py |
2632 | @@ -2,7 +2,12 @@ import asyncio |
2633 | |
2634 | import prometheus_client |
2635 | |
2636 | +from .. import process |
2637 | from ..server import parse_args |
2638 | +from ..testing.fixtures import ( |
2639 | + create_check_output_mock, |
2640 | + create_subprocess_mock, |
2641 | +) |
2642 | from ..testing.prometheus import ( |
2643 | create_promreg_app, |
2644 | PromRegHandlers, |
2645 | @@ -14,22 +19,58 @@ from ..web import ( |
2646 | start_machines_loop, |
2647 | ) |
2648 | |
2649 | - |
2650 | -def create_app(port=1234, iface='eth0', number=1): |
2651 | +subprocess_mock = create_subprocess_mock(module=asyncio) |
2652 | +check_output_mock = create_check_output_mock( |
2653 | + module=process, |
2654 | + output=""" |
2655 | +[ |
2656 | + { |
2657 | + "ifindex":1, |
2658 | + "ifname":"parent0", |
2659 | + "flags":["BROADCAST","MULTICAST","UP","LOWER_UP"], |
2660 | + "mtu":1500, |
2661 | + "qdisc":"mq", |
2662 | + "operstate":"UP", |
2663 | + "linkmode":"DEFAULT", |
2664 | + "group":"default", |
2665 | + "txqlen":1000, |
2666 | + "link_type":"ether", |
2667 | + "address":"80:61:5f:08:fc:16", |
2668 | + "broadcast":"ff:ff:ff:ff:ff:ff", |
2669 | + "promiscuity":0, |
2670 | + "min_mtu":68, |
2671 | + "max_mtu":9710, |
2672 | + "inet6_addr_gen_mode":"none", |
2673 | + "num_tx_queues":64, |
2674 | + "num_rx_queues":64, |
2675 | + "gso_max_size":65536, |
2676 | + "gso_max_segs":65535, |
2677 | + "parentbus":"pci", |
2678 | + "parentdev":"0000:01:00.0", |
2679 | + "vfinfo_list":[] |
2680 | + } |
2681 | +] |
2682 | +""") |
2683 | + |
2684 | + |
2685 | +def create_app(port=1234, iface='eth0', number=1, loop=None): |
2686 | args = parse_args(['--port', str(port), '--number', str(number), iface]) |
2687 | - return create_web_app(args) |
2688 | + return create_web_app(args, loop=loop) |
2689 | |
2690 | |
2691 | class TestStatus: |
2692 | |
2693 | - async def test_status(self, create_subprocess_mock, aiohttp_client): |
2694 | - app = create_app(number=2) |
2695 | + async def test_status( |
2696 | + self, subprocess_mock, aiohttp_client, check_output_mock, |
2697 | + event_loop): |
2698 | + app = create_app(number=2, loop=event_loop) |
2699 | client = await aiohttp_client(app) |
2700 | + app['machine-manager'].running = False |
2701 | await app['machine-manager'].finished |
2702 | resp = await client.get('/status') |
2703 | assert resp.status == 200 |
2704 | contents = await resp.json() |
2705 | - machines = app['machine-manager'].machines |
2706 | + machines = list(app['machine-manager'].machines.values()) |
2707 | assert contents == [ |
2708 | machines[0].get_status(), |
2709 | machines[1].get_status(), |
2710 | @@ -38,7 +79,8 @@ class TestStatus: |
2711 | |
2712 | class TestMetrics: |
2713 | |
2714 | - async def test_content_type(self, create_subprocess_mock, aiohttp_client): |
2715 | + async def test_content_type( |
2716 | + self, subprocess_mock, check_output_mock, aiohttp_client): |
2717 | app = create_app(number=2) |
2718 | client = await aiohttp_client(app) |
2719 | resp = await client.get('/metrics') |
2720 | @@ -48,7 +90,7 @@ class TestMetrics: |
2721 | prometheus_client.CONTENT_TYPE_LATEST) |
2722 | |
2723 | async def test_defined_metrics( |
2724 | - self, create_subprocess_mock, aiohttp_client): |
2725 | + self, subprocess_mock, check_output_mock, aiohttp_client): |
2726 | app = create_app(number=2) |
2727 | client = await aiohttp_client(app) |
2728 | resp = await client.get('/metrics') |
2729 | @@ -58,53 +100,56 @@ class TestMetrics: |
2730 | |
2731 | class TestCreateWebApp: |
2732 | |
2733 | - def test_create_web_app_sets_port(self): |
2734 | + def test_create_web_app_sets_port(self, check_output_mock): |
2735 | args = parse_args(['--port', '1234', '--number', '5', 'myif0']) |
2736 | app = create_web_app(args) |
2737 | assert app['port'] == '1234' |
2738 | |
2739 | - def test_create_web_app_creates_machine_manager(self): |
2740 | + def test_create_web_app_creates_machine_manager(self, check_output_mock): |
2741 | args = parse_args(['--port', '1234', '--number', '5', 'parent0']) |
2742 | app = create_web_app(args) |
2743 | manager = app['machine-manager'] |
2744 | |
2745 | - assert manager.parent_iface == 'parent0' |
2746 | + assert manager.parent_iface.name == 'parent0' |
2747 | assert manager.number_of_machines == 5 |
2748 | # We expect an empty machine list, since create_web_app() |
2749 | # shouldn't call manager.init() directly. |
2750 | assert manager.machines == [] |
2751 | |
2752 | async def test_create_web_app_starts_manager_on_startup( |
2753 | - self, create_subprocess_mock, loop): |
2754 | + self, subprocess_mock, check_output_mock, loop): |
2755 | args = parse_args(['--port', '1234', '--number', '5', 'parent0']) |
2756 | app = create_web_app(args, loop=loop) |
2757 | for startup_func in app.on_startup: |
2758 | await startup_func(app) |
2759 | + manager = app['machine-manager'] |
2760 | + manager.running = False |
2761 | await app['manager-task'] |
2762 | |
2763 | - manager = app['machine-manager'] |
2764 | assert len(manager.machines) == 5 |
2765 | - for machine in manager.machines: |
2766 | - assert machine.client_mac is not None |
2767 | + for machine in manager.machines.values(): |
2768 | + assert machine.client_iface is not None |
2769 | |
2770 | async def test_create_web_app_cleans_machines_on_cleanup( |
2771 | - self, create_subprocess_mock, loop): |
2772 | + self, subprocess_mock, check_output_mock, event_loop): |
2773 | args = parse_args(['--port', '1234', '--number', '5', 'parent0']) |
2774 | - app = create_web_app(args, loop=loop) |
2775 | + app = create_web_app(args, loop=event_loop) |
2776 | + manager = app['machine-manager'] |
2777 | for startup_func in app.on_startup: |
2778 | await startup_func(app) |
2779 | + manager.running = False |
2780 | await app['manager-task'] |
2781 | for cleanup_func in app.on_cleanup: |
2782 | await cleanup_func(app) |
2783 | |
2784 | - manager = app['machine-manager'] |
2785 | assert len(manager.machines) == 5 |
2786 | - for machine in manager.machines: |
2787 | - cleanup_cmd = ('ip', 'link', 'del', machine.client_iface) |
2788 | - assert cleanup_cmd in create_subprocess_mock.calls |
2789 | + for machine in manager.machines.values(): |
2790 | + cleanup_cmd = ('ip', 'link', 'del', machine.client_iface.name) |
2791 | + assert cleanup_cmd in subprocess_mock.calls |
2792 | |
2793 | async def test_registers_promreg_on_startup( |
2794 | - self, create_subprocess_mock, loop, aiohttp_server): |
2795 | + self, subprocess_mock, check_output_mock, event_loop, |
2796 | + aiohttp_server): |
2797 | handlers = PromRegHandlers('my-token') |
2798 | server = await aiohttp_server(create_promreg_app(handlers)) |
2799 | args = parse_args( |
2800 | @@ -113,15 +158,20 @@ class TestCreateWebApp: |
2801 | str(server.make_url('/')), '--promreg-token', 'my-token', |
2802 | '--port', '1234', 'parent0' |
2803 | ]) |
2804 | - app = create_web_app(args, loop=loop) |
2805 | + app = create_web_app(args, loop=event_loop) |
2806 | for startup_func in app.on_startup: |
2807 | await startup_func(app) |
2808 | - await app['manager-task'] |
2809 | + app['manager-task'].cancel() |
2810 | + try: |
2811 | + await app['manager-task'] |
2812 | + except asyncio.exceptions.CancelError: |
2813 | + pass |
2814 | |
2815 | assert (list(handlers.targets.keys()) == ['127.0.0.1:1234']) |
2816 | |
2817 | async def test_registers_promreg_on_startup_already_registered( |
2818 | - self, create_subprocess_mock, loop, aiohttp_server): |
2819 | + self, subprocess_mock, check_output_mock, event_loop, |
2820 | + aiohttp_server): |
2821 | handlers = PromRegHandlers('my-token') |
2822 | server = await aiohttp_server(create_promreg_app(handlers)) |
2823 | args = parse_args( |
2824 | @@ -130,16 +180,21 @@ class TestCreateWebApp: |
2825 | str(server.make_url('/')), '--promreg-token', 'my-token', |
2826 | '--port', '1234', 'parent0' |
2827 | ]) |
2828 | - app = create_web_app(args, loop=loop) |
2829 | + app = create_web_app(args, loop=event_loop) |
2830 | handlers.targets['127.0.0.1:1234'] = PromRegTarget() |
2831 | for startup_func in app.on_startup: |
2832 | await startup_func(app) |
2833 | - await app['manager-task'] |
2834 | + app['manager-task'].cancel() |
2835 | + try: |
2836 | + await app['manager-task'] |
2837 | + except asyncio.exceptions.CancelError: |
2838 | + pass |
2839 | |
2840 | assert (list(handlers.targets.keys()) == ['127.0.0.1:1234']) |
2841 | |
2842 | async def test_unregisters_promreg_on_shutdown( |
2843 | - self, create_subprocess_mock, loop, aiohttp_server): |
2844 | + self, subprocess_mock, check_output_mock, event_loop, |
2845 | + aiohttp_server): |
2846 | handlers = PromRegHandlers('my-token') |
2847 | server = await aiohttp_server(create_promreg_app(handlers)) |
2848 | args = parse_args( |
2849 | @@ -148,13 +203,17 @@ class TestCreateWebApp: |
2850 | str(server.make_url('/')), '--promreg-token', 'my-token', |
2851 | '--port', '1234', 'parent0' |
2852 | ]) |
2853 | - app = create_web_app(args, loop=loop) |
2854 | + app = create_web_app(args, loop=event_loop) |
2855 | handlers.targets['127.0.0.1:1234'] = PromRegTarget() |
2856 | for startup_func in app.on_startup: |
2857 | await startup_func(app) |
2858 | for cleanup_func in app.on_cleanup: |
2859 | await cleanup_func(app) |
2860 | - await app['manager-task'] |
2861 | + app['manager-task'].cancel() |
2862 | + try: |
2863 | + await app['manager-task'] |
2864 | + except asyncio.exceptions.CancelledError: |
2865 | + pass |
2866 | |
2867 | assert list(handlers.targets.keys()) == [] |
2868 | |
2869 | @@ -162,53 +221,61 @@ class TestCreateWebApp: |
2870 | class TestStartMachinesLoop: |
2871 | |
2872 | async def test_start_machines_loop_inits_manager( |
2873 | - self, create_subprocess_mock, loop): |
2874 | + self, subprocess_mock, event_loop, check_output_mock): |
2875 | args = parse_args(['--port', '1234', '--number', '5', 'parent0']) |
2876 | - app = create_web_app(args, loop=loop) |
2877 | + app = create_web_app(args, loop=event_loop) |
2878 | |
2879 | await start_machines_loop(app) |
2880 | manager = app['machine-manager'] |
2881 | assert len(manager.machines) == 5 |
2882 | - for machine in manager.machines: |
2883 | - assert machine.loop is loop |
2884 | - assert machine.client_mac is None |
2885 | + for machine in manager.machines.values(): |
2886 | + assert machine.loop is event_loop |
2887 | + assert machine.client_iface is not None |
2888 | |
2889 | assert not app['manager-task'].done() |
2890 | - await app['manager-task'] |
2891 | - for machine in manager.machines: |
2892 | - assert machine.client_mac is not None |
2893 | + app['manager-task'].cancel() |
2894 | + try: |
2895 | + await app['manager-task'] |
2896 | + except asyncio.exceptions.CancelledError: |
2897 | + pass |
2898 | + for machine in manager.machines.values(): |
2899 | + assert machine.client_iface is not None |
2900 | |
2901 | |
2902 | class TestCleanUpMachines: |
2903 | |
2904 | - async def test_cleans_up_machines(self, create_subprocess_mock, loop): |
2905 | + async def test_cleans_up_machines( |
2906 | + self, subprocess_mock, event_loop, check_output_mock): |
2907 | args = parse_args(['--port', '1234', '--number', '5', 'parent0']) |
2908 | - app = create_web_app(args, loop=loop) |
2909 | + app = create_web_app(args, loop=event_loop) |
2910 | |
2911 | await start_machines_loop(app) |
2912 | - await app['manager-task'] |
2913 | + await app['bmc-task'] |
2914 | await clean_up_machines(app) |
2915 | |
2916 | + assert app['manager-task'].done() |
2917 | + |
2918 | manager = app['machine-manager'] |
2919 | assert len(manager.machines) == 5 |
2920 | - for machine in manager.machines: |
2921 | - cleanup_cmd = ('ip', 'link', 'del', machine.client_iface) |
2922 | - assert cleanup_cmd in create_subprocess_mock.calls |
2923 | + for machine in manager.machines.values(): |
2924 | + cleanup_cmd = ('ip', 'link', 'del', machine.client_iface.name) |
2925 | + assert cleanup_cmd in subprocess_mock.calls |
2926 | |
2927 | - async def test_cancels_manager_task(self, create_subprocess_mock, loop): |
2928 | + async def test_cancels_manager_task( |
2929 | + self, subprocess_mock, event_loop, check_output_mock): |
2930 | |
2931 | async def stall(): |
2932 | machine.cleanups.append(machine.delete_pxe_interface) |
2933 | stall_point.set_result('there') |
2934 | - await asyncio.sleep(100000, loop=loop) |
2935 | + await asyncio.sleep(100000, loop=event_loop) |
2936 | |
2937 | args = parse_args(['--port', '1234', '--number', '1', 'parent0']) |
2938 | - app = create_web_app(args, loop=loop) |
2939 | + app = create_web_app(args, loop=event_loop) |
2940 | manager = app['machine-manager'] |
2941 | |
2942 | await start_machines_loop(app) |
2943 | stall_point = asyncio.Future() |
2944 | - machine = manager.machines[0] |
2945 | + machine = list(manager.machines.values())[0] |
2946 | # Simulate that the run() method takes a long time, and allow us |
2947 | # to run clean_up_machines methods while the task is not yet |
2948 | # complete. |
2949 | @@ -224,6 +291,6 @@ class TestCleanUpMachines: |
2950 | assert app['manager-task'].done() |
2951 | |
2952 | assert len(manager.machines) == 1 |
2953 | - for machine in manager.machines: |
2954 | - cleanup_cmd = ('ip', 'link', 'del', machine.client_iface) |
2955 | - assert cleanup_cmd in create_subprocess_mock.calls |
2956 | + for machine in manager.machines.values(): |
2957 | + cleanup_cmd = ('ip', 'link', 'del', machine.client_iface.name) |
2958 | + assert cleanup_cmd in subprocess_mock.calls |
2959 | diff --git a/maasperformance/web.py b/maasperformance/web.py |
2960 | index f3df5c6..38d352d 100644 |
2961 | --- a/maasperformance/web.py |
2962 | +++ b/maasperformance/web.py |
2963 | @@ -1,3 +1,4 @@ |
2964 | +from asyncio.exceptions import CancelledError |
2965 | import logging |
2966 | import time |
2967 | from urllib.parse import urlparse |
2968 | @@ -52,7 +53,10 @@ async def clean_up_machines(app): |
2969 | logging.info('Cancelling the manager task') |
2970 | app['manager-task'].cancel() |
2971 | logging.info('Awaiting the cancel...') |
2972 | - await app['manager-task'] |
2973 | + try: |
2974 | + await app['manager-task'] |
2975 | + except CancelledError: |
2976 | + pass |
2977 | logging.info('done.') |
2978 | await manager.clean_up() |
2979 | duration = time.time() - start |
2980 | diff --git a/pyproject.toml b/pyproject.toml |
2981 | new file mode 100644 |
2982 | index 0000000..3ac0a51 |
2983 | --- /dev/null |
2984 | +++ b/pyproject.toml |
2985 | @@ -0,0 +1,3 @@ |
2986 | +[tool.pytest.ini_options] |
2987 | +asyncio_mode = "auto" |
2988 | +addopts = "--cov-fail-under=90" |
2989 | diff --git a/requirements.txt b/requirements.txt |
2990 | index 7ece240..eb73fc9 100644 |
2991 | --- a/requirements.txt |
2992 | +++ b/requirements.txt |
2993 | @@ -1,26 +1,27 @@ |
2994 | -aioauth-client==0.25.8 |
2995 | +aioauth-client==0.27.3 |
2996 | aiodns==3.0.0 |
2997 | -aiofiles==0.7.0 |
2998 | -aiohttp==3.7.4.post0 |
2999 | -anyio==3.1.0 |
3000 | -async-timeout==3.0.1 |
3001 | -attrs==21.2.0 |
3002 | +aiofiles==22.1.0 |
3003 | +aiohttp==3.8.1 |
3004 | +aiosignal==1.2.0 |
3005 | +anyio==3.6.1 |
3006 | +async-timeout==4.0.2 |
3007 | +attrs==22.1.0 |
3008 | cchardet==2.1.7 |
3009 | -certifi==2021.5.30 |
3010 | -cffi==1.14.5 |
3011 | -chardet==4.0.0 |
3012 | +certifi==2022.6.15.1 |
3013 | +cffi==1.15.1 |
3014 | +charset-normalizer==2.1.1 |
3015 | +frozenlist==1.3.1 |
3016 | h11==0.12.0 |
3017 | -httpcore==0.13.4 |
3018 | -httpx==0.18.1 |
3019 | -idna==3.2 |
3020 | -multidict==5.1.0 |
3021 | +httpcore==0.15.0 |
3022 | +httpx==0.23.0 |
3023 | +idna==3.3 |
3024 | +multidict==6.0.2 |
3025 | netaddr==0.8.0 |
3026 | -prometheus-client==0.11.0 |
3027 | -pycares==4.0.0 |
3028 | -pycparser==2.20 |
3029 | -PyYAML==5.4.1 |
3030 | +prometheus-client==0.14.1 |
3031 | +pycares==4.2.2 |
3032 | +pycparser==2.21 |
3033 | +PyYAML==6.0 |
3034 | rfc3986==1.5.0 |
3035 | -sniffio==1.2.0 |
3036 | -typing-extensions==3.10.0.0 |
3037 | -uvloop==0.15.2 |
3038 | -yarl==1.6.3 |
3039 | +sniffio==1.3.0 |
3040 | +uvloop==0.16.0 |
3041 | +yarl==1.8.1 |
UNIT TESTS for_latest_ maas lp:~cgrabowski/maas/+git/maas-performance into -b 3.0-rework lp:~maas-committers/maas/+git/maas-performance
-b update_
STATUS: FAILED maas-ci. internal: 8080/job/ maas-performanc e-tester/ 7/consoleText b09b6525a531d1a 2dce781c93
LOG: http://
COMMIT: 36d139f78c83b37