Merge "Fix detection of deleted networks in DHCP agent."
This commit is contained in:
commit
d833c72a14
@ -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"))
|
||||||
|
@ -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 = {
|
||||||
|
@ -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):
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user