Merge ~ltrager/maas:hmcz_paused_terminated into maas:master

Proposed by Lee Trager
Status: Merged
Approved by: Lee Trager
Approved revision: aec91c1783fb58dbd6402005dac2d94734382854
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ltrager/maas:hmcz_paused_terminated
Merge into: maas:master
Prerequisite: ~ltrager/maas:condense_luns
Diff against target: 159 lines (+101/-7)
2 files modified
src/provisioningserver/drivers/power/hmcz.py (+17/-5)
src/provisioningserver/drivers/power/tests/test_hmcz.py (+84/-2)
Reviewer Review Type Date Requested Status
MAAS Lander Needs Fixing
Adam Collard (community) Approve
Review via email: mp+398576@code.launchpad.net

Commit message

Stop a partition before starting a paused or terminated partition.

When an IBM Z partition goes into a paused or terminated state it must be
stopped before it can be started. MAAS has to hold a thread on the rack
controller when this is detected while waiting for the partition to stop
before it can issue the start call. This isn't ideal and IBM is aware we
would like it changed.

To post a comment you must log in.
Revision history for this message
Adam Collard (adam-collard) wrote :

+1

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

UNIT TESTS
-b hmcz_paused_terminated lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

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

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

UNIT TESTS
-b hmcz_paused_terminated lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 268c15b8066c61d5436b453bd294603a1020b072

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

UNIT TESTS
-b hmcz_paused_terminated lp:~ltrager/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9388/console
COMMIT: 4aabb94323b86942738e07606a05d6c2b35a7498

review: Needs Fixing
~ltrager/maas:hmcz_paused_terminated updated
aec91c1... by Lee Trager

