diff --git a/releasenotes/notes/bug-1691363-5b8366640b16239b.yaml b/releasenotes/notes/bug-1691363-5b8366640b16239b.yaml new file mode 100644 index 000000000..42bca1f6d --- /dev/null +++ b/releasenotes/notes/bug-1691363-5b8366640b16239b.yaml @@ -0,0 +1,4 @@ +--- +fixes: + - | + VNF not be allowed to delete separately, if part of an active NS. diff --git a/tacker/db/nfvo/ns_db.py b/tacker/db/nfvo/ns_db.py index eb3afb161..844f8da5b 100644 --- a/tacker/db/nfvo/ns_db.py +++ b/tacker/db/nfvo/ns_db.py @@ -325,7 +325,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin): return ns_dict # reference implementation. needs to be overrided by subclass - def delete_ns(self, context, ns_id): + def delete_ns_pre(self, context, ns_id): with context.session.begin(subtransactions=True): ns_db = self._get_ns_db( context, ns_id, _ACTIVE_UPDATE_ERROR_DEAD, diff --git a/tacker/db/vnfm/vnfm_db.py b/tacker/db/vnfm/vnfm_db.py index a6df91789..3c39c2bac 100644 --- a/tacker/db/vnfm/vnfm_db.py +++ b/tacker/db/vnfm/vnfm_db.py @@ -33,6 +33,7 @@ from tacker.db.common_services import common_services_db_plugin from tacker.db import db_base from tacker.db import model_base from tacker.db import models_v1 +from tacker.db.nfvo import ns_db from tacker.db import types from tacker.extensions import vnfm from tacker import manager @@ -547,6 +548,13 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin): def _delete_vnf_pre(self, context, vnf_id): with context.session.begin(subtransactions=True): + + nss_db = context.session.query(ns_db.NS).filter( + ns_db.NS.vnf_ids.like("%" + vnf_id + "%")).first() + if (nss_db is not None and nss_db.status not in + [constants.PENDING_DELETE, constants.ERROR]): + raise vnfm.VNFInUse(vnf_id=vnf_id) + vnf_db = self._get_vnf_db( context, vnf_id, _ACTIVE_UPDATE_ERROR_DEAD, constants.PENDING_DELETE) diff --git a/tacker/nfvo/nfvo_plugin.py b/tacker/nfvo/nfvo_plugin.py index ced5d2e1b..a7dac7482 100644 --- a/tacker/nfvo/nfvo_plugin.py +++ b/tacker/nfvo/nfvo_plugin.py @@ -805,6 +805,7 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin, def delete_ns(self, context, ns_id): ns = super(NfvoPlugin, self).get_ns(context, ns_id) vim_res = self.vim_client.get_vim(context, ns['vim_id']) + super(NfvoPlugin, self).delete_ns_pre(context, ns_id) driver_type = vim_res['vim_type'] workflow = None try: @@ -835,7 +836,6 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin, auth_dict=self.get_auth_dict(context)) raise ex - super(NfvoPlugin, self).delete_ns(context, ns_id) def _delete_ns_wait(ns_id, execution_id): exec_state = "RUNNING" diff --git a/tacker/tests/unit/vnfm/test_plugin.py b/tacker/tests/unit/vnfm/test_plugin.py index ccccf0965..48f35e5b6 100644 --- a/tacker/tests/unit/vnfm/test_plugin.py +++ b/tacker/tests/unit/vnfm/test_plugin.py @@ -23,6 +23,7 @@ import yaml from tacker import context from tacker.db.common_services import common_services_db_plugin from tacker.db.nfvo import nfvo_db +from tacker.db.nfvo import ns_db from tacker.db.vnfm import vnfm_db from tacker.extensions import vnfm from tacker.plugins.common import constants @@ -344,6 +345,66 @@ class TestVNFMPlugin(db_base.SqlTestCase): res_state=mock.ANY, res_type=constants.RES_TYPE_VNF, tstamp=mock.ANY, details=mock.ANY) + def _insert_dummy_ns_template(self): + session = self.context.session + attributes = { + u'nsd': 'imports: [VNF1, VNF2]\ntopology_template:\n inputs:\n ' + ' vl1_name: {default: net_mgmt, description: name of VL1' + ' virtuallink, type: string}\n vl2_name: {default: ' + 'net0, description: name of VL2 virtuallink, type: string' + '}\n node_templates:\n VL1:\n properties:\n ' + ' network_name: {get_input: vl1_name}\n vendor: ' + 'tacker\n type: tosca.nodes.nfv.VL\n VL2:\n ' + 'properties:\n network_name: {get_input: vl2_name}' + '\n vendor: tacker\n type: tosca.nodes.nfv.VL' + '\n VNF1:\n requirements:\n - {virtualLink1: ' + 'VL1}\n - {virtualLink2: VL2}\n type: tosca.node' + 's.nfv.VNF1\n VNF2: {type: tosca.nodes.nfv.VNF2}\ntosca' + '_definitions_version: tosca_simple_profile_for_nfv_1_0_0' + '\n'} + nsd_template = ns_db.NSD( + id='eb094833-995e-49f0-a047-dfb56aaf7c4e', + tenant_id='ad7ebc56538745a08ef7c5e97f8bd437', + name='fake_template', + vnfds={'tosca.nodes.nfv.VNF1': 'vnf1', + 'tosca.nodes.nfv.VNF2': 'vnf2'}, + description='fake_nsd_template_description', + deleted_at=datetime.min, + template_source='onboarded') + session.add(nsd_template) + for (key, value) in attributes.items(): + attribute_db = ns_db.NSDAttribute( + id=uuidutils.generate_uuid(), + nsd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e', + key=key, + value=value) + session.add(attribute_db) + session.flush() + return nsd_template + + def _insert_dummy_ns(self): + session = self.context.session + ns = ns_db.NS( + id='ba6bf017-f6f7-45f1-a280-57b073bf78ea', + name='dummy_ns', + tenant_id='ad7ebc56538745a08ef7c5e97f8bd437', + status='ACTIVE', + nsd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e', + vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff', + description='dummy_ns_description', + vnf_ids='[5761579e-d6f3-49ad-8bc3-a9cb73477846,' + '6261579e-d6f3-49ad-8bc3-a9cb974778fe]', + deleted_at=datetime.min) + session.add(ns) + session.flush() + return ns + + def test_delete_vnf_of_active_ns(self): + self._insert_dummy_ns_template() + self._insert_dummy_ns() + self.assertRaises(vnfm.VNFInUse, self.vnfm_plugin.delete_vnf, + self.context, '6261579e-d6f3-49ad-8bc3-a9cb974778fe') + def test_update_vnf(self): self._insert_dummy_device_template() dummy_device_obj = self._insert_dummy_device()