def delete_subnet(self, context, id):
# REVISIT(rkukura) The super(Ml2Plugin, self).delete_subnet()
- # function is not used because it auto-deletes ports from the
- # DB without invoking the derived class's delete_port(),
- # preventing mechanism drivers from being called. This
- # approach should be revisited when the API layer is reworked
+ # function is not used because it deallocates the subnet's addresses
+ # from ports in the DB without invoking the derived class's
+ # update_port(), preventing mechanism drivers from being called.
+ # This approach should be revisited when the API layer is reworked
# during icehouse.
LOG.debug(_("Deleting subnet %s"), id)
while True:
with session.begin(subtransactions=True):
subnet = self.get_subnet(context, id)
- # Get ports to auto-delete.
+ # Get ports to auto-deallocate
allocated = (session.query(models_v2.IPAllocation).
filter_by(subnet_id=id).
join(models_v2.Port).
filter_by(network_id=subnet['network_id']).
with_lockmode('update').all())
- LOG.debug(_("Ports to auto-delete: %s"), allocated)
+ LOG.debug(_("Ports to auto-deallocate: %s"), allocated)
only_auto_del = all(not a.port_id or
a.ports.device_owner in db_base_plugin_v2.
AUTO_DELETE_PORT_OWNERS
break
for a in allocated:
- try:
- self.delete_port(context, a.port_id)
- except Exception:
- with excutils.save_and_reraise_exception():
- LOG.exception(_("Exception auto-deleting port %s"),
- a.port_id)
+ if a.port_id:
+ # calling update_port() for each allocation to remove the
+ # IP from the port and call the MechanismDrivers
+ data = {'port':
+ {'fixed_ips': [{'subnet_id': ip.subnet_id,
+ 'ip_address': ip.ip_address}
+ for ip in a.ports.fixed_ips
+ if ip.subnet_id != id]}}
+ try:
+ self.update_port(context, a.port_id, data)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ LOG.exception(_("Exception deleting fixed_ip from "
+ "port %s"), a.port_id)
+ session.delete(a)
try:
self.mechanism_manager.delete_subnet_postcommit(mech_context)
class TestN1kvSubnets(test_plugin.TestSubnetsV2,
N1kvPluginTestCase):
- pass
+ def setUp(self):
+ super(TestN1kvSubnets, self).setUp()
+
+ # Create some of the database entries that we require.
+ self._make_test_policy_profile(name='dhcp_pp')
class TestN1kvL3Test(test_l3_plugin.L3NatExtensionTestCase):
pass
+class TestMl2SubnetsV2(test_plugin.TestSubnetsV2,
+ Ml2PluginV2TestCase):
+ pass
+
+
class TestMl2PortsV2(test_plugin.TestPortsV2, Ml2PluginV2TestCase):
def test_update_port_status_build(self):
res = req.get_response(self.api)
self.assertEqual(res.status_int, webob.exc.HTTPNoContent.code)
+ def test_delete_subnet_dhcp_port_associated_with_other_subnets(self):
+ res = self._create_network(fmt=self.fmt, name='net',
+ admin_state_up=True)
+ network = self.deserialize(self.fmt, res)
+ subnet1 = self._make_subnet(self.fmt, network, '10.0.0.1',
+ '10.0.0.0/24', ip_version=4)
+ subnet2 = self._make_subnet(self.fmt, network, '10.0.1.1',
+ '10.0.1.0/24', ip_version=4)
+ res = self._create_port(self.fmt,
+ network['network']['id'],
+ device_owner=constants.DEVICE_OWNER_DHCP,
+ fixed_ips=[
+ {'subnet_id': subnet1['subnet']['id']},
+ {'subnet_id': subnet2['subnet']['id']}
+ ])
+ port = self.deserialize(self.fmt, res)
+ expected_subnets = [subnet1['subnet']['id'], subnet2['subnet']['id']]
+ self.assertEqual(expected_subnets,
+ [s['subnet_id'] for s in port['port']['fixed_ips']])
+ req = self.new_delete_request('subnets', subnet1['subnet']['id'])
+ res = req.get_response(self.api)
+ self.assertEqual(res.status_int, 204)
+ port = self._show('ports', port['port']['id'])
+
+ expected_subnets = [subnet2['subnet']['id']]
+ self.assertEqual(expected_subnets,
+ [s['subnet_id'] for s in port['port']['fixed_ips']])
+ req = self.new_delete_request('subnets', subnet2['subnet']['id'])
+ res = req.get_response(self.api)
+ self.assertEqual(res.status_int, 204)
+ port = self._show('ports', port['port']['id'])
+ self.assertFalse(port['port']['fixed_ips'])
+
def test_delete_subnet_port_exists_owned_by_other(self):
with self.subnet() as subnet:
with self.port(subnet=subnet):