Merge ~ack/maas:pod-delete-decompose-flag into maas:master

Proposed by Alberto Donato
Status: Merged
Approved by: Alberto Donato
Approved revision: 820b751bf8d5a734ff63881ff25c6575aa7e27e3
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~ack/maas:pod-delete-decompose-flag
Merge into: maas:master
Prerequisite: ~ack/maas:drop-machine-creation-type
Diff against target: 200 lines (+76/-9)
7 files modified
src/maasserver/api/pods.py (+9/-2)
src/maasserver/api/tests/test_pods.py (+16/-2)
src/maasserver/forms/pods.py (+6/-1)
src/maasserver/models/bmc.py (+5/-3)
src/maasserver/models/tests/test_bmc.py (+26/-0)
src/maasserver/websockets/handlers/pod.py (+2/-1)
src/maasserver/websockets/handlers/tests/test_pod.py (+12/-0)
Reviewer Review Type Date Requested Status
MAAS Lander Approve
Björn Tillenius Approve
Review via email: mp+396937@code.launchpad.net

Commit message

add decompose flag to API and websocket when removing pod

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

UNIT TESTS
-b pod-delete-decompose-flag lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 1eb4c3c85628fe4bed8beae08a1c21f08a677415

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

UNIT TESTS
-b pod-delete-decompose-flag lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 732faca42dd615c6e2c5e13dc7db6e5a17f20d77

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

+1 with a small comment inline.

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

LANDING
-b pod-delete-decompose-flag lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: FAILED BUILD
LOG: http://maas-ci.internal:8080/job/maas/job/branch-tester/9113/consoleText

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

UNIT TESTS
-b pod-delete-decompose-flag lp:~ack/maas/+git/maas into -b master lp:~maas-committers/maas

STATUS: SUCCESS
COMMIT: 820b751bf8d5a734ff63881ff25c6575aa7e27e3

review: Approve

