From 65f416745f6026ec410ba3e86c1cde79192b8b85 Mon Sep 17 00:00:00 2001 From: yinxiulin Date: Wed, 14 Sep 2016 16:59:50 +0800 Subject: [PATCH] Nova_APIGW Server Action Support(part2) 1. What is the problem The current Nova_APIGW does not support following server actions: reboot: Reboots a server. resize: Resizes a server. confirmResize: Confirms a pending resize action for a server. revertResize: Cancels and reverts a pending resize action for a server. os-resetState: Resets the state of a server. 2. What is the solution to the problem Implement the above server action 3. What the features need to be implemented to the Tricircle to realize the solution Add the above server action Change-Id: Ia3d0de1a42320cb1ee55b25210b227cb34a829a9 --- tricircle/common/constants.py | 3 + tricircle/nova_apigw/controllers/action.py | 19 ++- tricircle/tempestplugin/tempest_compute.sh | 30 +++-- .../nova_apigw/controllers/test_action.py | 112 +++++++++++++++++- 4 files changed, 150 insertions(+), 14 deletions(-) diff --git a/tricircle/common/constants.py b/tricircle/common/constants.py index f66d00bc..b9b38b5e 100644 --- a/tricircle/common/constants.py +++ b/tricircle/common/constants.py @@ -97,3 +97,6 @@ NOVA_MICRO_VERSION_PREFIX = 'compute' # support nova version range NOVA_APIGW_MIN_VERSION = '2.1' NOVA_APIGW_MAX_VERSION = '2.36' + +# server action url(part url) +SERVER_ACTION_URL = '/servers/%s/action' diff --git a/tricircle/nova_apigw/controllers/action.py b/tricircle/nova_apigw/controllers/action.py index 2a4a6b48..2bb56956 100644 --- a/tricircle/nova_apigw/controllers/action.py +++ b/tricircle/nova_apigw/controllers/action.py @@ -49,7 +49,12 @@ class ActionController(rest.RestController): 'unshelve': self._handle_unshelve, 'shelveOffload': self._handle_shelve_offload, 'migrate': self._handle_migrate, - 'trigger_crash_dump': self._handle_trigger_crash_dump + 'trigger_crash_dump': self._handle_trigger_crash_dump, + 'reboot': self._handle_action, + 'resize': self._handle_action, + 'confirmResize': self._handle_action, + 'revertResize': self._handle_action, + 'os-resetState': self._handle_action } def _get_client(self, pod_name=constants.TOP): @@ -114,6 +119,18 @@ class ActionController(rest.RestController): client = self._get_client(pod_name) return client.action_servers(context, 'migrate', self.server_id) + def _handle_action(self, context, pod_name, body): + """Perform a server action + + :param pod_name: the bottom pod name. + + :param body: action parameters body. + """ + url = constants.SERVER_ACTION_URL % self.server_id + api = self._get_client(pod_name).get_native_client(constants.RT_SERVER, + context) + return api.client.post(url, body=body) + @expose(generic=True, template='json') def post(self, **kw): context = t_context.extract_context_from_environ() diff --git a/tricircle/tempestplugin/tempest_compute.sh b/tricircle/tempestplugin/tempest_compute.sh index 19c8292f..5594bcc9 100755 --- a/tricircle/tempestplugin/tempest_compute.sh +++ b/tricircle/tempestplugin/tempest_compute.sh @@ -36,10 +36,16 @@ TESTCASES="$TESTCASES|tempest.api.compute.servers.test_servers_negative.ServersN TESTCASES="$TESTCASES|tempest.api.compute.servers.test_servers_negative.ServersNegativeTestJSON.test_suspend_server_invalid_state" TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_migrate_non_existent_server" TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_migrate_server_invalid_state" -# add new test cases like following line for volume_type test -# TESTCASES="$TESTCASES|tempest.api.volume.admin.test_volumes_type" +TESTCASES="$TESTCASES|tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_hard" +TESTCASES="$TESTCASES|tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_soft" +TESTCASES="$TESTCASES|tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_revert" +TESTCASES="$TESTCASES|tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm" +TESTCASES="$TESTCASES|tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm_from_stopped" +TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_reset_state_server" +TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_state" +TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_type" +TESTCASES="$TESTCASES|tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_nonexistent_server" TESTCASES="$TESTCASES)" - ostestr --regex $TESTCASES # --------------------- IMPORTANT begin -------------------- # @@ -212,13 +218,13 @@ ostestr --regex $TESTCASES # tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_list_servers_filter_by_exist_host[id-86c7a8f7-50cf-43a9-9bac-5b985317134f] # tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_rebuild_server_in_error_state[id-682cb127-e5bb-4f53-87ce-cb9003604442] # tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_reset_network_inject_network_info[id-7a1323b4-a6a2-497a-96cb-76c07b945c71] -# tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_reset_state_server[id-ee8ae470-db70-474d-b752-690b7892cab1] +# **DONE** tempest.api.compute.admin.test_servers.ServersAdminTestJSON.test_reset_state_server[id-ee8ae470-db70-474d-b752-690b7892cab1] # tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_get_server_diagnostics_by_non_admin[id-e84e2234-60d2-42fa-8b30-e2d3049724ac,negative] # **DONE** tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_migrate_non_existent_server[id-46a4e1ca-87ae-4d28-987a-1b6b136a0221,negative] # **DONE** tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_migrate_server_invalid_state[id-b0b17f83-d14e-4fc4-8f31-bcc9f3cfa629,negative] -# tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_state[id-b0b4d8af-1256-41ef-9ee7-25f1c19dde80,negative] -# tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_type[id-4cdcc984-fab0-4577-9a9d-6d558527ee9d,negative] -# tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_nonexistent_server[id-e741298b-8df2-46f0-81cb-8f814ff2504c,negative] +# **DONE** tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_state[id-b0b4d8af-1256-41ef-9ee7-25f1c19dde80,negative] +# **DONE** tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_invalid_type[id-4cdcc984-fab0-4577-9a9d-6d558527ee9d,negative] +# **DONE** tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_reset_state_server_nonexistent_server[id-e741298b-8df2-46f0-81cb-8f814ff2504c,negative] # tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_resize_server_using_overlimit_ram[id-28dcec23-f807-49da-822c-56a92ea3c687,negative] # tempest.api.compute.admin.test_servers_negative.ServersAdminNegativeTestJSON.test_resize_server_using_overlimit_vcpus[id-7368a427-2f26-4ad9-9ba9-911a0ec2b0db,negative] # tempest.api.compute.admin.test_servers_on_multinodes.ServersOnMultiNodesTest.test_create_servers_on_different_hosts[id-cc7ca884-6e3e-42a3-a92f-c522fcf25e8e] @@ -448,13 +454,13 @@ ostestr --regex $TESTCASES # tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_get_vnc_console[id-c6bc11bf-592e-4015-9319-1c98dc64daf5] # **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_lock_unlock_server[id-80a8094c-211e-440a-ab88-9e59d556c7ee] # **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_pause_unpause_server[id-bd61a9fd-062f-4670-972b-2d6c3e3b9e73] -# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_hard[id-2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32,smoke] -# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_soft[id-4640e3ef-a5df-482e-95a1-ceeeb0faa84d] +# **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_hard[id-2cb1baf6-ac8d-4429-bf0d-ba8a0ba53e32,smoke] +# **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_reboot_server_soft[id-4640e3ef-a5df-482e-95a1-ceeeb0faa84d] # tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_rebuild_server[id-aaa6cdf3-55a7-461a-add9-1c8596b9a07c] # tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_rebuild_server_in_stop_state[id-30449a88-5aff-4f9b-9866-6ee9b17f906d] -# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm[id-1499262a-9328-4eda-9068-db1ac57498d2] -# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm_from_stopped[id-138b131d-66df-48c9-a171-64f45eb92962] -# tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_revert[id-c03aab19-adb1-44f5-917d-c419577e9e68] +# **DONE**tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm[id-1499262a-9328-4eda-9068-db1ac57498d2] +# **DONE**tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_confirm_from_stopped[id-138b131d-66df-48c9-a171-64f45eb92962] +# **DONE**tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_resize_server_revert[id-c03aab19-adb1-44f5-917d-c419577e9e68] # **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_shelve_unshelve_server[id-77eba8e0-036e-4635-944b-f7a8f3b78dc9] # **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_stop_start_server[id-af8eafd4-38a7-4a4b-bdbc-75145a580560] # **DONE** tempest.api.compute.servers.test_server_actions.ServerActionsTestJSON.test_suspend_resume_server[id-0d8ee21e-b749-462d-83da-b85b41c86c7f] diff --git a/tricircle/tests/unit/nova_apigw/controllers/test_action.py b/tricircle/tests/unit/nova_apigw/controllers/test_action.py index 08cab7b7..9396350f 100644 --- a/tricircle/tests/unit/nova_apigw/controllers/test_action.py +++ b/tricircle/tests/unit/nova_apigw/controllers/test_action.py @@ -14,6 +14,7 @@ # under the License. from mock import patch +from novaclient.client import HTTPClient import pecan import unittest @@ -40,8 +41,9 @@ class ActionTest(unittest.TestCase): def setUp(self): core.initialize() core.ModelBase.metadata.create_all(core.get_engine()) - self.context = context.get_admin_context() + self.context = context.Context() self.project_id = 'test_project' + self.context.tenant = self.project_id self.controller = action.ActionController(self.project_id, '') def _prepare_pod(self, bottom_pod_num=1): @@ -63,6 +65,13 @@ class ActionTest(unittest.TestCase): b_pods.append(b_pod) return t_pod, b_pods + def _prepare_pod_service(self, pod_id, service): + config_dict = {'service_id': uuidutils.generate_uuid(), + 'pod_id': pod_id, + 'service_type': service, + 'service_url': 'fake_pod_service'} + api.create_pod_service_configuration(self.context, config_dict) + def _prepare_server(self, pod): t_server_id = uuidutils.generate_uuid() b_server_id = t_server_id @@ -359,5 +368,106 @@ class ActionTest(unittest.TestCase): 'server', self.context, 'migrate', t_server_id) self.assertEqual(202, res.status) + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_confirm_resize_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"confirmResize": ''} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_revert_resize_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"revertResize": ''} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_resize_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"resize": {"flavorRef": "2"}} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_reset_state_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"os-resetState": {"state": "active"}} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_soft_reboot_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"reboot": {"type": "SOFT"}} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + + @patch.object(pecan, 'response', new=FakeResponse) + @patch.object(HTTPClient, 'post') + @patch.object(context, 'extract_context_from_environ') + def test_hard_reboot_action(self, mock_context, mock_action): + mock_context.return_value = self.context + mock_action.return_value = (FakeResponse(202), None) + t_pod, b_pods = self._prepare_pod() + self._prepare_pod_service(b_pods[0]['pod_id'], constants.ST_NOVA) + t_server_id = self._prepare_server(b_pods[0]) + self.controller.server_id = t_server_id + body = {"reboot": {"type": "HARD"}} + res = self.controller.post(**body) + url = '/servers/%s/action' % t_server_id + mock_action.assert_called_once_with(url, body=body) + self.assertEqual(202, res.status) + def tearDown(self): core.ModelBase.metadata.drop_all(core.get_engine())