Merge branch 'master' into hmcz_paused_terminated

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/provisioningserver/drivers/power/hmcz.py b/src/provisioningserver/drivers/power/hmcz.py
2index 0f16af1..7b4dbc7 100644
3--- a/src/provisioningserver/drivers/power/hmcz.py
4+++ b/src/provisioningserver/drivers/power/hmcz.py
5@@ -89,6 +89,14 @@ class HMCZPowerDriver(PowerDriver):
6 def power_on(self, system_id: str, context: dict):
7 """Power on IBM Z DPM."""
8 partition = self._get_partition(context)
9+ status = partition.get_property("status")
10+ if status in {"paused", "terminated"}:
11+ # A "paused" or "terminated" partition can only be started if
12+ # it is stopped first. MAAS can't execute the start action until
13+ # the stop action completes. This holds the thread in MAAS for ~30s.
14+ # IBM is aware this isn't optimal for us so they are looking into
15+ # modifying IBM Z to go into a stopped state.
16+ partition.stop(wait_for_completion=True)
17 partition.start(wait_for_completion=False)
18
19 @typed
20@@ -110,12 +118,16 @@ class HMCZPowerDriver(PowerDriver):
21 # transitional state during this time. Associate the transitional
22 # state with on or off so MAAS doesn't repeatedly issue a power
23 # on or off command.
24- if status in {"starting", "active"}:
25- # When a partition is starting it can go into a "paused" state.
26- # This isn't on or off, just that the partition isn't currently
27- # executing instructions.
28+ if status in {"starting", "active", "degraded"}:
29 return "on"
30- elif status in {"stopping", "stopped"}:
31+ elif status in {"stopping", "stopped", "paused", "terminated"}:
32+ # A "paused" state isn't on or off, it just means the partition
33+ # isn't currently executing instructions. A partition can go into
34+ # a "paused" state if `shutdown -h now` is executed in the
35+ # partition. "paused" also happens when transitioning between
36+ # "starting" and "active". Consider it off so MAAS can start
37+ # it again when needed. IBM is aware this is weird and is working
38+ # on a solution.
39 return "off"
40 else:
41 return "unknown"
42diff --git a/src/provisioningserver/drivers/power/tests/test_hmcz.py b/src/provisioningserver/drivers/power/tests/test_hmcz.py
43index abaf15c..990274d 100644
44--- a/src/provisioningserver/drivers/power/tests/test_hmcz.py
45+++ b/src/provisioningserver/drivers/power/tests/test_hmcz.py
46@@ -7,7 +7,11 @@ from twisted.internet.defer import inlineCallbacks
47 from zhmcclient_mock import FakedSession
48
49 from maastesting.factory import factory
50-from maastesting.matchers import MockCalledOnce, MockCalledOnceWith
51+from maastesting.matchers import (
52+ MockCalledOnce,
53+ MockCalledOnceWith,
54+ MockNotCalled,
55+)
56 from maastesting.testcase import MAASTestCase, MAASTwistedRunTest
57 from provisioningserver.drivers.power import hmcz as hmcz_module
58 from provisioningserver.drivers.power import PowerActionError
59@@ -109,6 +113,40 @@ class TestHMCZPowerDriver(MAASTestCase):
60 mock_get_partition = self.patch(self.hmcz, "_get_partition")
61 yield self.hmcz.power_on(None, self.make_context())
62 self.assertThat(
63+ mock_get_partition.return_value.stop,
64+ MockNotCalled(),
65+ )
66+ self.assertThat(
67+ mock_get_partition.return_value.start,
68+ MockCalledOnceWith(wait_for_completion=False),
69+ )
70+
71+ @inlineCallbacks
72+ def test_power_on_stops_in_a_paused_state(self):
73+ mock_get_partition = self.patch(self.hmcz, "_get_partition")
74+ mock_get_partition.return_value.get_property.return_value = "paused"
75+ yield self.hmcz.power_on(None, self.make_context())
76+ self.assertThat(
77+ mock_get_partition.return_value.stop,
78+ MockCalledOnceWith(wait_for_completion=True),
79+ )
80+ self.assertThat(
81+ mock_get_partition.return_value.start,
82+ MockCalledOnceWith(wait_for_completion=False),
83+ )
84+
85+ @inlineCallbacks
86+ def test_power_on_stops_in_a_terminated_state(self):
87+ mock_get_partition = self.patch(self.hmcz, "_get_partition")
88+ mock_get_partition.return_value.get_property.return_value = (
89+ "terminated"
90+ )
91+ yield self.hmcz.power_on(None, self.make_context())
92+ self.assertThat(
93+ mock_get_partition.return_value.stop,
94+ MockCalledOnceWith(wait_for_completion=True),
95+ )
96+ self.assertThat(
97 mock_get_partition.return_value.start,
98 MockCalledOnceWith(wait_for_completion=False),
99 )
100@@ -167,6 +205,28 @@ class TestHMCZPowerDriver(MAASTestCase):
101 self.assertEqual("on", status)
102
103 @inlineCallbacks
104+ def test_power_query_degraded(self):
105+ power_partition_name = factory.make_name("power_partition_name")
106+ cpc = self.fake_session.hmc.cpcs.add(
107+ {
108+ "name": factory.make_name("cpc"),
109+ "dpm-enabled": True,
110+ }
111+ )
112+ cpc.partitions.add(
113+ {
114+ "name": power_partition_name,
115+ "status": "degraded",
116+ }
117+ )
118+
119+ status = yield self.hmcz.power_query(
120+ None, self.make_context(power_partition_name)
121+ )
122+
123+ self.assertEqual("on", status)
124+
125+ @inlineCallbacks
126 def test_power_query_stopping(self):
127 power_partition_name = factory.make_name("power_partition_name")
128 cpc = self.fake_session.hmc.cpcs.add(
129@@ -230,7 +290,29 @@ class TestHMCZPowerDriver(MAASTestCase):
130 None, self.make_context(power_partition_name)
131 )
132
133- self.assertEqual("unknown", status)
134+ self.assertEqual("off", status)
135+
136+ @inlineCallbacks
137+ def test_power_query_terminated(self):
138+ power_partition_name = factory.make_name("power_partition_name")
139+ cpc = self.fake_session.hmc.cpcs.add(
140+ {
141+ "name": factory.make_name("cpc"),
142+ "dpm-enabled": True,
143+ }
144+ )
145+ cpc.partitions.add(
146+ {
147+ "name": power_partition_name,
148+ "status": "terminated",
149+ }
150+ )
151+
152+ status = yield self.hmcz.power_query(
153+ None, self.make_context(power_partition_name)
154+ )
155+
156+ self.assertEqual("off", status)
157
158 @inlineCallbacks
159 def test_power_query_other(self):

Subscribers

People subscribed via source and target branches