From c212f3f6afc69b25ff12de55846bb4da8472bb1e Mon Sep 17 00:00:00 2001 From: Seungju Baek Date: Tue, 22 Aug 2023 00:39:41 +0900 Subject: [PATCH] Implemented heat 'stack suspend' and 'stack resume' function Added suspend_stack function to support stack resources suspend. Added resume_stack function to support stack resources resume. Also implemented functional test and unit test code. Change-Id: Idf4befef149e945a419a3434886a8ba5c76481c6 --- doc/source/user/proxies/orchestration.rst | 2 +- openstack/orchestration/v1/_proxy.py | 20 +++++++++++ openstack/orchestration/v1/stack.py | 18 ++++++++++ .../functional/orchestration/v1/test_stack.py | 23 ++++++++++++ .../tests/unit/orchestration/v1/test_proxy.py | 16 +++++++++ .../tests/unit/orchestration/v1/test_stack.py | 36 +++++++++++++++++++ ...k-suspend-and-resume-26d4fc5904291d5d.yaml | 4 +++ 7 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/add-stack-suspend-and-resume-26d4fc5904291d5d.yaml diff --git a/doc/source/user/proxies/orchestration.rst b/doc/source/user/proxies/orchestration.rst index 86662b7b1..2e8721b31 100644 --- a/doc/source/user/proxies/orchestration.rst +++ b/doc/source/user/proxies/orchestration.rst @@ -18,7 +18,7 @@ Stack Operations .. autoclass:: openstack.orchestration.v1._proxy.Proxy :noindex: - :members: create_stack, check_stack, update_stack, delete_stack, find_stack, + :members: create_stack, check_stack, update_stack, delete_stack, find_stack, suspend_stack, resume_stack, get_stack, get_stack_environment, get_stack_files, get_stack_template, stacks, validate_template, resources, export_stack diff --git a/openstack/orchestration/v1/_proxy.py b/openstack/orchestration/v1/_proxy.py index 9ada733e7..908c73fa6 100644 --- a/openstack/orchestration/v1/_proxy.py +++ b/openstack/orchestration/v1/_proxy.py @@ -240,6 +240,26 @@ class Proxy(proxy.Proxy): obj = self._find(_stack.Stack, stack, ignore_missing=False) return obj.export(self) + def suspend_stack(self, stack): + """Suspend a stack status + + :param stack: The value can be either the ID of a stack or an instance + of :class:`~openstack.orchestration.v1.stack.Stack`. + :returns: ``None`` + """ + res = self._get_resource(_stack.Stack, stack) + res.suspend(self) + + def resume_stack(self, stack): + """Resume a stack status + + :param stack: The value can be either the ID of a stack or an instance + of :class:`~openstack.orchestration.v1.stack.Stack`. + :returns: ``None`` + """ + res = self._get_resource(_stack.Stack, stack) + res.resume(self) + def get_stack_template(self, stack): """Get template used by a stack diff --git a/openstack/orchestration/v1/stack.py b/openstack/orchestration/v1/stack.py index a62e0017e..d7871e53c 100644 --- a/openstack/orchestration/v1/stack.py +++ b/openstack/orchestration/v1/stack.py @@ -192,6 +192,24 @@ class Stack(resource.Resource): exceptions.raise_from_response(resp) return resp.json() + def suspend(self, session): + """Suspend a stack + + :param session: The session to use for making this request + :returns: None + """ + body = {"suspend": None} + self._action(session, body) + + def resume(self, session): + """Resume a stack + + :param session: The session to use for making this request + :returns: None + """ + body = {"resume": None} + self._action(session, body) + def fetch( self, session, diff --git a/openstack/tests/functional/orchestration/v1/test_stack.py b/openstack/tests/functional/orchestration/v1/test_stack.py index 4abb3a556..bc561da35 100644 --- a/openstack/tests/functional/orchestration/v1/test_stack.py +++ b/openstack/tests/functional/orchestration/v1/test_stack.py @@ -80,3 +80,26 @@ class TestStack(base.BaseFunctionalTest): def test_list(self): names = [o.name for o in self.conn.orchestration.stacks()] self.assertIn(self.NAME, names) + + def test_suspend_resume(self): + # given + suspend_status = "SUSPEND_COMPLETE" + resume_status = "RESUME_COMPLETE" + + # when + self.conn.orchestration.suspend_stack(self.stack) + sot = self.conn.orchestration.wait_for_status( + self.stack, suspend_status, wait=self._wait_for_timeout + ) + + # then + self.assertEqual(suspend_status, sot.status) + + # when + self.conn.orchestration.resume_stack(self.stack) + sot = self.conn.orchestration.wait_for_status( + self.stack, resume_status, wait=self._wait_for_timeout + ) + + # then + self.assertEqual(resume_status, sot.status) diff --git a/openstack/tests/unit/orchestration/v1/test_proxy.py b/openstack/tests/unit/orchestration/v1/test_proxy.py index 0e3af53df..583d64b65 100644 --- a/openstack/tests/unit/orchestration/v1/test_proxy.py +++ b/openstack/tests/unit/orchestration/v1/test_proxy.py @@ -150,6 +150,22 @@ class TestOrchestrationStack(TestOrchestrationProxy): expected_args=[self.proxy], ) + def test_suspend_stack(self): + self._verify( + 'openstack.orchestration.v1.stack.Stack.suspend', + self.proxy.suspend_stack, + method_args=['stack'], + expected_args=[self.proxy], + ) + + def test_resume_stack(self): + self._verify( + 'openstack.orchestration.v1.stack.Stack.resume', + self.proxy.resume_stack, + method_args=['stack'], + expected_args=[self.proxy], + ) + def test_delete_stack(self): self.verify_delete(self.proxy.delete_stack, stack.Stack, False) diff --git a/openstack/tests/unit/orchestration/v1/test_stack.py b/openstack/tests/unit/orchestration/v1/test_stack.py index 1a0bd8f96..a6b7bcb61 100644 --- a/openstack/tests/unit/orchestration/v1/test_stack.py +++ b/openstack/tests/unit/orchestration/v1/test_stack.py @@ -340,3 +340,39 @@ class TestStack(base.TestCase): FAKE_UPDATE_PREVIEW_RESPONSE['unchanged'], ret.unchanged ) self.assertEqual(FAKE_UPDATE_PREVIEW_RESPONSE['updated'], ret.updated) + + def test_suspend(self): + sess = mock.Mock() + + mock_response = mock.Mock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.json.return_value = {} + sess.post = mock.Mock(return_value=mock_response) + url = "stacks/%s/actions" % FAKE_ID + body = {"suspend": None} + sot = stack.Stack(**FAKE) + + res = sot.suspend(sess) + + self.assertIsNone(res) + sess.post.assert_called_with(url, json=body, microversion=None) + + def test_resume(self): + sess = mock.Mock() + + mock_response = mock.Mock() + mock_response.status_code = 200 + mock_response.headers = {} + mock_response.json.return_value = {} + sess.post = mock.Mock(return_value=mock_response) + url = "stacks/%s/actions" % FAKE_ID + + body = {"resume": None} + + sot = stack.Stack(**FAKE) + + res = sot.resume(sess) + + self.assertIsNone(res) + sess.post.assert_called_with(url, json=body, microversion=None) diff --git a/releasenotes/notes/add-stack-suspend-and-resume-26d4fc5904291d5d.yaml b/releasenotes/notes/add-stack-suspend-and-resume-26d4fc5904291d5d.yaml new file mode 100644 index 000000000..fa3eecd46 --- /dev/null +++ b/releasenotes/notes/add-stack-suspend-and-resume-26d4fc5904291d5d.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + Adds ``suspend_stack`` and ``resume_stack`` to support stack non-lifecycle operations. \ No newline at end of file