From 099676307662f67f657e8369b3d6f8ef5ec41666 Mon Sep 17 00:00:00 2001 From: Ken Fujimoto Date: Wed, 15 Nov 2023 00:43:47 +0000 Subject: [PATCH] Fix not update stateEnteredTime in VnfLcmOpOcc This patch fixes a problem where stateEnteredTime is not updated when updating operationState in VnfLcmOpOcc of v2 API. Closes-Bug: #2043528 Change-Id: Ia9eba31f471bb234e537ae58cd7379ad569d8fc8 --- .../sol_refactored/common/lcm_op_occ_utils.py | 5 ++ .../sol_refactored/conductor/conductor_v2.py | 38 ++++++---- tacker/sol_refactored/controller/vnflcm_v2.py | 3 +- .../conductor/test_conductor_v2.py | 70 +++++++++++++++++++ .../controller/test_vnflcm_v2.py | 7 ++ 5 files changed, 109 insertions(+), 14 deletions(-) diff --git a/tacker/sol_refactored/common/lcm_op_occ_utils.py b/tacker/sol_refactored/common/lcm_op_occ_utils.py index af8e9017c..268321ff8 100644 --- a/tacker/sol_refactored/common/lcm_op_occ_utils.py +++ b/tacker/sol_refactored/common/lcm_op_occ_utils.py @@ -607,3 +607,8 @@ def check_lcmocc_in_progress(context, inst_id): operationState=fields.LcmOperationStateType.FAILED_TEMP) if lcmoccs: raise sol_ex.OtherOperationInProgress(inst_id=inst_id) + + +def update_lcmocc_status(lcmocc, op_state): + lcmocc.operationState = op_state + lcmocc.stateEnteredTime = datetime.utcnow() diff --git a/tacker/sol_refactored/conductor/conductor_v2.py b/tacker/sol_refactored/conductor/conductor_v2.py index b751e8d0f..d57f4e4e8 100644 --- a/tacker/sol_refactored/conductor/conductor_v2.py +++ b/tacker/sol_refactored/conductor/conductor_v2.py @@ -92,7 +92,7 @@ class ConductorV2(object): lcmoccs = objects.VnfLcmOpOccV2.get_by_filter(context, operationState=before_state) for lcmocc in lcmoccs: - lcmocc.operationState = after_state + lcmocc_utils.update_lcmocc_status(lcmocc, after_state) self._set_lcmocc_error(lcmocc, ex) inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId) lcmocc.update(context) @@ -158,7 +158,8 @@ class ConductorV2(object): self.vnflcm_driver.post_grant(context, lcmocc, inst, grant_req, grant, vnfd) - lcmocc.operationState = fields.LcmOperationStateType.PROCESSING + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.PROCESSING) lcmocc.grantId = grant.id with context.session.begin(subtransactions=True): # save grant_req and grant to be used when retry @@ -173,7 +174,8 @@ class ConductorV2(object): lcmocc.update(context) except Exception as ex: LOG.exception("STARTING %s failed", lcmocc.operation) - lcmocc.operationState = fields.LcmOperationStateType.ROLLED_BACK + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.ROLLED_BACK) self._set_lcmocc_error(lcmocc, ex) lcmocc.update(context) @@ -189,7 +191,8 @@ class ConductorV2(object): grant, vnfd, user_script_err_handling_data) - lcmocc.operationState = fields.LcmOperationStateType.COMPLETED + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.COMPLETED) # update inst and lcmocc at the same time with context.session.begin(subtransactions=True): inst.update(context) @@ -199,7 +202,8 @@ class ConductorV2(object): grant.delete(context) except Exception as ex: LOG.exception("PROCESSING %s failed", lcmocc.operation) - lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.FAILED_TEMP) self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data) lcmocc.update(context) @@ -229,7 +233,8 @@ class ConductorV2(object): else: user_script_err_handling_data = {} - lcmocc.operationState = fields.LcmOperationStateType.PROCESSING + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.PROCESSING) lcmocc.update(context) # send notification PROCESSING self.nfvo_client.send_lcmocc_notification(context, lcmocc, inst, @@ -250,7 +255,8 @@ class ConductorV2(object): grant, vnfd, user_script_err_handling_data) - lcmocc.operationState = fields.LcmOperationStateType.COMPLETED + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.COMPLETED) lcmocc.error = None # clear error # update inst and lcmocc at the same time with context.session.begin(subtransactions=True): @@ -262,7 +268,8 @@ class ConductorV2(object): grant.delete(context) except Exception as ex: LOG.exception("PROCESSING %s failed", lcmocc.operation) - lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.FAILED_TEMP) self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data) lcmocc.update(context) # grant_req and grant are already saved. they are not deleted @@ -294,7 +301,8 @@ class ConductorV2(object): else: user_script_err_handling_data = {} - lcmocc.operationState = fields.LcmOperationStateType.ROLLING_BACK + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.ROLLING_BACK) lcmocc.update(context) # send notification ROLLING_BACK self.nfvo_client.send_lcmocc_notification(context, lcmocc, inst, @@ -311,7 +319,8 @@ class ConductorV2(object): grant, vnfd, user_script_err_handling_data) - lcmocc.operationState = fields.LcmOperationStateType.ROLLED_BACK + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.ROLLED_BACK) if (lcmocc.obj_attr_is_set('error') and lcmocc.error.obj_attr_is_set('userScriptErrHandlingData')): del lcmocc.error.userScriptErrHandlingData @@ -330,7 +339,8 @@ class ConductorV2(object): grant.delete(context) except Exception as ex: LOG.exception("ROLLING_BACK %s failed", lcmocc.operation) - lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.FAILED_TEMP) self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data) lcmocc.update(context) # grant_req and grant are already saved. they are not deleted @@ -365,14 +375,16 @@ class ConductorV2(object): vnfd = self.nfvo_client.get_vnfd(context, inst.vnfdId) self.vnflcm_driver.process(context, lcmocc, inst, None, None, vnfd, user_script_err_handling_data) - lcmocc.operationState = fields.LcmOperationStateType.COMPLETED + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.COMPLETED) # update inst and lcmocc at the same time with context.session.begin(subtransactions=True): inst.update(context) lcmocc.update(context) except Exception as ex: LOG.exception("PROCESSING %s failed", lcmocc.operation) - lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP + lcmocc_utils.update_lcmocc_status( + lcmocc, fields.LcmOperationStateType.FAILED_TEMP) self._set_lcmocc_error(lcmocc, ex, user_script_err_handling_data) lcmocc.update(context) diff --git a/tacker/sol_refactored/controller/vnflcm_v2.py b/tacker/sol_refactored/controller/vnflcm_v2.py index c47092955..f4ce67101 100644 --- a/tacker/sol_refactored/controller/vnflcm_v2.py +++ b/tacker/sol_refactored/controller/vnflcm_v2.py @@ -610,7 +610,8 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController): grant_req, grant = lcmocc_utils.get_grant_req_and_grant(context, lcmocc) - lcmocc.operationState = v2fields.LcmOperationStateType.FAILED + lcmocc_utils.update_lcmocc_status( + lcmocc, v2fields.LcmOperationStateType.FAILED) with context.session.begin(subtransactions=True): lcmocc.update(context) if grant_req is not None: diff --git a/tacker/tests/unit/sol_refactored/conductor/test_conductor_v2.py b/tacker/tests/unit/sol_refactored/conductor/test_conductor_v2.py index 5ed22802d..ba4154bdf 100644 --- a/tacker/tests/unit/sol_refactored/conductor/test_conductor_v2.py +++ b/tacker/tests/unit/sol_refactored/conductor/test_conductor_v2.py @@ -152,9 +152,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_grant.return_value = grant_req, grant op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -167,6 +169,10 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[1]) self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[2]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + self.assertNotEqual(op_state_entered_time[1], op_state_entered_time[2]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc) @@ -184,9 +190,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_grant.return_value = self._make_grant_req_and_grant(lcmocc) mocked_process.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -199,6 +207,10 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[1]) self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[2]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + self.assertNotEqual(op_state_entered_time[1], op_state_entered_time[2]) + @mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification') @mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd') @mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'grant') @@ -211,9 +223,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_grant.side_effect = ex op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -225,6 +239,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.STARTING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.ROLLED_BACK, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -247,9 +264,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_process.side_effect = ex op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -262,6 +281,10 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[1]) self.assertEqual(fields.LcmOperationStateType.FAILED_TEMP, op_state[2]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + self.assertNotEqual(op_state_entered_time[1], op_state_entered_time[2]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -285,9 +308,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_get_vnfd.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -299,6 +324,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc) @@ -321,9 +349,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_process.side_effect = ex op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -335,6 +365,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.FAILED_TEMP, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -362,9 +395,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_get_vnfd.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -377,6 +412,9 @@ class TestConductorV2(db_base.SqlTestCase): op_state[0]) self.assertEqual(fields.LcmOperationStateType.ROLLED_BACK, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc) @@ -400,9 +438,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_rollback.side_effect = ex op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -415,6 +455,9 @@ class TestConductorV2(db_base.SqlTestCase): op_state[0]) self.assertEqual(fields.LcmOperationStateType.FAILED_TEMP, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -437,9 +480,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_get_vnfd.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -451,6 +496,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + @mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification') @mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd') @mock.patch.object(vnflcm_driver_v2.VnfLcmDriverV2, 'process') @@ -465,9 +513,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_process.side_effect = ex op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -479,6 +529,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.FAILED_TEMP, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -528,13 +581,16 @@ class TestConductorV2(db_base.SqlTestCase): mocked_send_lcmocc_notification, before_state, after_state): # prepare lcmocc = self._prepare_change_lcm_op_state(before_state) + before_state_entered_time = lcmocc.stateEnteredTime mocked_get_vnfd.return_value = mock.Mock() ex = sol_ex.ConductorProcessingError() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -545,6 +601,10 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(1, mocked_send_lcmocc_notification.call_count) self.assertEqual(after_state, op_state[0]) + # check stateEnteredTime + self.assertNotEqual(before_state_entered_time, + op_state_entered_time[0]) + # check lcmocc.error # get lcmocc from DB to be sure lcmocc saved to DB lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id) @@ -582,9 +642,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_get_vnfd.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -596,6 +658,9 @@ class TestConductorV2(db_base.SqlTestCase): self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[0]) self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc) @@ -621,9 +686,11 @@ class TestConductorV2(db_base.SqlTestCase): mocked_get_vnfd.return_value = mock.Mock() op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -636,6 +703,9 @@ class TestConductorV2(db_base.SqlTestCase): op_state[0]) self.assertEqual(fields.LcmOperationStateType.ROLLED_BACK, op_state[1]) + # check stateEnteredTime + self.assertNotEqual(op_state_entered_time[0], op_state_entered_time[1]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc) diff --git a/tacker/tests/unit/sol_refactored/controller/test_vnflcm_v2.py b/tacker/tests/unit/sol_refactored/controller/test_vnflcm_v2.py index 7ce68aa0e..69ef00c59 100644 --- a/tacker/tests/unit/sol_refactored/controller/test_vnflcm_v2.py +++ b/tacker/tests/unit/sol_refactored/controller/test_vnflcm_v2.py @@ -1465,11 +1465,14 @@ class TestVnflcmV2(db_base.SqlTestCase): def test_lcm_op_occ_fail(self, mocked_send_lcmocc_notification): # prepare lcmocc = self._prepare_db_for_fail() + before_state_entered_time = lcmocc.stateEnteredTime op_state = [] + op_state_entered_time = [] def _store_state(context, lcmocc, inst, endpoint): op_state.append(lcmocc.operationState) + op_state_entered_time.append(lcmocc.stateEnteredTime) mocked_send_lcmocc_notification.side_effect = _store_state @@ -1480,6 +1483,10 @@ class TestVnflcmV2(db_base.SqlTestCase): self.assertEqual(1, mocked_send_lcmocc_notification.call_count) self.assertEqual(fields.LcmOperationStateType.FAILED, op_state[0]) + # check stateEnteredTime + self.assertNotEqual(before_state_entered_time, + op_state_entered_time[0]) + # check grant_req and grant are deleted self.assertRaises(sol_ex.GrantRequestOrGrantNotFound, lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)