Merge ~bjornt/maas:power-driver-metrics-3.2 into maas:3.2
- Git
- lp:~bjornt/maas
- power-driver-metrics-3.2
- Merge into 3.2
Status: | Merged |
---|---|
Approved by: | Björn Tillenius |
Approved revision: | f3f7d9906acff90e81b3ae9ece6a0e2f171eb1a4 |
Merge reported by: | MAAS Lander |
Merged at revision: | not available |
Proposed branch: | ~bjornt/maas:power-driver-metrics-3.2 |
Merge into: | maas:3.2 |
Diff against target: |
542 lines (+215/-38) 14 files modified
src/maasserver/forms/__init__.py (+1/-1) src/maasserver/forms/tests/test_controller.py (+1/-0) src/maasserver/forms/tests/test_machine.py (+1/-1) src/maasserver/migrations/maasserver/0276_bmc_autodetect_metric.py (+27/-0) src/maasserver/models/bmc.py (+8/-0) src/maasserver/models/node.py (+20/-14) src/maasserver/models/tests/test_node.py (+73/-7) src/maasserver/rpc/tests/test_nodes.py (+6/-7) src/maasserver/rpc/tests/test_regionservice_calls.py (+2/-2) src/maasserver/stats.py (+27/-0) src/maasserver/testing/factory.py (+2/-4) src/maasserver/tests/test_stats.py (+42/-0) src/metadataserver/api.py (+3/-1) src/metadataserver/tests/test_api.py (+2/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Approve | ||
Björn Tillenius | Approve | ||
Review via email: mp+423940@code.launchpad.net |
Commit message
Remove unused code from Node.set_
There was code that checked for self.bmc, even though it couldn't really
be None, except for in unit tests.
A lot of tests passed in "" as the power type, expecting to get a node
without a BMC configured. But in reality, that created a BMC with power
type "", which is invalid.
(cherry picked from commit eb055f00437974e
Add stats for power drivers.
Also added an attribute to BMC, so that we can track whether the BMC was
auto-detected in commissioning, or whether the user created it manually.
(cherry picked from commit ab360b09b24e750
Description of the change
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b power-driver-
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b power-driver-
STATUS: FAILED
LOG: http://
COMMIT: 652a123f80edcdf
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b power-driver-
STATUS: FAILED BUILD
LOG: http://
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b power-driver-
STATUS: SUCCESS
COMMIT: f3f7d9906acff90
MAAS Lander (maas-lander) wrote : | # |
LANDING
-b power-driver-
STATUS: FAILED BUILD
LOG: http://
Preview Diff
1 | diff --git a/src/maasserver/forms/__init__.py b/src/maasserver/forms/__init__.py |
2 | index fa8983d..f77e344 100644 |
3 | --- a/src/maasserver/forms/__init__.py |
4 | +++ b/src/maasserver/forms/__init__.py |
5 | @@ -385,7 +385,7 @@ class WithPowerTypeMixin: |
6 | power_parameters["certificate"] = cert.certificate_pem() |
7 | power_parameters["key"] = cert.private_key_pem() |
8 | |
9 | - if type_changed or initial_parameters: |
10 | + if type_changed or initial_parameters and power_type: |
11 | machine.set_power_config(power_type, power_parameters) |
12 | |
13 | def clean(self): |
14 | diff --git a/src/maasserver/forms/tests/test_controller.py b/src/maasserver/forms/tests/test_controller.py |
15 | index 904501f..61385f7 100644 |
16 | --- a/src/maasserver/forms/tests/test_controller.py |
17 | +++ b/src/maasserver/forms/tests/test_controller.py |
18 | @@ -55,6 +55,7 @@ class TestControllerForm(MAASServerTestCase): |
19 | power_parameters_field = factory.make_string() |
20 | form = ControllerForm( |
21 | data={ |
22 | + "power_type": "ipmi", |
23 | "power_parameters_field": power_parameters_field, |
24 | "power_parameters_skip_check": "true", |
25 | }, |
26 | diff --git a/src/maasserver/forms/tests/test_machine.py b/src/maasserver/forms/tests/test_machine.py |
27 | index cb7d6f2..089587b 100644 |
28 | --- a/src/maasserver/forms/tests/test_machine.py |
29 | +++ b/src/maasserver/forms/tests/test_machine.py |
30 | @@ -572,7 +572,7 @@ class TestAdminMachineForm(MAASServerTestCase): |
31 | self.assertEqual(power_type, node.power_type) |
32 | |
33 | def test_AdminMachineForm_needs_interface_for_power_types(self): |
34 | - node = factory.make_Node(power_type="") |
35 | + node = factory.make_Node(power_type=None) |
36 | arch = make_usable_architecture(self) |
37 | form = AdminMachineForm( |
38 | data={ |
39 | diff --git a/src/maasserver/migrations/maasserver/0276_bmc_autodetect_metric.py b/src/maasserver/migrations/maasserver/0276_bmc_autodetect_metric.py |
40 | new file mode 100644 |
41 | index 0000000..d9ac5a0 |
42 | --- /dev/null |
43 | +++ b/src/maasserver/migrations/maasserver/0276_bmc_autodetect_metric.py |
44 | @@ -0,0 +1,27 @@ |
45 | +# Generated by Django 2.2.12 on 2022-05-24 13:08 |
46 | + |
47 | +from django.db import migrations, models |
48 | + |
49 | + |
50 | +class Migration(migrations.Migration): |
51 | + |
52 | + dependencies = [ |
53 | + ("maasserver", "0275_interface_children"), |
54 | + ] |
55 | + |
56 | + operations = [ |
57 | + # All existing rows should have NULL values |
58 | + migrations.AddField( |
59 | + model_name="bmc", |
60 | + name="created_by_commissioning", |
61 | + field=models.BooleanField(default=None, editable=False, null=True), |
62 | + ), |
63 | + # All new rows should default to False. |
64 | + migrations.AlterField( |
65 | + model_name="bmc", |
66 | + name="created_by_commissioning", |
67 | + field=models.BooleanField( |
68 | + default=False, editable=False, null=True |
69 | + ), |
70 | + ), |
71 | + ] |
72 | diff --git a/src/maasserver/models/bmc.py b/src/maasserver/models/bmc.py |
73 | index 5fda784..7baf693 100644 |
74 | --- a/src/maasserver/models/bmc.py |
75 | +++ b/src/maasserver/models/bmc.py |
76 | @@ -289,6 +289,14 @@ class BMC(CleanSave, TimestampedModel): |
77 | default=None, |
78 | editable=False, |
79 | ) |
80 | + created_by_commissioning = BooleanField( |
81 | + # We allow None, since before 3.2 we didn't track this, and we |
82 | + # don't know whether the BMC was created manually or |
83 | + # automatically by commissioning. |
84 | + null=True, |
85 | + default=False, |
86 | + editable=False, |
87 | + ) |
88 | |
89 | def __str__(self): |
90 | return "{} ({})".format( |
91 | diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py |
92 | index 2a01c4b..2543ae2 100644 |
93 | --- a/src/maasserver/models/node.py |
94 | +++ b/src/maasserver/models/node.py |
95 | @@ -1374,7 +1374,12 @@ class Node(CleanSave, TimestampedModel): |
96 | def is_commissioning(self): |
97 | return self.status not in (NODE_STATUS.DEPLOYED, NODE_STATUS.DEPLOYING) |
98 | |
99 | - def set_power_config(self, power_type, power_params): |
100 | + def set_power_config( |
101 | + self, |
102 | + power_type, |
103 | + power_params, |
104 | + from_commissioning=False, |
105 | + ): |
106 | """Update the power configuration for a node. |
107 | |
108 | If power_type is not changed, this will update power parameters for the |
109 | @@ -1383,6 +1388,7 @@ class Node(CleanSave, TimestampedModel): |
110 | """ |
111 | from maasserver.models.bmc import BMC |
112 | |
113 | + created_by_commissioning = from_commissioning |
114 | old_bmc = self.bmc |
115 | chassis, bmc_params, node_params = BMC.scope_power_parameters( |
116 | power_type, power_params |
117 | @@ -1396,27 +1402,27 @@ class Node(CleanSave, TimestampedModel): |
118 | power_type=power_type, power_parameters=bmc_params |
119 | ).first() |
120 | if existing_bmc and existing_bmc.id != self.bmc_id: |
121 | - if self.bmc: |
122 | - # Point all nodes using old BMC at the new one. |
123 | - for node in self.bmc.node_set.exclude(id=self.id): |
124 | - node.bmc = existing_bmc |
125 | - node.save() |
126 | + # Point all nodes using old BMC at the new one. |
127 | + for node in self.bmc.node_set.exclude(id=self.id): |
128 | + node.bmc = existing_bmc |
129 | + node.save() |
130 | self.bmc = existing_bmc |
131 | elif not existing_bmc: |
132 | - if self.bmc: |
133 | - self.bmc.power_parameters = bmc_params |
134 | - else: |
135 | - self.bmc = BMC.objects.create( |
136 | - power_type=power_type, power_parameters=bmc_params |
137 | - ) |
138 | + self.bmc.power_parameters = bmc_params |
139 | self.bmc.save() |
140 | elif chassis: |
141 | self.bmc, _ = BMC.objects.get_or_create( |
142 | - power_type=power_type, power_parameters=bmc_params |
143 | + power_type=power_type, |
144 | + power_parameters=bmc_params, |
145 | + defaults={ |
146 | + "created_by_commissioning": created_by_commissioning |
147 | + }, |
148 | ) |
149 | else: |
150 | self.bmc = BMC.objects.create( |
151 | - power_type=power_type, power_parameters=bmc_params |
152 | + power_type=power_type, |
153 | + power_parameters=bmc_params, |
154 | + created_by_commissioning=created_by_commissioning, |
155 | ) |
156 | self.bmc.save() |
157 | |
158 | diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py |
159 | index 7a468c0..987e03f 100644 |
160 | --- a/src/maasserver/models/tests/test_node.py |
161 | +++ b/src/maasserver/models/tests/test_node.py |
162 | @@ -1641,7 +1641,7 @@ class TestNode(MAASServerTestCase): |
163 | self.assertEqual("new-hostname", node.hostname) |
164 | |
165 | def test_get_effective_power_type_raises_if_not_set(self): |
166 | - node = factory.make_Node(power_type="") |
167 | + node = factory.make_Node(power_type=None) |
168 | self.assertRaises(UnknownPowerType, node.get_effective_power_type) |
169 | |
170 | def test_get_effective_power_type_reads_node_field(self): |
171 | @@ -1706,7 +1706,7 @@ class TestNode(MAASServerTestCase): |
172 | self.assertEqual("pxe", params["boot_mode"]) |
173 | |
174 | def test_get_effective_power_info_is_False_for_unset_power_type(self): |
175 | - node = factory.make_Node(power_type="") |
176 | + node = factory.make_Node(power_type=None) |
177 | self.assertEqual( |
178 | (False, False, False, False, None, None), |
179 | node.get_effective_power_info(), |
180 | @@ -3338,7 +3338,9 @@ class TestNode(MAASServerTestCase): |
181 | |
182 | def test_start_commissioning_errors_for_unconfigured_power_type(self): |
183 | node = factory.make_Node( |
184 | - interface=True, status=NODE_STATUS.NEW, power_type="" |
185 | + interface=True, |
186 | + status=NODE_STATUS.NEW, |
187 | + power_type=None, |
188 | ) |
189 | admin = factory.make_admin() |
190 | self.assertRaises(UnknownPowerType, node.start_commissioning, admin) |
191 | @@ -4026,7 +4028,9 @@ class TestNode(MAASServerTestCase): |
192 | |
193 | def test_start_testing_errors_for_unconfigured_power_type(self): |
194 | node = factory.make_Node( |
195 | - interface=True, status=NODE_STATUS.DEPLOYED, power_type="" |
196 | + interface=True, |
197 | + status=NODE_STATUS.DEPLOYED, |
198 | + power_type=None, |
199 | ) |
200 | admin = factory.make_admin() |
201 | self.assertRaises(UnknownPowerType, node.start_testing, admin) |
202 | @@ -5673,7 +5677,7 @@ class TestNode(MAASServerTestCase): |
203 | status=random.choice( |
204 | [NODE_STATUS.READY, NODE_STATUS.BROKEN, NODE_STATUS.DEPLOYED] |
205 | ), |
206 | - power_type="", |
207 | + power_type=None, |
208 | ) |
209 | self.assertRaises( |
210 | UnknownPowerType, node.start_rescue_mode, factory.make_admin() |
211 | @@ -6179,14 +6183,76 @@ class TestNodePowerParameters(MAASServerTestCase): |
212 | |
213 | def test_power_parameters_are_stored(self): |
214 | parameters = dict(user="tarquin", address="10.1.2.3") |
215 | - node = factory.make_Node(power_type="", power_parameters=parameters) |
216 | + node = factory.make_Node( |
217 | + power_type="ipmi", power_parameters=parameters |
218 | + ) |
219 | node.save() |
220 | node = reload_object(node) |
221 | self.assertEqual(parameters, node.power_parameters) |
222 | |
223 | def test_power_parameters_default(self): |
224 | - node = factory.make_Node(power_type="") |
225 | + node = factory.make_Node(power_type=None) |
226 | self.assertEqual({}, node.power_parameters) |
227 | + self.assertIsNone(node.bmc) |
228 | + |
229 | + def test_power_parameters_from_commissioning_not_new(self): |
230 | + node = factory.make_Node(power_type="virsh") |
231 | + node.set_power_config("ipmi", {}, from_commissioning=True) |
232 | + self.assertTrue(node.bmc.created_by_commissioning) |
233 | + |
234 | + def test_power_parameters_not_from_commissioning_not_new(self): |
235 | + node = factory.make_Node(power_type="virsh") |
236 | + node.set_power_config("ipmi", {}, from_commissioning=False) |
237 | + self.assertIsNotNone(node.bmc.created_by_commissioning) |
238 | + self.assertFalse(node.bmc.created_by_commissioning) |
239 | + |
240 | + def test_power_parameters_from_commissioning_not_new_chassis(self): |
241 | + node = factory.make_Node(power_type="virsh") |
242 | + node.set_power_config("redfish", {}, from_commissioning=True) |
243 | + self.assertTrue(node.bmc.created_by_commissioning) |
244 | + |
245 | + def test_power_parameters_not_from_commissioning_not_new_chassis(self): |
246 | + node = factory.make_Node(power_type="virsh") |
247 | + node.set_power_config("redfish", {}, from_commissioning=False) |
248 | + self.assertIsNotNone(node.bmc.created_by_commissioning) |
249 | + self.assertFalse(node.bmc.created_by_commissioning) |
250 | + |
251 | + def test_power_parameters_from_commissioning_new(self): |
252 | + node = factory.make_Node(power_type=None) |
253 | + node.set_power_config("ipmi", {}, from_commissioning=True) |
254 | + self.assertTrue(node.bmc.created_by_commissioning) |
255 | + |
256 | + def test_power_parameters_not_from_commissioning_new(self): |
257 | + node = factory.make_Node(power_type=None) |
258 | + node.set_power_config("ipmi", {}, from_commissioning=False) |
259 | + self.assertIsNotNone(node.bmc.created_by_commissioning) |
260 | + self.assertFalse(node.bmc.created_by_commissioning) |
261 | + |
262 | + def test_power_parameters_from_commissioning_new_chassis(self): |
263 | + node = factory.make_Node(power_type=None) |
264 | + node.set_power_config("redfish", {}, from_commissioning=True) |
265 | + self.assertTrue(node.bmc.created_by_commissioning) |
266 | + |
267 | + def test_power_parameters_not_from_commissioning_new_chassis(self): |
268 | + node = factory.make_Node(power_type=None) |
269 | + node.set_power_config("redfish", {}, from_commissioning=False) |
270 | + self.assertIsNotNone(node.bmc.created_by_commissioning) |
271 | + self.assertFalse(node.bmc.created_by_commissioning) |
272 | + |
273 | + def test_power_parameters_from_commissioning_same(self): |
274 | + node = factory.make_Node( |
275 | + power_type="ipmi", |
276 | + power_parameters={"power_address": factory.make_ipv4_address()}, |
277 | + ) |
278 | + old_value = random.choice([None, True, False]) |
279 | + node.bmc.created_by_commissioning = old_value |
280 | + node.bmc.save() |
281 | + node.set_power_config( |
282 | + "ipmi", |
283 | + {"power_address": factory.make_ipv6_address()}, |
284 | + from_commissioning=True, |
285 | + ) |
286 | + self.assertEqual(node.bmc.created_by_commissioning, old_value) |
287 | |
288 | def test_power_type_and_bmc_power_parameters_stored_in_bmc(self): |
289 | node = factory.make_Node(power_type="hmc") |
290 | diff --git a/src/maasserver/rpc/tests/test_nodes.py b/src/maasserver/rpc/tests/test_nodes.py |
291 | index 0d4bc07..1132db8 100644 |
292 | --- a/src/maasserver/rpc/tests/test_nodes.py |
293 | +++ b/src/maasserver/rpc/tests/test_nodes.py |
294 | @@ -353,14 +353,12 @@ class TestListClusterNodesPowerParameters(MAASServerTestCase): |
295 | # Those tests have been left there for now because they also check |
296 | # that the return values are being formatted correctly for RPC. |
297 | |
298 | - def make_Node(self, power_type=None, power_state_queried=None, **kwargs): |
299 | + def make_Node(self, power_state_queried=None, **kwargs): |
300 | if power_state_queried is None: |
301 | # Ensure that this node was last queried at least 5 minutes ago. |
302 | power_state_queried = now() - timedelta(minutes=randint(6, 16)) |
303 | node = factory.make_Node( |
304 | - power_type=power_type, |
305 | - power_state_queried=power_state_queried, |
306 | - **kwargs |
307 | + power_state_queried=power_state_queried, **kwargs |
308 | ) |
309 | return node |
310 | |
311 | @@ -452,10 +450,11 @@ class TestListClusterNodesPowerParameters(MAASServerTestCase): |
312 | rack = factory.make_RackController() |
313 | node_queryable = self.make_Node(bmc_connected_to=rack) |
314 | |
315 | - factory.make_Device(power_type="") |
316 | - factory.make_Device(power_type="") |
317 | + factory.make_Device(power_type=None) |
318 | + factory.make_Device(power_type=None) |
319 | factory.make_Device( |
320 | - power_type="", power_state_queried=(now() - timedelta(minutes=10)) |
321 | + power_type=None, |
322 | + power_state_queried=(now() - timedelta(minutes=10)), |
323 | ) |
324 | |
325 | power_parameters = list_cluster_nodes_power_parameters(rack.system_id) |
326 | diff --git a/src/maasserver/rpc/tests/test_regionservice_calls.py b/src/maasserver/rpc/tests/test_regionservice_calls.py |
327 | index 2843f66..fbb553b 100644 |
328 | --- a/src/maasserver/rpc/tests/test_regionservice_calls.py |
329 | +++ b/src/maasserver/rpc/tests/test_regionservice_calls.py |
330 | @@ -549,10 +549,10 @@ class TestRegionProtocol_ListNodePowerParameters( |
331 | } |
332 | ) |
333 | |
334 | - # Create a node with an invalid power type (i.e. the empty string). |
335 | + # Create a node with an invalid power type. |
336 | # This will not be reported by the call to ListNodePowerParameters. |
337 | yield deferToDatabase( |
338 | - self.create_node, power_type="", power_state_updated=None |
339 | + self.create_node, power_type="invalid", power_state_updated=None |
340 | ) |
341 | |
342 | response = yield call_responder( |
343 | diff --git a/src/maasserver/stats.py b/src/maasserver/stats.py |
344 | index 4f6d960..f2596cc 100644 |
345 | --- a/src/maasserver/stats.py |
346 | +++ b/src/maasserver/stats.py |
347 | @@ -25,6 +25,7 @@ from maasserver.enum import ( |
348 | NODE_TYPE, |
349 | ) |
350 | from maasserver.models import ( |
351 | + BMC, |
352 | BootResourceFile, |
353 | Config, |
354 | Fabric, |
355 | @@ -408,6 +409,31 @@ def get_tls_configuration_stats(): |
356 | return {"tls_enabled": True, "tls_cert_validity_days": validity_days} |
357 | |
358 | |
359 | +def get_bmc_stats(): |
360 | + stats = ( |
361 | + BMC.objects.all() |
362 | + .values("power_type") |
363 | + .annotate( |
364 | + auto_detected=count_of(created_by_commissioning=True), |
365 | + user_created=count_of(created_by_commissioning__exact=False), |
366 | + unknown=count_of(created_by_commissioning__exact=None), |
367 | + ) |
368 | + ) |
369 | + result = { |
370 | + # BMCs that were auto-detected and created by commissoning. |
371 | + "auto_detected": {}, |
372 | + # BMCs that the user manually created. |
373 | + "user_created": {}, |
374 | + # Before 3.2 we didn't track whether the BMC was auto-detected. |
375 | + "unknown": {}, |
376 | + } |
377 | + for entry in stats: |
378 | + for key in result.keys(): |
379 | + if entry[key]: |
380 | + result[key][entry["power_type"]] = entry[key] |
381 | + return result |
382 | + |
383 | + |
384 | def get_maas_stats(): |
385 | # TODO |
386 | # - architectures |
387 | @@ -457,6 +483,7 @@ def get_maas_stats(): |
388 | "vmcluster": get_vmcluster_stats(), |
389 | "storage_layouts": get_storage_layouts_stats(), |
390 | "tls_configuration": get_tls_configuration_stats(), |
391 | + "bmcs": get_bmc_stats(), |
392 | } |
393 | |
394 | |
395 | diff --git a/src/maasserver/testing/factory.py b/src/maasserver/testing/factory.py |
396 | index dc72f91..62bbd01 100644 |
397 | --- a/src/maasserver/testing/factory.py |
398 | +++ b/src/maasserver/testing/factory.py |
399 | @@ -380,7 +380,7 @@ class Factory(maastesting.factory.Factory): |
400 | zone=None, |
401 | networks=None, |
402 | sortable_name=False, |
403 | - power_type=None, |
404 | + power_type="virsh", |
405 | power_parameters=None, |
406 | power_state=None, |
407 | power_state_updated=undefined, |
408 | @@ -420,8 +420,6 @@ class Factory(maastesting.factory.Factory): |
409 | status = NODE_STATUS.DEFAULT |
410 | if zone is None: |
411 | zone = Zone.objects.get_default_zone() |
412 | - if power_type is None: |
413 | - power_type = "virsh" |
414 | if power_state is None: |
415 | power_state = self.pick_enum(POWER_STATE) |
416 | if power_state_updated is undefined: |
417 | @@ -449,7 +447,7 @@ class Factory(maastesting.factory.Factory): |
418 | cpu_speed=random.randint(1000, 5000), |
419 | **kwargs, |
420 | ) |
421 | - if bmc is None: |
422 | + if bmc is None and power_type: |
423 | # These setters will overwrite the BMC, so don't use them if the |
424 | # BMC was specified. |
425 | node.set_power_config(power_type, power_parameters or {}) |
426 | diff --git a/src/maasserver/tests/test_stats.py b/src/maasserver/tests/test_stats.py |
427 | index 5abc2cc..a11b2d4 100644 |
428 | --- a/src/maasserver/tests/test_stats.py |
429 | +++ b/src/maasserver/tests/test_stats.py |
430 | @@ -21,6 +21,7 @@ from maasserver.enum import ( |
431 | ) |
432 | from maasserver.forms import AdminMachineForm |
433 | from maasserver.models import ( |
434 | + BMC, |
435 | BootResourceFile, |
436 | Config, |
437 | Fabric, |
438 | @@ -31,6 +32,7 @@ from maasserver.models import ( |
439 | VLAN, |
440 | ) |
441 | from maasserver.stats import ( |
442 | + get_bmc_stats, |
443 | get_brownfield_stats, |
444 | get_custom_images_deployed_stats, |
445 | get_custom_images_uploaded_stats, |
446 | @@ -456,6 +458,11 @@ class TestMAASStats(MAASServerTestCase): |
447 | "tls_cert_validity_days": None, |
448 | "tls_enabled": False, |
449 | }, |
450 | + "bmcs": { |
451 | + "auto_detected": {}, |
452 | + "user_created": {"lxd": 1, "virsh": 2}, |
453 | + "unknown": {}, |
454 | + }, |
455 | } |
456 | self.assertEqual(stats, expected) |
457 | |
458 | @@ -628,6 +635,11 @@ class TestMAASStats(MAASServerTestCase): |
459 | "tls_cert_validity_days": None, |
460 | "tls_enabled": False, |
461 | }, |
462 | + "bmcs": { |
463 | + "auto_detected": {}, |
464 | + "user_created": {}, |
465 | + "unknown": {}, |
466 | + }, |
467 | } |
468 | self.assertEqual(get_maas_stats(), expected) |
469 | |
470 | @@ -1047,6 +1059,36 @@ class TestGetSubnetsUtilisationStats(MAASServerTestCase): |
471 | ) |
472 | |
473 | |
474 | +class TestGetBMCStats(MAASServerTestCase): |
475 | + def test_get_bmc_stats_no_bmcs(self): |
476 | + self.assertEqual(0, BMC.objects.all().count()) |
477 | + self.assertEqual( |
478 | + { |
479 | + "auto_detected": {}, |
480 | + "user_created": {}, |
481 | + "unknown": {}, |
482 | + }, |
483 | + get_bmc_stats(), |
484 | + ) |
485 | + |
486 | + def test_get_bmc_stats_with_bmcs(self): |
487 | + factory.make_BMC(power_type="redfish", created_by_commissioning=True) |
488 | + factory.make_BMC(power_type="ipmi", created_by_commissioning=False) |
489 | + factory.make_BMC(power_type="lxd", created_by_commissioning=None) |
490 | + self.assertEqual( |
491 | + { |
492 | + "auto_detected": {"redfish": 1}, |
493 | + "user_created": { |
494 | + "ipmi": 1, |
495 | + }, |
496 | + "unknown": { |
497 | + "lxd": 1, |
498 | + }, |
499 | + }, |
500 | + get_bmc_stats(), |
501 | + ) |
502 | + |
503 | + |
504 | class TestStatsService(MAASTestCase): |
505 | """Tests for `ImportStatsService`.""" |
506 | |
507 | diff --git a/src/metadataserver/api.py b/src/metadataserver/api.py |
508 | index 5c4ad54..2de6c83 100644 |
509 | --- a/src/metadataserver/api.py |
510 | +++ b/src/metadataserver/api.py |
511 | @@ -374,7 +374,9 @@ def store_node_power_parameters(node, request): |
512 | **node.instance_power_parameters, |
513 | **power_parameters, |
514 | } |
515 | - node.set_power_config(power_type, power_parameters) |
516 | + node.set_power_config( |
517 | + power_type, power_parameters, from_commissioning=True |
518 | + ) |
519 | node.save() |
520 | |
521 | |
522 | diff --git a/src/metadataserver/tests/test_api.py b/src/metadataserver/tests/test_api.py |
523 | index a020b2e..6bfd733 100644 |
524 | --- a/src/metadataserver/tests/test_api.py |
525 | +++ b/src/metadataserver/tests/test_api.py |
526 | @@ -4166,7 +4166,7 @@ class TestStoreNodeParameters(APITestCase.ForUser): |
527 | def test_power_type_set_with_parameters(self): |
528 | # When power_type is valid, and power_parameters is valid JSON, both |
529 | # fields are set on the node, and the node is saved. |
530 | - power_type = factory.pick_power_type() |
531 | + power_type = factory.pick_power_type(but_not=[self.node.power_type]) |
532 | power_parameters = {"foo": [1, 2, 3]} |
533 | self.request.POST = { |
534 | "power_type": power_type, |
535 | @@ -4175,6 +4175,7 @@ class TestStoreNodeParameters(APITestCase.ForUser): |
536 | store_node_power_parameters(self.node, self.request) |
537 | self.assertEqual(power_type, self.node.power_type) |
538 | self.assertEqual(power_parameters, self.node.power_parameters) |
539 | + self.assertTrue(self.node.bmc.created_by_commissioning) |
540 | self.save.assert_called_once_with() |
541 | |
542 | def test_power_type_set_with_invalid_parameters(self): |
Self-approve backport.