Merge "Fix detection of deleted networks in DHCP agent."

This commit is contained in:
Jenkins 2013-03-12 23:13:15 +00:00 committed by Gerrit Code Review
commit d833c72a14
4 changed files with 124 additions and 0 deletions

View File

@ -79,6 +79,28 @@ class DhcpAgent(manager.Manager):
self.device_manager = DeviceManager(self.conf, self.plugin_rpc) self.device_manager = DeviceManager(self.conf, self.plugin_rpc)
self.lease_relay = DhcpLeaseRelay(self.update_lease) self.lease_relay = DhcpLeaseRelay(self.update_lease)
self._populate_networks_cache()
def _populate_networks_cache(self):
"""Populate the networks cache when the DHCP-agent starts"""
try:
existing_networks = self.dhcp_driver_cls.existing_dhcp_networks(
self.conf,
self.root_helper
)
for net_id in existing_networks:
net = DictModel({"id": net_id, "subnets": [], "ports": []})
self.cache.put(net)
except NotImplementedError:
# just go ahead with an empty networks cache
LOG.debug(
_("The '%s' DHCP-driver does not support retrieving of a "
"list of existing networks"),
self.conf.dhcp_driver
)
def after_start(self): def after_start(self):
self.run() self.run()
LOG.info(_("DHCP agent started")) LOG.info(_("DHCP agent started"))

View File

