Merge ~d0ugal/maas:bug/1876855 into maas:master

Proposed by Dougal Matthews
Status: Merged
Approved by: Dougal Matthews
Approved revision: e1568b038dba1e83178514ca58fd13b2ce3fca68
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~d0ugal/maas:bug/1876855
Merge into: maas:master
Diff against target: 675 lines (+122/-42)
30 files modified
src/maasserver/api/machines.py (+33/-17)
src/maasserver/api/tests/test_machines.py (+19/-4)
src/maasserver/clusterrpc/driver_parameters.py (+6/-0)
src/maasserver/clusterrpc/tests/test_driver_parameters.py (+5/-0)
src/provisioningserver/drivers/pod/lxd.py (+1/-0)
src/provisioningserver/drivers/pod/rsd.py (+1/-0)
src/provisioningserver/drivers/pod/tests/test_base.py (+2/-0)
src/provisioningserver/drivers/pod/tests/test_registry.py (+2/-0)
src/provisioningserver/drivers/pod/virsh.py (+1/-0)
src/provisioningserver/drivers/power/__init__.py (+6/-0)
src/provisioningserver/drivers/power/amt.py (+1/-0)
src/provisioningserver/drivers/power/apc.py (+1/-0)
src/provisioningserver/drivers/power/dli.py (+1/-0)
src/provisioningserver/drivers/power/hmc.py (+1/-0)
src/provisioningserver/drivers/power/ipmi.py (+1/-0)
src/provisioningserver/drivers/power/manual.py (+1/-0)
src/provisioningserver/drivers/power/moonshot.py (+1/-0)
src/provisioningserver/drivers/power/mscm.py (+1/-0)
src/provisioningserver/drivers/power/msftocs.py (+1/-0)
src/provisioningserver/drivers/power/nova.py (+1/-0)
src/provisioningserver/drivers/power/openbmc.py (+1/-0)
src/provisioningserver/drivers/power/recs.py (+1/-0)
src/provisioningserver/drivers/power/redfish.py (+1/-0)
src/provisioningserver/drivers/power/seamicro.py (+1/-0)
src/provisioningserver/drivers/power/tests/test_base.py (+4/-1)
src/provisioningserver/drivers/power/tests/test_registry.py (+4/-1)
src/provisioningserver/drivers/power/ucsm.py (+1/-0)
src/provisioningserver/drivers/power/vmware.py (+1/-0)
src/provisioningserver/drivers/power/wedge.py (+1/-0)
src/provisioningserver/tests/test_power_driver_command.py (+21/-19)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Lee Trager (community) Approve
Review via email: mp+384349@code.launchpad.net

Commit message

LP: #1876855 Add can_probe to the driver power classes

This boolean flag determines if the power driver is suitable for use with
add_chassis.

The http API has been updated to use this flag when validating the allowed
list of driver names and the websocket API now includes this flag when
returning the list of power drivers from the general.power_drivers call.

To support backwards compatibility a new alias field was added to the driver classes. This allows them to be referred to by other names. For example "powerkvm" for virsh and "seamicro15k" for "sm15k"

To post a comment you must log in.
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/7587/console
COMMIT: 6d422ef802f869a9bb8b22c1fcace07a50a991c4

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/7588/console
COMMIT: 56a25e7063c3f2ab44a65510f282553baa08dbed

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 7d9c44fea608a1fa06cf0945682253a61d9552e1

review: Approve
Revision history for this message
Dougal Matthews (d0ugal) wrote :

I think this change is mostly ready for feedback. Sure there are some improvements that can be made

Revision history for this message
Lee Trager (ltrager) wrote :

Power drivers and Pod drivers use a different base class. get_all_power_types() should only be returning power drivers so you should be able to skip modifying the Pod drivers.

Revision history for this message
Dougal Matthews (d0ugal) wrote :

> Power drivers and Pod drivers use a different base class.
> get_all_power_types() should only be returning power drivers so you should be
> able to skip modifying the Pod drivers.

I made that change because I was seeing some unexpected behaviour. The PowerDriverRegistry also contains the pod drivers (as per the end of provisioningserver.drivers.power.registry).

However, because some of the names are duplicated (there is a virsh power driver and pod driver) I think the power driver is replaced by the pod driver in the registry. I thought this was a bug and got stuck trying to figure it out. I wonder if we still need the power drivers that also have pod drivers?

Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/7700/console
COMMIT: 31c2f3091ff479c097d6ec8f379d94cb42dfd637

review: Needs Fixing
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 035697a137f9fa67d7e1edcfa70add790f980e9a

