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