Adds support force delete for NS
This patch adds '--force' parameter to NS delete command, to force delete NS instances. e.g: openstack ns delete test_ns --force blueprint force-delete-resources Change-Id: I739ad1e1e764f38edffa3d7fa423da370b53242b
This commit is contained in:
parent
3a198b0ba6
commit
216b3859ab
@ -133,7 +133,7 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
else:
|
||||
raise
|
||||
|
||||
def _get_ns_db(self, context, ns_id, current_statuses, new_status):
|
||||
def _get_ns_db(self, context, ns_id, current_statuses):
|
||||
try:
|
||||
ns_db = (
|
||||
self._model_query(context, NS).
|
||||
@ -142,6 +142,9 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
with_lockmode('update').one())
|
||||
except orm_exc.NoResultFound:
|
||||
raise network_service.NSNotFound(ns_id=ns_id)
|
||||
return ns_db
|
||||
|
||||
def _update_ns_db(self, ns_db, new_status):
|
||||
ns_db.update({'status': new_status})
|
||||
return ns_db
|
||||
|
||||
@ -340,11 +343,17 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
return ns_dict
|
||||
|
||||
# reference implementation. needs to be overrided by subclass
|
||||
def delete_ns_pre(self, context, ns_id):
|
||||
def delete_ns_pre(self, context, ns_id, force_delete=False):
|
||||
with context.session.begin(subtransactions=True):
|
||||
ns_db = self._get_ns_db(
|
||||
context, ns_id, _ACTIVE_UPDATE_ERROR_DEAD,
|
||||
constants.PENDING_DELETE)
|
||||
context, ns_id, _ACTIVE_UPDATE_ERROR_DEAD)
|
||||
if not force_delete:
|
||||
if (ns_db is not None and ns_db.status in
|
||||
[constants.PENDING_DELETE,
|
||||
constants.PENDING_CREATE,
|
||||
constants.PENDING_UPDATE]):
|
||||
raise network_service.NSInUse(ns_id=ns_id)
|
||||
ns_db = self._update_ns_db(ns_db, constants.PENDING_DELETE)
|
||||
deleted_ns_db = self._make_ns_dict(ns_db)
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=ns_id,
|
||||
@ -355,15 +364,21 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
return deleted_ns_db
|
||||
|
||||
def delete_ns_post(self, context, ns_id, mistral_obj,
|
||||
error_reason, soft_delete=True):
|
||||
error_reason, soft_delete=True, force_delete=False):
|
||||
ns = self.get_ns(context, ns_id)
|
||||
nsd_id = ns.get('nsd_id')
|
||||
with context.session.begin(subtransactions=True):
|
||||
query = (
|
||||
self._model_query(context, NS).
|
||||
filter(NS.id == ns_id).
|
||||
filter(NS.status == constants.PENDING_DELETE))
|
||||
if mistral_obj and mistral_obj.state == 'ERROR':
|
||||
if force_delete:
|
||||
query = (
|
||||
self._model_query(context, NS).
|
||||
filter(NS.id == ns_id))
|
||||
else:
|
||||
query = (
|
||||
self._model_query(context, NS).
|
||||
filter(NS.id == ns_id).
|
||||
filter(NS.status == constants.PENDING_DELETE))
|
||||
if not force_delete and (mistral_obj
|
||||
and mistral_obj.state == 'ERROR'):
|
||||
query.update({'status': constants.ERROR})
|
||||
self._cos_db_plg.create_event(
|
||||
context, res_id=ns_id,
|
||||
@ -385,9 +400,12 @@ class NSPluginDb(network_service.NSPluginBase, db_base.CommonDbMixin):
|
||||
details="ns Delete Complete")
|
||||
else:
|
||||
query.delete()
|
||||
template_db = self._get_resource(context, NSD, nsd_id)
|
||||
if template_db.get('template_source') == 'inline':
|
||||
self.delete_nsd(context, nsd_id)
|
||||
try:
|
||||
template_db = self._get_resource(context, NSD, nsd_id)
|
||||
if template_db.get('template_source') == 'inline':
|
||||
self.delete_nsd(context, nsd_id)
|
||||
except orm_exc.NoResultFound:
|
||||
pass
|
||||
|
||||
def get_ns(self, context, ns_id, fields=None):
|
||||
ns_db = self._get_resource(context, NS, ns_id)
|
||||
|
@ -60,3 +60,7 @@ class NSDNotFound(exceptions.NotFound):
|
||||
|
||||
class NSNotFound(exceptions.NotFound):
|
||||
message = _('NS %(ns_id)s could not be found')
|
||||
|
||||
|
||||
class NSInUse(exceptions.InUse):
|
||||
message = _('NS %(ns_id)s in use')
|
||||
|
@ -96,6 +96,8 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
|
||||
def _add_delete_vnf_tasks(self, ns, vnffg_ids=None):
|
||||
vnfds = ns['vnfd_details']
|
||||
vnf_attr = {'vnf': {'attributes': {
|
||||
'force': ns.get('force_delete', False)}}}
|
||||
task_dict = dict()
|
||||
for vnfd_name, vnfd_info in (vnfds).items():
|
||||
nodes = vnfd_info['instances']
|
||||
@ -104,6 +106,7 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
task_dict[task] = {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_{0}'
|
||||
'%>'.format(node),
|
||||
'input': {'body': vnf_attr},
|
||||
}
|
||||
if vnffg_ids and len(vnffg_ids):
|
||||
task_dict[task].update({'join': 'all'})
|
||||
@ -271,5 +274,6 @@ class WorkflowGenerator(workflow_generator.WorkflowGeneratorBase):
|
||||
ns_dict['vnffg_details'] = vnffg_ids
|
||||
self.definition[self.wf_identifier]['tasks'].update(
|
||||
self._add_delete_vnffg_task(ns_dict))
|
||||
ns_dict['force_delete'] = ns.get('force_delete', False)
|
||||
self.definition[self.wf_identifier]['tasks'].update(
|
||||
self._add_delete_vnf_tasks(ns_dict, vnffg_ids))
|
||||
|
@ -31,6 +31,7 @@ from toscaparser.tosca_template import ToscaTemplate
|
||||
|
||||
from tacker._i18n import _
|
||||
from tacker.common import driver_manager
|
||||
from tacker.common import exceptions
|
||||
from tacker.common import log
|
||||
from tacker.common import utils
|
||||
from tacker import context as t_context
|
||||
@ -896,15 +897,23 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
raise cs.ParamYAMLInputMissing()
|
||||
|
||||
@log.log
|
||||
def delete_ns(self, context, ns_id):
|
||||
def delete_ns(self, context, ns_id, ns=None):
|
||||
# Extract "force_delete" from request's body
|
||||
force_delete = False
|
||||
if ns and ns.get('ns', {}).get('attributes', {}).get('force'):
|
||||
force_delete = ns['ns'].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")
|
||||
ns = super(NfvoPlugin, self).get_ns(context, ns_id)
|
||||
LOG.debug("Deleting ns: %s", ns)
|
||||
vim_res = self.vim_client.get_vim(context, ns['vim_id'])
|
||||
super(NfvoPlugin, self).delete_ns_pre(context, ns_id)
|
||||
super(NfvoPlugin, self).delete_ns_pre(context, ns_id, force_delete)
|
||||
driver_type = vim_res['vim_type']
|
||||
workflow = None
|
||||
try:
|
||||
if ns['vnf_ids']:
|
||||
ns['force_delete'] = force_delete
|
||||
workflow = self._vim_drivers.invoke(
|
||||
driver_type,
|
||||
'prepare_and_create_workflow',
|
||||
@ -969,11 +978,12 @@ class NfvoPlugin(nfvo_db_plugin.NfvoPluginDb, vnffg_db.VnffgPluginDbMixin,
|
||||
workflow_id=workflow['id'],
|
||||
auth_dict=self.get_auth_dict(context))
|
||||
super(NfvoPlugin, self).delete_ns_post(context, ns_id, exec_obj,
|
||||
error_reason)
|
||||
error_reason,
|
||||
force_delete=force_delete)
|
||||
|
||||
if workflow:
|
||||
self.spawn_n(_delete_ns_wait, ns['id'], mistral_execution.id)
|
||||
else:
|
||||
super(NfvoPlugin, self).delete_ns_post(
|
||||
context, ns_id, None, None)
|
||||
context, ns_id, None, None, force_delete=force_delete)
|
||||
return ns['id']
|
||||
|
@ -174,9 +174,13 @@ def get_dummy_create_workflow():
|
||||
'on-success': [{'delete_vnf_VNF1': '<% $.status_VNF1='
|
||||
'"ERROR" %>'}]},
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}},
|
||||
'delete_vnf_VNF2': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>'}},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}}},
|
||||
'type': 'direct', 'output': {
|
||||
'status_VNF1': '<% $.status_VNF1 %>',
|
||||
'status_VNF2': '<% $.status_VNF2 %>',
|
||||
@ -268,9 +272,13 @@ def get_dummy_create_vnffg_ns_workflow():
|
||||
'<% task(create_ns_VNF2).result.vnf.id %>'},
|
||||
'on-success': ['wait_vnf_active_VNF2']},
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}},
|
||||
'delete_vnf_VNF2': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>'}},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF2%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}}},
|
||||
'type': 'direct',
|
||||
'output': {
|
||||
'status_VNF1': '<% $.status_VNF1 %>',
|
||||
@ -301,7 +309,9 @@ def get_dummy_delete_workflow():
|
||||
'input': ['vnf_id_VNF1'],
|
||||
'tasks': {
|
||||
'delete_vnf_VNF1': {
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'}},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}}},
|
||||
'type': 'direct'}}
|
||||
|
||||
|
||||
@ -312,7 +322,9 @@ def get_dummy_delete_vnffg_ns_workflow():
|
||||
'tasks': {
|
||||
'delete_vnf_VNF1': {
|
||||
'join': 'all',
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>'},
|
||||
'action': 'tacker.delete_vnf vnf=<% $.vnf_id_VNF1%>',
|
||||
'input': {'body': {'vnf': {'attributes': {
|
||||
'force': False}}}}},
|
||||
'delete_vnffg_VNFFG1': {
|
||||
'action': 'tacker.delete_vnffg vnffg='
|
||||
'<% $.VNFFG1 %>',
|
||||
|
@ -21,6 +21,7 @@ from oslo_utils import uuidutils
|
||||
|
||||
from mock import patch
|
||||
|
||||
from tacker.common import exceptions
|
||||
from tacker import context
|
||||
from tacker.db.common_services import common_services_db_plugin
|
||||
from tacker.db.nfvo import nfvo_db
|
||||
@ -1043,13 +1044,13 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
session.flush()
|
||||
return nsd_template
|
||||
|
||||
def _insert_dummy_ns(self):
|
||||
def _insert_dummy_ns(self, status='ACTIVE'):
|
||||
session = self.context.session
|
||||
ns = ns_db.NS(
|
||||
id='ba6bf017-f6f7-45f1-a280-57b073bf78ea',
|
||||
name='dummy_ns',
|
||||
tenant_id='ad7ebc56538745a08ef7c5e97f8bd437',
|
||||
status='ACTIVE',
|
||||
status=status,
|
||||
nsd_id='eb094833-995e-49f0-a047-dfb56aaf7c4e',
|
||||
vim_id='6261579e-d6f3-49ad-8bc3-a9cb974778ff',
|
||||
description='dummy_ns_description',
|
||||
@ -1269,4 +1270,57 @@ class TestNfvoPlugin(db_base.SqlTestCase):
|
||||
self.nfvo_plugin.delete_ns(self.context,
|
||||
DUMMY_NS_2)
|
||||
mock_delete_ns_post.assert_called_with(
|
||||
self.context, DUMMY_NS_2, None, None)
|
||||
self.context, DUMMY_NS_2, None, None, force_delete=False)
|
||||
|
||||
@mock.patch.object(nfvo_plugin.NfvoPlugin, 'get_auth_dict')
|
||||
@mock.patch.object(vim_client.VimClient, 'get_vim')
|
||||
@mock.patch.object(nfvo_plugin.NfvoPlugin, '_get_by_name')
|
||||
def test_delete_ns_force(self, mock_get_by_name,
|
||||
mock_get_vim, mock_auth_dict):
|
||||
self._insert_dummy_vim()
|
||||
self._insert_dummy_ns_template()
|
||||
self._insert_dummy_ns(status='PENDING_DELETE')
|
||||
mock_auth_dict.return_value = {
|
||||
'auth_url': 'http://127.0.0.1',
|
||||
'token': 'DummyToken',
|
||||
'project_domain_name': 'dummy_domain',
|
||||
'project_name': 'dummy_project'
|
||||
}
|
||||
nsattr = {'ns': {'attributes': {'force': True}}}
|
||||
|
||||
with patch.object(TackerManager, 'get_service_plugins') as \
|
||||
mock_plugins:
|
||||
mock_plugins.return_value = {'VNFM': FakeVNFMPlugin()}
|
||||
mock_get_by_name.return_value = get_by_name()
|
||||
result = self.nfvo_plugin.delete_ns(self.context,
|
||||
'ba6bf017-f6f7-45f1-a280-57b073bf78ea', ns=nsattr)
|
||||
self.assertIsNotNone(result)
|
||||
|
||||
@mock.patch.object(nfvo_plugin.NfvoPlugin, 'get_auth_dict')
|
||||
@mock.patch.object(vim_client.VimClient, 'get_vim')
|
||||
@mock.patch.object(nfvo_plugin.NfvoPlugin, '_get_by_name')
|
||||
def test_delete_ns_force_non_admin(self, mock_get_by_name,
|
||||
mock_get_vim, mock_auth_dict):
|
||||
self._insert_dummy_vim()
|
||||
self._insert_dummy_ns_template()
|
||||
self._insert_dummy_ns(status='PENDING_DELETE')
|
||||
mock_auth_dict.return_value = {
|
||||
'auth_url': 'http://127.0.0.1',
|
||||
'token': 'DummyToken',
|
||||
'project_domain_name': 'dummy_domain',
|
||||
'project_name': 'dummy_project'
|
||||
}
|
||||
nsattr = {'ns': {'attributes': {'force': True}}}
|
||||
|
||||
with patch.object(TackerManager, 'get_service_plugins') as \
|
||||
mock_plugins:
|
||||
mock_plugins.return_value = {'VNFM': FakeVNFMPlugin()}
|
||||
mock_get_by_name.return_value = get_by_name()
|
||||
non_admin_context = context.Context(user_id=None,
|
||||
tenant_id=None,
|
||||
is_admin=False)
|
||||
self.assertRaises(exceptions.AdminRequired,
|
||||
self.nfvo_plugin.delete_ns,
|
||||
non_admin_context,
|
||||
'ba6bf017-f6f7-45f1-a280-57b073bf78ea',
|
||||
ns=nsattr)
|
||||
|
Loading…
Reference in New Issue
Block a user