review: Approve
Revision history for this message
Dougal Matthews (d0ugal) wrote :

> Power drivers and Pod drivers use a different base class.
> get_all_power_types() should only be returning power drivers so you should be
> able to skip modifying the Pod drivers.

I attempted to do this. However, I think this is the class heirachy;

provisioningserver.drivers.pod.PodDriver -> provisioningserver.drivers.pod.PodDriverBase -> provisioningserver.drivers.power.PowerDriverBase

So it seems that I can't skip pod drivers when adding a new abstract property to the power drivers. Maybe this wasn't intentional?

The method get_all_power_types currently returns all of the power drivers and pod drivers. I added a quick test to verify that which currently fails; https://pastebin.ubuntu.com/p/hm5qx9kdZn/

Revision history for this message
Dougal Matthews (d0ugal) wrote :

I also noticed that in provisioningserver.drivers.power.registry the PodDriverRegistry entries are added to the PowerDriverRegistry. However, since they are registered by name this means the virsh power driver is replaced by the virsh pod driver.

Revision history for this message
Lee Trager (ltrager) wrote :

LGTM!

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: c4bc28e1217465a86e1a26eefd02c20461b087db

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 0bbca16f198642ab6e971245800d0808e5224695

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :

UNIT TESTS
-b bug/1876855 lp:~d0ugal/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: b586e6323baa1b28a66fae55e1dba8ede9de04a4

