Add terminate vnf instance API
Implemented terminate vnf instance API.
* GET /vnflcm/v1/vnf_instances/{vnf_instance_id}/terminate
Co-Authored-By: tpatil <tushar.vitthal.patil@gmail.com>
Co-Authored-By: Shubham Potale <shubham.potale@nttdata.com>
Co-Authored-By: Sameer Thakur <sameer.thakur@nttdata.com>
Change-Id: I69b7ef12038aa410db7e50671df2931beb761223
Blueprint: support-etsi-nfv-specs
			
			
This commit is contained in:
		@@ -199,3 +199,14 @@ instantiate = {
 | 
			
		||||
    'required': ['flavourId'],
 | 
			
		||||
    'additionalProperties': False,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
terminate = {
 | 
			
		||||
    'type': 'object',
 | 
			
		||||
    'properties': {
 | 
			
		||||
        'terminationType': {'type': 'string',
 | 
			
		||||
                            'enum': ['FORCEFUL', 'GRACEFUL']},
 | 
			
		||||
        'gracefulTerminationTimeout': {'type': 'integer', 'minimum': 0}
 | 
			
		||||
    },
 | 
			
		||||
    'required': ['terminationType'],
 | 
			
		||||
    'additionalProperties': False,
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -245,8 +245,29 @@ class VnfLcmController(wsgi.Controller):
 | 
			
		||||
 | 
			
		||||
        self._instantiate(context, vnf_instance, body)
 | 
			
		||||
 | 
			
		||||
    @check_vnf_state(action="terminate",
 | 
			
		||||
        instantiation_state=[fields.VnfInstanceState.INSTANTIATED],
 | 
			
		||||
        task_state=[None])
 | 
			
		||||
    def _terminate(self, context, vnf_instance, request_body):
 | 
			
		||||
        req_body = utils.convert_camelcase_to_snakecase(request_body)
 | 
			
		||||
        terminate_vnf_req = \
 | 
			
		||||
            objects.TerminateVnfRequest.obj_from_primitive(
 | 
			
		||||
                req_body, context=context)
 | 
			
		||||
 | 
			
		||||
        vnf_instance.task_state = fields.VnfInstanceTaskState.TERMINATING
 | 
			
		||||
        vnf_instance.save()
 | 
			
		||||
        self.rpc_api.terminate(context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
    @wsgi.response(http_client.ACCEPTED)
 | 
			
		||||
    @wsgi.expected_errors((http_client.BAD_REQUEST, http_client.FORBIDDEN,
 | 
			
		||||
                           http_client.NOT_FOUND, http_client.CONFLICT))
 | 
			
		||||
    @validation.schema(vnf_lcm.terminate)
 | 
			
		||||
    def terminate(self, request, id, body):
 | 
			
		||||
        raise webob.exc.HTTPNotImplemented()
 | 
			
		||||
        context = request.environ['tacker.context']
 | 
			
		||||
        context.can(vnf_lcm_policies.VNFLCM % 'terminate')
 | 
			
		||||
 | 
			
		||||
        vnf_instance = self._get_vnf_instance(context, id)
 | 
			
		||||
        self._terminate(context, vnf_instance, body)
 | 
			
		||||
 | 
			
		||||
    def heal(self, request, id, body):
 | 
			
		||||
        raise webob.exc.HTTPNotImplemented()
 | 
			
		||||
 
 | 
			
		||||
@@ -424,6 +424,20 @@ class Conductor(manager.Manager):
 | 
			
		||||
 | 
			
		||||
        vnf_package.save()
 | 
			
		||||
 | 
			
		||||
    def terminate(self, context, vnf_instance, terminate_vnf_req):
 | 
			
		||||
        self.vnflcm_driver.terminate_vnf(context, vnf_instance,
 | 
			
		||||
            terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
        vnf_package_vnfd = objects.VnfPackageVnfd.get_by_id(context,
 | 
			
		||||
                vnf_instance.vnfd_id)
 | 
			
		||||
        vnf_package = objects.VnfPackage.get_by_id(context,
 | 
			
		||||
                vnf_package_vnfd.package_uuid, expected_attrs=['vnfd'])
 | 
			
		||||
        try:
 | 
			
		||||
            self._update_package_usage_state(context, vnf_package)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            LOG.error("Failed to update usage_state of vnf package %s",
 | 
			
		||||
                      vnf_package.id)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def init(args, **kwargs):
 | 
			
		||||
    CONF(args=args, project='tacker',
 | 
			
		||||
 
 | 
			
		||||
@@ -38,3 +38,14 @@ class VNFLcmRPCAPI(object):
 | 
			
		||||
        return rpc_method(context, 'instantiate',
 | 
			
		||||
                          vnf_instance=vnf_instance,
 | 
			
		||||
                          instantiate_vnf=instantiate_vnf)
 | 
			
		||||
 | 
			
		||||
    def terminate(self, context, vnf_instance, terminate_vnf_req, cast=True):
 | 
			
		||||
        serializer = objects_base.TackerObjectSerializer()
 | 
			
		||||
 | 
			
		||||
        client = rpc.get_client(self.target, version_cap=None,
 | 
			
		||||
                                serializer=serializer)
 | 
			
		||||
        cctxt = client.prepare()
 | 
			
		||||
        rpc_method = cctxt.cast if cast else cctxt.call
 | 
			
		||||
        return rpc_method(context, 'terminate',
 | 
			
		||||
                          vnf_instance=vnf_instance,
 | 
			
		||||
                          terminate_vnf_req=terminate_vnf_req)
 | 
			
		||||
 
 | 
			
		||||
@@ -34,3 +34,4 @@ def register_all():
 | 
			
		||||
    __import__('tacker.objects.vim_connection')
 | 
			
		||||
    __import__('tacker.objects.instantiate_vnf_req')
 | 
			
		||||
    __import__('tacker.objects.vnf_resources')
 | 
			
		||||
    __import__('tacker.objects.terminate_vnf_req')
 | 
			
		||||
 
 | 
			
		||||
@@ -166,3 +166,14 @@ class IpAddressType(BaseTackerEnum):
 | 
			
		||||
 | 
			
		||||
class IpAddressTypeField(BaseEnumField):
 | 
			
		||||
    AUTO_TYPE = IpAddressType()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VnfInstanceTerminationType(BaseTackerEnum):
 | 
			
		||||
    FORCEFUL = 'FORCEFUL'
 | 
			
		||||
    GRACEFUL = 'GRACEFUL'
 | 
			
		||||
 | 
			
		||||
    ALL = (FORCEFUL, GRACEFUL)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VnfInstanceTerminationTypeField(BaseEnumField):
 | 
			
		||||
    AUTO_TYPE = VnfInstanceTerminationType()
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										54
									
								
								tacker/objects/terminate_vnf_req.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								tacker/objects/terminate_vnf_req.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,54 @@
 | 
			
		||||
# Copyright (C) 2020 NTT DATA
 | 
			
		||||
# 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 oslo_log import log as logging
 | 
			
		||||
 | 
			
		||||
from tacker.objects import base
 | 
			
		||||
from tacker.objects import fields
 | 
			
		||||
 | 
			
		||||
LOG = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@base.TackerObjectRegistry.register
 | 
			
		||||
class TerminateVnfRequest(base.TackerObject, base.TackerPersistentObject):
 | 
			
		||||
    # Version 1.0: Initial version
 | 
			
		||||
    VERSION = '1.0'
 | 
			
		||||
 | 
			
		||||
    fields = {
 | 
			
		||||
        'termination_type': fields.VnfInstanceTerminationTypeField(
 | 
			
		||||
            nullable=False),
 | 
			
		||||
        'graceful_termination_timeout': fields.IntegerField(nullable=True,
 | 
			
		||||
                                                            default=0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def obj_from_primitive(cls, primitive, context):
 | 
			
		||||
        if 'tacker_object.name' in primitive:
 | 
			
		||||
            obj_terminate_vnf_req = super(
 | 
			
		||||
                TerminateVnfRequest, cls).obj_from_primitive(
 | 
			
		||||
                primitive, context)
 | 
			
		||||
        else:
 | 
			
		||||
            obj_terminate_vnf_req = TerminateVnfRequest._from_dict(primitive)
 | 
			
		||||
 | 
			
		||||
        return obj_terminate_vnf_req
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def _from_dict(cls, data_dict):
 | 
			
		||||
        termination_type = data_dict.get('termination_type')
 | 
			
		||||
        graceful_termination_timeout = \
 | 
			
		||||
            data_dict.get('graceful_termination_timeout', 0)
 | 
			
		||||
 | 
			
		||||
        return cls(termination_type=termination_type,
 | 
			
		||||
                   graceful_termination_timeout=graceful_termination_timeout)
 | 
			
		||||
@@ -293,6 +293,17 @@ class InstantiatedVnfInfo(base.TackerObject, base.TackerObjectDictCompat,
 | 
			
		||||
 | 
			
		||||
        return data
 | 
			
		||||
 | 
			
		||||
    def reinitialize(self):
 | 
			
		||||
        # Reinitialize vnf to non instantiated state.
 | 
			
		||||
        self.ext_cp_info = []
 | 
			
		||||
        self.ext_virtual_link_info = []
 | 
			
		||||
        self.ext_managed_virtual_link_info = []
 | 
			
		||||
        self.vnfc_resource_info = []
 | 
			
		||||
        self.vnf_virtual_link_resource_info = []
 | 
			
		||||
        self.virtual_storage_resource_info = []
 | 
			
		||||
        self.instance_id = None
 | 
			
		||||
        self.vnf_state = fields.VnfOperationalStateType.STOPPED
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@base.TackerObjectRegistry.register
 | 
			
		||||
class VnfExtCpInfo(base.TackerObject, base.TackerObjectDictCompat,
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,17 @@ rules = [
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    ),
 | 
			
		||||
    policy.DocumentedRuleDefault(
 | 
			
		||||
        name=VNFLCM % 'terminate',
 | 
			
		||||
        check_str=base.RULE_ADMIN_OR_OWNER,
 | 
			
		||||
        description="Terminate a VNF instance.",
 | 
			
		||||
        operations=[
 | 
			
		||||
            {
 | 
			
		||||
                'method': 'POST',
 | 
			
		||||
                'path': '/vnflcm/v1/vnf_instances/{vnfInstanceId}/terminate'
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    )
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ import tacker.conf
 | 
			
		||||
from tacker import context
 | 
			
		||||
from tacker.glance_store import store as glance_store
 | 
			
		||||
from tacker import objects
 | 
			
		||||
from tacker.objects import vnf_package
 | 
			
		||||
from tacker.objects import fields
 | 
			
		||||
from tacker.tests.unit.conductor import fakes
 | 
			
		||||
from tacker.tests.unit.db.base import SqlTestCase
 | 
			
		||||
from tacker.tests.unit.objects import fakes as fake_obj
 | 
			
		||||
@@ -73,8 +73,8 @@ class TestConductor(SqlTestCase):
 | 
			
		||||
            'tacker.vnflcm.vnflcm_driver.VnfLcmDriver', fake_vnflcm_driver)
 | 
			
		||||
 | 
			
		||||
    def _create_vnf_package(self):
 | 
			
		||||
        vnfpkgm = vnf_package.VnfPackage(context=self.context,
 | 
			
		||||
                                         **fakes.VNF_PACKAGE_DATA)
 | 
			
		||||
        vnfpkgm = objects.VnfPackage(context=self.context,
 | 
			
		||||
                                     **fakes.VNF_PACKAGE_DATA)
 | 
			
		||||
        vnfpkgm.create()
 | 
			
		||||
        return vnfpkgm
 | 
			
		||||
 | 
			
		||||
@@ -251,10 +251,101 @@ class TestConductor(SqlTestCase):
 | 
			
		||||
        mock_log.error.assert_called_once_with(expected_log,
 | 
			
		||||
            vnf_package_vnfd.package_uuid)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfPackage, 'is_package_in_use')
 | 
			
		||||
    def test_terminate_vnf_instance(self, mock_package_in_use):
 | 
			
		||||
        vnf_package_vnfd = self._create_and_upload_vnf_package()
 | 
			
		||||
        vnf_instance_data = fake_obj.get_vnf_instance_data(
 | 
			
		||||
            vnf_package_vnfd.vnfd_id)
 | 
			
		||||
        mock_package_in_use.return_value = True
 | 
			
		||||
        vnf_instance_data['instantiation_state'] =\
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED
 | 
			
		||||
        vnf_instance = objects.VnfInstance(context=self.context,
 | 
			
		||||
            **vnf_instance_data)
 | 
			
		||||
        vnf_instance.create()
 | 
			
		||||
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
 | 
			
		||||
 | 
			
		||||
        self.conductor.terminate(self.context, vnf_instance,
 | 
			
		||||
                                 terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
        self.vnflcm_driver.terminate_vnf.assert_called_once_with(
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        mock_package_in_use.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfPackage, 'is_package_in_use')
 | 
			
		||||
    def test_terminate_vnf_instance_with_usage_state_not_in_use(self,
 | 
			
		||||
            mock_vnf_package_is_package_in_use):
 | 
			
		||||
        vnf_package_vnfd = self._create_and_upload_vnf_package()
 | 
			
		||||
        vnf_instance_data = fake_obj.get_vnf_instance_data(
 | 
			
		||||
            vnf_package_vnfd.vnfd_id)
 | 
			
		||||
        vnf_instance_data['instantiation_state'] =\
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED
 | 
			
		||||
        vnf_instance = objects.VnfInstance(context=self.context,
 | 
			
		||||
            **vnf_instance_data)
 | 
			
		||||
        vnf_instance.create()
 | 
			
		||||
 | 
			
		||||
        mock_vnf_package_is_package_in_use.return_value = False
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
 | 
			
		||||
 | 
			
		||||
        self.conductor.terminate(self.context, vnf_instance,
 | 
			
		||||
                                 terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
        self.vnflcm_driver.terminate_vnf.assert_called_once_with(
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        mock_vnf_package_is_package_in_use.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfPackage, 'is_package_in_use')
 | 
			
		||||
    def test_terminate_vnf_instance_with_usage_state_already_in_use(self,
 | 
			
		||||
            mock_vnf_package_is_package_in_use):
 | 
			
		||||
        vnf_package_vnfd = self._create_and_upload_vnf_package()
 | 
			
		||||
        vnf_instance_data = fake_obj.get_vnf_instance_data(
 | 
			
		||||
            vnf_package_vnfd.vnfd_id)
 | 
			
		||||
        vnf_instance_data['instantiation_state'] =\
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED
 | 
			
		||||
        vnf_instance = objects.VnfInstance(context=self.context,
 | 
			
		||||
            **vnf_instance_data)
 | 
			
		||||
        vnf_instance.create()
 | 
			
		||||
 | 
			
		||||
        mock_vnf_package_is_package_in_use.return_value = True
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
 | 
			
		||||
 | 
			
		||||
        self.conductor.terminate(self.context, vnf_instance,
 | 
			
		||||
                                 terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
        self.vnflcm_driver.terminate_vnf.assert_called_once_with(
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        mock_vnf_package_is_package_in_use.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfPackage, 'is_package_in_use')
 | 
			
		||||
    @mock.patch('tacker.conductor.conductor_server.LOG')
 | 
			
		||||
    def test_terminate_vnf_instance_failed_to_update_usage_state(
 | 
			
		||||
            self, mock_log, mock_is_package_in_use):
 | 
			
		||||
        vnf_package_vnfd = self._create_and_upload_vnf_package()
 | 
			
		||||
        vnf_instance_data = fake_obj.get_vnf_instance_data(
 | 
			
		||||
            vnf_package_vnfd.vnfd_id)
 | 
			
		||||
        vnf_instance_data['instantiation_state'] =\
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED
 | 
			
		||||
        vnf_instance = objects.VnfInstance(context=self.context,
 | 
			
		||||
                                           **vnf_instance_data)
 | 
			
		||||
        vnf_instance.create()
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
 | 
			
		||||
        mock_is_package_in_use.side_effect = Exception
 | 
			
		||||
        self.conductor.terminate(self.context, vnf_instance,
 | 
			
		||||
                                 terminate_vnf_req)
 | 
			
		||||
        self.vnflcm_driver.terminate_vnf.assert_called_once_with(
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        expected_msg = "Failed to update usage_state of vnf package %s"
 | 
			
		||||
        mock_log.error.assert_called_once_with(expected_msg,
 | 
			
		||||
            vnf_package_vnfd.package_uuid)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(os, 'remove')
 | 
			
		||||
    @mock.patch.object(shutil, 'rmtree')
 | 
			
		||||
    @mock.patch.object(os.path, 'exists')
 | 
			
		||||
    @mock.patch.object(vnf_package.VnfPackagesList, 'get_by_filters')
 | 
			
		||||
    @mock.patch.object(objects.VnfPackagesList, 'get_by_filters')
 | 
			
		||||
    def test_run_cleanup_vnf_packages(self, mock_get_by_filter,
 | 
			
		||||
                                      mock_exists, mock_rmtree,
 | 
			
		||||
                                      mock_remove):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										51
									
								
								tacker/tests/unit/objects/test_terminate_vnf_request.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								tacker/tests/unit/objects/test_terminate_vnf_request.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,51 @@
 | 
			
		||||
# Copyright (C) 2020 NTT DATA
 | 
			
		||||
# 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 tacker import context
 | 
			
		||||
from tacker import objects
 | 
			
		||||
from tacker.tests.unit.db.base import SqlTestCase
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class TestTerminateVnfRequest(SqlTestCase):
 | 
			
		||||
 | 
			
		||||
    def setUp(self):
 | 
			
		||||
        super(TestTerminateVnfRequest, self).setUp()
 | 
			
		||||
        self.context = context.get_admin_context()
 | 
			
		||||
 | 
			
		||||
    def _get_terminate_vnf_request(self):
 | 
			
		||||
        terminate_vnf_request = {
 | 
			
		||||
            'termination_type': 'GRACEFUL',
 | 
			
		||||
            'graceful_termination_timeout': 10
 | 
			
		||||
        }
 | 
			
		||||
        return terminate_vnf_request
 | 
			
		||||
 | 
			
		||||
    def test_obj_from_primitive(self):
 | 
			
		||||
        terminate_vnf_request = self._get_terminate_vnf_request()
 | 
			
		||||
        result = objects.TerminateVnfRequest.obj_from_primitive(
 | 
			
		||||
            terminate_vnf_request, self.context)
 | 
			
		||||
        self.assertTrue(isinstance(result, objects.TerminateVnfRequest))
 | 
			
		||||
        self.assertEqual('GRACEFUL', result.termination_type)
 | 
			
		||||
        self.assertEqual(terminate_vnf_request['graceful_termination_timeout'],
 | 
			
		||||
            result.graceful_termination_timeout)
 | 
			
		||||
 | 
			
		||||
    def test_obj_from_primitive_without_timeout(self):
 | 
			
		||||
        terminate_vnf_request = self._get_terminate_vnf_request()
 | 
			
		||||
        terminate_vnf_request.pop('graceful_termination_timeout')
 | 
			
		||||
 | 
			
		||||
        result = objects.TerminateVnfRequest.obj_from_primitive(
 | 
			
		||||
            terminate_vnf_request, self.context)
 | 
			
		||||
        self.assertTrue(isinstance(result, objects.TerminateVnfRequest))
 | 
			
		||||
        self.assertEqual('GRACEFUL', result.termination_type)
 | 
			
		||||
        self.assertEqual(0, result.graceful_termination_timeout)
 | 
			
		||||
@@ -695,3 +695,143 @@ class TestController(base.TestCase):
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
        self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "get_by_id")
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(VNFLcmRPCAPI, "terminate")
 | 
			
		||||
    @ddt.data({'terminationType': 'FORCEFUL'},
 | 
			
		||||
              {'terminationType': 'GRACEFUL'},
 | 
			
		||||
              {'terminationType': 'GRACEFUL',
 | 
			
		||||
               'gracefulTerminationTimeout': 10})
 | 
			
		||||
    def test_terminate(self, body, mock_terminate, mock_save, mock_get_by_id):
 | 
			
		||||
        vnf_instance_obj = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        mock_get_by_id.return_value = vnf_instance_obj
 | 
			
		||||
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
        self.assertEqual(http_client.ACCEPTED, resp.status_code)
 | 
			
		||||
        mock_terminate.assert_called_once()
 | 
			
		||||
 | 
			
		||||
    @ddt.data(
 | 
			
		||||
        {'attribute': 'terminationType', 'value': "TEST",
 | 
			
		||||
         'expected_type': 'enum'},
 | 
			
		||||
        {'attribute': 'terminationType', 'value': 123,
 | 
			
		||||
         'expected_type': 'enum'},
 | 
			
		||||
        {'attribute': 'terminationType', 'value': True,
 | 
			
		||||
         'expected_type': 'enum'},
 | 
			
		||||
        {'attribute': 'gracefulTerminationTimeout', 'value': True,
 | 
			
		||||
         'expected_type': 'integer'},
 | 
			
		||||
        {'attribute': 'gracefulTerminationTimeout', 'value': "test",
 | 
			
		||||
         'expected_type': 'integer'}
 | 
			
		||||
    )
 | 
			
		||||
    @ddt.unpack
 | 
			
		||||
    def test_terminate_with_invalid_request_body(
 | 
			
		||||
            self, attribute, value, expected_type):
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        body = {'terminationType': 'GRACEFUL',
 | 
			
		||||
                'gracefulTerminationTimeout': 10}
 | 
			
		||||
        body.update({attribute: value})
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        expected_message = ("Invalid input for field/attribute {attribute}. "
 | 
			
		||||
             "Value: {value}.".format(value=value, attribute=attribute))
 | 
			
		||||
 | 
			
		||||
        exception = self.assertRaises(exceptions.ValidationError,
 | 
			
		||||
                                      self.controller.terminate,
 | 
			
		||||
                                      req, constants.UUID, body=body)
 | 
			
		||||
        self.assertIn(expected_message, exception.msg)
 | 
			
		||||
 | 
			
		||||
    def test_terminate_missing_termination_type(self):
 | 
			
		||||
        body = {'gracefulTerminationTimeout': 10}
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        # Call terminate API
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
        self.assertEqual(http_client.BAD_REQUEST, resp.status_code)
 | 
			
		||||
        self.assertEqual("'terminationType' is a required property",
 | 
			
		||||
            resp.json['badRequest']['message'])
 | 
			
		||||
 | 
			
		||||
    @ddt.data('GET', 'HEAD', 'PUT', 'DELETE', 'PATCH')
 | 
			
		||||
    def test_terminate_invalid_http_method(self, method):
 | 
			
		||||
        # Wrong HTTP method
 | 
			
		||||
        body = {'terminationType': 'GRACEFUL',
 | 
			
		||||
                'gracefulTerminationTimeout': 10}
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = method
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
        self.assertEqual(http_client.METHOD_NOT_ALLOWED, resp.status_code)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
 | 
			
		||||
    def test_terminate_non_existing_vnf_instance(self, mock_vnf_by_id):
 | 
			
		||||
        body = {'terminationType': 'GRACEFUL',
 | 
			
		||||
                'gracefulTerminationTimeout': 10}
 | 
			
		||||
        mock_vnf_by_id.side_effect = exceptions.VnfInstanceNotFound
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(http_client.NOT_FOUND, resp.status_code)
 | 
			
		||||
        self.assertEqual("Can not find requested vnf instance: %s" %
 | 
			
		||||
            uuidsentinel.vnf_instance_id,
 | 
			
		||||
            resp.json['itemNotFound']['message'])
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.vnf_instance, "_vnf_instance_get_by_id")
 | 
			
		||||
    def test_terminate_incorrect_instantiation_state(self, mock_vnf_by_id):
 | 
			
		||||
        mock_vnf_by_id.return_value = fakes.return_vnf_instance()
 | 
			
		||||
        body = {"terminationType": "FORCEFUL"}
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(http_client.CONFLICT, resp.status_code)
 | 
			
		||||
        expected_msg = ("Vnf instance %s in instantiation_state "
 | 
			
		||||
                        "NOT_INSTANTIATED. Cannot terminate while the vnf "
 | 
			
		||||
                        "instance is in this state.")
 | 
			
		||||
        self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
 | 
			
		||||
            resp.json['conflictingRequest']['message'])
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "get_by_id")
 | 
			
		||||
    def test_terminate_incorrect_task_state(self, mock_vnf_by_id):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            instantiated_state=fields.VnfInstanceState.INSTANTIATED,
 | 
			
		||||
            task_state=fields.VnfInstanceTaskState.TERMINATING)
 | 
			
		||||
        mock_vnf_by_id.return_value = vnf_instance
 | 
			
		||||
 | 
			
		||||
        body = {"terminationType": "FORCEFUL"}
 | 
			
		||||
        req = fake_request.HTTPRequest.blank(
 | 
			
		||||
            '/vnf_instances/%s/terminate' % uuidsentinel.vnf_instance_id)
 | 
			
		||||
        req.body = jsonutils.dump_as_bytes(body)
 | 
			
		||||
        req.headers['Content-Type'] = 'application/json'
 | 
			
		||||
        req.method = 'POST'
 | 
			
		||||
 | 
			
		||||
        resp = req.get_response(self.app)
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(http_client.CONFLICT, resp.status_code)
 | 
			
		||||
        expected_msg = ("Vnf instance %s in task_state TERMINATING. Cannot "
 | 
			
		||||
                        "terminate while the vnf instance is in this state.")
 | 
			
		||||
        self.assertEqual(expected_msg % uuidsentinel.vnf_instance_id,
 | 
			
		||||
            resp.json['conflictingRequest']['message'])
 | 
			
		||||
 
 | 
			
		||||
@@ -23,10 +23,12 @@ from tacker.common import exceptions
 | 
			
		||||
from tacker.common import utils
 | 
			
		||||
from tacker import context
 | 
			
		||||
from tacker import objects
 | 
			
		||||
from tacker.objects import fields
 | 
			
		||||
from tacker.tests.unit.db import base as db_base
 | 
			
		||||
from tacker.tests.unit.vnflcm import fakes
 | 
			
		||||
from tacker.tests import uuidsentinel
 | 
			
		||||
from tacker.vnflcm import vnflcm_driver
 | 
			
		||||
from tacker.vnfm import vim_client
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InfraDriverException(Exception):
 | 
			
		||||
@@ -256,7 +258,7 @@ class TestVnflcmDriver(db_base.SqlTestCase):
 | 
			
		||||
        self.assertEqual(expected_error % vnf_instance_obj.id, str(error))
 | 
			
		||||
        self.assertEqual("NOT_INSTANTIATED",
 | 
			
		||||
            vnf_instance_obj.instantiation_state)
 | 
			
		||||
        self.assertEqual(1, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(2, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(2, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 | 
			
		||||
        shutil.rmtree(fake_csar)
 | 
			
		||||
@@ -298,8 +300,8 @@ class TestVnflcmDriver(db_base.SqlTestCase):
 | 
			
		||||
        self.assertEqual(expected_error % vnf_instance_obj.id, str(error))
 | 
			
		||||
        self.assertEqual("NOT_INSTANTIATED",
 | 
			
		||||
            vnf_instance_obj.instantiation_state)
 | 
			
		||||
        self.assertEqual(1, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(3, self._vnf_manager.invoke.call_count)
 | 
			
		||||
        self.assertEqual(3, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(5, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 | 
			
		||||
        shutil.rmtree(fake_csar)
 | 
			
		||||
 | 
			
		||||
@@ -334,3 +336,106 @@ class TestVnflcmDriver(db_base.SqlTestCase):
 | 
			
		||||
        self.assertEqual(2, mock_create.call_count)
 | 
			
		||||
        self.assertEqual("INSTANTIATED", vnf_instance_obj.instantiation_state)
 | 
			
		||||
        shutil.rmtree(fake_csar)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(vim_client.VimClient, "get_vim")
 | 
			
		||||
    @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
 | 
			
		||||
    @mock.patch.object(objects.VnfResource, "destroy")
 | 
			
		||||
    def test_terminate_vnf(self, mock_resource_destroy, mock_resource_list,
 | 
			
		||||
            mock_vim, mock_vnf_instance_save):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        vnf_instance.instantiated_vnf_info.instance_id =\
 | 
			
		||||
            uuidsentinel.instance_id
 | 
			
		||||
 | 
			
		||||
        mock_resource_list.return_value = [fakes.return_vnf_resource()]
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.FORCEFUL)
 | 
			
		||||
 | 
			
		||||
        self._mock_vnf_manager()
 | 
			
		||||
        driver = vnflcm_driver.VnfLcmDriver()
 | 
			
		||||
        driver.terminate_vnf(self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        self.assertEqual(2, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(1, mock_resource_destroy.call_count)
 | 
			
		||||
        self.assertEqual(3, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(vim_client.VimClient, "get_vim")
 | 
			
		||||
    @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
 | 
			
		||||
    @mock.patch.object(objects.VnfResource, "destroy")
 | 
			
		||||
    def test_terminate_vnf_graceful_no_timeout(self, mock_resource_destroy,
 | 
			
		||||
            mock_resource_list, mock_vim, mock_vnf_instance_save):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        vnf_instance.instantiated_vnf_info.instance_id =\
 | 
			
		||||
            uuidsentinel.instance_id
 | 
			
		||||
 | 
			
		||||
        mock_resource_list.return_value = [fakes.return_vnf_resource()]
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL)
 | 
			
		||||
 | 
			
		||||
        self._mock_vnf_manager()
 | 
			
		||||
        driver = vnflcm_driver.VnfLcmDriver()
 | 
			
		||||
        driver.terminate_vnf(self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        self.assertEqual(2, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(1, mock_resource_destroy.call_count)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(vim_client.VimClient, "get_vim")
 | 
			
		||||
    def test_terminate_vnf_delete_instance_failed(self, mock_vim,
 | 
			
		||||
            mock_vnf_instance_save):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        vnf_instance.instantiated_vnf_info.instance_id =\
 | 
			
		||||
            uuidsentinel.instance_id
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.GRACEFUL,
 | 
			
		||||
            graceful_termination_timeout=10)
 | 
			
		||||
 | 
			
		||||
        self._mock_vnf_manager(fail_method_name='delete')
 | 
			
		||||
        driver = vnflcm_driver.VnfLcmDriver()
 | 
			
		||||
        error = self.assertRaises(InfraDriverException, driver.terminate_vnf,
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        self.assertEqual("delete failed", str(error))
 | 
			
		||||
        self.assertEqual(1, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(1, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(vim_client.VimClient, "get_vim")
 | 
			
		||||
    def test_terminate_vnf_delete_wait_instance_failed(self, mock_vim,
 | 
			
		||||
            mock_vnf_instance_save):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        vnf_instance.instantiated_vnf_info.instance_id =\
 | 
			
		||||
            uuidsentinel.instance_id
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.FORCEFUL)
 | 
			
		||||
 | 
			
		||||
        self._mock_vnf_manager(fail_method_name='delete_wait')
 | 
			
		||||
        driver = vnflcm_driver.VnfLcmDriver()
 | 
			
		||||
        error = self.assertRaises(InfraDriverException, driver.terminate_vnf,
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        self.assertEqual("delete_wait failed", str(error))
 | 
			
		||||
        self.assertEqual(2, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(2, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 | 
			
		||||
    @mock.patch.object(objects.VnfInstance, "save")
 | 
			
		||||
    @mock.patch.object(vim_client.VimClient, "get_vim")
 | 
			
		||||
    @mock.patch.object(objects.VnfResourceList, "get_by_vnf_instance_id")
 | 
			
		||||
    def test_terminate_vnf_delete_vnf_resource_failed(self, mock_resource_list,
 | 
			
		||||
            mock_vim, mock_vnf_instance_save):
 | 
			
		||||
        vnf_instance = fakes.return_vnf_instance(
 | 
			
		||||
            fields.VnfInstanceState.INSTANTIATED)
 | 
			
		||||
        vnf_instance.instantiated_vnf_info.instance_id =\
 | 
			
		||||
            uuidsentinel.instance_id
 | 
			
		||||
        terminate_vnf_req = objects.TerminateVnfRequest(
 | 
			
		||||
            termination_type=fields.VnfInstanceTerminationType.FORCEFUL)
 | 
			
		||||
 | 
			
		||||
        mock_resource_list.return_value = [fakes.return_vnf_resource()]
 | 
			
		||||
        self._mock_vnf_manager(fail_method_name='delete_vnf_resource')
 | 
			
		||||
        driver = vnflcm_driver.VnfLcmDriver()
 | 
			
		||||
        error = self.assertRaises(InfraDriverException, driver.terminate_vnf,
 | 
			
		||||
            self.context, vnf_instance, terminate_vnf_req)
 | 
			
		||||
        self.assertEqual("delete_vnf_resource failed", str(error))
 | 
			
		||||
        self.assertEqual(2, mock_vnf_instance_save.call_count)
 | 
			
		||||
        self.assertEqual(3, self._vnf_manager.invoke.call_count)
 | 
			
		||||
 
 | 
			
		||||
@@ -31,3 +31,14 @@ class VnfInstanceAbstractDriver(object):
 | 
			
		||||
        :return: None
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 | 
			
		||||
    @abc.abstractmethod
 | 
			
		||||
    def terminate_vnf(self, context, vnf_instance, terminate_vnf_req):
 | 
			
		||||
        """terminate vnf request.
 | 
			
		||||
 | 
			
		||||
        :param context: the request context
 | 
			
		||||
        :param vnf_instance: object of VnfInstance
 | 
			
		||||
        :param terminate_vnf_req: object of TerminateVnfRequest
 | 
			
		||||
        :return: None
 | 
			
		||||
        """
 | 
			
		||||
        pass
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,10 @@
 | 
			
		||||
#    under the License.
 | 
			
		||||
 | 
			
		||||
import copy
 | 
			
		||||
import functools
 | 
			
		||||
import inspect
 | 
			
		||||
import six
 | 
			
		||||
import time
 | 
			
		||||
 | 
			
		||||
from oslo_config import cfg
 | 
			
		||||
from oslo_log import log as logging
 | 
			
		||||
@@ -24,6 +28,8 @@ from tacker.common import log
 | 
			
		||||
 | 
			
		||||
from tacker.common import driver_manager
 | 
			
		||||
from tacker.common import exceptions
 | 
			
		||||
from tacker.common import safe_utils
 | 
			
		||||
from tacker.common import utils
 | 
			
		||||
from tacker import objects
 | 
			
		||||
from tacker.objects import fields
 | 
			
		||||
from tacker.vnflcm import abstract_driver
 | 
			
		||||
@@ -34,6 +40,84 @@ LOG = logging.getLogger(__name__)
 | 
			
		||||
CONF = cfg.CONF
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.expects_func_args('vnf_instance')
 | 
			
		||||
def rollback_vnf_instantiated_resources(function):
 | 
			
		||||
    """Decorator to rollback resources created during vnf instantiation"""
 | 
			
		||||
 | 
			
		||||
    def _rollback_vnf(vnflcm_driver, context, vnf_instance):
 | 
			
		||||
        vim_info = vnflcm_utils._get_vim(context,
 | 
			
		||||
                vnf_instance.vim_connection_info)
 | 
			
		||||
 | 
			
		||||
        vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
 | 
			
		||||
            vim_info, context)
 | 
			
		||||
 | 
			
		||||
        LOG.info("Rollback vnf %s", vnf_instance.id)
 | 
			
		||||
        try:
 | 
			
		||||
            vnflcm_driver._delete_vnf_instance_resources(context, vnf_instance,
 | 
			
		||||
                    vim_connection_info)
 | 
			
		||||
 | 
			
		||||
            if vnf_instance.instantiated_vnf_info:
 | 
			
		||||
                vnf_instance.instantiated_vnf_info.reinitialize()
 | 
			
		||||
 | 
			
		||||
            vnflcm_driver._vnf_instance_update(context, vnf_instance,
 | 
			
		||||
                    vim_connection_info=[], task_state=None)
 | 
			
		||||
 | 
			
		||||
            LOG.info("Vnf %s rollback completed successfully", vnf_instance.id)
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
            LOG.error("Unable to rollback vnf instance "
 | 
			
		||||
                      "%s due to error: %s", vnf_instance.id, ex)
 | 
			
		||||
 | 
			
		||||
    @functools.wraps(function)
 | 
			
		||||
    def decorated_function(self, context, *args, **kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
            return function(self, context, *args, **kwargs)
 | 
			
		||||
        except Exception as exp:
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                wrapped_func = safe_utils.get_wrapped_function(function)
 | 
			
		||||
                keyed_args = inspect.getcallargs(wrapped_func, self, context,
 | 
			
		||||
                                                 *args, **kwargs)
 | 
			
		||||
                vnf_instance = keyed_args['vnf_instance']
 | 
			
		||||
                LOG.error("Failed to instantiate vnf %(id)s. Error: %(error)s",
 | 
			
		||||
                          {"id": vnf_instance.id,
 | 
			
		||||
                           "error": six.text_type(exp)})
 | 
			
		||||
 | 
			
		||||
                _rollback_vnf(self, context, vnf_instance)
 | 
			
		||||
 | 
			
		||||
    return decorated_function
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@utils.expects_func_args('vnf_instance')
 | 
			
		||||
def revert_to_error_task_state(function):
 | 
			
		||||
    """Decorator to revert task_state to error  on failure."""
 | 
			
		||||
 | 
			
		||||
    @functools.wraps(function)
 | 
			
		||||
    def decorated_function(self, context, *args, **kwargs):
 | 
			
		||||
        try:
 | 
			
		||||
            return function(self, context, *args, **kwargs)
 | 
			
		||||
        except Exception:
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                wrapped_func = safe_utils.get_wrapped_function(function)
 | 
			
		||||
                keyed_args = inspect.getcallargs(wrapped_func, self, context,
 | 
			
		||||
                                                 *args, **kwargs)
 | 
			
		||||
                vnf_instance = keyed_args['vnf_instance']
 | 
			
		||||
                previous_task_state = vnf_instance.task_state
 | 
			
		||||
                try:
 | 
			
		||||
                    self._vnf_instance_update(context, vnf_instance,
 | 
			
		||||
                        task_state=fields.VnfInstanceTaskState.ERROR)
 | 
			
		||||
                    LOG.info("Successfully reverted task state from "
 | 
			
		||||
                             "%(state)s to %(error)s on failure for vnf "
 | 
			
		||||
                             "instance %(id)s.",
 | 
			
		||||
                             {"state": previous_task_state,
 | 
			
		||||
                              "id": vnf_instance.id,
 | 
			
		||||
                              "error": fields.VnfInstanceTaskState.ERROR})
 | 
			
		||||
                except Exception as e:
 | 
			
		||||
                    LOG.warning("Failed to revert task state for vnf "
 | 
			
		||||
                                "instance %(id)s. Error: %(error)s",
 | 
			
		||||
                                {"id": vnf_instance.id, "error": e})
 | 
			
		||||
 | 
			
		||||
    return decorated_function
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
@@ -130,6 +214,7 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
 | 
			
		||||
                vim_connection_info=vim_connection_info)
 | 
			
		||||
 | 
			
		||||
    @log.log
 | 
			
		||||
    @rollback_vnf_instantiated_resources
 | 
			
		||||
    def instantiate_vnf(self, context, vnf_instance, instantiate_vnf_req):
 | 
			
		||||
 | 
			
		||||
        vim_connection_info_list = vnflcm_utils.\
 | 
			
		||||
@@ -151,3 +236,71 @@ class VnfLcmDriver(abstract_driver.VnfInstanceAbstractDriver):
 | 
			
		||||
        self._vnf_instance_update(context, vnf_instance,
 | 
			
		||||
                    instantiation_state=fields.VnfInstanceState.INSTANTIATED,
 | 
			
		||||
                    task_state=None)
 | 
			
		||||
 | 
			
		||||
    @log.log
 | 
			
		||||
    @revert_to_error_task_state
 | 
			
		||||
    def terminate_vnf(self, context, vnf_instance, terminate_vnf_req):
 | 
			
		||||
 | 
			
		||||
        vim_info = vnflcm_utils._get_vim(context,
 | 
			
		||||
            vnf_instance.vim_connection_info)
 | 
			
		||||
 | 
			
		||||
        vim_connection_info = objects.VimConnectionInfo.obj_from_primitive(
 | 
			
		||||
            vim_info, context)
 | 
			
		||||
 | 
			
		||||
        LOG.info("Terminating vnf %s", vnf_instance.id)
 | 
			
		||||
        try:
 | 
			
		||||
            self._delete_vnf_instance_resources(context, vnf_instance,
 | 
			
		||||
                    vim_connection_info, terminate_vnf_req=terminate_vnf_req)
 | 
			
		||||
 | 
			
		||||
            vnf_instance.instantiated_vnf_info.reinitialize()
 | 
			
		||||
            self._vnf_instance_update(context, vnf_instance,
 | 
			
		||||
                vim_connection_info=[], task_state=None)
 | 
			
		||||
 | 
			
		||||
            LOG.info("Vnf terminated %s successfully", vnf_instance.id)
 | 
			
		||||
        except Exception as exp:
 | 
			
		||||
            with excutils.save_and_reraise_exception():
 | 
			
		||||
                LOG.error("Unable to terminate vnf '%s' instance. "
 | 
			
		||||
                          "Error: %s", vnf_instance.id,
 | 
			
		||||
                          encodeutils.exception_to_unicode(exp))
 | 
			
		||||
 | 
			
		||||
    def _delete_vnf_instance_resources(self, context, vnf_instance,
 | 
			
		||||
                                       vim_connection_info,
 | 
			
		||||
                                       terminate_vnf_req=None):
 | 
			
		||||
 | 
			
		||||
        if vnf_instance.instantiated_vnf_info and \
 | 
			
		||||
                vnf_instance.instantiated_vnf_info.instance_id:
 | 
			
		||||
 | 
			
		||||
            instance_id = vnf_instance.instantiated_vnf_info.instance_id
 | 
			
		||||
            access_info = vim_connection_info.access_info
 | 
			
		||||
 | 
			
		||||
            LOG.info("Deleting stack %(instance)s for vnf %(id)s ",
 | 
			
		||||
                    {"instance": instance_id, "id": vnf_instance.id})
 | 
			
		||||
 | 
			
		||||
            if terminate_vnf_req:
 | 
			
		||||
                if (terminate_vnf_req.termination_type == 'GRACEFUL' and
 | 
			
		||||
                        terminate_vnf_req.graceful_termination_timeout > 0):
 | 
			
		||||
                    time.sleep(terminate_vnf_req.graceful_termination_timeout)
 | 
			
		||||
 | 
			
		||||
            self._vnf_manager.invoke(vim_connection_info.vim_type,
 | 
			
		||||
                'delete', plugin=self, context=context,
 | 
			
		||||
                vnf_id=instance_id, auth_attr=access_info)
 | 
			
		||||
 | 
			
		||||
            vnf_instance.instantiation_state = \
 | 
			
		||||
                fields.VnfInstanceState.NOT_INSTANTIATED
 | 
			
		||||
            vnf_instance.save()
 | 
			
		||||
 | 
			
		||||
            self._vnf_manager.invoke(vim_connection_info.vim_type,
 | 
			
		||||
                'delete_wait', plugin=self, context=context,
 | 
			
		||||
                vnf_id=instance_id, auth_attr=access_info)
 | 
			
		||||
 | 
			
		||||
        vnf_resources = objects.VnfResourceList.get_by_vnf_instance_id(
 | 
			
		||||
            context, vnf_instance.id)
 | 
			
		||||
 | 
			
		||||
        for vnf_resource in vnf_resources:
 | 
			
		||||
            self._vnf_manager.invoke(vim_connection_info.vim_type,
 | 
			
		||||
                    'delete_vnf_instance_resource',
 | 
			
		||||
                    context=context, vnf_instance=vnf_instance,
 | 
			
		||||
                    vim_connection_info=vim_connection_info,
 | 
			
		||||
                    vnf_resource=vnf_resource)
 | 
			
		||||
 | 
			
		||||
            vnf_resource.destroy(context)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user