There was an error fetching revisions from git servers. Please try again in a few minutes. If the problem persists, contact Launchpad support.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/api/pods.py b/src/maasserver/api/pods.py
2index a0097b3..b6b1686 100644
3--- a/src/maasserver/api/pods.py
4+++ b/src/maasserver/api/pods.py
5@@ -12,7 +12,7 @@ from piston3.utils import rc
6 from maasserver.api.support import admin_method, operation, OperationsHandler
7 from maasserver.api.utils import get_mandatory_param
8 from maasserver.exceptions import MAASAPIValidationError
9-from maasserver.forms.pods import ComposeMachineForm, PodForm
10+from maasserver.forms.pods import ComposeMachineForm, DeletePodForm, PodForm
11 from maasserver.models.bmc import Pod
12 from maasserver.permissions import PodPermission
13 from provisioningserver.drivers.pod import Capabilities
14@@ -170,6 +170,9 @@ class PodHandler(OperationsHandler):
15 @description Deletes a pod with the given pod ID.
16
17 @param (int) "{id}" [required=true] The pod's ID.
18+ @param (boolean) "decompose" [required=false] Whether to also also
19+ decompose all machines in the pod on removal. If not provided, machines
20+ will not be removed.
21
22 @success (http-status-code) "204" 204
23
24@@ -183,9 +186,13 @@ class PodHandler(OperationsHandler):
25 to delete the pod.
26 @error-example (content) "no-perms"
27 This method is reserved for admin users.
28+
29 """
30 pod = Pod.objects.get_pod_or_404(id, request.user, PodPermission.edit)
31- pod.delete_and_wait()
32+ form = DeletePodForm(data=request.GET)
33+ if not form.is_valid():
34+ raise MAASAPIValidationError(form.errors)
35+ pod.delete_and_wait(decompose=form.cleaned_data["decompose"])
36 return rc.DELETED
37
38 @admin_method
39diff --git a/src/maasserver/api/tests/test_pods.py b/src/maasserver/api/tests/test_pods.py
40index edb6b86..41142aa 100644
41--- a/src/maasserver/api/tests/test_pods.py
42+++ b/src/maasserver/api/tests/test_pods.py
43@@ -573,7 +573,6 @@ class TestPodAPIAdmin(PodAPITestForAdmin, PodMixin):
44 pod = factory.make_Pod()
45 for _ in range(3):
46 factory.make_Machine(bmc=pod)
47- factory.make_Machine(bmc=pod, dynamic=True)
48 mock_eventual = MagicMock()
49 mock_async_delete = self.patch(Pod, "async_delete")
50 mock_async_delete.return_value = mock_eventual
51@@ -581,7 +580,22 @@ class TestPodAPIAdmin(PodAPITestForAdmin, PodMixin):
52 self.assertEqual(
53 http.client.NO_CONTENT, response.status_code, response.content
54 )
55- self.assertThat(mock_eventual.wait, MockCalledOnceWith(60 * 7))
56+ self.assertThat(mock_eventual.wait, MockCalledOnceWith(60))
57+
58+ def test_DELETE_calls_async_delete_decompose(self):
59+ pod = factory.make_Pod()
60+ for _ in range(3):
61+ factory.make_Machine(bmc=pod)
62+ mock_eventual = MagicMock()
63+ mock_async_delete = self.patch(Pod, "async_delete")
64+ mock_async_delete.return_value = mock_eventual
65+ response = self.client.delete(
66+ get_pod_uri(pod), query={"decompose": True}
67+ )
68+ self.assertEqual(
69+ http.client.NO_CONTENT, response.status_code, response.content
70+ )
71+ self.assertThat(mock_eventual.wait, MockCalledOnceWith(60 * 4))
72
73 def test_add_tag_to_pod(self):
74 pod = factory.make_Pod()
75diff --git a/src/maasserver/forms/pods.py b/src/maasserver/forms/pods.py
76index 4efe200..8bafaf5 100644
77--- a/src/maasserver/forms/pods.py
78+++ b/src/maasserver/forms/pods.py
79@@ -952,7 +952,12 @@ class ComposeMachineForPodsForm(forms.Form):
80 self.valid_pod_forms = [
81 pod_form for pod_form in self.pod_forms if pod_form.is_valid()
82 ]
83- if len(self.valid_pod_forms) == 0:
84+ if not self.valid_pod_forms:
85 self.add_error(
86 "__all__", "No current pod resources match constraints."
87 )
88+
89+
90+class DeletePodForm(forms.Form):
91+
92+ decompose = BooleanField(required=False, initial=False)
93diff --git a/src/maasserver/models/bmc.py b/src/maasserver/models/bmc.py
94index 58a4de1..9376837 100644
95--- a/src/maasserver/models/bmc.py
96+++ b/src/maasserver/models/bmc.py
97@@ -1733,7 +1733,7 @@ class Pod(BMC):
98 "an asynchronous action."
99 )
100
101- def delete_and_wait(self):
102+ def delete_and_wait(self, decompose=False):
103 """Block the current thread while waiting for the pod to be deleted.
104
105 This must not be called from a deferToDatabase thread; use the
106@@ -1743,8 +1743,10 @@ class Pod(BMC):
107 # machines. We allow maximum of 60 seconds per machine plus 60 seconds
108 # for the pod.
109 pod = self.as_pod()
110- num_machines = Machine.objects.filter(bmc=pod).count()
111- pod.async_delete().wait((num_machines * 60) + 60)
112+ machine_wait = 0
113+ if decompose:
114+ machine_wait = Machine.objects.filter(bmc=pod).count() * 60
115+ pod.async_delete(decompose=decompose).wait(machine_wait + 60)
116
117 @asynchronous
118 def async_delete(self, decompose=False):
119diff --git a/src/maasserver/models/tests/test_bmc.py b/src/maasserver/models/tests/test_bmc.py
120index cb938ca..f30458a 100644
121--- a/src/maasserver/models/tests/test_bmc.py
122+++ b/src/maasserver/models/tests/test_bmc.py
123@@ -2957,6 +2957,32 @@ class TestPodDelete(MAASTransactionServerTestCase):
124 self.assertIsNone(delete_machine)
125 self.assertIsNone(pod)
126
127+ @wait_for_reactor
128+ @inlineCallbacks
129+ def test_delete_and_wait_with_decompose(self):
130+ pod = yield deferToDatabase(factory.make_Pod)
131+ yield deferToDatabase(factory.make_Machine, bmc=pod)
132+ yield deferToDatabase(factory.make_Machine, bmc=pod)
133+ mock_result = Mock()
134+ mock_async_delete = self.patch(pod, "async_delete")
135+ mock_async_delete.return_value = mock_result
136+ yield deferToDatabase(pod.delete_and_wait, decompose=True)
137+ mock_async_delete.assert_called_with(decompose=True)
138+ mock_result.wait.assert_called_with(180)
139+
140+ @wait_for_reactor
141+ @inlineCallbacks
142+ def test_delete_and_wait_no_decompose(self):
143+ pod = yield deferToDatabase(factory.make_Pod)
144+ yield deferToDatabase(factory.make_Machine, bmc=pod)
145+ yield deferToDatabase(factory.make_Machine, bmc=pod)
146+ mock_result = Mock()
147+ mock_async_delete = self.patch(pod, "async_delete")
148+ mock_async_delete.return_value = mock_result
149+ yield deferToDatabase(pod.delete_and_wait)
150+ mock_async_delete.assert_called_with(decompose=False)
151+ mock_result.wait.assert_called_with(60)
152+
153
154 class TestPodDefaultMACVlanMode(MAASServerTestCase):
155 def test_allows_default_macvlan_mode(self):
156diff --git a/src/maasserver/websockets/handlers/pod.py b/src/maasserver/websockets/handlers/pod.py
157index c337ff3..5e0b5b5 100644
158--- a/src/maasserver/websockets/handlers/pod.py
159+++ b/src/maasserver/websockets/handlers/pod.py
160@@ -329,8 +329,9 @@ class PodHandler(TimestampedModelHandler):
161 raise HandlerPermissionError()
162 return obj
163
164+ decompose = params.get("decompose", False)
165 d = deferToDatabase(get_object, params)
166- d.addCallback(lambda pod: pod.async_delete())
167+ d.addCallback(lambda pod: pod.async_delete(decompose=decompose))
168 return d
169
170 @asynchronous
171diff --git a/src/maasserver/websockets/handlers/tests/test_pod.py b/src/maasserver/websockets/handlers/tests/test_pod.py
172index 572c204..d9c485c 100644
173--- a/src/maasserver/websockets/handlers/tests/test_pod.py
174+++ b/src/maasserver/websockets/handlers/tests/test_pod.py
175@@ -14,6 +14,7 @@ from twisted.internet.defer import inlineCallbacks, succeed
176 from maasserver.enum import INTERFACE_TYPE
177 from maasserver.forms import pods
178 from maasserver.forms.pods import PodForm
179+from maasserver.models import Pod
180 from maasserver.models.virtualmachine import MB, VirtualMachineInterface
181 from maasserver.testing.factory import factory
182 from maasserver.testing.testcase import MAASTransactionServerTestCase
183@@ -522,6 +523,17 @@ class TestPodHandler(MAASTransactionServerTestCase):
184
185 @wait_for_reactor
186 @inlineCallbacks
187+ def test_delete_decompose(self):
188+ user = yield deferToDatabase(factory.make_admin)
189+ handler = PodHandler(user, {}, None)
190+ pod = yield deferToDatabase(self.make_pod_with_hints)
191+ mock_async_delete = self.patch(Pod, "async_delete")
192+ yield deferToDatabase(factory.make_Machine, bmc=pod)
193+ yield handler.delete({"id": pod.id, "decompose": True})
194+ mock_async_delete.assert_called_once_with(decompose=True)
195+
196+ @wait_for_reactor
197+ @inlineCallbacks
198 def test_create(self):
199 user = yield deferToDatabase(factory.make_admin)
200 handler = PodHandler(user, {}, None)

Subscribers

People subscribed via source and target branches