review: Approve
Revision history for this message
MAAS Lander (maas-lander) wrote :
Revision history for this message
MAAS Lander (maas-lander) wrote :

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/machines.py b/src/maasserver/api/machines.py
2index 72281b4..0ecf00a 100644
3--- a/src/maasserver/api/machines.py
4+++ b/src/maasserver/api/machines.py
5@@ -43,6 +43,7 @@ from maasserver.api.utils import (
6 get_optional_list,
7 get_optional_param,
8 )
9+from maasserver.clusterrpc.driver_parameters import get_all_power_types
10 from maasserver.enum import (
11 BMC_TYPE,
12 BRIDGE_TYPE,
13@@ -2508,6 +2509,34 @@ class MachinesHandler(NodesHandler, PowersMixin):
14 machine.constraints_by_type["verbose_interfaces"] = interfaces
15 return machine
16
17+ def _get_chassis_param(self, request):
18+ power_type_names = [
19+ pt["name"] for pt in get_all_power_types() if pt["can_probe"]
20+ ]
21+
22+ # NOTE: The http API accepts two additional names for backwards
23+ # compatability. Previously the hardcoded list of chassis_type's was
24+ # stored here and included powerkvm and seamicro15k. Neither of these
25+ # are valid power driver names but they can be treated as virsh and
26+ # sm15k.
27+ power_type_names.extend(["powerkvm", "seamicro15k"])
28+
29+ chassis_type = get_mandatory_param(
30+ request.POST,
31+ "chassis_type",
32+ validator=validators.OneOf(power_type_names),
33+ )
34+
35+ # Convert sm15k to seamicro15k. This code was written to work with
36+ # 'seamicro15k' but the power driver name in the provisioningserver is
37+ # 'sm15k'. The following code expects the longer name. See the NOTE
38+ # above for more context. Both powerkvm and virsh were previously
39+ # supported so they don't need to be converted.
40+ if chassis_type == "sm15k":
41+ chassis_type = "seamicro15k"
42+
43+ return chassis_type
44+
45 @admin_method
46 @operation(idempotent=False)
47 def add_chassis(self, request):
48@@ -2521,7 +2550,7 @@ class MachinesHandler(NodesHandler, PowersMixin):
49 - ``msftocs``: Microsoft OCS Chassis Manager.
50 - ``powerkvm``: Virtual Machines on Power KVM, managed by Virsh.
51 - ``recs_box``: Christmann RECS|Box servers.
52- - ``seamicro15k``: Seamicro 1500 Chassis.
53+ - ``sm15k``: Seamicro 1500 Chassis.
54 - ``ucsm``: Cisco UCS Manager.
55 - ``virsh``: virtual machines managed by Virsh.
56 - ``vmware`` is the type for virtual machines managed by VMware.
57@@ -2585,22 +2614,9 @@ class MachinesHandler(NodesHandler, PowersMixin):
58 @error (http-status-code) "400" 400
59 @error (content) "bad-params" Required parameters are missing.
60 """
61- chassis_type = get_mandatory_param(
62- request.POST,
63- "chassis_type",
64- validator=validators.OneOf(
65- [
66- "mscm",
67- "msftocs",
68- "powerkvm",
69- "recs_box",
70- "seamicro15k",
71- "ucsm",
72- "virsh",
73- "vmware",
74- ]
75- ),
76- )
77+
78+ chassis_type = self._get_chassis_param(request)
79+
80 hostname = get_mandatory_param(request.POST, "hostname")
81
82 if chassis_type in (
83diff --git a/src/maasserver/api/tests/test_machines.py b/src/maasserver/api/tests/test_machines.py
84index 98c4485..3adf781 100644
85--- a/src/maasserver/api/tests/test_machines.py
86+++ b/src/maasserver/api/tests/test_machines.py
87@@ -2665,6 +2665,21 @@ class TestMachinesAPI(APITestCase.ForUser):
88 )
89 self.assertEqual(b"No provided chassis_type!", response.content)
90
91+ def test_POST_add_chassis_invalid_chasis_type(self):
92+ self.become_admin()
93+ response = self.client.post(
94+ reverse("machines_handler"),
95+ {"op": "add_chassis", "chassis_type": "invalid"},
96+ )
97+ self.assertEqual(
98+ http.client.BAD_REQUEST, response.status_code, response.content
99+ )
100+ self.assertTrue(
101+ response.content.startswith(
102+ b"Invalid chassis_type: Value must be one of: "
103+ )
104+ )
105+
106 def test_POST_add_chassis_requires_hostname(self):
107 self.become_admin()
108 response = self.client.post(
109@@ -3058,7 +3073,7 @@ class TestMachinesAPI(APITestCase.ForUser):
110 ),
111 )
112
113- def test_POST_add_chasis_only_allows_port_with_vmware_and_msftocs(self):
114+ def test_POST_add_chassis_only_allows_port_with_vmware_and_msftocs(self):
115 self.become_admin()
116 for chassis_type in (
117 "mscm",
118@@ -3088,7 +3103,7 @@ class TestMachinesAPI(APITestCase.ForUser):
119 response.content,
120 )
121
122- def test_POST_add_chasis_checks_port_too_high(self):
123+ def test_POST_add_chassis_checks_port_too_high(self):
124 self.become_admin()
125 for chassis_type in ("msftocs", "recs_box", "vmware"):
126 params = {
127@@ -3110,7 +3125,7 @@ class TestMachinesAPI(APITestCase.ForUser):
128 response.content,
129 )
130
131- def test_POST_add_chasis_checks_port_too_low(self):
132+ def test_POST_add_chassis_checks_port_too_low(self):
133 self.become_admin()
134 for chassis_type in ("msftocs", "recs_box", "vmware"):
135 params = {
136@@ -3175,7 +3190,7 @@ class TestMachinesAPI(APITestCase.ForUser):
137 ),
138 )
139
140- def test_POST_add_chasis_only_allows_protocol_with_vmware(self):
141+ def test_POST_add_chassis_only_allows_protocol_with_vmware(self):
142 self.become_admin()
143 for chassis_type in (
144 "mscm",
145diff --git a/src/maasserver/clusterrpc/driver_parameters.py b/src/maasserver/clusterrpc/driver_parameters.py
146index 3c2175e..c29bba0 100644
147--- a/src/maasserver/clusterrpc/driver_parameters.py
148+++ b/src/maasserver/clusterrpc/driver_parameters.py
149@@ -94,6 +94,7 @@ def add_power_driver_parameters(
150 name,
151 description,
152 chassis,
153+ can_probe,
154 fields,
155 missing_packages,
156 parameters_set,
157@@ -111,6 +112,8 @@ def add_power_driver_parameters(
158 :type description: string
159 :param chassis: Whether the power driver is for a chassis
160 :type chassis: bool
161+ :param can_probe: Whether the power driver supports add_chassis
162+ :type can_probe: bool
163 :param fields: The fields that make up the parameters for the power
164 type. Will be validated against
165 SETTING_PARAMETER_FIELD_SCHEMA.
166@@ -137,6 +140,7 @@ def add_power_driver_parameters(
167 "fields": fields,
168 "missing_packages": missing_packages,
169 "chassis": chassis,
170+ "can_probe": can_probe,
171 }
172 if queryable is not None:
173 params["queryable"] = queryable
174@@ -262,6 +266,7 @@ def get_all_power_types(controllers=None, ignore_errors=True):
175 fields = power_type.get("fields", [])
176 description = power_type["description"]
177 chassis = power_type["chassis"]
178+ can_probe = power_type["can_probe"]
179 missing_packages = power_type["missing_packages"]
180 queryable = power_type.get("queryable")
181 add_power_driver_parameters(
182@@ -269,6 +274,7 @@ def get_all_power_types(controllers=None, ignore_errors=True):
183 name,
184 description,
185 chassis,
186+ can_probe,
187 fields,
188 missing_packages,
189 merged_types,
190diff --git a/src/maasserver/clusterrpc/tests/test_driver_parameters.py b/src/maasserver/clusterrpc/tests/test_driver_parameters.py
191index 6e31cad..9334fa3 100644
192--- a/src/maasserver/clusterrpc/tests/test_driver_parameters.py
193+++ b/src/maasserver/clusterrpc/tests/test_driver_parameters.py
194@@ -297,6 +297,7 @@ class TestAddPowerTypeParameters(MAASServerTestCase):
195 name="blah",
196 description="baz",
197 chassis=True,
198+ can_probe=False,
199 fields=[self.make_field()],
200 missing_packages=[],
201 parameters_set=existing_parameters,
202@@ -323,6 +324,7 @@ class TestAddPowerTypeParameters(MAASServerTestCase):
203 name="blah",
204 description="baz",
205 chassis=False,
206+ can_probe=False,
207 fields=fields,
208 missing_packages=missing_packages,
209 parameters_set=existing_parameters,
210@@ -334,6 +336,7 @@ class TestAddPowerTypeParameters(MAASServerTestCase):
211 "name": "blah",
212 "description": "baz",
213 "chassis": False,
214+ "can_probe": False,
215 "fields": fields,
216 "missing_packages": missing_packages,
217 }
218@@ -349,6 +352,7 @@ class TestAddPowerTypeParameters(MAASServerTestCase):
219 name="blah",
220 description="baz",
221 chassis=False,
222+ can_probe=False,
223 fields=[{}],
224 missing_packages=[],
225 parameters_set=[],
226@@ -362,6 +366,7 @@ class TestAddPowerTypeParameters(MAASServerTestCase):
227 name="blah",
228 description="baz",
229 chassis=False,
230+ can_probe=False,
231 fields=fields,
232 missing_packages=[],
233 parameters_set=parameters_set,
234diff --git a/src/provisioningserver/drivers/pod/lxd.py b/src/provisioningserver/drivers/pod/lxd.py
235index 5e06104..bd47a07 100644
236--- a/src/provisioningserver/drivers/pod/lxd.py
237+++ b/src/provisioningserver/drivers/pod/lxd.py
238@@ -93,6 +93,7 @@ class LXDPodDriver(PodDriver):
239
240 name = "lxd"
241 chassis = True
242+ can_probe = False
243 description = "LXD (virtual systems)"
244 settings = [
245 make_setting_field("power_address", "LXD address", required=True),
246diff --git a/src/provisioningserver/drivers/pod/rsd.py b/src/provisioningserver/drivers/pod/rsd.py
247index 91aceef..25401b3 100644
248--- a/src/provisioningserver/drivers/pod/rsd.py
249+++ b/src/provisioningserver/drivers/pod/rsd.py
250@@ -68,6 +68,7 @@ RSD_NODE_POWER_STATE = {"PoweredOn": "on", "PoweredOff": "off"}
251 class RSDPodDriver(RedfishPowerDriverBase, PodDriverBase):
252
253 chassis = True # Pods are always a chassis
254+ can_probe = False
255
256 # RSDPodDriver inherits from RedfishPowerDriver.
257 # Power parameters will need to be changed to reflect this.
258diff --git a/src/provisioningserver/drivers/pod/tests/test_base.py b/src/provisioningserver/drivers/pod/tests/test_base.py
259index 829761a..df60ddc 100644
260--- a/src/provisioningserver/drivers/pod/tests/test_base.py
261+++ b/src/provisioningserver/drivers/pod/tests/test_base.py
262@@ -874,6 +874,7 @@ class FakePodDriverBase(PodDriverBase):
263
264 name = ""
265 chassis = True
266+ can_probe = True
267 description = ""
268 settings = []
269 ip_extractor = None
270@@ -1020,6 +1021,7 @@ class TestPodDriverBase(MAASTestCase):
271 "queryable": fake_driver.queryable,
272 "missing_packages": [],
273 "chassis": True,
274+ "can_probe": True,
275 },
276 fake_driver.get_schema(),
277 )
278diff --git a/src/provisioningserver/drivers/pod/tests/test_registry.py b/src/provisioningserver/drivers/pod/tests/test_registry.py
279index 1e1c2e5..34e790b 100644
280--- a/src/provisioningserver/drivers/pod/tests/test_registry.py
281+++ b/src/provisioningserver/drivers/pod/tests/test_registry.py
282@@ -41,6 +41,7 @@ class TestPodDriverRegistry(MAASTestCase):
283 "queryable": fake_driver_one.queryable,
284 "missing_packages": fake_driver_one.detect_missing_packages(),
285 "chassis": True,
286+ "can_probe": True,
287 },
288 {
289 "driver_type": "pod",
290@@ -50,6 +51,7 @@ class TestPodDriverRegistry(MAASTestCase):
291 "queryable": fake_driver_two.queryable,
292 "missing_packages": fake_driver_two.detect_missing_packages(),
293 "chassis": True,
294+ "can_probe": True,
295 },
296 ],
297 PodDriverRegistry.get_schema(),
298diff --git a/src/provisioningserver/drivers/pod/virsh.py b/src/provisioningserver/drivers/pod/virsh.py
299index 50a8b97..690e015 100644
300--- a/src/provisioningserver/drivers/pod/virsh.py
301+++ b/src/provisioningserver/drivers/pod/virsh.py
302@@ -1292,6 +1292,7 @@ class VirshPodDriver(PodDriver):
303
304 name = "virsh"
305 description = "Virsh (virtual systems)"
306+ can_probe = True
307 settings = [
308 make_setting_field("power_address", "Address", required=True),
309 make_setting_field(
310diff --git a/src/provisioningserver/drivers/power/__init__.py b/src/provisioningserver/drivers/power/__init__.py
311index c9534f1..b6e68af 100644
312--- a/src/provisioningserver/drivers/power/__init__.py
313+++ b/src/provisioningserver/drivers/power/__init__.py
314@@ -49,6 +49,7 @@ JSON_POWER_DRIVER_SCHEMA = {
315 "driver_type": {"type": "string"},
316 "name": {"type": "string"},
317 "chassis": {"type": "boolean"},
318+ "can_probe": {"type": "boolean"},
319 "description": {"type": "string"},
320 "fields": {"type": "array", "items": SETTING_PARAMETER_FIELD_SCHEMA},
321 "ip_extractor": IP_EXTRACTOR_SCHEMA,
322@@ -168,6 +169,10 @@ class PowerDriverBase(metaclass=ABCMeta):
323 def chassis(self):
324 """Return True if the power driver is for a chassis."""
325
326+ @abstractproperty
327+ def can_probe(self):
328+ """Return True if the power driver can be used with add_chassis."""
329+
330 @abstractmethod
331 def detect_missing_packages(self):
332 """Implement this method for the actual implementation
333@@ -220,6 +225,7 @@ class PowerDriverBase(metaclass=ABCMeta):
334 name=self.name,
335 description=self.description,
336 chassis=self.chassis,
337+ can_probe=self.can_probe,
338 fields=self.settings,
339 queryable=self.queryable,
340 missing_packages=(
341diff --git a/src/provisioningserver/drivers/power/amt.py b/src/provisioningserver/drivers/power/amt.py
342index 7410383..14288a0 100644
343--- a/src/provisioningserver/drivers/power/amt.py
344+++ b/src/provisioningserver/drivers/power/amt.py
345@@ -47,6 +47,7 @@ class AMTPowerDriver(PowerDriver):
346
347 name = "amt"
348 chassis = False
349+ can_probe = False
350 description = "Intel AMT"
351 settings = [
352 make_setting_field(
353diff --git a/src/provisioningserver/drivers/power/apc.py b/src/provisioningserver/drivers/power/apc.py
354index 680175c..1fe8505 100644
355--- a/src/provisioningserver/drivers/power/apc.py
356+++ b/src/provisioningserver/drivers/power/apc.py
357@@ -29,6 +29,7 @@ class APCPowerDriver(PowerDriver):
358
359 name = "apc"
360 chassis = True
361+ can_probe = False
362 description = "American Power Conversion (APC) PDU"
363 settings = [
364 make_setting_field("power_address", "IP for APC PDU", required=True),
365diff --git a/src/provisioningserver/drivers/power/dli.py b/src/provisioningserver/drivers/power/dli.py
366index d389d20..f27bdf3 100644
367--- a/src/provisioningserver/drivers/power/dli.py
368+++ b/src/provisioningserver/drivers/power/dli.py
369@@ -29,6 +29,7 @@ from provisioningserver.utils.shell import (
370 class DLIPowerDriver(PowerDriver):
371 name = "dli"
372 chassis = True
373+ can_probe = False
374 description = "Digital Loggers, Inc. PDU"
375 settings = [
376 make_setting_field(
377diff --git a/src/provisioningserver/drivers/power/hmc.py b/src/provisioningserver/drivers/power/hmc.py
378index b12ea76..1679d7e 100644
379--- a/src/provisioningserver/drivers/power/hmc.py
380+++ b/src/provisioningserver/drivers/power/hmc.py
381@@ -35,6 +35,7 @@ class HMCPowerDriver(PowerDriver):
382
383 name = "hmc"
384 chassis = True
385+ can_probe = False
386 description = "IBM Hardware Management Console (HMC)"
387 settings = [
388 make_setting_field("power_address", "IP for HMC", required=True),
389diff --git a/src/provisioningserver/drivers/power/ipmi.py b/src/provisioningserver/drivers/power/ipmi.py
390index 02d49a0..877fff9 100644
391--- a/src/provisioningserver/drivers/power/ipmi.py
392+++ b/src/provisioningserver/drivers/power/ipmi.py
393@@ -203,6 +203,7 @@ class IPMIPowerDriver(PowerDriver):
394
395 name = "ipmi"
396 chassis = False
397+ can_probe = False
398 description = "IPMI"
399 settings = [
400 make_setting_field(
401diff --git a/src/provisioningserver/drivers/power/manual.py b/src/provisioningserver/drivers/power/manual.py
402index daaf5ed..44edd69 100644
403--- a/src/provisioningserver/drivers/power/manual.py
404+++ b/src/provisioningserver/drivers/power/manual.py
405@@ -17,6 +17,7 @@ class ManualPowerDriver(PowerDriver):
406
407 name = "manual"
408 chassis = False
409+ can_probe = False
410 description = "Manual"
411 settings = []
412 ip_extractor = None
413diff --git a/src/provisioningserver/drivers/power/moonshot.py b/src/provisioningserver/drivers/power/moonshot.py
414index c8f2bf5..9608528 100644
415--- a/src/provisioningserver/drivers/power/moonshot.py
416+++ b/src/provisioningserver/drivers/power/moonshot.py
417@@ -26,6 +26,7 @@ class MoonshotIPMIPowerDriver(PowerDriver):
418
419 name = "moonshot"
420 chassis = True
421+ can_probe = False
422 description = "HP Moonshot - iLO4 (IPMI)"
423 settings = [
424 make_setting_field("power_address", "Power address", required=True),
425diff --git a/src/provisioningserver/drivers/power/mscm.py b/src/provisioningserver/drivers/power/mscm.py
426index 02f21a6..3db65f8 100644
427--- a/src/provisioningserver/drivers/power/mscm.py
428+++ b/src/provisioningserver/drivers/power/mscm.py
429@@ -56,6 +56,7 @@ class MSCMPowerDriver(PowerDriver):
430
431 name = "mscm"
432 chassis = True
433+ can_probe = True
434 description = "HP Moonshot - iLO Chassis Manager"
435 settings = [
436 make_setting_field(
437diff --git a/src/provisioningserver/drivers/power/msftocs.py b/src/provisioningserver/drivers/power/msftocs.py
438index 5b09f96..a549a76 100644
439--- a/src/provisioningserver/drivers/power/msftocs.py
440+++ b/src/provisioningserver/drivers/power/msftocs.py
441@@ -37,6 +37,7 @@ class MicrosoftOCSPowerDriver(PowerDriver):
442
443 name = "msftocs"
444 chassis = True
445+ can_probe = True
446 description = "Microsoft OCS - Chassis Manager"
447 settings = [
448 make_setting_field("power_address", "Power address", required=True),
449diff --git a/src/provisioningserver/drivers/power/nova.py b/src/provisioningserver/drivers/power/nova.py
450index 392ae4b..607515c 100644
451--- a/src/provisioningserver/drivers/power/nova.py
452+++ b/src/provisioningserver/drivers/power/nova.py
453@@ -39,6 +39,7 @@ class NovaPowerDriver(PowerDriver):
454
455 name = "nova"
456 chassis = True
457+ can_probe = False
458 description = "OpenStack Nova"
459 settings = [
460 make_setting_field(
461diff --git a/src/provisioningserver/drivers/power/openbmc.py b/src/provisioningserver/drivers/power/openbmc.py
462index 24c7c21..3c0a461 100644
463--- a/src/provisioningserver/drivers/power/openbmc.py
464+++ b/src/provisioningserver/drivers/power/openbmc.py
465@@ -39,6 +39,7 @@ REG_MODE = {"data": "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"}
466 class OpenBMCPowerDriver(PowerDriver):
467
468 chassis = False
469+ can_probe = False
470
471 name = "openbmc"
472 description = "OpenBMC Power Driver"
473diff --git a/src/provisioningserver/drivers/power/recs.py b/src/provisioningserver/drivers/power/recs.py
474index 4f45beb..3d1b948 100644
475--- a/src/provisioningserver/drivers/power/recs.py
476+++ b/src/provisioningserver/drivers/power/recs.py
477@@ -206,6 +206,7 @@ class RECSPowerDriver(PowerDriver):
478
479 name = "recs_box"
480 chassis = True
481+ can_probe = True
482 description = "Christmann RECS|Box Power Driver"
483 settings = [
484 make_setting_field(
485diff --git a/src/provisioningserver/drivers/power/redfish.py b/src/provisioningserver/drivers/power/redfish.py
486index 27f6354..bd388a6 100644
487--- a/src/provisioningserver/drivers/power/redfish.py
488+++ b/src/provisioningserver/drivers/power/redfish.py
489@@ -134,6 +134,7 @@ class RedfishPowerDriverBase(PowerDriver):
490 class RedfishPowerDriver(RedfishPowerDriverBase):
491
492 chassis = True # Redfish API endpoints can be probed and enlisted.
493+ can_probe = False
494
495 name = "redfish"
496 description = "Redfish"
497diff --git a/src/provisioningserver/drivers/power/seamicro.py b/src/provisioningserver/drivers/power/seamicro.py
498index 7b5134d..aee9b4c 100644
499--- a/src/provisioningserver/drivers/power/seamicro.py
500+++ b/src/provisioningserver/drivers/power/seamicro.py
501@@ -40,6 +40,7 @@ class SeaMicroPowerDriver(PowerDriver):
502
503 name = "sm15k"
504 chassis = True
505+ can_probe = True
506 description = "SeaMicro 15000"
507 settings = [
508 make_setting_field(
509diff --git a/src/provisioningserver/drivers/power/tests/test_base.py b/src/provisioningserver/drivers/power/tests/test_base.py
510index 0e6039e..8c56180 100644
511--- a/src/provisioningserver/drivers/power/tests/test_base.py
512+++ b/src/provisioningserver/drivers/power/tests/test_base.py
513@@ -43,6 +43,7 @@ class FakePowerDriverBase(PowerDriverBase):
514
515 name = ""
516 chassis = False
517+ can_probe = False
518 description = ""
519 settings = []
520 ip_extractor = None
521@@ -183,6 +184,7 @@ class TestPowerDriverBase(MAASTestCase):
522 "name": fake_name,
523 "description": fake_description,
524 "chassis": fake_chassis,
525+ "can_probe": False,
526 "fields": fake_settings,
527 "queryable": fake_driver.queryable,
528 "missing_packages": fake_driver.detect_missing_packages(),
529@@ -251,6 +253,7 @@ class FakePowerDriver(PowerDriver):
530
531 name = ""
532 chassis = False
533+ can_probe = False
534 description = ""
535 settings = []
536 ip_extractor = None
537@@ -264,7 +267,7 @@ class FakePowerDriver(PowerDriver):
538 self.settings = settings
539 if wait_time is not None:
540 self.wait_time = wait_time
541- super(FakePowerDriver, self).__init__(clock)
542+ super().__init__(clock)
543
544 def detect_missing_packages(self):
545 return []
546diff --git a/src/provisioningserver/drivers/power/tests/test_registry.py b/src/provisioningserver/drivers/power/tests/test_registry.py
547index 9ee55eb..0e9c392 100644
548--- a/src/provisioningserver/drivers/power/tests/test_registry.py
549+++ b/src/provisioningserver/drivers/power/tests/test_registry.py
550@@ -18,7 +18,7 @@ from provisioningserver.utils.testing import RegistryFixture
551
552 class TestPowerDriverRegistry(MAASTestCase):
553 def setUp(self):
554- super(TestPowerDriverRegistry, self).setUp()
555+ super().setUp()
556 # Ensure the global registry is empty for each test run.
557 self.useFixture(RegistryFixture())
558
559@@ -49,6 +49,7 @@ class TestPowerDriverRegistry(MAASTestCase):
560 "name": fake_driver_one.name,
561 "description": fake_driver_one.description,
562 "chassis": fake_driver_one.chassis,
563+ "can_probe": fake_driver_one.can_probe,
564 "fields": [],
565 "queryable": fake_driver_one.queryable,
566 "missing_packages": fake_driver_one.detect_missing_packages(),
567@@ -58,6 +59,7 @@ class TestPowerDriverRegistry(MAASTestCase):
568 "name": fake_driver_two.name,
569 "description": fake_driver_two.description,
570 "chassis": fake_driver_two.chassis,
571+ "can_probe": fake_driver_two.can_probe,
572 "fields": [],
573 "queryable": fake_driver_two.queryable,
574 "missing_packages": fake_driver_two.detect_missing_packages(),
575@@ -67,6 +69,7 @@ class TestPowerDriverRegistry(MAASTestCase):
576 "name": fake_pod_driver.name,
577 "description": fake_pod_driver.description,
578 "chassis": fake_pod_driver.chassis,
579+ "can_probe": fake_pod_driver.can_probe,
580 "fields": [],
581 "queryable": fake_pod_driver.queryable,
582 "missing_packages": fake_pod_driver.detect_missing_packages(),
583diff --git a/src/provisioningserver/drivers/power/ucsm.py b/src/provisioningserver/drivers/power/ucsm.py
584index bc9accb..6fdd9b1 100644
585--- a/src/provisioningserver/drivers/power/ucsm.py
586+++ b/src/provisioningserver/drivers/power/ucsm.py
587@@ -30,6 +30,7 @@ class UCSMPowerDriver(PowerDriver):
588
589 name = "ucsm"
590 chassis = True
591+ can_probe = True
592 description = "Cisco UCS Manager"
593 settings = [
594 make_setting_field(
595diff --git a/src/provisioningserver/drivers/power/vmware.py b/src/provisioningserver/drivers/power/vmware.py
596index 86b7e2c..b222a24 100644
597--- a/src/provisioningserver/drivers/power/vmware.py
598+++ b/src/provisioningserver/drivers/power/vmware.py
599@@ -38,6 +38,7 @@ class VMwarePowerDriver(PowerDriver):
600
601 name = "vmware"
602 chassis = True
603+ can_probe = True
604 description = "VMware"
605 settings = [
606 make_setting_field(
607diff --git a/src/provisioningserver/drivers/power/wedge.py b/src/provisioningserver/drivers/power/wedge.py
608index aee85df..6470b5d 100644
609--- a/src/provisioningserver/drivers/power/wedge.py
610+++ b/src/provisioningserver/drivers/power/wedge.py
611@@ -27,6 +27,7 @@ class WedgePowerDriver(PowerDriver):
612
613 name = "wedge"
614 chassis = False
615+ can_probe = False
616 description = "Facebook's Wedge"
617 settings = [
618 make_setting_field("power_address", "IP address", required=True),
619diff --git a/src/provisioningserver/tests/test_power_driver_command.py b/src/provisioningserver/tests/test_power_driver_command.py
620index 2884951..12f505a 100644
621--- a/src/provisioningserver/tests/test_power_driver_command.py
622+++ b/src/provisioningserver/tests/test_power_driver_command.py
623@@ -15,6 +15,27 @@ from provisioningserver import power_driver_command
624 from provisioningserver.drivers.power import PowerDriver
625
626
627+class FakeDriver(PowerDriver):
628+ # These are required by the base class, but unused in this test.
629+ chassis = False
630+ can_probe = False
631+ description = None
632+ detect_missing_packages = None
633+ ip_extractor = None
634+ name = None
635+ settings = []
636+ _state = "off"
637+
638+ def power_on(self, *args, **kwargs):
639+ self._state = "on"
640+
641+ def power_off(self, *args, **kwargs):
642+ self._state = "off"
643+
644+ def power_query(self, *args, **kwargs):
645+ return self._state
646+
647+
648 class TestPowerDriverCommand(MAASTestCase):
649
650 run_tests_with = MAASTwistedRunTest.make_factory(timeout=5000)
651@@ -90,25 +111,6 @@ class TestPowerDriverCommand(MAASTestCase):
652 args.command = "on"
653 args.driver = "fake"
654
655- class FakeDriver(PowerDriver):
656- # These are required by the base class, but unused in this test.
657- chassis = None
658- description = None
659- detect_missing_packages = None
660- ip_extractor = None
661- name = None
662- settings = []
663- _state = "off"
664-
665- def power_on(self, *args, **kwargs):
666- self._state = "on"
667-
668- def power_off(self, *args, **kwargs):
669- self._state = "off"
670-
671- def power_query(self, *args, **kwargs):
672- return self._state
673-
674 driver = FakeDriver()
675 registry = {"fake": driver}
676

Subscribers

People subscribed via source and target branches