of the network.
"""
- if not self.cache.get_network_by_id(network_id):
+ old_network = self.cache.get_network_by_id(network_id)
+ if not old_network:
# DHCP current not running for network.
- self.enable_dhcp_helper(network_id)
+ return self.enable_dhcp_helper(network_id)
network = self.plugin_rpc.get_network_info(network_id)
- for subnet in network.subnets:
- if subnet.enable_dhcp:
- self.cache.put(network)
- self.call_driver('enable', network)
- break
+
+ old_cidrs = set(s.cidr for s in old_network.subnets if s.enable_dhcp)
+ new_cidrs = set(s.cidr for s in network.subnets if s.enable_dhcp)
+
+ if new_cidrs and old_cidrs == new_cidrs:
+ self.call_driver('reload_allocations', network)
+ self.cache.put(network)
+ elif new_cidrs:
+ self.call_driver('restart', network)
+ self.cache.put(network)
else:
self.disable_dhcp_helper(network.id)
"""Enables DHCP for this network."""
@abc.abstractmethod
- def disable(self):
+ def disable(self, retain_port=False):
"""Disable dhcp for this network."""
def restart(self):
"""Restart the dhcp service for the network."""
- self.disable()
+ self.disable(retain_port=True)
self.enable()
@abc.abstractproperty
interface_name = self.device_delegate.setup(self.network,
reuse_existing=True)
if self.active:
- self.reload_allocations()
+ self.restart()
elif self._enable_dhcp():
self.interface_name = interface_name
self.spawn_process()
- def disable(self):
+ def disable(self, retain_port=False):
"""Disable DHCP for this network by killing the local process."""
pid = self.pid
else:
utils.execute(cmd, self.root_helper)
- self.device_delegate.destroy(self.network, self.interface_name)
+ if not retain_port:
+ self.device_delegate.destroy(self.network, self.interface_name)
elif pid:
LOG.debug(_('DHCP for %s pid %d is stale, ignoring command') %
network_id='12345678-1234-5678-1234567890ab',
enable_dhcp=False)
+fake_subnet3 = FakeModel('bbbbbbbb-1111-2222-bbbbbbbbbbbb',
+ network_id='12345678-1234-5678-1234567890ab',
+ cidr='192.168.1.1/24', enable_dhcp=True)
+
fake_fixed_ip = FakeModel('', subnet=fake_subnet1, ip_address='172.9.9.9')
fake_port1 = FakeModel('12345678-1234-aaaa-1234567890ab',
disable.assertCalledOnceWith(fake_network.id)
def test_refresh_dhcp_helper_no_dhcp_enabled_networks(self):
- network = FakeModel('12345678-1234-5678-1234567890ab',
+ network = FakeModel('net-id',
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
admin_state_up=True,
subnets=[],
ports=[])
+ self.cache.get_network_by_id.return_value = network
self.plugin.get_network_info.return_value = network
with mock.patch.object(self.dhcp, 'disable_dhcp_helper') as disable:
self.dhcp.refresh_dhcp_helper(network.id)
disable.called_once_with_args(network.id)
self.assertFalse(self.cache.called)
self.assertFalse(self.call_driver.called)
+ self.cache.assert_has_calls(
+ [mock.call.get_network_by_id('net-id')])
def test_subnet_update_end(self):
payload = dict(subnet=dict(network_id=fake_network.id))
self.dhcp.subnet_update_end(payload)
self.cache.assert_has_calls([mock.call.put(fake_network)])
- self.call_driver.assert_called_once_with('enable', fake_network)
+ self.call_driver.assert_called_once_with('reload_allocations',
+ fake_network)
+
+ def test_subnet_update_end(self):
+ new_state = FakeModel(fake_network.id,
+ tenant_id=fake_network.tenant_id,
+ admin_state_up=True,
+ subnets=[fake_subnet1, fake_subnet3],
+ ports=[fake_port1])
+
+ payload = dict(subnet=dict(network_id=fake_network.id))
+ self.cache.get_network_by_id.return_value = fake_network
+ self.plugin.get_network_info.return_value = new_state
+
+ self.dhcp.subnet_update_end(payload)
+
+ self.cache.assert_has_calls([mock.call.put(new_state)])
+ self.call_driver.assert_called_once_with('restart',
+ new_state)
def test_subnet_update_end_delete_payload(self):
+ prev_state = FakeModel(fake_network.id,
+ tenant_id=fake_network.tenant_id,
+ admin_state_up=True,
+ subnets=[fake_subnet1, fake_subnet3],
+ ports=[fake_port1])
+
payload = dict(subnet_id=fake_subnet1.id)
- self.cache.get_network_by_subnet_id.return_value = fake_network
+ self.cache.get_network_by_subnet_id.return_value = prev_state
+ self.cache.get_network_by_id.return_value = prev_state
self.plugin.get_network_info.return_value = fake_network
self.dhcp.subnet_delete_end(payload)
- self.cache.assert_has_calls([mock.call.put(fake_network)])
- self.call_driver.assert_called_once_with('enable', fake_network)
+ self.cache.assert_has_calls([
+ mock.call.get_network_by_subnet_id(
+ 'bbbbbbbb-bbbb-bbbb-bbbbbbbbbbbb'),
+ mock.call.get_network_by_id('12345678-1234-5678-1234567890ab'),
+ mock.call.put(fake_network)])
+ self.call_driver.assert_called_once_with('restart',
+ fake_network)
def test_port_update_end(self):
payload = dict(port=vars(fake_port2))
def enable(self):
self.called.append('enable')
- def disable(self):
- self.called.append('disable')
+ def disable(self, retain_port=False):
+ self.called.append('disable %s' % retain_port)
def reload_allocations(self):
pass
c = SubClass()
c.restart()
- self.assertEquals(c.called, ['disable', 'enable'])
+ self.assertEquals(c.called, ['disable True', 'enable'])
class LocalChild(dhcp.DhcpLocalProcess):
def reload_allocations(self):
self.called.append('reload')
+ def restart(self):
+ self.called.append('restart')
+
def spawn_process(self):
self.called.append('spawn')
device_delegate=delegate)
lp.enable()
- self.assertEqual(lp.called, ['reload'])
+ self.assertEqual(lp.called, ['restart'])
def test_enable(self):
delegate = mock.Mock(return_value='tap0')
msg = log.call_args[0][0]
self.assertIn('No DHCP', msg)
+ def test_disable_retain_port(self):
+ attrs_to_mock = dict([(a, mock.DEFAULT) for a in
+ ['active', 'interface_name', 'pid']])
+ delegate = mock.Mock()
+ network = FakeDualNetwork()
+ with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
+ mocks['active'].__get__ = mock.Mock(return_value=True)
+ mocks['pid'].__get__ = mock.Mock(return_value=5)
+ mocks['interface_name'].__get__ = mock.Mock(return_value='tap0')
+ lp = LocalChild(self.conf, network, device_delegate=delegate,
+ namespace='qdhcp-ns')
+ lp.disable(retain_port=True)
+
+ self.assertFalse(delegate.called)
+ exp_args = ['ip', 'netns', 'exec', 'qdhcp-ns', 'kill', '-9', 5]
+ self.execute.assert_called_once_with(exp_args, root_helper='sudo')
+
def test_disable(self):
attrs_to_mock = dict([(a, mock.DEFAULT) for a in
['active', 'interface_name', 'pid']])