support fail operation task of v2 API
This patch implements fail operation task defined in ETSI NFV-SOL003 v3.3.1 5.4.16. Fail operation is changed the state to "FAILED" when the retry or rollback operation unsuccessful. Functional tests will be provided with another patch. Implements: blueprint support-nfv-solv3-error-handling Change-Id: I3a64acde4cfdfeef18959a80a3a86b3c7f1023dd
This commit is contained in:
5
releasenotes/notes/add-v2-fail-api-b35b605f262210b2.yaml
Executable file
5
releasenotes/notes/add-v2-fail-api-b35b605f262210b2.yaml
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Add the Version "2.0.0" of Fail operation API
|
||||||
|
based on ETSI NFV specifications.
|
@@ -168,6 +168,15 @@ rules = [
|
|||||||
'path': VNF_LCM_OP_OCCS_ID_PATH + '/rollback'}
|
'path': VNF_LCM_OP_OCCS_ID_PATH + '/rollback'}
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
policy.DocumentedRuleDefault(
|
||||||
|
name=POLICY_NAME.format('lcm_op_occ_fail'),
|
||||||
|
check_str=RULE_ANY,
|
||||||
|
description="Fail VnfLcmOpOcc.",
|
||||||
|
operations=[
|
||||||
|
{'method': 'POST',
|
||||||
|
'path': VNF_LCM_OP_OCCS_ID_PATH + '/fail'}
|
||||||
|
]
|
||||||
|
),
|
||||||
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
||||||
# use since it is convenient to be able to delete under development.
|
# use since it is convenient to be able to delete under development.
|
||||||
# It is available when config parameter
|
# It is available when config parameter
|
||||||
|
@@ -47,6 +47,7 @@ class VnflcmAPIRouterV2(sol_wsgi.SolAPIRouter):
|
|||||||
("/vnf_lcm_op_occs", {"GET": "lcm_op_occ_list"}),
|
("/vnf_lcm_op_occs", {"GET": "lcm_op_occ_list"}),
|
||||||
("/vnf_lcm_op_occs/{id}/retry", {"POST": "lcm_op_occ_retry"}),
|
("/vnf_lcm_op_occs/{id}/retry", {"POST": "lcm_op_occ_retry"}),
|
||||||
("/vnf_lcm_op_occs/{id}/rollback", {"POST": "lcm_op_occ_rollback"}),
|
("/vnf_lcm_op_occs/{id}/rollback", {"POST": "lcm_op_occ_rollback"}),
|
||||||
|
("/vnf_lcm_op_occs/{id}/fail", {"POST": "lcm_op_occ_fail"}),
|
||||||
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
# NOTE: 'DELETE' is not defined in the specification. It is for test
|
||||||
# use since it is convenient to be able to delete under development.
|
# use since it is convenient to be able to delete under development.
|
||||||
# It is available when config parameter
|
# It is available when config parameter
|
||||||
|
@@ -55,13 +55,14 @@ def make_lcmocc_links(lcmocc, endpoint):
|
|||||||
links.vnfInstance = objects.Link(
|
links.vnfInstance = objects.Link(
|
||||||
href=inst_utils.inst_href(lcmocc.vnfInstanceId, endpoint))
|
href=inst_utils.inst_href(lcmocc.vnfInstanceId, endpoint))
|
||||||
links.retry = objects.Link(
|
links.retry = objects.Link(
|
||||||
href=lcmocc_task_href(lcmocc.vnfInstanceId, 'retry', endpoint))
|
href=lcmocc_task_href(lcmocc.id, 'retry', endpoint))
|
||||||
links.rollback = objects.Link(
|
links.rollback = objects.Link(
|
||||||
href=lcmocc_task_href(lcmocc.vnfInstanceId, 'rollback', endpoint))
|
href=lcmocc_task_href(lcmocc.id, 'rollback', endpoint))
|
||||||
|
links.fail = objects.Link(
|
||||||
|
href=lcmocc_task_href(lcmocc.id, 'fail', endpoint))
|
||||||
# TODO(oda-g): add when implemented
|
# TODO(oda-g): add when implemented
|
||||||
# links.grant
|
# links.grant
|
||||||
# links.cancel
|
# links.cancel
|
||||||
# links.fail
|
|
||||||
# links.vnfSnapshot
|
# links.vnfSnapshot
|
||||||
|
|
||||||
return links
|
return links
|
||||||
|
@@ -359,6 +359,35 @@ class VnfLcmControllerV2(sol_wsgi.SolAPIController):
|
|||||||
|
|
||||||
return sol_wsgi.SolResponse(202, None)
|
return sol_wsgi.SolResponse(202, None)
|
||||||
|
|
||||||
|
def lcm_op_occ_fail(self, request, id):
|
||||||
|
context = request.context
|
||||||
|
lcmocc = lcmocc_utils.get_lcmocc(context, id)
|
||||||
|
|
||||||
|
return self._lcm_op_occ_fail(context, lcmocc)
|
||||||
|
|
||||||
|
@coordinate.lock_vnf_instance('{lcmocc.vnfInstanceId}')
|
||||||
|
def _lcm_op_occ_fail(self, context, lcmocc):
|
||||||
|
if lcmocc.operationState != v2fields.LcmOperationStateType.FAILED_TEMP:
|
||||||
|
raise sol_ex.LcmOpOccNotFailedTemp(lcmocc_id=lcmocc.id)
|
||||||
|
|
||||||
|
inst = inst_utils.get_inst(context, lcmocc.vnfInstanceId)
|
||||||
|
grant_req, grant = lcmocc_utils.get_grant_req_and_grant(context,
|
||||||
|
lcmocc)
|
||||||
|
|
||||||
|
lcmocc.operationState = v2fields.LcmOperationStateType.FAILED
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
lcmocc.update(context)
|
||||||
|
grant_req.delete(context)
|
||||||
|
grant.delete(context)
|
||||||
|
|
||||||
|
# send notification FAILED
|
||||||
|
self.nfvo_client.send_lcmocc_notification(context, lcmocc, inst,
|
||||||
|
self.endpoint)
|
||||||
|
|
||||||
|
resp_body = self._lcmocc_view.detail(lcmocc)
|
||||||
|
|
||||||
|
return sol_wsgi.SolResponse(200, resp_body)
|
||||||
|
|
||||||
def lcm_op_occ_delete(self, request, id):
|
def lcm_op_occ_delete(self, request, id):
|
||||||
# not allowed to delete on the specification
|
# not allowed to delete on the specification
|
||||||
if not CONF.v2_vnfm.test_enable_lcm_op_occ_delete:
|
if not CONF.v2_vnfm.test_enable_lcm_op_occ_delete:
|
||||||
|
@@ -98,6 +98,11 @@ class Client(object):
|
|||||||
path = self.path + '/' + id + '/rollback'
|
path = self.path + '/' + id + '/rollback'
|
||||||
resp, body = self.client.do_request(path, "POST", version="2.0.0")
|
resp, body = self.client.do_request(path, "POST", version="2.0.0")
|
||||||
|
|
||||||
|
def fail(self, id):
|
||||||
|
path = self.path + '/' + id + '/fail'
|
||||||
|
resp, body = self.client.do_request(path, "POST", version="2.0.0")
|
||||||
|
self.print(resp, body)
|
||||||
|
|
||||||
|
|
||||||
def usage():
|
def usage():
|
||||||
print("usage: cli resource action [arg...]")
|
print("usage: cli resource action [arg...]")
|
||||||
@@ -116,6 +121,7 @@ def usage():
|
|||||||
print(" lcmocc delete {id}")
|
print(" lcmocc delete {id}")
|
||||||
print(" lcmocc retry {id}")
|
print(" lcmocc retry {id}")
|
||||||
print(" lcmocc rollback {id}")
|
print(" lcmocc rollback {id}")
|
||||||
|
print(" lcmocc fail {id}")
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
|
|
||||||
|
|
||||||
@@ -139,7 +145,8 @@ if __name__ == '__main__':
|
|||||||
usage()
|
usage()
|
||||||
client = Client("/vnflcm/v2/subscriptions")
|
client = Client("/vnflcm/v2/subscriptions")
|
||||||
elif resource == "lcmocc":
|
elif resource == "lcmocc":
|
||||||
if action not in ["list", "show", "delete", "retry", "rollback"]:
|
if action not in ["list", "show", "delete", "retry", "rollback",
|
||||||
|
"fail"]:
|
||||||
usage()
|
usage()
|
||||||
client = Client("/vnflcm/v2/vnf_lcm_op_occs")
|
client = Client("/vnflcm/v2/vnf_lcm_op_occs")
|
||||||
else:
|
else:
|
||||||
@@ -180,3 +187,7 @@ if __name__ == '__main__':
|
|||||||
if len(sys.argv) != 4:
|
if len(sys.argv) != 4:
|
||||||
usage()
|
usage()
|
||||||
client.rollback(sys.argv[3])
|
client.rollback(sys.argv[3])
|
||||||
|
elif action == "fail":
|
||||||
|
if len(sys.argv) != 4:
|
||||||
|
usage()
|
||||||
|
client.fail(sys.argv[3])
|
||||||
|
@@ -21,6 +21,7 @@ from oslo_utils import uuidutils
|
|||||||
from tacker import context
|
from tacker import context
|
||||||
from tacker.sol_refactored.api import api_version
|
from tacker.sol_refactored.api import api_version
|
||||||
from tacker.sol_refactored.common import exceptions as sol_ex
|
from tacker.sol_refactored.common import exceptions as sol_ex
|
||||||
|
from tacker.sol_refactored.common import lcm_op_occ_utils as lcmocc_utils
|
||||||
from tacker.sol_refactored.controller import vnflcm_v2
|
from tacker.sol_refactored.controller import vnflcm_v2
|
||||||
from tacker.sol_refactored.nfvo import nfvo_client
|
from tacker.sol_refactored.nfvo import nfvo_client
|
||||||
from tacker.sol_refactored import objects
|
from tacker.sol_refactored import objects
|
||||||
@@ -39,7 +40,7 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||||||
self.request = mock.Mock()
|
self.request = mock.Mock()
|
||||||
self.request.context = self.context
|
self.request.context = self.context
|
||||||
|
|
||||||
def _create_inst_and_lcmocc(self, inst_state, op_state):
|
def _set_inst_and_lcmocc(self, inst_state, op_state):
|
||||||
inst = objects.VnfInstanceV2(
|
inst = objects.VnfInstanceV2(
|
||||||
# required fields
|
# required fields
|
||||||
id=uuidutils.generate_uuid(),
|
id=uuidutils.generate_uuid(),
|
||||||
@@ -62,7 +63,13 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||||||
operation=fields.LcmOperationType.INSTANTIATE,
|
operation=fields.LcmOperationType.INSTANTIATE,
|
||||||
isAutomaticInvocation=False,
|
isAutomaticInvocation=False,
|
||||||
isCancelPending=False,
|
isCancelPending=False,
|
||||||
operationParams=req)
|
operationParams=req
|
||||||
|
)
|
||||||
|
|
||||||
|
return inst, lcmocc
|
||||||
|
|
||||||
|
def _create_inst_and_lcmocc(self, inst_state, op_state):
|
||||||
|
inst, lcmocc = self._set_inst_and_lcmocc(inst_state, op_state)
|
||||||
|
|
||||||
inst.create(self.context)
|
inst.create(self.context)
|
||||||
lcmocc.create(self.context)
|
lcmocc.create(self.context)
|
||||||
@@ -191,3 +198,64 @@ class TestVnflcmV2(db_base.SqlTestCase):
|
|||||||
self.assertRaises(sol_ex.LcmOpOccNotFailedTemp,
|
self.assertRaises(sol_ex.LcmOpOccNotFailedTemp,
|
||||||
self.controller.lcm_op_occ_rollback, request=self.request,
|
self.controller.lcm_op_occ_rollback, request=self.request,
|
||||||
id=lcmocc_id)
|
id=lcmocc_id)
|
||||||
|
|
||||||
|
def test_fail_not_failed_temp(self):
|
||||||
|
_, lcmocc_id = self._create_inst_and_lcmocc('INSTANTIATED',
|
||||||
|
fields.LcmOperationStateType.COMPLETED)
|
||||||
|
|
||||||
|
self.assertRaises(sol_ex.LcmOpOccNotFailedTemp,
|
||||||
|
self.controller.lcm_op_occ_fail, request=self.request,
|
||||||
|
id=lcmocc_id)
|
||||||
|
|
||||||
|
def _prepare_db_for_fail(self):
|
||||||
|
inst, lcmocc = self._set_inst_and_lcmocc('NOT_INSTANTIATED',
|
||||||
|
fields.LcmOperationStateType.FAILED_TEMP)
|
||||||
|
|
||||||
|
inst.create(self.context)
|
||||||
|
lcmocc.create(self.context)
|
||||||
|
|
||||||
|
grant_req = objects.GrantRequestV1(
|
||||||
|
# required fields
|
||||||
|
vnfInstanceId=lcmocc.vnfInstanceId,
|
||||||
|
vnfLcmOpOccId=lcmocc.id,
|
||||||
|
vnfdId=uuidutils.generate_uuid(),
|
||||||
|
operation=lcmocc.operation,
|
||||||
|
isAutomaticInvocation=lcmocc.isAutomaticInvocation
|
||||||
|
)
|
||||||
|
|
||||||
|
grant = objects.GrantV1(
|
||||||
|
# required fields
|
||||||
|
id=uuidutils.generate_uuid(),
|
||||||
|
vnfInstanceId=lcmocc.vnfInstanceId,
|
||||||
|
vnfLcmOpOccId=lcmocc.id
|
||||||
|
)
|
||||||
|
|
||||||
|
grant_req.create(self.context)
|
||||||
|
grant.create(self.context)
|
||||||
|
lcmocc.grantId = grant.id
|
||||||
|
lcmocc.update(self.context)
|
||||||
|
|
||||||
|
return lcmocc
|
||||||
|
|
||||||
|
@mock.patch.object(nfvo_client.NfvoClient, 'send_lcmocc_notification')
|
||||||
|
def test_lcm_op_occ_fail(self, mocked_send_lcmocc_notification):
|
||||||
|
# prepare
|
||||||
|
lcmocc = self._prepare_db_for_fail()
|
||||||
|
|
||||||
|
op_state = []
|
||||||
|
|
||||||
|
def _store_state(context, lcmocc, inst, endpoint):
|
||||||
|
op_state.append(lcmocc.operationState)
|
||||||
|
|
||||||
|
mocked_send_lcmocc_notification.side_effect = _store_state
|
||||||
|
|
||||||
|
# run lcm_op_occ_fail
|
||||||
|
self.controller.lcm_op_occ_fail(self.request, lcmocc.id)
|
||||||
|
|
||||||
|
# check operationstate
|
||||||
|
self.assertEqual(1, mocked_send_lcmocc_notification.call_count)
|
||||||
|
self.assertEqual(fields.LcmOperationStateType.FAILED, op_state[0])
|
||||||
|
|
||||||
|
# check grant_req and grant are deleted
|
||||||
|
self.assertRaises(sol_ex.GrantRequestOrGrantNotFound,
|
||||||
|
lcmocc_utils.get_grant_req_and_grant, self.context, lcmocc)
|
||||||
|
Reference in New Issue
Block a user