From: Bharath Thiruveedula Date: Tue, 4 Sep 2018 13:46:05 +0000 (+0530) Subject: Add GET /vnf_lcm_op_occs API in GVNFM X-Git-Tag: 1.2.0~14 X-Git-Url: https://gerrit.onap.org/r/gitweb?p=vfc%2Fgvnfm%2Fvnflcm.git;a=commitdiff_plain;h=5b075b9740c4d57e219df38ae4fb7446fbdf5833 Add GET /vnf_lcm_op_occs API in GVNFM Signed-off-by: Bharath Thiruveedula Change-Id: I73f6be2d4c1ff130576ff3c0de2a6de0eb123a81 Issue-ID: VFC-997 --- diff --git a/lcm/lcm/nf/biz/query_vnf_lcm_op_occ.py b/lcm/lcm/nf/biz/query_vnf_lcm_op_occ.py new file mode 100644 index 00000000..467dc4f0 --- /dev/null +++ b/lcm/lcm/nf/biz/query_vnf_lcm_op_occ.py @@ -0,0 +1,72 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved +# +# 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. + +import json +import logging + +from lcm.pub.database.models import VNFLcmOpOccModel +from lcm.pub.exceptions import NFLCMException + +logger = logging.getLogger(__name__) +FILTERS = { + 'id': 'id', + 'operationState': 'operation_state', + 'stateEnteredTime': 'state_entered_time', + 'startTime': 'start_time', + 'vnfInstanceId': 'vnf_instance_id', + 'grantId': 'grant_id', + 'operation': 'operation' +} + + +class QueryVnfLcmOpOcc: + def __init__(self, data, lcm_op_occ_id=''): + self.vnf_lcm_op_occ_id = lcm_op_occ_id + self.params = data + + def query_multi_vnf_lcm_op_occ(self): + query_data = {} + logger.debug("QueryMultiVnfLcmOpOccs--get--biz::> Check for filters in query params" % self.params) + for query, value in self.params.iteritems(): + if query in FILTERS: + query_data[FILTERS[query]] = value + # Query the database with filters if the request has fields in request params, else fetch all records + if query_data: + lcm_ops = VNFLcmOpOccModel.objects.filter(**query_data) + else: + lcm_ops = VNFLcmOpOccModel.objects.all() + if not lcm_ops.exists(): + raise NFLCMException('LCM Operation Occurances do not exist') + return [self.fill_resp_data(lcm_op) for lcm_op in lcm_ops] + + def fill_resp_data(self, lcm_op): + resp_data = { + 'id': lcm_op.id, + 'operationState': lcm_op.operation_state, + 'stateEnteredTime': lcm_op.state_entered_time, + 'startTime': lcm_op.start_time, + 'vnfInstanceId': lcm_op.vnf_instance_id, + 'grantId': None, + 'operation': lcm_op.operation, + 'isAutomaticInvocation': lcm_op.is_automatic_invocation, + 'operationParams': json.loads(lcm_op.operation_params), + 'isCancelPending': lcm_op.is_cancel_pending, + 'cancelMode': lcm_op.cancel_mode, + 'error': None if not lcm_op.error else json.loads(lcm_op.error), + 'resourceChanges': None if not lcm_op.resource_changes else json.loads(lcm_op.resource_changes), + 'changedInfo': None if not lcm_op.changed_info else json.loads(lcm_op.changed_info), + 'changedExtConnectivity': None if not lcm_op.changed_ext_connectivity else json.loads(lcm_op.changed_ext_connectivity), + '_links': json.loads(lcm_op.links) + } + return resp_data diff --git a/lcm/lcm/nf/serializers/affected_storages.py b/lcm/lcm/nf/serializers/affected_storages.py new file mode 100644 index 00000000..de66f288 --- /dev/null +++ b/lcm/lcm/nf/serializers/affected_storages.py @@ -0,0 +1,51 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers + +from resource_handle import ResourceHandleSerializer + +CHANGE_TYPES = [ + "ADDED", + "REMOVED", + "MODIFIED", + "TEMPORARY" +] + + +class AffectedStoragesSerializer(serializers.Serializer): + id = serializers.UUIDField( + help_text="Identifier of the Storage instance, identifying the " + + "applicable 'virtualStorageResourceInfo' entry in the 'VnfInstance' data type", + required=True + ) + virtualStorageDescId = serializers.UUIDField( + help_text="Identifier of the related VirtualStorage descriptor " + + "in the VNFD. ", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change", + required=True, + choices=CHANGE_TYPES + ) + metadata = serializers.DictField( + help_text="Metadata about this resource. ", + required=False, + allow_null=True) + storageResource = ResourceHandleSerializer( + help_text="Reference to the VirtualStorage resource.", + required=True, + allow_null=False) diff --git a/lcm/lcm/nf/serializers/affected_vls.py b/lcm/lcm/nf/serializers/affected_vls.py new file mode 100644 index 00000000..7bd362ec --- /dev/null +++ b/lcm/lcm/nf/serializers/affected_vls.py @@ -0,0 +1,52 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers + +from resource_handle import ResourceHandleSerializer + +CHANGE_TYPES = [ + "ADDED", + "REMOVED", + "MODIFIED", + "TEMPORARY", + "LINK_PORT_ADDED", + "LINK_PORT_REMOVED" +] + + +class AffectedVLsSerializer(serializers.Serializer): + id = serializers.UUIDField( + help_text="Identifier of the virtual link instance, identifying " + + "the applicable 'vnfVirtualLinkResourceInfo' ", + required=True + ) + virtualLinkDescId = serializers.UUIDField( + help_text="Identifier of the related VLD in the VNFD.", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change", + required=True, + choices=CHANGE_TYPES + ) + metadata = serializers.DictField( + help_text="Metadata about this resource. ", + required=False, + allow_null=True) + networkResource = ResourceHandleSerializer( + help_text="Reference to the VirtualNetwork resource.", + required=True, + allow_null=False) diff --git a/lcm/lcm/nf/serializers/affected_vnfcs.py b/lcm/lcm/nf/serializers/affected_vnfcs.py new file mode 100644 index 00000000..85b83641 --- /dev/null +++ b/lcm/lcm/nf/serializers/affected_vnfcs.py @@ -0,0 +1,68 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers + +from resource_handle import ResourceHandleSerializer + +CHANGE_TYPES = [ + "ADDED", + "REMOVED", + "MODIFIED", + "TEMPORARY" +] + + +class AffectedVnfcsSerializer(serializers.Serializer): + id = serializers.UUIDField( + help_text="Identifier of the Vnfc instance, identifying the " + + "applicable 'vnfcResourceInfo' entry in the 'VnfInstance' data type", + required=True + ) + vduId = serializers.UUIDField( + help_text="Identifier of the related VDU in the VNFD.", + required=True + ) + changeType = serializers.ChoiceField( + help_text="Signals the type of change", + required=True, + choices=CHANGE_TYPES + ) + affectedVnfcCpIds = serializers.ListField( + help_text="Identifiers of CP(s) of the VNFC instance that " + + "were affected by the change", + required=False, + child=serializers.UUIDField(required=True) + ) + addedStorageResourceIds = serializers.ListField( + help_text="References to VirtualStorage resources that " + + "have been added", + required=False, + child=serializers.UUIDField() + ) + removedStorageResourceIds = serializers.ListField( + help_text="References to VirtualStorage resources that " + + "have been removed.", + required=False, + child=serializers.UUIDField() + ) + metadata = serializers.DictField( + help_text="Metadata about this resource. ", + required=False, + allow_null=True) + computeResource = ResourceHandleSerializer( + help_text="Reference to the VirtualCompute resource.", + required=True, + allow_null=False) diff --git a/lcm/lcm/nf/serializers/response.py b/lcm/lcm/nf/serializers/response.py index 81f5ed54..132788ac 100644 --- a/lcm/lcm/nf/serializers/response.py +++ b/lcm/lcm/nf/serializers/response.py @@ -21,3 +21,8 @@ class ProblemDetailsSerializer(serializers.Serializer): status = serializers.IntegerField(help_text="Status", required=True) detail = serializers.CharField(help_text="Detail", required=True, allow_null=True) instance = serializers.CharField(help_text="Instance", required=False, allow_null=True) + additional_details = serializers.ListField( + help_text="Any number of additional attributes, as defined in a " + + "specification or by an implementation.", + required=False, + allow_null=True) diff --git a/lcm/lcm/nf/serializers/vnf_info_modifications.py b/lcm/lcm/nf/serializers/vnf_info_modifications.py new file mode 100644 index 00000000..8098a6ca --- /dev/null +++ b/lcm/lcm/nf/serializers/vnf_info_modifications.py @@ -0,0 +1,99 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers + +from vim_connection_info import VimConnectionInfoSerializer + + +class VnfInfoModificationsSerializer(serializers.Serializer): + vnfInstanceName = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfInstanceName' attribute in 'VnfInstance'", + max_length=255, + required=False, + allow_null=True, + allow_blank=True) + vnfInstanceDescription = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfInstanceDescription' attribute in 'VnfInstance'", + required=False, + allow_null=True, + allow_blank=True) + vnfdId = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfdId' attribute in 'VnfInstance'", + max_length=255, + required=False, + allow_null=True, + allow_blank=True) + vnfProvider = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfProvider' attribute in 'VnfInstance'", + max_length=255, + required=False, + allow_null=True) + vnfProductName = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfProductName' attribute in 'vnfInstance'", + max_length=255, + required=False, + allow_null=True, + allow_blank=True) + vnfSoftwareVersion = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfSoftwareVersion' attribute in 'VnfInstance'.", + max_length=255, + required=False, + allow_null=True, + allow_blank=True) + vnfdVersion = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfdVersion' attribute in 'VnfInstance'. ", + max_length=255, + required=False, + allow_null=True, + allow_blank=False) + vnfPkgId = serializers.CharField( + help_text="If present, this attribute signals modifications of the " + + "'vnfPkgId' attribute in 'VnfInstance'.", + max_length=255, + required=False, + allow_null=True, + allow_blank=False) + vnfConfigurableProperties = serializers.DictField( + help_text="If present, this attribute signals modifications of the " + + "'vnfConfigurableProperties' attribute in 'VnfInstance'. ", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True,) + vimConnectionInfo = VimConnectionInfoSerializer( + help_text="If present, this attribute signals modifications of certain" + + "entries in the 'vimConnectionInfo'", + required=False, + many=True, + allow_null=True) + metadata = serializers.DictField( + help_text="If present, this attribute signals modifications of certain" + + "'metadata' attribute in 'vnfInstance'.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True) + extensions = serializers.DictField( + help_text="If present, this attribute signals modifications of certain" + + "'extensions' attribute in 'vnfInstance'.", + child=serializers.CharField(help_text="KeyValue Pairs", allow_blank=True), + required=False, + allow_null=True) diff --git a/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py b/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py new file mode 100644 index 00000000..f2ef664d --- /dev/null +++ b/lcm/lcm/nf/serializers/vnf_lcm_op_occ.py @@ -0,0 +1,200 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers + +from affected_vnfcs import AffectedVnfcsSerializer +from affected_vls import AffectedVLsSerializer +from affected_storages import AffectedStoragesSerializer +from response import ProblemDetailsSerializer +from ext_virtual_link_info import ExtVirtualLinkInfoSerializer +from vnf_info_modifications import VnfInfoModificationsSerializer + + +LCM_OPERATION_TYPES = [ + "INSTANTIATE", + "SCALE", + "SCALE_TO_LEVEL", + "CHANGE_FLAVOUR", + "TERMINATE", + "HEAL", + "OPERATE", + "CHANGE_EXT_CONN", + "MODIFY_INFO" +] + +LCM_OPERATION_STATE_TYPES = [ + "STARTING", + "PROCESSING", + "COMPLETED", + "FAILED_TEMP", + "FAILED", + "ROLLING_BACK", + "ROLLED_BACK" +] + + +class ResourceChangesSerializer(serializers.Serializer): + affectedVnfcs = AffectedVnfcsSerializer( + help_text="Information about VNFC instances that were affected " + + "during the lifecycle operation.", + required=False, + many=True + ) + affectedVirtualLinks = AffectedVLsSerializer( + help_text="Information about VL instances that were affected " + + "during the lifecycle operation. ", + required=False, + many=True + ) + affectedVirtualStorages = AffectedStoragesSerializer( + help_text="Information about virtualised storage instances that " + + "were affected during the lifecycle operation", + required=False, + many=True + ) + + +class LcmOpLinkSerializer(serializers.Serializer): + self = serializers.CharField( + help_text="URI of this resource.", + max_length=255, + required=True, + allow_null=False) + vnfInstance = serializers.CharField( + help_text="Link to the VNF instance that the operation applies to.", + required=True) + grant = serializers.CharField( + help_text="Link to the grant for this operation, if one exists.", + required=False) + cancel = serializers.CharField( + help_text="Link to the task resource that represents the 'cancel' " + + "operation for this VNF LCM operation occurrence.", + required=False) + retry = serializers.CharField( + help_text="Link to the task resource that represents the 'retry' " + + "operation for this VNF LCM operation occurrence, if" + + " retrying is currently allowed", + required=False) + rollback = serializers.CharField( + help_text="Link to the task resource that represents the 'cancel' " + + "operation for this VNF LCM operation occurrence.", + required=False) + fail = serializers.CharField( + help_text="Link to the task resource that represents the 'fail' " + + "operation for this VNF LCM operation occurrence.", + required=False) + + +class VNFLCMOpOccSerializer(serializers.Serializer): + id = serializers.CharField( + help_text="Identifier of this VNF lifecycle management operation" + + "occurrence,", + max_length=255, + required=True, + allow_null=False + ) + operationState = serializers.ChoiceField( + help_text="The state of the VNF LCM operation occurrence. ", + required=True, + choices=LCM_OPERATION_STATE_TYPES + ) + stateEnteredTime = serializers.CharField( + help_text="Date-time when the current state was entered.", + max_length=50 + ) + startTime = serializers.CharField( + help_text="Date-time of the start of the operation.", + max_length=50 + ) + vnfInstanceId = serializers.UUIDField( + help_text="Identifier of the VNF instance to which the operation" + + "applies" + ) + grantId = serializers.UUIDField( + help_text="Identifier of the grant related to this VNF LCM operation " + + "occurrence, if such grant exists.", + allow_null=True + ) + operation = serializers.ChoiceField( + help_text="The lifecycle management operation", + required=True, + choices=LCM_OPERATION_TYPES + ) + isAutomaticInvocation = serializers.BooleanField( + help_text="Set to true if this VNF LCM operation occurrence has " + + "been triggered by an automated procedure inside the VNFM. " + + "Set to False otherwise.", + default=False + ) + operationParams = serializers.DictField( + help_text="Input parameters of the LCM operation. This attribute " + + "shall be formatted according to the request data type of the " + + "related LCM operation. The following mapping between operationType and the " + + "data type of this attribute shall apply: " + + "1. INSTANTIATE: InstantiateVnfRequest" + + "2. SCALE: ScaleVnfRequest " + + "3. SCALE_TO_LEVEL: ScaleVnfToLevelRequest " + + "4. CHANGE_FLAVOUR: ChangeVnfFlavourRequest " + + "5. OPERATE: OperateVnfRequest " + + "6. HEAL: HealVnfRequest " + + "7. CHANGE_EXT_CONN: ChangeExtVnfConnectivityRequest " + + "8. TERMINATE: TerminateVnfRequest " + + "9. MODIFY_INFO: VnfInfoModifications", + required=True, + allow_null=False + ) + isCancelPending = serializers.BooleanField( + help_text="If the VNF LCM operation occurrence is in 'STARTING'" + + "'PROCESSING' or 'ROLLING_BACK' state and the operation is being" + + " cancelled, this attribute shall be set to True. Otherwise, " + + " it shall be set to False.", + required=True + ) + cancelMode = serializers.CharField( + help_text="The mode of an ongoing cancellation. Shall be present " + + "when isCancelPending=true, and shall be None otherwise.", + allow_null=True, + required=False + ) + error = ProblemDetailsSerializer( + help_text="If 'operationState' is 'FAILED_TEMP' or 'FAILED' or " + + "'PROCESSING' or 'ROLLING_BACK' and previous value of 'operationState' " + + "was 'FAILED_TEMP' this attribute shall be present ", + allow_null=True, + required=False + ) + resourceChanges = ResourceChangesSerializer( + help_text="It contains information about the cumulative changes " + + "to virtualised resources that were performed so far by the LCM " + + "operation since its start, if applicable.", + required=False, + allow_null=True) + changedInfo = VnfInfoModificationsSerializer( + help_text="Information about the changed VNF instance information, " + + "including VNF configurable properties", + required=False, + allow_null=True) + changedExtConnectivity = ExtVirtualLinkInfoSerializer( + help_text="Information about changed external connectivity, if this " + + "notification represents the result of a lifecycle operation occurrence. " + + "Shall be present if the 'notificationStatus' is set to 'RESULT' and the " + + "'operation' is set to 'CHANGE_EXT_CONN'. Shall be absent otherwise.", + many=True, + required=False, + allow_null=True) + _links = LcmOpLinkSerializer( + help_text="Links to resources related to this resource.", + required=True) diff --git a/lcm/lcm/nf/serializers/vnf_lcm_op_occs.py b/lcm/lcm/nf/serializers/vnf_lcm_op_occs.py new file mode 100644 index 00000000..6cb70906 --- /dev/null +++ b/lcm/lcm/nf/serializers/vnf_lcm_op_occs.py @@ -0,0 +1,20 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved. +# +# 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 rest_framework import serializers +from vnf_lcm_op_occ import VNFLCMOpOccSerializer + + +class VNFLCMOpOccsSerializer(serializers.ListSerializer): + child = VNFLCMOpOccSerializer() diff --git a/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py b/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py new file mode 100644 index 00000000..2e1c15a0 --- /dev/null +++ b/lcm/lcm/nf/tests/test_query_vnf_lcm_op.py @@ -0,0 +1,159 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved +# +# 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. + +import json + +from django.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import VNFLcmOpOccModel + + +class TestVNFLcmOpOccs(TestCase): + def setUp(self): + self.client = Client() + self.vnf_lcm_op_occ_id = "99442b18-a5c7-11e8-998c-bf1755941f16" + VNFLcmOpOccModel.objects.all().delete() + self.test_single_vnf_lcm_op = [{ + "id": "99442b18-a5c7-11e8-998c-bf1755941f16", + "operationState": "STARTING", + "stateEnteredTime": "2018-07-09", + "startTime": "2018-07-09", + "vnfInstanceId": "cd552c9c-ab6f-11e8-b354-236c32aa91a1", + "grantId": None, + "operation": "SCALE", + "isAutomaticInvocation": False, + "operationParams": {}, + "isCancelPending": False, + "cancelMode": None, + "error": None, + "resourceChanges": None, + "changedInfo": None, + "changedExtConnectivity": None, + "_links": { + "self": "demo", + "vnfInstance": "demo" + } + }] + self.test_vnflcmop_with_exclude_default = [{ + "id": "99442b18-a5c7-11e8-998c-bf1755941f16", + "operationState": "STARTING", + "stateEnteredTime": "2018-07-09", + "startTime": "2018-07-09", + "vnfInstanceId": "cd552c9c-ab6f-11e8-b354-236c32aa91a1", + "grantId": None, + "operation": "SCALE", + "isAutomaticInvocation": False, + "isCancelPending": False, + "cancelMode": None, + "_links": { + "self": "demo", + "vnfInstance": "demo" + } + }] + + self.test_multiple_vnf_lcm_op = [{ + "id": "a6b9415c-ab99-11e8-9d37-dbb5e0378955", + "operationState": "STARTING", + "stateEnteredTime": "2018-07-09", + "startTime": "2018-07-09", + "vnfInstanceId": "cd552c9c-ab6f-11e8-b354-236c32aa91a1", + "grantId": None, + "operation": "INSTANTIATE", + "isAutomaticInvocation": False, + "operationParams": {}, + "isCancelPending": False, + "cancelMode": None, + "error": None, + "resourceChanges": None, + "changedInfo": None, + "changedExtConnectivity": None, + "_links": { + "self": "demo", + "vnfInstance": "demo" + } + }] + self.test_multiple_vnf_lcm_op.append( + self.test_single_vnf_lcm_op[0]) + + def tearDown(self): + pass + + def test_get_vnflcmopocc(self): + lcm_op_id = "99442b18-a5c7-11e8-998c-bf1755941f16" + vnf_instance_id = "cd552c9c-ab6f-11e8-b354-236c32aa91a1" + VNFLcmOpOccModel(id=lcm_op_id, operation_state="STARTING", + state_entered_time="2018-07-09", start_time="2018-07-09", + vnf_instance_id=vnf_instance_id, + grant_id=None, operation="SCALE", is_automatic_invocation=False, + operation_params='{}', is_cancel_pending=False, cancel_mode=None, + error=None, resource_changes=None, changed_ext_connectivity=None, + links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save() + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs", format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.test_single_vnf_lcm_op, response.data) + + def test_get_vnflcmopocc_with_id_not_exist(self): + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs?id=dummy", format='json') + self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR) + expected_data = { + "status": 500, + "detail": "LCM Operation Occurances do not exist" + } + self.assertEqual(expected_data, response.data) + + def test_get_vnflcmopocc_with_filters(self): + lcm_op_id = "a6b9415c-ab99-11e8-9d37-dbb5e0378955" + vnf_instance_id = "cd552c9c-ab6f-11e8-b354-236c32aa91a1" + VNFLcmOpOccModel(id=lcm_op_id, operation_state="STARTING", + state_entered_time="2018-07-09", start_time="2018-07-09", + vnf_instance_id=vnf_instance_id, + grant_id=None, operation="INSTANTIATE", is_automatic_invocation=False, + operation_params='{}', is_cancel_pending=False, cancel_mode=None, + error=None, resource_changes=None, changed_ext_connectivity=None, + links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save() + + lcm_op_id = "99442b18-a5c7-11e8-998c-bf1755941f16" + VNFLcmOpOccModel(id=lcm_op_id, operation_state="STARTING", + state_entered_time="2018-07-09", start_time="2018-07-09", + vnf_instance_id=vnf_instance_id, + grant_id=None, operation="SCALE", is_automatic_invocation=False, + operation_params='{}', is_cancel_pending=False, cancel_mode=None, + error=None, resource_changes=None, changed_ext_connectivity=None, + links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save() + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs", format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.test_multiple_vnf_lcm_op, response.data) + + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs?operation=SCALE", format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.test_single_vnf_lcm_op, response.data) + + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs?vnfInstanceId=%s" % vnf_instance_id, format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.test_multiple_vnf_lcm_op, response.data) + + def test_get_vnflcmopocc_with_extra_flags(self): + lcm_op_id = "99442b18-a5c7-11e8-998c-bf1755941f16" + vnf_instance_id = "cd552c9c-ab6f-11e8-b354-236c32aa91a1" + VNFLcmOpOccModel(id=lcm_op_id, operation_state="STARTING", + state_entered_time="2018-07-09", start_time="2018-07-09", + vnf_instance_id=vnf_instance_id, + grant_id=None, operation="SCALE", is_automatic_invocation=False, + operation_params='{}', is_cancel_pending=False, cancel_mode=None, + error=None, resource_changes=None, changed_ext_connectivity=None, + links=json.dumps({"self": "demo", "vnfInstance": "demo"})).save() + response = self.client.get("/api/vnflcm/v1/vnf_lcm_op_occs?exclude_default", format='json') + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(self.test_vnflcmop_with_exclude_default, response.data) diff --git a/lcm/lcm/nf/urls.py b/lcm/lcm/nf/urls.py index b73cb02e..7377c085 100644 --- a/lcm/lcm/nf/urls.py +++ b/lcm/lcm/nf/urls.py @@ -19,6 +19,7 @@ from lcm.nf.views.instantiate_vnf_view import InstantiateVnfView from lcm.nf.views.terminate_vnf_view import TerminateVnfView from lcm.nf.views.subscriptions_view import SubscriptionsView from lcm.nf.views.operate_vnf_view import OperateVnfView +from lcm.nf.views.lcm_op_occs_view import QueryMultiVnfLcmOpOccs urlpatterns = [ url(r'^api/vnflcm/v1/subscriptions$', SubscriptionsView.as_view()), @@ -27,4 +28,5 @@ urlpatterns = [ url(r'^api/vnflcm/v1/vnf_instances/(?P[0-9a-zA-Z_-]+)$', DeleteVnfAndQueryVnf.as_view()), url(r'^api/vnflcm/v1/vnf_instances/(?P[0-9a-zA-Z_-]+)/terminate$', TerminateVnfView.as_view()), url(r'^api/vnflcm/v1/vnf_instances/(?P[0-9a-zA-Z_-]+)/operate$', OperateVnfView.as_view()), + url(r'^api/vnflcm/v1/vnf_lcm_op_occs$', QueryMultiVnfLcmOpOccs.as_view()), ] diff --git a/lcm/lcm/nf/views/lcm_op_occs_view.py b/lcm/lcm/nf/views/lcm_op_occs_view.py new file mode 100644 index 00000000..c49b64c1 --- /dev/null +++ b/lcm/lcm/nf/views/lcm_op_occs_view.py @@ -0,0 +1,82 @@ +# Copyright (C) 2018 Verizon. All Rights Reserved +# +# 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. + +import logging +import traceback + +from drf_yasg.utils import swagger_auto_schema +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.nf.biz.query_vnf_lcm_op_occ import QueryVnfLcmOpOcc +from lcm.nf.serializers.response import ProblemDetailsSerializer +from lcm.nf.serializers.vnf_lcm_op_occs import VNFLCMOpOccsSerializer +from lcm.pub.exceptions import NFLCMException + +logger = logging.getLogger(__name__) +EXCLUDE_DEFAULT = ['operationParams', 'error', 'resourceChanges', 'changedInfo', 'changedExtConnectivity'] +VALID_FILTERS = ["all_fields", "fields", "exclude_fields", "exclude_default", + "id", "operationState", "stateEnteredTime", "startTime", + "vnfInstanceId", "grantId", "operation"] + + +def get_problem_details_serializer(status_code, error_message): + problem_details = { + "status": status_code, + "detail": error_message + } + problem_details_serializer = ProblemDetailsSerializer(data=problem_details) + problem_details_serializer.is_valid() + return problem_details_serializer + + +class QueryMultiVnfLcmOpOccs(APIView): + @swagger_auto_schema( + responses={ + status.HTTP_200_OK: VNFLCMOpOccsSerializer(), + status.HTTP_400_BAD_REQUEST: ProblemDetailsSerializer(), + status.HTTP_500_INTERNAL_SERVER_ERROR: ProblemDetailsSerializer() + } + ) + def get(self, request): + logger.debug("QueryMultiVnfLcmOpOccs--get::> %s" % request.query_params) + try: + if request.query_params and not set(request.query_params).issubset(set(VALID_FILTERS)): + problem_details_serializer = get_problem_details_serializer(status.HTTP_400_BAD_REQUEST, "Not a valid filter") + return Response(data=problem_details_serializer.data, status=status.HTTP_400_BAD_REQUEST) + resp_data = QueryVnfLcmOpOcc(request.query_params).query_multi_vnf_lcm_op_occ() + + vnf_lcm_op_occs_serializer = VNFLCMOpOccsSerializer(data=resp_data) + if not vnf_lcm_op_occs_serializer.is_valid(): + raise NFLCMException(vnf_lcm_op_occs_serializer.errors) + + logger.debug("QueryMultiVnfLcmOpOccs--get::> Remove default fields if exclude_default" + + " is specified") + # TODO(bharath): Add support for "fields", "exclude_fields" in query parameters + if 'exclude_default' in request.query_params.keys(): + for field in EXCLUDE_DEFAULT: + for lcm_op in vnf_lcm_op_occs_serializer.data: + del lcm_op[field] + return Response(data=vnf_lcm_op_occs_serializer.data, status=status.HTTP_200_OK) + except NFLCMException as e: + logger.error(e.message) + problem_details_serializer = get_problem_details_serializer(status.HTTP_500_INTERNAL_SERVER_ERROR, e.message) + return Response(data=problem_details_serializer.data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + except Exception as e: + logger.error(e.message) + logger.error(traceback.format_exc()) + problem_details_serializer = get_problem_details_serializer(status.HTTP_500_INTERNAL_SERVER_ERROR, e.message) + return Response(data=problem_details_serializer.data, status=status.HTTP_500_INTERNAL_SERVER_ERROR) diff --git a/lcm/lcm/pub/database/models.py b/lcm/lcm/pub/database/models.py index 57de616f..cbdd85cc 100644 --- a/lcm/lcm/pub/database/models.py +++ b/lcm/lcm/pub/database/models.py @@ -311,3 +311,25 @@ class SubscriptionModel(models.Model): vnf_instance_filter = models.TextField(db_column='VNFINSTANCEFILTER', null=True) links = models.TextField(db_column='LINKS', max_length=20000) + + +class VNFLcmOpOccModel(models.Model): + class Meta: + db_table = 'VNFLCMOPOCCS' + + id = models.CharField(db_column='ID', max_length=255, primary_key=True) + operation_state = models.CharField(db_column='OPERATIONSTATE', null=False, max_length=30) + state_entered_time = models.CharField(db_column='STATEENTEREDTIME', null=False, max_length=30) + start_time = models.CharField(db_column='STARTTIME', null=False, max_length=30) + vnf_instance_id = models.CharField(db_column='VNFINSTANCEID', null=False, max_length=255) + grant_id = models.CharField(db_column='GRANTID', null=True, max_length=255) + operation = models.CharField(db_column='OPERATION', null=False, max_length=30) + is_automatic_invocation = models.CharField(db_column='ISAUTOMATICINVOCATION', null=False, max_length=5) + operation_params = models.TextField(db_column='OPERATIONPARAMS', null=False) + is_cancel_pending = models.CharField(db_column='ISCANCELPENDING', null=False, max_length=5) + cancel_mode = models.TextField(db_column='CANCELMODE', null=True) + error = models.TextField(db_column='ERROR', null=True) + resource_changes = models.TextField(db_column='RESOURCECHANGES', null=True) + changed_info = models.TextField(db_column='CHANGEDINFO', null=True) + changed_ext_connectivity = models.TextField(db_column='CHANGEDEXTCONNECTIVITY', null=True) + links = models.TextField(db_column='LINKS', null=False)