From 82255a0e099908b23c71f3f614d1bb1e9275c0a4 Mon Sep 17 00:00:00 2001 From: songwenping Date: Wed, 24 Feb 2021 17:12:36 +0800 Subject: [PATCH] Add ARQ_UNBIND_FAILED status for ARQ When we unbind ARQ faild, we should set ARQ status to ARQ_UNBIND_FAILED. Add try except for unbind ARQ operation. Depends-On: https://review.opendev.org/c/openstack/cyborg/+/833529 Change-Id: I9a04b63a8ac7f20a0265f604eabc4107a40010b2 --- cyborg/common/constants.py | 8 +- cyborg/objects/ext_arq.py | 18 ++++- cyborg/tests/unit/fake_attach_handle.py | 77 +++++++++++++++++++ cyborg/tests/unit/objects/test_extarq.py | 35 +++++++++ ...UNBIND_FAILED_status-ea8636c64dd616eb.yaml | 5 ++ 5 files changed, 137 insertions(+), 6 deletions(-) create mode 100644 cyborg/tests/unit/fake_attach_handle.py create mode 100644 releasenotes/notes/add_ARQ_UNBIND_FAILED_status-ea8636c64dd616eb.yaml diff --git a/cyborg/common/constants.py b/cyborg/common/constants.py index c8aedd4b..0f9a0368 100644 --- a/cyborg/common/constants.py +++ b/cyborg/common/constants.py @@ -24,13 +24,15 @@ DEVICE_NIC = 'NIC' ARQ_STATES = (ARQ_INITIAL, ARQ_BIND_STARTED, ARQ_BOUND, ARQ_UNBOUND, - ARQ_BIND_FAILED, ARQ_DELETING) = ( - 'Initial', 'BindStarted', 'Bound', 'Unbound', 'BindFailed', 'Deleting') + ARQ_BIND_FAILED, ARQ_UNBIND_FAILED, ARQ_DELETING) = ( + 'Initial', 'BindStarted', 'Bound', 'Unbound', 'BindFailed', 'UnbindFailed', + 'Deleting') ARQ_BIND_STAGE = (ARQ_PRE_BIND, ARQ_FINISH_BIND, ARQ_OUFOF_BIND_FLOW) = ( - [ARQ_INITIAL, ARQ_BIND_STARTED], [ARQ_BOUND, ARQ_BIND_FAILED], + [ARQ_INITIAL, ARQ_BIND_STARTED], + [ARQ_BOUND, ARQ_BIND_FAILED], [ARQ_UNBOUND, ARQ_DELETING]) diff --git a/cyborg/objects/ext_arq.py b/cyborg/objects/ext_arq.py index cc3762c3..d33eabcf 100644 --- a/cyborg/objects/ext_arq.py +++ b/cyborg/objects/ext_arq.py @@ -198,7 +198,7 @@ class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat, self.update_check_state( context, constants.ARQ_BIND_FAILED) raise - LOG.info('Attach handle(%s) for ARQ(%s) successfully.', + LOG.info('Attach handle(%s) allocate for ARQ(%s) successfully.', ah.uuid, self.arq.uuid) def bind(self, context, deployable): @@ -211,6 +211,19 @@ class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat, # if (self.arq.state == constants.ARQ_DELETING # or self.arq.state == ARQ_UNBOUND): + def _deallocate_attach_handle(self, context, ah_id): + try: + attach_handle = AttachHandle.get_by_id(context, ah_id) + attach_handle.deallocate(context) + except Exception as e: + LOG.error("Failed to deallocate attach handle %s for ARQ %s." + "Reason: %s", ah_id, self.arq.uuid, str(e)) + self.update_check_state( + context, constants.ARQ_UNBIND_FAILED) + raise + LOG.info('Attach handle(%s) deallocate for ARQ(%s) successfully.', + ah_id, self.arq.uuid) + def unbind(self, context): arq = self.arq arq.hostname = None @@ -221,8 +234,7 @@ class ExtARQ(base.CyborgObject, object_base.VersionedObjectDictCompat, # Unbind: mark attach handles as freed ah_id = self.attach_handle_id if ah_id: - attach_handle = AttachHandle.get_by_id(context, ah_id) - attach_handle.deallocate(context) + self._deallocate_attach_handle(context, ah_id) self.attach_handle_id = None self.save(context) diff --git a/cyborg/tests/unit/fake_attach_handle.py b/cyborg/tests/unit/fake_attach_handle.py new file mode 100644 index 00000000..e203ff4a --- /dev/null +++ b/cyborg/tests/unit/fake_attach_handle.py @@ -0,0 +1,77 @@ +# Copyright 2021 Inspur. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +from oslo_utils import uuidutils + +from cyborg import objects +from cyborg.objects import attach_handle +from cyborg.objects import fields + + +def get_fake_attach_handle_as_dict(): + attach_handle1 = { + 'id': 1, + 'uuid': uuidutils.generate_uuid(), + 'in_use': 0, + 'cp_id': 1, + 'deployable_id': 1, + 'attach_type': "PCI", + 'attach_info': '{"domain": "0000", "bus": "0c",' + '"device": "0", "function": "1"}', + } + + attach_handle2 = { + 'id': 2, + 'uuid': uuidutils.generate_uuid(), + 'in_use': 1, + 'cp_id': 2, + 'deployable_id': 2, + 'attach_type': "PCI", + 'attach_info': '{"domain": "0000", "bus": "0c",' + '"device": "0", "function": "1"}', + } + + return [attach_handle1, attach_handle2] + + +def _convert_from_dict_to_obj(ah_dict): + obj_ah = attach_handle.AttachHandle() + for field in ah_dict.keys(): + obj_ah[field] = ah_dict[field] + return obj_ah + + +def _convert_to_db_ah(ah_dict): + for name, field in objects.AttachHandle.fields.items(): + if name in ah_dict: + continue + if field.nullable: + ah_dict[name] = None + elif field.default != fields.UnspecifiedDefault: + ah_dict[name] = field.default + else: + raise Exception('fake_db_attach_handle needs help with %s' % name) + return ah_dict + + +def get_db_attach_handles(): + ahs_list = get_fake_attach_handle_as_dict() + db_ahs = list(map(_convert_to_db_ah, ahs_list)) + return db_ahs + + +def get_fake_attach_handle_objs(): + ahs_list = get_fake_attach_handle_as_dict() + obj_ahs = list(map(_convert_from_dict_to_obj, ahs_list)) + return obj_ahs diff --git a/cyborg/tests/unit/objects/test_extarq.py b/cyborg/tests/unit/objects/test_extarq.py index cdda9274..e8ff1e96 100644 --- a/cyborg/tests/unit/objects/test_extarq.py +++ b/cyborg/tests/unit/objects/test_extarq.py @@ -21,6 +21,7 @@ from cyborg.common import constants from cyborg.common import exception from cyborg import objects from cyborg.tests.unit.db import base +from cyborg.tests.unit import fake_attach_handle from cyborg.tests.unit import fake_deployable from cyborg.tests.unit import fake_device_profile from cyborg.tests.unit import fake_extarq @@ -33,6 +34,7 @@ class TestExtARQObject(base.DbTestCase): self.fake_db_extarqs = fake_extarq.get_fake_db_extarqs() self.fake_obj_extarqs = fake_extarq.get_fake_extarq_objs() self.fake_obj_fpga_extarqs = fake_extarq.get_fake_fpga_extarq_objs() + self.fake_obj_ahs = fake_attach_handle.get_fake_attach_handle_objs() self.deployable_uuids = ['0acbf8d6-e02a-4394-aae3-57557d209498'] @mock.patch('cyborg.objects.ExtARQ._from_db_object') @@ -334,6 +336,39 @@ class TestExtARQObject(base.DbTestCase): obj_extarq._allocate_attach_handle, self.context, fake_dep) mock_log.assert_called_once_with( msg, obj_extarq.arq.uuid, fake_dep.uuid, str(e)) + mock_check_state.assert_called_once_with( + self.context, constants.ARQ_BIND_FAILED) + + @mock.patch('cyborg.objects.ExtARQ.update_check_state') + @mock.patch('cyborg.objects.attach_handle.AttachHandle.get_by_id') + @mock.patch('cyborg.objects.attach_handle.AttachHandle.deallocate') + def test_deallocate_attach_handle( + self, mock_deallocate, mock_ah, mock_check_state): + obj_extarq = self.fake_obj_extarqs[0] + mock_ah.return_value = self.fake_obj_ahs[0] + obj_extarq._deallocate_attach_handle(self.context, mock_ah.id) + mock_check_state.assert_not_called() + + @mock.patch('logging.LoggerAdapter.error') + @mock.patch('cyborg.objects.ExtARQ.update_check_state') + @mock.patch('cyborg.objects.attach_handle.AttachHandle.get_by_id') + @mock.patch('cyborg.objects.attach_handle.AttachHandle.deallocate') + def test_deallocate_attach_handle_with_error_log( + self, mock_ah, mock_deallocate, mock_check_state, mock_log): + obj_extarq = self.fake_obj_extarqs[0] + mock_ah.return_value = self.fake_obj_ahs[0] + e = exception.ResourceNotFound( + resource='AttachHandle', msg="Just for Test") + msg = ("Failed to deallocate attach handle %s for ARQ %s." + "Reason: %s") + mock_deallocate.side_effect = e + self.assertRaises( + exception.ResourceNotFound, + obj_extarq._deallocate_attach_handle, self.context, mock_ah.id) + mock_log.assert_called_once_with( + msg, mock_ah.id, obj_extarq.arq.uuid, str(e)) + mock_check_state.assert_called_once_with( + self.context, constants.ARQ_UNBIND_FAILED) @mock.patch('cyborg.objects.ExtARQ.get') @mock.patch('cyborg.objects.ExtARQ._from_db_object') diff --git a/releasenotes/notes/add_ARQ_UNBIND_FAILED_status-ea8636c64dd616eb.yaml b/releasenotes/notes/add_ARQ_UNBIND_FAILED_status-ea8636c64dd616eb.yaml new file mode 100644 index 00000000..45a514b0 --- /dev/null +++ b/releasenotes/notes/add_ARQ_UNBIND_FAILED_status-ea8636c64dd616eb.yaml @@ -0,0 +1,5 @@ +--- +features: + - | + Add ``ARQ_UNBIND_FAILED`` status for Accelerator Requests (arq) unbind process. Nowdays the status is + needed to accurate record the arq status.