Merge "Clean up at conductor start up"

This commit is contained in:
Zuul 2022-03-04 14:25:49 +00:00 committed by Gerrit Code Review
commit 8e91d8fa2e
4 changed files with 125 additions and 13 deletions

View File

@ -240,3 +240,8 @@ class InvalidScaleNumberOfSteps(SolHttpError400):
class DeltaMissingInVnfd(SolHttpError400):
message = _("Delta '%(delta)s' is not defined in "
"VduScalingAspectDeltas.")
class ConductorProcessingError(SolException):
title = 'Internal Server Error'
message = _("Failure due to conductor processing error.")

View File

@ -16,6 +16,7 @@
from oslo_log import log as logging
from tacker.common import log
from tacker import context as tacker_context
from tacker.sol_refactored.common import config
from tacker.sol_refactored.common import coordinate
from tacker.sol_refactored.common import exceptions as sol_ex
@ -39,6 +40,33 @@ class ConductorV2(object):
self.endpoint = CONF.v2_vnfm.endpoint
self.nfvo_client = nfvo_client.NfvoClient()
self._change_lcm_op_state()
def _change_lcm_op_state(self):
# NOTE: If the conductor down during processing and
# the LcmOperationState STARTING/PROCESSING/ROLLING_BACK remain,
# change it at the next startup.
context = tacker_context.get_admin_context()
ex = sol_ex.ConductorProcessingError()
state_list = [(fields.LcmOperationStateType.STARTING,
fields.LcmOperationStateType.ROLLED_BACK),
(fields.LcmOperationStateType.PROCESSING,
fields.LcmOperationStateType.FAILED_TEMP),
(fields.LcmOperationStateType.ROLLING_BACK,
fields.LcmOperationStateType.FAILED_TEMP)]
for before_state, after_state in state_list:
lcmoccs = objects.VnfLcmOpOccV2.get_by_filter(context,
operationState=before_state)
for lcmocc in lcmoccs:
lcmocc.operationState = after_state
self._set_lcmocc_error(lcmocc, ex)
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
lcmocc.update(context)
# send notification
self.nfvo_client.send_lcmocc_notification(context, lcmocc,
inst, self.endpoint)
def _set_lcmocc_error(self, lcmocc, ex):
if isinstance(ex, sol_ex.SolException):
problem_details = ex.make_problem_details()
@ -87,7 +115,17 @@ class ConductorV2(object):
lcmocc.operationState = fields.LcmOperationStateType.PROCESSING
lcmocc.grantId = grant.id
lcmocc.update(context)
with context.session.begin(subtransactions=True):
# save grant_req and grant to be used when retry
# NOTE: grant_req is saved because it is necessary to interpret
# the contents of grant. Though grant can be gotten from NFVO,
# it is saved here with grant_req so that it is not necessary
# to communicate with NFVO when retry. They are saved temporary
# and will be deleted when operationState becomes an end state
# (COMPLETED/FAILED/ROLLED_BACK).
grant_req.create(context)
grant.create(context)
lcmocc.update(context)
except Exception as ex:
LOG.exception("STARTING %s failed", lcmocc.operation)
lcmocc.operationState = fields.LcmOperationStateType.ROLLED_BACK
@ -110,21 +148,14 @@ class ConductorV2(object):
with context.session.begin(subtransactions=True):
inst.update(context)
lcmocc.update(context)
# grant_req and grant are not necessary any more.
grant_req.delete(context)
grant.delete(context)
except Exception as ex:
LOG.exception("PROCESSING %s failed", lcmocc.operation)
lcmocc.operationState = fields.LcmOperationStateType.FAILED_TEMP
self._set_lcmocc_error(lcmocc, ex)
with context.session.begin(subtransactions=True):
# save grant_req and grant to be used when retry
# NOTE: grant_req is saved because it is necessary to interpret
# the contents of grant. Though grant can be gotten from NFVO,
# it is saved here with grant_req so that it is not necessary
# to communicate with NFVO when retry. They are saved temporary
# and will be deleted when operationState becomes an end state
# (COMPLETED/FAILED/ROLLED_BACK).
grant_req.create(context)
grant.create(context)
lcmocc.update(context)
lcmocc.update(context)
# send notification COMPLETED or FAILED_TEMP
self.nfvo_client.send_lcmocc_notification(context, lcmocc, inst,

View File

@ -346,6 +346,8 @@ class TackerPersistentObject(TackerObject):
context.session.add(inst)
context.session.flush()
# 'flush' must have succeeded because we are here.
if self._db_obj is None:
self._db_obj = inst
self.obj_reset_changes()
@db_api.context_manager.writer

View File

@ -16,6 +16,7 @@
from datetime import datetime
from unittest import mock
import ddt
from oslo_utils import uuidutils
from tacker import context
@ -29,6 +30,7 @@ from tacker.sol_refactored.objects.v2 import fields
from tacker.tests.unit.db import base as db_base
@ddt.ddt
class TestConductorV2(db_base.SqlTestCase):
def setUp(self):
@ -103,7 +105,9 @@ class TestConductorV2(db_base.SqlTestCase):
# prepare
lcmocc = self._create_inst_and_lcmocc()
mocked_get_vnfd.return_value = mock.Mock()
mocked_grant.return_value = self._make_grant_req_and_grant(lcmocc)
grant_req, grant = self._make_grant_req_and_grant(lcmocc)
lcmocc.grantId = grant.id
mocked_grant.return_value = grant_req, grant
op_state = []
@ -121,6 +125,10 @@ class TestConductorV2(db_base.SqlTestCase):
self.assertEqual(fields.LcmOperationStateType.PROCESSING, op_state[1])
self.assertEqual(fields.LcmOperationStateType.COMPLETED, op_state[2])
# check grant_req and grant are deleted
self.assertRaises(sol_ex.GrantRequestOrGrantNotFound,
lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)
@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')
@ -398,3 +406,69 @@ class TestConductorV2(db_base.SqlTestCase):
lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id)
expected = ex.make_problem_details()
self.assertEqual(expected, lcmocc.error.to_dict())
def _prepare_change_lcm_op_state(self, op_state):
inst = objects.VnfInstanceV2(
# required fields
id=uuidutils.generate_uuid(),
vnfdId=uuidutils.generate_uuid(),
vnfProvider='provider',
vnfProductName='product name',
vnfSoftwareVersion='software version',
vnfdVersion='vnfd version',
instantiationState='INSTANTIATED'
)
req = {"flavourId": "simple"} # instantiate request
lcmocc = objects.VnfLcmOpOccV2(
# required fields
id=uuidutils.generate_uuid(),
operationState=op_state,
stateEnteredTime=datetime.utcnow(),
startTime=datetime.utcnow(),
vnfInstanceId=inst.id,
operation=fields.LcmOperationType.SCALE,
isAutomaticInvocation=False,
isCancelPending=False,
operationParams=req)
inst.create(self.context)
lcmocc.create(self.context)
return lcmocc
@ddt.data({'before_state': fields.LcmOperationStateType.STARTING,
'after_state': fields.LcmOperationStateType.ROLLED_BACK},
{'before_state': fields.LcmOperationStateType.PROCESSING,
'after_state': fields.LcmOperationStateType.FAILED_TEMP},
{'before_state': fields.LcmOperationStateType.ROLLING_BACK,
'after_state': fields.LcmOperationStateType.FAILED_TEMP})
@ddt.unpack
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
@mock.patch.object(nfvo_client.NfvoClient, 'get_vnfd')
def test_change_lcm_op_state(self, mocked_get_vnfd,
mocked_send_lcmocc_notification, before_state, after_state):
# prepare
lcmocc = self._prepare_change_lcm_op_state(before_state)
mocked_get_vnfd.return_value = mock.Mock()
ex = sol_ex.ConductorProcessingError()
op_state = []
def _store_state(context, lcmocc, inst, endpoint):
op_state.append(lcmocc.operationState)
mocked_send_lcmocc_notification.side_effect = _store_state
# run _change_lcm_op_state
self.conductor._change_lcm_op_state()
# check operationState transition
self.assertEqual(1, mocked_send_lcmocc_notification.call_count)
self.assertEqual(after_state, op_state[0])
# check lcmocc.error
# get lcmocc from DB to be sure lcmocc saved to DB
lcmocc = lcmocc_utils.get_lcmocc(self.context, lcmocc.id)
expected = ex.make_problem_details()
self.assertEqual(expected, lcmocc.error.to_dict())