summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-06-13 01:04:37 +0000
committerGerrit Code Review <review@openstack.org>2018-06-13 01:04:37 +0000
commitb7c90ff987e4b57a5490dd3af5527aeade2d138b (patch)
tree2a73673d1bbae43bbd646e9af42f1b665aab893c
parent3b06755cb5df4cb6ffc9b47fbc3b1180c26382dd (diff)
parented59cfef849f003d8b0bb9a5efb3b41fbc778c9c (diff)
Merge "Improve force delete"
-rw-r--r--releasenotes/notes/force-delete-changes-2e0881a99742c339.yaml6
-rw-r--r--sahara/api/v2/clusters.py8
-rw-r--r--sahara/service/heat/heat_engine.py13
-rw-r--r--sahara/tests/unit/service/test_engine.py27
-rw-r--r--sahara/tests/unit/utils/openstack/test_heat.py16
-rw-r--r--sahara/utils/openstack/heat.py9
6 files changed, 40 insertions, 39 deletions
diff --git a/releasenotes/notes/force-delete-changes-2e0881a99742c339.yaml b/releasenotes/notes/force-delete-changes-2e0881a99742c339.yaml
new file mode 100644
index 0000000..9cd4186
--- /dev/null
+++ b/releasenotes/notes/force-delete-changes-2e0881a99742c339.yaml
@@ -0,0 +1,6 @@
1---
2features:
3 - The behavior of force deletion of clusters (APIv2) has changed.
4 Stack-abandon is no longer used. The response from the force-delete
5 API call now includes the name of the stack which had underlain
6 that deleted cluster.
diff --git a/sahara/api/v2/clusters.py b/sahara/api/v2/clusters.py
index 7dce8b6..b2ae322 100644
--- a/sahara/api/v2/clusters.py
+++ b/sahara/api/v2/clusters.py
@@ -84,5 +84,11 @@ def clusters_update(cluster_id, data):
84def clusters_delete(cluster_id): 84def clusters_delete(cluster_id):
85 data = u.request_data() 85 data = u.request_data()
86 force = data.get('force', False) 86 force = data.get('force', False)
87 stack_name = api.get_cluster(cluster_id).get(
88 'extra', {}).get(
89 'heat_stack_name', None)
87 api.terminate_cluster(cluster_id, force=force) 90 api.terminate_cluster(cluster_id, force=force)
88 return u.render() 91 if force:
92 return u.render({"stack_name": stack_name}, status=200)
93 else:
94 return u.render(res=None, status=204)
diff --git a/sahara/service/heat/heat_engine.py b/sahara/service/heat/heat_engine.py
index de22e39..d76ce4c 100644
--- a/sahara/service/heat/heat_engine.py
+++ b/sahara/service/heat/heat_engine.py
@@ -19,6 +19,7 @@ from oslo_log import log as logging
19 19
20from sahara import conductor as c 20from sahara import conductor as c
21from sahara import context 21from sahara import context
22from sahara import exceptions as ex
22from sahara.i18n import _ 23from sahara.i18n import _
23from sahara.service import engine as e 24from sahara.service import engine as e
24from sahara.service.heat import commons as heat_common 25from sahara.service.heat import commons as heat_common
@@ -194,7 +195,7 @@ class HeatEngine(e.Engine):
194 def shutdown_cluster(self, cluster, force=False): 195 def shutdown_cluster(self, cluster, force=False):
195 """Shutdown specified cluster and all related resources.""" 196 """Shutdown specified cluster and all related resources."""
196 if force: 197 if force:
197 heat_shutdown = heat.abandon_stack 198 heat_shutdown = heat.lazy_delete_stack
198 else: 199 else:
199 heat_shutdown = heat.delete_stack 200 heat_shutdown = heat.delete_stack
200 201
@@ -202,11 +203,11 @@ class HeatEngine(e.Engine):
202 heat_shutdown(cluster) 203 heat_shutdown(cluster)
203 except heat_exc.HTTPNotFound: 204 except heat_exc.HTTPNotFound:
204 LOG.warning('Did not find stack for cluster.') 205 LOG.warning('Did not find stack for cluster.')
205 except heat_exc.BadRequest: 206 except ex.HeatStackException:
206 LOG.error("Can't force delete cluster.", exc_info=True) 207 raise
207 finally: 208
208 self._clean_job_executions(cluster) 209 self._clean_job_executions(cluster)
209 self._remove_db_objects(cluster) 210 self._remove_db_objects(cluster)
210 211
211 @cpo.event_wrapper( 212 @cpo.event_wrapper(
212 True, step=_('Create Heat stack'), param=('cluster', 1)) 213 True, step=_('Create Heat stack'), param=('cluster', 1))
diff --git a/sahara/tests/unit/service/test_engine.py b/sahara/tests/unit/service/test_engine.py
index 807099c..72cb4ee 100644
--- a/sahara/tests/unit/service/test_engine.py
+++ b/sahara/tests/unit/service/test_engine.py
@@ -127,42 +127,27 @@ class TestDeletion(base.SaharaTestCase):
127 127
128 @mock.patch('sahara.service.heat.heat_engine.LOG.error') 128 @mock.patch('sahara.service.heat.heat_engine.LOG.error')
129 @mock.patch('sahara.utils.openstack.heat.delete_stack') 129 @mock.patch('sahara.utils.openstack.heat.delete_stack')
130 @mock.patch('sahara.utils.openstack.heat.abandon_stack') 130 @mock.patch('sahara.utils.openstack.heat.lazy_delete_stack')
131 @mock.patch('sahara.service.engine.Engine._remove_db_objects') 131 @mock.patch('sahara.service.engine.Engine._remove_db_objects')
132 @mock.patch('sahara.service.engine.Engine._clean_job_executions') 132 @mock.patch('sahara.service.engine.Engine._clean_job_executions')
133 def test_force_delete_calls(self, cj, rdb, abandon, delete, log_err): 133 def test_force_delete_calls(self, cj, rdb, lazy_delete, delete, log_err):
134 engine = heat_engine.HeatEngine() 134 engine = heat_engine.HeatEngine()
135 135
136 # Force delete when Heat service support abandon 136 # Force delete (lazy_delete)
137 engine.shutdown_cluster(mock.Mock(), force=True) 137 engine.shutdown_cluster(mock.Mock(), force=True)
138 self.assertEqual(delete.call_count, 0) 138 self.assertEqual(delete.call_count, 0)
139 self.assertEqual(abandon.call_count, 1) 139 self.assertEqual(lazy_delete.call_count, 1)
140 self.assertEqual(cj.call_count, 1) 140 self.assertEqual(cj.call_count, 1)
141 self.assertEqual(rdb.call_count, 1) 141 self.assertEqual(rdb.call_count, 1)
142 142
143 delete.reset_mock() 143 delete.reset_mock()
144 abandon.reset_mock() 144 lazy_delete.reset_mock()
145 rdb.reset_mock() 145 rdb.reset_mock()
146 cj.reset_mock() 146 cj.reset_mock()
147 147
148 # Regular delete 148 # Regular delete
149 engine.shutdown_cluster(mock.Mock(), force=False) 149 engine.shutdown_cluster(mock.Mock(), force=False)
150 self.assertEqual(delete.call_count, 1) 150 self.assertEqual(delete.call_count, 1)
151 self.assertEqual(abandon.call_count, 0) 151 self.assertEqual(lazy_delete.call_count, 0)
152 self.assertEqual(cj.call_count, 1) 152 self.assertEqual(cj.call_count, 1)
153 self.assertEqual(rdb.call_count, 1) 153 self.assertEqual(rdb.call_count, 1)
154
155 delete.reset_mock()
156 abandon.reset_mock()
157 rdb.reset_mock()
158 cj.reset_mock()
159
160 # Force delete when stack abandon unavailable
161 abandon.side_effect = heat_exc.BadRequest()
162 engine.shutdown_cluster(mock.Mock(), force=True)
163 self.assertEqual(delete.call_count, 0)
164 self.assertEqual(abandon.call_count, 1)
165 self.assertEqual(cj.call_count, 1)
166 self.assertEqual(rdb.call_count, 1)
167 log_err.assert_called_once_with(
168 "Can't force delete cluster.", exc_info=True)
diff --git a/sahara/tests/unit/utils/openstack/test_heat.py b/sahara/tests/unit/utils/openstack/test_heat.py
index ca2d789..53d858e 100644
--- a/sahara/tests/unit/utils/openstack/test_heat.py
+++ b/sahara/tests/unit/utils/openstack/test_heat.py
@@ -20,20 +20,26 @@ from sahara.utils.openstack import heat as heat_u
20 20
21 21
22class HeatClientTest(testtools.TestCase): 22class HeatClientTest(testtools.TestCase):
23 @mock.patch('sahara.utils.openstack.heat.get_stack')
23 @mock.patch('heatclient.client.Client') 24 @mock.patch('heatclient.client.Client')
24 @mock.patch('sahara.utils.openstack.base.url_for') 25 @mock.patch('sahara.utils.openstack.base.url_for')
25 @mock.patch('sahara.service.sessions.cache') 26 @mock.patch('sahara.service.sessions.cache')
26 @mock.patch('sahara.context.ctx') 27 @mock.patch('sahara.context.ctx')
27 def test_abandon(self, ctx, cache, url_for, heat): 28 def test_deleting(self, ctx, cache, url_for, heat, get_stack):
28 class _FakeHeatStacks(object): 29 class _FakeHeatStacks(object):
29 def delete(self, stack): 30 def delete(self, stack):
30 call_list.append("delete") 31 call_list.append("delete")
31 32
32 def abandon(self, stack): 33 call_list = None
33 call_list.append("abandon") 34 get_stack.return_value = None
35 get_stack.side_effect = lambda *args, **kwargs: call_list.append("get")
34 36
35 heat.return_value.stacks = _FakeHeatStacks() 37 heat.return_value.stacks = _FakeHeatStacks()
36 38
37 call_list = [] 39 call_list = []
38 heat_u.abandon_stack(mock.Mock()) 40 heat_u.lazy_delete_stack(mock.Mock())
39 self.assertEqual(call_list, ["delete", "abandon"]) 41 self.assertEqual(call_list, ["delete"])
42
43 call_list = []
44 heat_u.delete_stack(mock.Mock())
45 self.assertEqual(call_list, ["delete", "get"])
diff --git a/sahara/utils/openstack/heat.py b/sahara/utils/openstack/heat.py
index 8e7d9f3..ff1ae40 100644
--- a/sahara/utils/openstack/heat.py
+++ b/sahara/utils/openstack/heat.py
@@ -84,13 +84,10 @@ def delete_stack(cluster):
84 stack = get_stack(stack_name, raise_on_missing=False) 84 stack = get_stack(stack_name, raise_on_missing=False)
85 85
86 86
87def abandon_stack(cluster): 87def lazy_delete_stack(cluster):
88 '''Put stack in deleting state if not already, then abandon it.''' 88 '''Attempt to delete stack once, but do not await successful deletion'''
89 heat_client = client()
90 stack_name = cluster.stack_name 89 stack_name = cluster.stack_name
91 base.execute_with_retries(heat_client.stacks.delete, stack_name) 90 base.execute_with_retries(client().stacks.delete, stack_name)
92 # TODO(jfreud): sleep between calls?
93 base.execute_with_retries(heat_client.stacks.abandon, stack_name)
94 91
95 92
96def get_stack_outputs(cluster): 93def get_stack_outputs(cluster):