Unplug the VIF if dhcp port is deleted

if user delete the dhcp port, dhcp-agent should unplug
the VIF for this dhcp port, then the driver do
'reload_allocations' will raise a exception, dhcp-agent
resync for this network and re-create dhcp port.

Change-Id: I40b85033d075562c43ce4d0e68296211b3241197
Closes-bug: #1469615
This commit is contained in:
shihanzhang 2015-06-30 10:14:19 +08:00
parent 0d93458d1e
commit 9d80002129
2 changed files with 64 additions and 0 deletions

View File

@ -657,14 +657,23 @@ class Dnsmasq(DhcpLocalProcess):
old_leases = self._read_hosts_file_leases(filename)
new_leases = set()
dhcp_port_exists = False
dhcp_port_on_this_host = self.device_manager.get_device_id(
self.network)
for port in self.network.ports:
client_id = self._get_client_id(port)
for alloc in port.fixed_ips:
new_leases.add((alloc.ip_address, port.mac_address, client_id))
if port.device_id == dhcp_port_on_this_host:
dhcp_port_exists = True
for ip, mac, client_id in old_leases - new_leases:
self._release_lease(mac, ip, client_id)
if not dhcp_port_exists:
self.device_manager.driver.unplug(
self.interface_name, namespace=self.network.namespace)
def _output_addn_hosts_file(self):
"""Writes a dnsmasq compatible additional hosts file.

View File

@ -48,6 +48,19 @@ class DhcpOpt(object):
return str(self.__dict__)
class FakeDhcpPort(object):
id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaa'
admin_state_up = True
device_owner = 'network:dhcp'
fixed_ips = [FakeIPAllocation('192.168.0.1',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:80:aa:bb:ee'
device_id = 'fake_dhcp_port'
def __init__(self):
self.extra_dhcp_opts = []
class FakePort1(object):
id = 'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee'
admin_state_up = True
@ -55,6 +68,7 @@ class FakePort1(object):
fixed_ips = [FakeIPAllocation('192.168.0.2',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:80:aa:bb:cc'
device_id = 'fake_port1'
def __init__(self):
self.extra_dhcp_opts = []
@ -67,6 +81,7 @@ class FakePort2(object):
fixed_ips = [FakeIPAllocation('192.168.0.3',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:f3:aa:bb:cc'
device_id = 'fake_port2'
def __init__(self):
self.extra_dhcp_opts = []
@ -81,6 +96,7 @@ class FakePort3(object):
FakeIPAllocation('192.168.1.2',
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
mac_address = '00:00:0f:aa:bb:cc'
device_id = 'fake_port3'
def __init__(self):
self.extra_dhcp_opts = []
@ -96,6 +112,7 @@ class FakePort4(object):
FakeIPAllocation('ffda:3ba5:a17a:4ba3:0216:3eff:fec2:771d',
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
mac_address = '00:16:3E:C2:77:1D'
device_id = 'fake_port4'
def __init__(self):
self.extra_dhcp_opts = []
@ -108,6 +125,7 @@ class FakePort5(object):
fixed_ips = [FakeIPAllocation('192.168.0.5',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:0f:aa:bb:55'
device_id = 'fake_port5'
def __init__(self):
self.extra_dhcp_opts = [
@ -122,6 +140,7 @@ class FakePort6(object):
fixed_ips = [FakeIPAllocation('192.168.0.6',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:0f:aa:bb:66'
device_id = 'fake_port6'
def __init__(self):
self.extra_dhcp_opts = [
@ -140,6 +159,7 @@ class FakeV6Port(object):
fixed_ips = [FakeIPAllocation('fdca:3ba5:a17a:4ba3::2',
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
mac_address = '00:00:f3:aa:bb:cc'
device_id = 'fake_port6'
def __init__(self):
self.extra_dhcp_opts = []
@ -152,6 +172,7 @@ class FakeV6PortExtraOpt(object):
fixed_ips = [FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d',
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
mac_address = '00:16:3e:c2:77:1d'
device_id = 'fake_port6'
def __init__(self):
self.extra_dhcp_opts = [
@ -169,6 +190,7 @@ class FakeDualPortWithV6ExtraOpt(object):
FakeIPAllocation('ffea:3ba5:a17a:4ba3:0216:3eff:fec2:771d',
'eeeeeeee-eeee-eeee-eeee-eeeeeeeeeeee')]
mac_address = '00:16:3e:c2:77:1d'
device_id = 'fake_port6'
def __init__(self):
self.extra_dhcp_opts = [
@ -186,6 +208,7 @@ class FakeDualPort(object):
FakeIPAllocation('fdca:3ba5:a17a:4ba3::3',
'ffffffff-ffff-ffff-ffff-ffffffffffff')]
mac_address = '00:00:0f:aa:bb:cc'
device_id = 'fake_dual_port'
def __init__(self):
self.extra_dhcp_opts = []
@ -196,6 +219,7 @@ class FakeRouterPort(object):
admin_state_up = True
device_owner = constants.DEVICE_OWNER_ROUTER_INTF
mac_address = '00:00:0f:rr:rr:rr'
device_id = 'fake_router_port'
def __init__(self, dev_owner=constants.DEVICE_OWNER_ROUTER_INTF,
ip_address='192.168.0.1'):
@ -212,6 +236,7 @@ class FakeRouterPort2(object):
fixed_ips = [FakeIPAllocation('192.168.1.1',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:0f:rr:rr:r2'
device_id = 'fake_router_port2'
def __init__(self):
self.extra_dhcp_opts = []
@ -224,6 +249,7 @@ class FakePortMultipleAgents1(object):
fixed_ips = [FakeIPAllocation('192.168.0.5',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:0f:dd:dd:dd'
device_id = 'fake_multiple_agents_port'
def __init__(self):
self.extra_dhcp_opts = []
@ -236,6 +262,7 @@ class FakePortMultipleAgents2(object):
fixed_ips = [FakeIPAllocation('192.168.0.6',
'dddddddd-dddd-dddd-dddd-dddddddddddd')]
mac_address = '00:00:0f:ee:ee:ee'
device_id = 'fake_multiple_agents_port2'
def __init__(self):
self.extra_dhcp_opts = []
@ -437,6 +464,13 @@ class FakeDualNetwork(object):
namespace = 'qdhcp-ns'
class FakeNetworkDhcpPort(object):
id = 'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'
subnets = [FakeV4Subnet()]
ports = [FakePort1(), FakeDhcpPort()]
namespace = 'qdhcp-ns'
class FakeDualNetworkGatewayRoute(object):
id = 'cccccccc-cccc-cccc-cccc-cccccccccccc'
subnets = [FakeV4SubnetGatewayRoute(), FakeV6SubnetDHCPStateful()]
@ -1425,12 +1459,33 @@ class TestDnsmasq(TestBase):
dnsmasq._output_hosts_file = mock.Mock()
dnsmasq._release_lease = mock.Mock()
dnsmasq.network.ports = []
dnsmasq.device_manager.driver.unplug = mock.Mock()
dnsmasq._release_unused_leases()
dnsmasq._release_lease.assert_has_calls([mock.call(mac1, ip1, None),
mock.call(mac2, ip2, None)],
any_order=True)
dnsmasq.device_manager.driver.unplug.assert_has_calls(
[mock.call(dnsmasq.interface_name,
namespace=dnsmasq.network.namespace)])
def test_release_unused_leases_with_dhcp_port(self):
dnsmasq = self._get_dnsmasq(FakeNetworkDhcpPort())
ip1 = '192.168.1.2'
mac1 = '00:00:80:aa:bb:cc'
ip2 = '192.168.1.3'
mac2 = '00:00:80:cc:bb:aa'
old_leases = set([(ip1, mac1, None), (ip2, mac2, None)])
dnsmasq._read_hosts_file_leases = mock.Mock(return_value=old_leases)
dnsmasq._output_hosts_file = mock.Mock()
dnsmasq._release_lease = mock.Mock()
dnsmasq.device_manager.get_device_id = mock.Mock(
return_value='fake_dhcp_port')
dnsmasq._release_unused_leases()
self.assertFalse(
dnsmasq.device_manager.driver.unplug.called)
def test_release_unused_leases_with_client_id(self):
dnsmasq = self._get_dnsmasq(FakeDualNetwork())