@ -18,6 +18,7 @@
import abc import abc
import os import os
import re import re
import shutil
import socket import socket
import StringIO import StringIO
import sys import sys
@ -29,6 +30,7 @@ from quantum.agent.linux import ip_lib
from quantum.agent.linux import utils from quantum.agent.linux import utils
from quantum.openstack.common import jsonutils from quantum.openstack.common import jsonutils
from quantum.openstack.common import log as logging from quantum.openstack.common import log as logging
from quantum.openstack.common import uuidutils
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -92,6 +94,12 @@ class DhcpBase(object):
def reload_allocations(self): def reload_allocations(self):
"""Force the DHCP server to reload the assignment database.""" """Force the DHCP server to reload the assignment database."""
@classmethod
def existing_dhcp_networks(cls, conf, root_helper):
"""Return a list of existing networks ids (ones we have configs for)"""
raise NotImplementedError
class DhcpLocalProcess(DhcpBase): class DhcpLocalProcess(DhcpBase):
PORTS = [] PORTS = []
@ -134,6 +142,13 @@ class DhcpLocalProcess(DhcpBase):
else: else:
LOG.debug(_('No DHCP started for %s'), self.network.id) LOG.debug(_('No DHCP started for %s'), self.network.id)
self._remove_config_files()
def _remove_config_files(self):
confs_dir = os.path.abspath(os.path.normpath(self.conf.dhcp_confs))
conf_dir = os.path.join(confs_dir, self.network.id)
shutil.rmtree(conf_dir, ignore_errors=True)
def get_conf_file_name(self, kind, ensure_conf_dir=False): def get_conf_file_name(self, kind, ensure_conf_dir=False):
"""Returns the file name for a given kind of config file.""" """Returns the file name for a given kind of config file."""
confs_dir = os.path.abspath(os.path.normpath(self.conf.dhcp_confs)) confs_dir = os.path.abspath(os.path.normpath(self.conf.dhcp_confs))
@ -206,6 +221,22 @@ class Dnsmasq(DhcpLocalProcess):
QUANTUM_NETWORK_ID_KEY = 'QUANTUM_NETWORK_ID' QUANTUM_NETWORK_ID_KEY = 'QUANTUM_NETWORK_ID'
QUANTUM_RELAY_SOCKET_PATH_KEY = 'QUANTUM_RELAY_SOCKET_PATH' QUANTUM_RELAY_SOCKET_PATH_KEY = 'QUANTUM_RELAY_SOCKET_PATH'
@classmethod
def existing_dhcp_networks(cls, conf, root_helper):
"""Return a list of existing networks ids (ones we have configs for)"""
confs_dir = os.path.abspath(os.path.normpath(conf.dhcp_confs))
class FakeNetwork:
def __init__(self, net_id):
self.id = net_id
return [
c for c in os.listdir(confs_dir)
if (uuidutils.is_uuid_like(c) and
cls(conf, FakeNetwork(c), root_helper).active)
]
def spawn_process(self): def spawn_process(self):
"""Spawns a Dnsmasq process for the network.""" """Spawns a Dnsmasq process for the network."""
env = { env = {

View File

@ -121,6 +121,7 @@ class TestDhcpAgent(base.BaseTestCase):
self.driver_cls_p = mock.patch( self.driver_cls_p = mock.patch(
'quantum.agent.dhcp_agent.importutils.import_class') 'quantum.agent.dhcp_agent.importutils.import_class')
self.driver = mock.Mock(name='driver') self.driver = mock.Mock(name='driver')
self.driver.existing_dhcp_networks.return_value = []
self.driver_cls = self.driver_cls_p.start() self.driver_cls = self.driver_cls_p.start()
self.driver_cls.return_value = self.driver self.driver_cls.return_value = self.driver
@ -324,6 +325,34 @@ class TestDhcpAgent(base.BaseTestCase):
sleep.assert_called_once_with(dhcp.conf.resync_interval) sleep.assert_called_once_with(dhcp.conf.resync_interval)
self.assertFalse(dhcp.needs_resync) self.assertFalse(dhcp.needs_resync)
def test_populate_cache_on_start_without_active_networks_support(self):
# emul dhcp driver that doesn't support retrieving of active networks
self.driver.existing_dhcp_networks.side_effect = NotImplementedError
with mock.patch.object(dhcp_agent.LOG, 'debug') as log:
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
self.driver.existing_dhcp_networks.assert_called_once_with(
dhcp.conf,
cfg.CONF.root_helper
)
self.assertFalse(dhcp.cache.get_network_ids())
self.assertTrue(log.called)
def test_populate_cache_on_start(self):
networks = ['aaa', 'bbb']
self.driver.existing_dhcp_networks.return_value = networks
dhcp = dhcp_agent.DhcpAgent(HOSTNAME)
self.driver.existing_dhcp_networks.assert_called_once_with(
dhcp.conf,
cfg.CONF.root_helper
)
self.assertEquals(set(networks), set(dhcp.cache.get_network_ids()))
class TestLogArgs(base.BaseTestCase): class TestLogArgs(base.BaseTestCase):

View File

@ -135,6 +135,11 @@ class FakeV4NoGatewayNetwork:
class TestDhcpBase(base.BaseTestCase): class TestDhcpBase(base.BaseTestCase):
def test_existing_dhcp_networks_abstract_error(self):
self.assertRaises(NotImplementedError,
dhcp.DhcpBase.existing_dhcp_networks,
None, None)
def test_base_abc_error(self): def test_base_abc_error(self):
self.assertRaises(TypeError, dhcp.DhcpBase, None) self.assertRaises(TypeError, dhcp.DhcpBase, None)
@ -630,3 +635,40 @@ tag:tag1,option:classless-static-route,%s,%s""".lstrip() % (fake_v6,
def test_lease_relay_script_add_socket_missing(self): def test_lease_relay_script_add_socket_missing(self):
self._test_lease_relay_script_helper('add', 120, False) self._test_lease_relay_script_helper('add', 120, False)
def test_remove_config_files(self):
net = FakeV4Network()
path = '/opt/data/quantum/dhcp'
self.conf.dhcp_confs = path
with mock.patch('shutil.rmtree') as rmtree:
lp = LocalChild(self.conf, net)
lp._remove_config_files()
rmtree.assert_called_once_with(os.path.join(path, net.id),
ignore_errors=True)
def test_existing_dhcp_networks(self):
path = '/opt/data/quantum/dhcp'
self.conf.dhcp_confs = path
cases = {
# network_uuid --> is_dhcp_alive?
'aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa': True,
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb': False,
'not_uuid_like_name': True
}
def active_fake(self, instance, cls):
return cases[instance.network.id]
with mock.patch('os.listdir') as mock_listdir:
with mock.patch.object(dhcp.Dnsmasq, 'active') as mock_active:
mock_active.__get__ = active_fake
mock_listdir.return_value = cases.keys()
result = dhcp.Dnsmasq.existing_dhcp_networks(self.conf, 'sudo')
mock_listdir.assert_called_once_with(path)
self.assertEquals(['aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa'],
result)