Merge ~troyanov/maas:fix-2042645 into maas:master

Proposed by Anton Troyanov
Status: Merged
Approved by: Anton Troyanov
Approved revision: 3f73eb999cfa32551e48d8b4f2766da6182c97cd
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~troyanov/maas:fix-2042645
Merge into: maas:master
Diff against target: 234 lines (+29/-76)
4 files modified
src/maasagent/internal/workflow/power.go (+5/-31)
src/maasagent/internal/workflow/power_test.go (+5/-1)
src/maasserver/models/node.py (+14/-25)
src/maasserver/models/tests/test_node.py (+5/-19)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Björn Tillenius Approve
Jacopo Rota Approve
Review via email: mp+455135@code.launchpad.net

Commit message

refactor: get_effective_power_parameters only required

This change addresses the disclosure of various power parameters to drivers that do not rely on them.

- 'mac_address' used by IPMI only, but it should be BMC address (which MAAS doesn't now), so this parameter can be simply removed
- 'power_address', 'username', 'power_pass' should be set by the driver parameters
- 'power_id' used only by 'virsh
- 'power_driver' default empty value is not used by any driver='power_off_mode' - used by 'ipmi' only
- 'boot_mode' used by 'amt' only

Resolves LP:2042645

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

UNIT TESTS
-b fix-2042645 lp:~troyanov/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: ac6fae857039252029340b1d6494c45e2d9a4610

review: Approve
Revision history for this message
Jacopo Rota (r00ta) wrote :

lgtm

review: Approve
Revision history for this message
Björn Tillenius (bjornt) wrote :

Why do we send parameters that should be ignored?

I'm a bit against ignoring parameters like that. That means that if we get the parameter wrong for some reason, we will get a subtle error later on, rather than to get a clear error up front.

So why can't we send only the required parameters? We control all the code.

review: Needs Information
Revision history for this message
Anton Troyanov (troyanov) wrote (last edit ):

We send parameters that we fetch from existing model methods (and it used to work this way).
Driver logic ignores unknown parameters, it's just maas-power CLI that chokes (because of ArgParse) and thats why this proposal is removing strict check for the CLI (to be on par with driver logic)

Extra parameters are added here:
`get_effective_power_parameters` from src/maasserver/models/node.py adds power_params["mac_address"] each time

If I understand correctly:
- `mac_address` is used only by IPMI
- `power_off_mode` is also used by IPMI
- `power_id` is used only by virsh?

(I didn't check all of them)

Revision history for this message
Björn Tillenius (bjornt) wrote :

Maybe this is a good opportunity to clean things up.

For example, mac_address is interesting. Yes, IPMI accepts a mac_address. But it expects the MAC of the BMC port, which the host won't see. So the mac_address returned by get_effective_power_parameters() is not usable by the IPMI power driver.

Revision history for this message
Björn Tillenius (bjornt) wrote :

We had a meet and went over this. The TL;DR is that we should clean up get_effective_power_parameters() to only return what's actually needed.

'system_id' is part of the power API contract, so that one probably has to stay.
  '
'power_address', 'username', 'power_id', 'power_driver', 'power_pass' should be removed, since they are already set when you configure a power driver.

'mac_address' should be removed, since it's only used by IPMI, but as per my comment above, it's the wrong mac address.

'power_off_mode' and 'boot_mode' are a bit special. 'power_off_mode' is kind of part of the power control contract, but only IPMI is actually accepting it. 'boot_mode' is only used for AMT.

We should look into how to deal with 'power_off_mode' and 'boot_mode' better, but for now, let's add them only to the power parameters if the power driver requires them.

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

UNIT TESTS
-b fix-2042645 lp:~troyanov/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 3388903e9ce22d374260d91b95a8305aada511e9

review: Approve
~troyanov/maas:fix-2042645 updated
3f8f2fc... by Anton Troyanov

fixup! fix: remove power_off_mode

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

UNIT TESTS
-b fix-2042645 lp:~troyanov/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: d95e5217bfbabee6bedf5a5bd02188b251fc98ed

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

UNIT TESTS
-b fix-2042645 lp:~troyanov/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas-tester/4020/console
COMMIT: 3f8f2fcb7573dabde120711a8a08c035d09ed76c

review: Needs Fixing
Revision history for this message
Björn Tillenius (bjornt) wrote :

+1

review: Approve
~troyanov/maas:fix-2042645 updated
3f73eb9... by Anton Troyanov

fix: fix tests to reflect power param changes

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

UNIT TESTS
-b fix-2042645 lp:~troyanov/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 3f73eb999cfa32551e48d8b4f2766da6182c97cd

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasagent/internal/workflow/power.go b/src/maasagent/internal/workflow/power.go
2index f29df39..563bbad 100644
3--- a/src/maasagent/internal/workflow/power.go
4+++ b/src/maasagent/internal/workflow/power.go
5@@ -27,24 +27,6 @@ var (
6 ErrWrongPowerState = errors.New("BMC is in the wrong power state")
7 )
8
9-var (
10- // power parameters fetched from the DB contain extra
11- // parameters the CLI does not need and will cause errors in the CLI
12- // so we ignore them
13- ignoredPowerOptions = map[string]struct{}{
14- "power_id": {},
15- "system_id": {},
16- "boot_mode": {},
17- "power_off_mode": {},
18- }
19-
20- // these driver types do not take a MAC address argument on the CLI
21- ignoredMACDriverTypes = map[string]struct{}{
22- "lxd": {},
23- "virsh": {},
24- }
25-)
26-
27 // PowerParam is the workflow parameter for power management of a host
28 type PowerParam struct {
29 SystemID string `json:"system_id"`
30@@ -54,16 +36,6 @@ type PowerParam struct {
31 DriverType string `json:"driver_type"`
32 }
33
34-func shouldIgnoreMACDriverType(driver, key string) bool {
35- _, ignore := ignoredMACDriverTypes[driver]
36- return ignore && key == "mac_address"
37-}
38-
39-func shouldIgnorePowerOption(key string) bool {
40- _, ignore := ignoredPowerOptions[key]
41- return ignore
42-}
43-
44 // powerCLIExecutableName returns correct MAAS Power CLI executable name
45 // depending on the installation type (snap or deb package)
46 func powerCLIExecutableName() string {
47@@ -74,11 +46,13 @@ func powerCLIExecutableName() string {
48 return "maas-power"
49 }
50
51-func fmtPowerOpts(driver string, opts map[string]interface{}) []string {
52+func fmtPowerOpts(opts map[string]interface{}) []string {
53 var res []string
54
55 for k, v := range opts {
56- if shouldIgnoreMACDriverType(driver, k) || shouldIgnorePowerOption(k) {
57+ // skip 'system_id' which is added to parameters.
58+ // it has nothing to do with power driver parameters
59+ if k == "system_id" {
60 continue
61 }
62
63@@ -128,7 +102,7 @@ func PowerActivity(ctx context.Context, params PowerActivityParam) (*PowerResult
64 return nil, err
65 }
66
67- opts := fmtPowerOpts(params.DriverType, params.DriverOpts)
68+ opts := fmtPowerOpts(params.DriverOpts)
69 args := append([]string{params.Action, params.DriverType}, opts...)
70
71 log.Debug("Executing MAAS power CLI", tag.Builder().KV("args", args).KeyVals...)
72diff --git a/src/maasagent/internal/workflow/power_test.go b/src/maasagent/internal/workflow/power_test.go
73index 1682595..93a2fcb 100644
74--- a/src/maasagent/internal/workflow/power_test.go
75+++ b/src/maasagent/internal/workflow/power_test.go
76@@ -31,6 +31,10 @@ func TestFmtPowerOpts(t *testing.T) {
77 in: map[string]interface{}{"key1": "multi\nline\nstring"},
78 out: []string{"--key1", "multi\nline\nstring"},
79 },
80+ "ignore system_id": {
81+ in: map[string]interface{}{"system_id": "value1"},
82+ out: []string{},
83+ },
84 }
85
86 for name, tc := range testcases {
87@@ -38,7 +42,7 @@ func TestFmtPowerOpts(t *testing.T) {
88
89 t.Run(name, func(t *testing.T) {
90 t.Parallel()
91- res := fmtPowerOpts("driver", tc.in)
92+ res := fmtPowerOpts(tc.in)
93 assert.ElementsMatch(t, tc.out, res)
94 })
95 }
96diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
97index ba5d7c6..8a330d1 100644
98--- a/src/maasserver/models/node.py
99+++ b/src/maasserver/models/node.py
100@@ -3115,33 +3115,21 @@ class Node(CleanSave, TimestampedModel):
101 # TODO: This default ought to be in the virsh template.
102 if self.bmc is not None and self.bmc.power_type == "virsh":
103 power_params.setdefault("power_address", "qemu://localhost/system")
104- else:
105- power_params.setdefault("power_address", "")
106- power_params.setdefault("username", "")
107- power_params.setdefault("power_id", self.system_id)
108- power_params.setdefault("power_driver", "")
109- power_params.setdefault("power_pass", "")
110- power_params.setdefault("power_off_mode", "")
111-
112- # The "mac" parameter defaults to the node's boot interace MAC
113- # address, but only if not already set.
114- if "mac_address" not in power_params:
115- boot_interface = self.get_boot_interface()
116- if (
117- boot_interface is not None
118- and boot_interface.mac_address is not None
119- ):
120- power_params["mac_address"] = boot_interface.mac_address
121+ power_params.setdefault("power_id", self.system_id)
122+
123+ if self.bmc is not None and self.bmc.power_type == "ipmi":
124+ power_params.setdefault("power_off_mode", "")
125
126 # boot_mode is something that tells the template whether this is
127 # a PXE boot or a local HD boot.
128- if (
129- self.status == NODE_STATUS.DEPLOYED
130- or self.node_type != NODE_TYPE.MACHINE
131- ):
132- power_params["boot_mode"] = "local"
133- else:
134- power_params["boot_mode"] = "pxe"
135+ if self.bmc is not None and self.bmc.power_type == "amt":
136+ if (
137+ self.status == NODE_STATUS.DEPLOYED
138+ or self.node_type != NODE_TYPE.MACHINE
139+ ):
140+ power_params["boot_mode"] = "local"
141+ else:
142+ power_params["boot_mode"] = "pxe"
143
144 return power_params
145
146@@ -5857,7 +5845,8 @@ class Node(CleanSave, TimestampedModel):
147 boot_order = []
148
149 # Smuggle in a hint about how to power-off the self.
150- power_info.power_parameters["power_off_mode"] = stop_mode
151+ if power_info.power_type == "ipmi":
152+ power_info.power_parameters["power_off_mode"] = stop_mode
153
154 # Request that the node be powered off post-commit.
155 d = post_commit()
156diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
157index 075da6c..eefc848 100644
158--- a/src/maasserver/models/tests/test_node.py
159+++ b/src/maasserver/models/tests/test_node.py
160@@ -1607,31 +1607,18 @@ class TestNode(MAASServerTestCase):
161 node.system_id, node.get_effective_power_parameters()["system_id"]
162 )
163
164- def test_get_effective_power_parameters_adds_mac_if_no_params_set(self):
165- node = factory.make_Node()
166- mac = factory.make_mac_address()
167- node.add_physical_interface(mac)
168- self.assertEqual(
169- mac, node.get_effective_power_parameters()["mac_address"]
170- )
171-
172 def test_get_effective_power_parameters_adds_no_mac_if_params_set(self):
173 node = factory.make_Node(power_parameters={"foo": "bar"})
174 mac = factory.make_mac_address()
175 node.add_physical_interface(mac)
176 self.assertNotIn("mac", node.get_effective_power_parameters())
177
178- def test_get_effective_power_parameters_adds_empty_power_off_mode(self):
179- node = factory.make_Node()
180- params = node.get_effective_power_parameters()
181- self.assertEqual("", params["power_off_mode"])
182-
183 def test_get_effective_power_type_no_default_power_address_if_not_virsh(
184 self,
185 ):
186 node = factory.make_Node(power_type="manual")
187 params = node.get_effective_power_parameters()
188- self.assertEqual("", params["power_address"])
189+ self.assertEqual(None, params.get("power_address"))
190
191 def test_get_effective_power_type_defaults_power_address_if_virsh(self):
192 node = factory.make_Node(power_type="virsh")
193@@ -1639,13 +1626,13 @@ class TestNode(MAASServerTestCase):
194 self.assertEqual("qemu://localhost/system", params["power_address"])
195
196 def test_get_effective_power_parameters_sets_local_boot_mode(self):
197- node = factory.make_Node(status=NODE_STATUS.DEPLOYED)
198+ node = factory.make_Node(status=NODE_STATUS.DEPLOYED, power_type="amt")
199 params = node.get_effective_power_parameters()
200 self.assertEqual("local", params["boot_mode"])
201
202 def test_get_effective_power_parameters_sets_pxe_boot_mode(self):
203 status = factory.pick_enum(NODE_STATUS, but_not=[NODE_STATUS.DEPLOYED])
204- node = factory.make_Node(status=status)
205+ node = factory.make_Node(status=status, power_type="amt")
206 params = node.get_effective_power_parameters()
207 self.assertEqual("pxe", params["boot_mode"])
208
209@@ -2675,7 +2662,6 @@ class TestNode(MAASServerTestCase):
210 self.expectThat(node.install_rackd, Is(False))
211
212 expected_power_info = node.get_effective_power_info()
213- expected_power_info.power_parameters["power_off_mode"] = "hard"
214 node._power_control_node.assert_called_once_with(
215 d, "power_off", expected_power_info, []
216 )
217@@ -10274,7 +10260,7 @@ class TestNode_Stop(MAASServerTestCase):
218 d = self.patch_post_commit()
219 admin = factory.make_admin()
220 stop_mode = factory.make_name("stop")
221- node = self.make_acquired_node_with_interface(admin)
222+ node = self.make_acquired_node_with_interface(admin, power_type="ipmi")
223 mock_power_control = self.patch_autospec(node, "_power_control_node")
224 node.stop(admin, stop_mode=stop_mode)
225 expected_power_info = node.get_effective_power_info()
226@@ -10287,7 +10273,7 @@ class TestNode_Stop(MAASServerTestCase):
227 d = self.patch_post_commit()
228 admin = factory.make_admin()
229 stop_mode = factory.make_name("stop")
230- node = self.make_acquired_node_with_interface(admin)
231+ node = self.make_acquired_node_with_interface(admin, power_type="ipmi")
232 mock_power_control = self.patch_autospec(node, "_power_control_node")
233 node.stop(stop_mode=stop_mode)
234 expected_power_info = node.get_effective_power_info()

Subscribers

People subscribed via source and target branches