Automatically remove interfaces on router delete

This change removes all attached interfaces from a router when it
is deleted from the project dashboard. This was only being done when
the router was deleted from the admin dashboard.

Change-Id: Ic0b639bfad7451bb5acbf486c39e349c938723db
Closes-Bug: 1384400
This commit is contained in:
Miguel Grinberg 2014-11-05 01:46:28 +00:00
parent c88e944b97
commit b37fa64bdd
4 changed files with 175 additions and 15 deletions

View File

@ -21,19 +21,6 @@ from openstack_dashboard.dashboards.project.routers import tables as r_tables
class DeleteRouter(r_tables.DeleteRouter):
redirect_url = "horizon:admin:routers:index"
policy_rules = (("network", "delete_router"),)
def delete(self, request, obj_id):
search_opts = {'device_owner': 'network:router_interface',
'device_id': obj_id}
ports = api.neutron.port_list(request, **search_opts)
for port in ports:
api.neutron.router_remove_interface(request, obj_id,
port_id=port.id)
super(DeleteRouter, self).delete(request, obj_id)
def allowed(self, request, router=None):
return True
class EditRouter(r_tables.EditRouter):

View File

@ -15,6 +15,7 @@
from django.core.urlresolvers import reverse
from django import http
from mox import IgnoreArg # noqa
from mox import IsA # noqa
from openstack_dashboard import api
@ -80,3 +81,84 @@ class RouterTests(test.BaseAdminViewTests, r_test.RouterTests):
table_data[0]['external_gateway_info']['network'])
self.assertTemplateUsed(res, '%s/routers/index.html' % self.DASHBOARD)
self.assertMessageCount(res, error=1)
@test.create_stubs({api.neutron: ('router_list', 'network_list',
'port_list', 'router_delete',),
api.keystone: ('tenant_list',)})
def test_router_delete(self):
router = self.routers.first()
tenants = self.tenants.list()
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
api.neutron.port_list(IsA(http.HttpRequest),
device_id=router.id, device_owner=IgnoreArg())\
.AndReturn([])
api.neutron.router_delete(IsA(http.HttpRequest), router.id)
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL)
formData = {'action': 'Routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name, res.content)
@test.create_stubs({api.neutron: ('router_list', 'network_list',
'port_list', 'router_remove_interface',
'router_delete',),
api.keystone: ('tenant_list',)})
def test_router_with_interface_delete(self):
router = self.routers.first()
ports = self.ports.list()
tenants = self.tenants.list()
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
api.neutron.port_list(IsA(http.HttpRequest),
device_id=router.id, device_owner=IgnoreArg())\
.AndReturn(ports)
for port in ports:
api.neutron.router_remove_interface(IsA(http.HttpRequest),
router.id, port_id=port.id)
api.neutron.router_delete(IsA(http.HttpRequest), router.id)
api.neutron.router_list(
IsA(http.HttpRequest),
search_opts=None).AndReturn(self.routers.list())
api.keystone.tenant_list(IsA(http.HttpRequest))\
.AndReturn([tenants, False])
self._mock_external_network_list()
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL)
formData = {'action': 'Routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name, res.content)

View File

@ -53,9 +53,14 @@ class DeleteRouter(policy.PolicyTargetMixin, tables.DeleteAction):
policy_rules = (("network", "delete_router"),)
def delete(self, request, obj_id):
obj = self.table.get_object_by_id(obj_id)
name = self.table.get_object_display(obj)
try:
# detach all interfaces before attempting to delete the router
search_opts = {'device_owner': 'network:router_interface',
'device_id': obj_id}
ports = api.neutron.port_list(request, **search_opts)
for port in ports:
api.neutron.router_remove_interface(request, obj_id,
port_id=port.id)
api.neutron.router_delete(request, obj_id)
except q_ext.NeutronClientException as e:
msg = _('Unable to delete router "%s"') % e
@ -64,6 +69,8 @@ class DeleteRouter(policy.PolicyTargetMixin, tables.DeleteAction):
redirect = reverse(self.redirect_url)
raise exceptions.Http302(redirect, message=msg)
except Exception:
obj = self.table.get_object_by_id(obj_id)
name = self.table.get_object_display(obj)
msg = _('Unable to delete router "%s"') % name
LOG.info(msg)
exceptions.handle(request, msg)

View File

@ -16,6 +16,7 @@ import copy
from django.core.urlresolvers import reverse
from django import http
from mox import IgnoreArg # noqa
from mox import IsA # noqa
from openstack_dashboard import api
@ -146,6 +147,89 @@ class RouterTests(test.TestCase):
args=[router.id]))
self.assertRedirectsNoFollow(res, self.INDEX_URL)
@test.create_stubs({api.neutron: ('router_list', 'network_list',
'port_list', 'router_delete',),
quotas: ('tenant_quota_usages',)})
def test_router_delete(self):
router = self.routers.first()
quota_data = self.quota_usages.first()
quota_data['routers']['available'] = 5
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
quotas.tenant_quota_usages(
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(quota_data)
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
self._mock_external_network_list()
api.neutron.port_list(IsA(http.HttpRequest),
device_id=router.id, device_owner=IgnoreArg())\
.AndReturn([])
api.neutron.router_delete(IsA(http.HttpRequest), router.id)
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL)
formData = {'action': 'Routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name, res.content)
@test.create_stubs({api.neutron: ('router_list', 'network_list',
'port_list', 'router_remove_interface',
'router_delete',),
quotas: ('tenant_quota_usages',)})
def test_router_with_interface_delete(self):
router = self.routers.first()
ports = self.ports.list()
quota_data = self.quota_usages.first()
quota_data['routers']['available'] = 5
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
quotas.tenant_quota_usages(
IsA(http.HttpRequest)) \
.MultipleTimes().AndReturn(quota_data)
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
self._mock_external_network_list()
api.neutron.router_list(
IsA(http.HttpRequest),
tenant_id=self.tenant.id,
search_opts=None).AndReturn(self.routers.list())
self._mock_external_network_list()
api.neutron.port_list(IsA(http.HttpRequest),
device_id=router.id, device_owner=IgnoreArg())\
.AndReturn(ports)
for port in ports:
api.neutron.router_remove_interface(IsA(http.HttpRequest),
router.id, port_id=port.id)
api.neutron.router_delete(IsA(http.HttpRequest), router.id)
self.mox.ReplayAll()
res = self.client.get(self.INDEX_URL)
formData = {'action': 'Routers__delete__' + router.id}
res = self.client.post(self.INDEX_URL, formData, follow=True)
self.assertNoFormErrors(res)
self.assertMessageCount(response=res, success=1)
self.assertIn('Deleted Router: ' + router.name, res.content)
class RouterActionTests(test.TestCase):
DASHBOARD = 'project'