Merge "Delete VNF should fail with 409 error"
This commit is contained in:
commit
e82d982d7b
@ -731,6 +731,12 @@ vnf_error_reason:
|
||||
in: body
|
||||
required: true
|
||||
type: string
|
||||
vnf_force_delete_flag:
|
||||
description: |
|
||||
VNF attributes object with ``force`` key which can contain true/false value.
|
||||
in: body
|
||||
required: false
|
||||
type: object
|
||||
vnf_heat_template:
|
||||
description: |
|
||||
Heat template which is translated from the VNFD template.
|
||||
|
8
api-ref/source/v1/samples/vnfs/vnfs-delete-request.json
Normal file
8
api-ref/source/v1/samples/vnfs/vnfs-delete-request.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"vnf": {
|
||||
"attributes": {
|
||||
"force": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +266,11 @@ Delete VNF
|
||||
|
||||
Deletes a given VNF.
|
||||
|
||||
.. note::
|
||||
Non-admin users are not allowed to delete a VNF in ``PENDING_CREATE`` and
|
||||
``PENDING_DELETE`` status. In this case, it would return 409 error. Admin
|
||||
users can delete a VNF in any status with force flag true.
|
||||
|
||||
Response Codes
|
||||
--------------
|
||||
|
||||
@ -286,6 +291,15 @@ Request Parameters
|
||||
.. rest_parameters:: parameters.yaml
|
||||
|
||||
- vnf_id: vnf_id_path
|
||||
- attributes: vnf_force_delete_flag
|
||||
|
||||
Request Example
|
||||
---------------
|
||||
|
||||
Admin user can request to delete VNF forcefully.
|
||||
|
||||
.. literalinclude:: samples/vnfs/vnfs-delete-request.json
|
||||
:language: javascript
|
||||
|
||||
List VNF resources
|
||||
==================
|
||||
|
@ -492,8 +492,9 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
return vnf_db
|
||||
|
||||
def _update_vnf_status_db(self, context, vnf_id, current_statuses,
|
||||
new_status):
|
||||
vnf_db = self._get_vnf_db(context, vnf_id, current_statuses)
|
||||
new_status, vnf_db=None):
|
||||
if not vnf_db:
|
||||
vnf_db = self._get_vnf_db(context, vnf_id, current_statuses)
|
||||
if self.check_vnf_status_legality(vnf_db, vnf_id):
|
||||
vnf_db.update({'status': new_status})
|
||||
return vnf_db
|
||||
@ -508,7 +509,7 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
def check_vnf_status_legality(vnf_db, vnf_id):
|
||||
if vnf_db.status == constants.PENDING_DELETE:
|
||||
error_reason = _("Operation on PENDING_DELETE VNF "
|
||||
"is not permited. Please contact your "
|
||||
"is not permitted. Please contact your "
|
||||
"Administrator.")
|
||||
raise vnfm.VNFDeleteFailed(reason=error_reason)
|
||||
if(vnf_db.status in [constants.PENDING_UPDATE,
|
||||
@ -585,13 +586,25 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
||||
ns_db.NS.vnf_ids.like("%" + vnf_id + "%")).first()
|
||||
|
||||
if not force_delete:
|
||||
if (nss_db is not None and nss_db.status not in
|
||||
[constants.PENDING_DELETE, constants.ERROR]):
|
||||
raise vnfm.VNFInUse(vnf_id=vnf_id)
|
||||
# If vnf is deleted by NFVO, then vnf_id would
|
||||
# exist in the nss_db otherwise it should be queried from
|
||||
# vnf db table.
|
||||
if nss_db is not None:
|
||||
if nss_db.status not in [constants.PENDING_DELETE,
|
||||
constants.ERROR]:
|
||||
raise vnfm.VNFInUse(vnf_id=vnf_id)
|
||||
else:
|
||||
vnf_db = self._get_vnf_db(context, vnf_id,
|
||||
_ACTIVE_UPDATE_ERROR_DEAD)
|
||||
if (vnf_db is not None and vnf_db.status == constants.
|
||||
PENDING_CREATE):
|
||||
raise vnfm.VNFInUse(
|
||||
message="Operation on PENDING_CREATE VNF is not "
|
||||
"permitted.")
|
||||
|
||||
vnf_db = self._update_vnf_status_db(
|
||||
context, vnf_id, _ACTIVE_UPDATE_ERROR_DEAD,
|
||||
constants.PENDING_DELETE)
|
||||
vnf_db = self._update_vnf_status_db(
|
||||
context, vnf_id, _ACTIVE_UPDATE_ERROR_DEAD,
|
||||
constants.PENDING_DELETE, vnf_db=vnf_db)
|
||||
else:
|
||||
vnf_db = self._update_vnf_status_db_no_check(context,
|
||||
vnf_id, _ACTIVE_UPDATE_ERROR_DEAD,
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from mock import patch
|
||||
from oslo_utils import uuidutils
|
||||
@ -131,6 +132,7 @@ class TestVNFMPluginMonitor(db_base.SqlTestCase):
|
||||
self.assertEqual(1, len(hosting_vnfs))
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
def setUp(self):
|
||||
super(TestVNFMPlugin, self).setUp()
|
||||
@ -254,7 +256,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
session.flush()
|
||||
return vnfd_attr
|
||||
|
||||
def _insert_dummy_vnf(self):
|
||||
def _insert_dummy_vnf(self, status="ACTIVE"):
|
||||
session = self.context.session
|
||||
vnf_db = vnfm_db.VNF(
|
||||
id='6261579e-d6f3-49ad-8bc3-a9cb974778fe',
|
||||
@ -265,13 +267,13 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
vnfd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||
vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
placement_attr={'region': 'RegionOne'},
|
||||
status='ACTIVE',
|
||||
status=status,
|
||||
deleted_at=datetime.min)
|
||||
session.add(vnf_db)
|
||||
session.flush()
|
||||
return vnf_db
|
||||
|
||||
def _insert_dummy_pending_vnf(self, context):
|
||||
def _insert_dummy_pending_vnf(self, context, status='PENDING_DELETE'):
|
||||
session = context.session
|
||||
vnf_db = vnfm_db.VNF(
|
||||
id='6261579e-d6f3-49ad-8bc3-a9cb974778fe',
|
||||
@ -282,7 +284,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
vnfd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||
vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
placement_attr={'region': 'RegionOne'},
|
||||
status='PENDING_DELETE',
|
||||
status=status,
|
||||
deleted_at=datetime.min)
|
||||
session.add(vnf_db)
|
||||
session.flush()
|
||||
@ -510,7 +512,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
self.vnfm_plugin.create_vnf,
|
||||
self.context, vnf_obj)
|
||||
|
||||
@patch('tacker.vnfm.plugin.VNFMPlugin.delete_vnf')
|
||||
@patch('tacker.vnfm.plugin.VNFMPlugin._delete_vnf')
|
||||
def test_create_vnf_fail(self, mock_delete_vnf):
|
||||
self._insert_dummy_vnf_template()
|
||||
vnf_obj = utils.get_dummy_vnf_obj()
|
||||
@ -518,8 +520,9 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
self.assertRaises(vnfm.HeatClientException,
|
||||
self.vnfm_plugin.create_vnf,
|
||||
self.context, vnf_obj)
|
||||
vnf_id = self.vnfm_plugin.delete_vnf.call_args[0][1]
|
||||
mock_delete_vnf.assert_called_once_with(self.context, vnf_id)
|
||||
vnf_id = self.vnfm_plugin._delete_vnf.call_args[0][1]
|
||||
mock_delete_vnf.assert_called_once_with(self.context, vnf_id,
|
||||
force_delete=True)
|
||||
|
||||
def test_create_vnf_create_wait_failed_exception(self):
|
||||
self._insert_dummy_vnf_template()
|
||||
@ -610,9 +613,10 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
self.context,
|
||||
dummy_vnf_obj['id'])
|
||||
|
||||
def test_force_delete_vnf(self):
|
||||
@ddt.data('PENDING_DELETE', 'PENDING_CREATE')
|
||||
def test_force_delete_vnf(self, status):
|
||||
self._insert_dummy_vnf_template()
|
||||
dummy_vnf_obj = self._insert_dummy_pending_vnf(self.context)
|
||||
dummy_vnf_obj = self._insert_dummy_pending_vnf(self.context, status)
|
||||
vnfattr = {'vnf': {'attributes': {'force': True}}}
|
||||
self.vnfm_plugin.delete_vnf(self.context, dummy_vnf_obj[
|
||||
'id'], vnf=vnfattr)
|
||||
@ -624,7 +628,7 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
self._cos_db_plugin.create_event.assert_called_with(
|
||||
self.context, evt_type=constants.RES_EVT_DELETE, res_id=mock.ANY,
|
||||
res_state=mock.ANY, res_type=constants.RES_TYPE_VNF,
|
||||
tstamp=mock.ANY, details=mock.ANY)
|
||||
tstamp=mock.ANY, details="VNF Delete Complete")
|
||||
|
||||
def test_force_delete_vnf_non_admin(self):
|
||||
self._insert_dummy_vnf_template()
|
||||
@ -667,6 +671,14 @@ class TestVNFMPlugin(db_base.SqlTestCase):
|
||||
mock.ANY,
|
||||
mock.ANY)
|
||||
|
||||
def test_delete_vnf_failed_with_status_pending_create(self):
|
||||
self._insert_dummy_vnf_template()
|
||||
dummy_device_obj_with_pending_create_status = self. \
|
||||
_insert_dummy_vnf(status="PENDING_CREATE")
|
||||
self.assertRaises(vnfm.VNFInUse, self.vnfm_plugin.delete_vnf,
|
||||
self.context,
|
||||
dummy_device_obj_with_pending_create_status['id'])
|
||||
|
||||
def _insert_dummy_ns_template(self):
|
||||
session = self.context.session
|
||||
attributes = {
|
||||
|
@ -376,7 +376,7 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
||||
'so delete this vnf',
|
||||
vnf_dict['id'])
|
||||
with excutils.save_and_reraise_exception():
|
||||
self.delete_vnf(context, vnf_id)
|
||||
self._delete_vnf(context, vnf_id, force_delete=True)
|
||||
|
||||
vnf_dict['instance_id'] = instance_id
|
||||
return vnf_dict
|
||||
@ -621,15 +621,8 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
||||
self.mgmt_delete_post(context, vnf_dict)
|
||||
self._delete_vnf_post(context, vnf_dict, e)
|
||||
|
||||
def delete_vnf(self, context, vnf_id, vnf=None):
|
||||
def _delete_vnf(self, context, vnf_id, force_delete=False):
|
||||
|
||||
# Extract "force_delete" from request's body
|
||||
force_delete = False
|
||||
if vnf and vnf['vnf'].get('attributes').get('force'):
|
||||
force_delete = vnf['vnf'].get('attributes').get('force')
|
||||
if force_delete and not context.is_admin:
|
||||
LOG.warning("force delete is admin only operation")
|
||||
raise exceptions.AdminRequired(reason="Admin only operation")
|
||||
vnf_dict = self._delete_vnf_pre(context, vnf_id,
|
||||
force_delete=force_delete)
|
||||
driver_name, vim_auth = self._get_infra_driver(context, vnf_dict)
|
||||
@ -673,6 +666,18 @@ class VNFMPlugin(vnfm_db.VNFMPluginDb, VNFMMgmtMixin):
|
||||
self.spawn_n(self._delete_vnf_wait, context, vnf_dict, vim_auth,
|
||||
driver_name)
|
||||
|
||||
def delete_vnf(self, context, vnf_id, vnf=None):
|
||||
|
||||
# Extract "force_delete" from request's body
|
||||
force_delete = False
|
||||
if vnf and vnf['vnf'].get('attributes').get('force'):
|
||||
force_delete = vnf['vnf'].get('attributes').get('force')
|
||||
if force_delete and not context.is_admin:
|
||||
LOG.warning("force delete is admin only operation")
|
||||
raise exceptions.AdminRequired(reason="Admin only operation")
|
||||
|
||||
self._delete_vnf(context, vnf_id, force_delete=force_delete)
|
||||
|
||||
def _handle_vnf_scaling(self, context, policy):
|
||||
# validate
|
||||
def _validate_scaling_policy():
|
||||
|
Loading…
Reference in New Issue
Block a user