Merge "Add netns to support overlapping address ranges"
This commit is contained in:
commit
a9829daa6b
@ -231,7 +231,9 @@ class DeviceManager(object):
|
|||||||
port = self._get_or_create_port(network)
|
port = self._get_or_create_port(network)
|
||||||
interface_name = self.get_interface_name(network, port)
|
interface_name = self.get_interface_name(network, port)
|
||||||
|
|
||||||
if ip_lib.device_exists(interface_name):
|
if ip_lib.device_exists(interface_name,
|
||||||
|
self.conf.root_helper,
|
||||||
|
network.id):
|
||||||
if not reuse_existing:
|
if not reuse_existing:
|
||||||
raise exceptions.PreexistingDeviceFailure(
|
raise exceptions.PreexistingDeviceFailure(
|
||||||
dev_name=interface_name)
|
dev_name=interface_name)
|
||||||
|
@ -24,6 +24,7 @@ import tempfile
|
|||||||
|
|
||||||
import netaddr
|
import netaddr
|
||||||
|
|
||||||
|
from quantum.agent.linux import ip_lib
|
||||||
from quantum.agent.linux import utils
|
from quantum.agent.linux import utils
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.openstack.common import importutils
|
from quantum.openstack.common import importutils
|
||||||
@ -110,7 +111,10 @@ class DhcpLocalProcess(DhcpBase):
|
|||||||
pid = self.pid
|
pid = self.pid
|
||||||
|
|
||||||
if self.active:
|
if self.active:
|
||||||
utils.execute(['kill', '-9', pid], self.root_helper)
|
cmd = ['kill', '-9', pid]
|
||||||
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
|
namespace=self.network.id)
|
||||||
|
ip_wrapper.netns.execute(cmd)
|
||||||
self.device_delegate.destroy(self.network)
|
self.device_delegate.destroy(self.network)
|
||||||
elif pid:
|
elif pid:
|
||||||
LOG.debug(_('DHCP for %s pid %d is stale, ignoring command') %
|
LOG.debug(_('DHCP for %s pid %d is stale, ignoring command') %
|
||||||
@ -178,7 +182,6 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
"""Spawns a Dnsmasq process for the network."""
|
"""Spawns a Dnsmasq process for the network."""
|
||||||
interface_name = self.device_delegate.get_interface_name(self.network)
|
interface_name = self.device_delegate.get_interface_name(self.network)
|
||||||
cmd = [
|
cmd = [
|
||||||
'NETWORK_ID=%s' % self.network.id,
|
|
||||||
# TODO (mark): this is dhcpbridge script we'll need to know
|
# TODO (mark): this is dhcpbridge script we'll need to know
|
||||||
# when an IP address has been released
|
# when an IP address has been released
|
||||||
'dnsmasq',
|
'dnsmasq',
|
||||||
@ -219,7 +222,9 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
if self.conf.dnsmasq_dns_server:
|
if self.conf.dnsmasq_dns_server:
|
||||||
cmd.append('--server=%s' % self.conf.dnsmasq_dns_server)
|
cmd.append('--server=%s' % self.conf.dnsmasq_dns_server)
|
||||||
|
|
||||||
utils.execute(cmd, self.root_helper)
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
|
namespace=self.network.id)
|
||||||
|
ip_wrapper.netns.execute(cmd)
|
||||||
|
|
||||||
def reload_allocations(self):
|
def reload_allocations(self):
|
||||||
"""If all subnets turn off dhcp, kill the process."""
|
"""If all subnets turn off dhcp, kill the process."""
|
||||||
@ -232,7 +237,10 @@ class Dnsmasq(DhcpLocalProcess):
|
|||||||
"""Rebuilds the dnsmasq config and signal the dnsmasq to reload."""
|
"""Rebuilds the dnsmasq config and signal the dnsmasq to reload."""
|
||||||
self._output_hosts_file()
|
self._output_hosts_file()
|
||||||
self._output_opts_file()
|
self._output_opts_file()
|
||||||
utils.execute(['kill', '-HUP', self.pid], self.root_helper)
|
cmd = ['kill', '-HUP', self.pid]
|
||||||
|
ip_wrapper = ip_lib.IPWrapper(self.root_helper,
|
||||||
|
namespace=self.network.id)
|
||||||
|
ip_wrapper.netns.execute(cmd)
|
||||||
LOG.debug(_('Reloading allocations for network: %s') % self.network.id)
|
LOG.debug(_('Reloading allocations for network: %s') % self.network.id)
|
||||||
|
|
||||||
def _output_hosts_file(self):
|
def _output_hosts_file(self):
|
||||||
|
@ -52,7 +52,9 @@ class LinuxInterfaceDriver(object):
|
|||||||
|
|
||||||
def init_l3(self, port, device_name):
|
def init_l3(self, port, device_name):
|
||||||
"""Set the L3 settings for the interface using data from the port."""
|
"""Set the L3 settings for the interface using data from the port."""
|
||||||
device = ip_lib.IPDevice(device_name, self.conf.root_helper)
|
device = ip_lib.IPDevice(device_name,
|
||||||
|
self.conf.root_helper,
|
||||||
|
port.network.id)
|
||||||
|
|
||||||
previous = {}
|
previous = {}
|
||||||
for address in device.addr.list(scope='global', filters=['permanent']):
|
for address in device.addr.list(scope='global', filters=['permanent']):
|
||||||
@ -107,7 +109,10 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
|
|
||||||
self.check_bridge_exists(bridge)
|
self.check_bridge_exists(bridge)
|
||||||
|
|
||||||
if not ip_lib.device_exists(device_name):
|
if not ip_lib.device_exists(device_name,
|
||||||
|
self.conf.root_helper,
|
||||||
|
namespace=network_id):
|
||||||
|
|
||||||
utils.execute(['ovs-vsctl',
|
utils.execute(['ovs-vsctl',
|
||||||
'--', '--may-exist', 'add-port', bridge,
|
'--', '--may-exist', 'add-port', bridge,
|
||||||
device_name,
|
device_name,
|
||||||
@ -127,6 +132,9 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
device.link.set_address(mac_address)
|
device.link.set_address(mac_address)
|
||||||
if self.conf.network_device_mtu:
|
if self.conf.network_device_mtu:
|
||||||
device.link.set_mtu(self.conf.network_device_mtu)
|
device.link.set_mtu(self.conf.network_device_mtu)
|
||||||
|
|
||||||
|
namespace = ip.ensure_namespace(network_id)
|
||||||
|
namespace.add_device_to_namespace(device)
|
||||||
device.link.set_up()
|
device.link.set_up()
|
||||||
else:
|
else:
|
||||||
LOG.error(_('Device %s already exists') % device)
|
LOG.error(_('Device %s already exists') % device)
|
||||||
@ -147,14 +155,21 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
|
|
||||||
def plug(self, network_id, port_id, device_name, mac_address):
|
def plug(self, network_id, port_id, device_name, mac_address):
|
||||||
"""Plugin the interface."""
|
"""Plugin the interface."""
|
||||||
if not ip_lib.device_exists(device_name):
|
if not ip_lib.device_exists(device_name,
|
||||||
|
self.conf.root_helper,
|
||||||
|
namespace=network_id):
|
||||||
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
||||||
|
|
||||||
tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap')
|
tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap')
|
||||||
root_veth, dhcp_veth = ip.add_veth(tap_name, device_name)
|
root_veth, dhcp_veth = ip.add_veth(tap_name, device_name)
|
||||||
root_veth.link.set_address(mac_address)
|
root_veth.link.set_address(mac_address)
|
||||||
|
|
||||||
|
namespace = ip.ensure_namespace(network_id)
|
||||||
|
namespace.add_device_to_namespace(root_veth)
|
||||||
|
|
||||||
root_veth.link.set_up()
|
root_veth.link.set_up()
|
||||||
dhcp_veth.link.set_up()
|
dhcp_veth.link.set_up()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
LOG.warn(_("Device %s already exists") % device_name)
|
LOG.warn(_("Device %s already exists") % device_name)
|
||||||
|
|
||||||
|
@ -360,6 +360,7 @@ class TestDeviceManager(unittest.TestCase):
|
|||||||
self.conf.register_opts(dhcp_agent.DeviceManager.OPTS)
|
self.conf.register_opts(dhcp_agent.DeviceManager.OPTS)
|
||||||
self.conf.set_override('interface_driver',
|
self.conf.set_override('interface_driver',
|
||||||
'quantum.agent.linux.interface.NullDriver')
|
'quantum.agent.linux.interface.NullDriver')
|
||||||
|
self.conf.root_helper = 'sudo'
|
||||||
|
|
||||||
self.client_cls_p = mock.patch('quantumclient.v2_0.client.Client')
|
self.client_cls_p = mock.patch('quantumclient.v2_0.client.Client')
|
||||||
client_cls = self.client_cls_p.start()
|
client_cls = self.client_cls_p.start()
|
||||||
|
@ -280,7 +280,9 @@ class TestDhcpLocalProcess(TestBase):
|
|||||||
lp.disable()
|
lp.disable()
|
||||||
|
|
||||||
delegate.assert_has_calls([mock.call.destroy(network)])
|
delegate.assert_has_calls([mock.call.destroy(network)])
|
||||||
self.execute.assert_called_once_with(['kill', '-9', 5], 'sudo')
|
exp_args = ['ip', 'netns', 'exec',
|
||||||
|
'cccccccc-cccc-cccc-cccc-cccccccccccc', 'kill', '-9', 5]
|
||||||
|
self.execute.assert_called_once_with(exp_args, root_helper='sudo')
|
||||||
|
|
||||||
def test_pid(self):
|
def test_pid(self):
|
||||||
with mock.patch('__builtin__.open') as mock_open:
|
with mock.patch('__builtin__.open') as mock_open:
|
||||||
@ -311,7 +313,10 @@ class TestDnsmasq(TestBase):
|
|||||||
return '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/%s' % kind
|
return '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/%s' % kind
|
||||||
|
|
||||||
expected = [
|
expected = [
|
||||||
'NETWORK_ID=cccccccc-cccc-cccc-cccc-cccccccccccc',
|
'ip',
|
||||||
|
'netns',
|
||||||
|
'exec',
|
||||||
|
'cccccccc-cccc-cccc-cccc-cccccccccccc',
|
||||||
'dnsmasq',
|
'dnsmasq',
|
||||||
'--no-hosts',
|
'--no-hosts',
|
||||||
'--no-resolv',
|
'--no-resolv',
|
||||||
@ -347,7 +352,7 @@ class TestDnsmasq(TestBase):
|
|||||||
device_delegate=delegate)
|
device_delegate=delegate)
|
||||||
dm.spawn_process()
|
dm.spawn_process()
|
||||||
self.assertTrue(mocks['_output_opts_file'].called)
|
self.assertTrue(mocks['_output_opts_file'].called)
|
||||||
self.execute.assert_called_once_with(expected, 'sudo')
|
self.execute.assert_called_once_with(expected, root_helper='sudo')
|
||||||
|
|
||||||
def test_spawn(self):
|
def test_spawn(self):
|
||||||
self._test_spawn([])
|
self._test_spawn([])
|
||||||
@ -388,6 +393,8 @@ class TestDnsmasq(TestBase):
|
|||||||
""".lstrip()
|
""".lstrip()
|
||||||
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
|
exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts'
|
||||||
exp_opt_data = "tag:tag0,option:router,192.168.0.1"
|
exp_opt_data = "tag:tag0,option:router,192.168.0.1"
|
||||||
|
exp_args = ['ip', 'netns', 'exec',
|
||||||
|
'cccccccc-cccc-cccc-cccc-cccccccccccc', 'kill', '-HUP', 5]
|
||||||
|
|
||||||
with mock.patch('os.path.isdir') as isdir:
|
with mock.patch('os.path.isdir') as isdir:
|
||||||
isdir.return_value = True
|
isdir.return_value = True
|
||||||
@ -398,4 +405,4 @@ class TestDnsmasq(TestBase):
|
|||||||
|
|
||||||
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
|
self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_data),
|
||||||
mock.call(exp_opt_name, exp_opt_data)])
|
mock.call(exp_opt_name, exp_opt_data)])
|
||||||
self.execute.assert_called_once_with(['kill', '-HUP', 5], 'sudo')
|
self.execute.assert_called_once_with(exp_args, root_helper='sudo')
|
||||||
|
@ -94,7 +94,7 @@ class TestABCDriver(TestBase):
|
|||||||
bc = BaseChild(self.conf)
|
bc = BaseChild(self.conf)
|
||||||
bc.init_l3(FakePort(), 'tap0')
|
bc.init_l3(FakePort(), 'tap0')
|
||||||
self.ip_dev.assert_has_calls(
|
self.ip_dev.assert_has_calls(
|
||||||
[mock.call('tap0', 'sudo'),
|
[mock.call('tap0', 'sudo', '12345678-1234-5678-90ab-ba0987654321'),
|
||||||
mock.call().addr.list(scope='global', filters=['permanent']),
|
mock.call().addr.list(scope='global', filters=['permanent']),
|
||||||
mock.call().addr.add(4, '192.168.1.2/24', '192.168.1.255'),
|
mock.call().addr.add(4, '192.168.1.2/24', '192.168.1.255'),
|
||||||
mock.call().addr.delete(4, '172.16.77.240/24')])
|
mock.call().addr.delete(4, '172.16.77.240/24')])
|
||||||
@ -127,7 +127,10 @@ class TestOVSInterfaceDriver(TestBase):
|
|||||||
mock.call().device('tap0'),
|
mock.call().device('tap0'),
|
||||||
mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')]
|
mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')]
|
||||||
expected.extend(additional_expectation)
|
expected.extend(additional_expectation)
|
||||||
expected.extend([mock.call().device().link.set_up()])
|
expected.extend(
|
||||||
|
[mock.call().ensure_namespace('01234567-1234-1234-99'),
|
||||||
|
mock.call().ensure_namespace().add_device_to_namespace(mock.ANY),
|
||||||
|
mock.call().device().link.set_up()])
|
||||||
|
|
||||||
self.ip.assert_has_calls(expected)
|
self.ip.assert_has_calls(expected)
|
||||||
|
|
||||||
@ -172,9 +175,10 @@ class TestBridgeInterfaceDriver(TestBase):
|
|||||||
'aa:bb:cc:dd:ee:ff')
|
'aa:bb:cc:dd:ee:ff')
|
||||||
|
|
||||||
self.ip.assert_has_calls(
|
self.ip.assert_has_calls(
|
||||||
[mock.call(),
|
[mock.call('sudo'),
|
||||||
mock.call('sudo'),
|
mock.call().add_veth('tap0', 'dhc0'),
|
||||||
mock.call().add_veth('tap0', 'dhc0')])
|
mock.call().ensure_namespace('01234567-1234-1234-99'),
|
||||||
|
mock.call().ensure_namespace().add_device_to_namespace(mock.ANY)])
|
||||||
|
|
||||||
root_veth.assert_has_calls([mock.call.link.set_up()])
|
root_veth.assert_has_calls([mock.call.link.set_up()])
|
||||||
ns_veth.assert_has_calls([mock.call.link.set_up()])
|
ns_veth.assert_has_calls([mock.call.link.set_up()])
|
||||||
@ -237,7 +241,7 @@ class TestRyuInterfaceDriver(TestBase):
|
|||||||
super(TestRyuInterfaceDriver, self).tearDown()
|
super(TestRyuInterfaceDriver, self).tearDown()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _device_exists(dev, root_helper=None):
|
def _device_exists(dev, root_helper=None, namespace=None):
|
||||||
return dev == 'br-int'
|
return dev == 'br-int'
|
||||||
|
|
||||||
_vsctl_cmd_init = ['ovs-vsctl', '--timeout=2',
|
_vsctl_cmd_init = ['ovs-vsctl', '--timeout=2',
|
||||||
@ -281,8 +285,12 @@ class TestRyuInterfaceDriver(TestBase):
|
|||||||
|
|
||||||
self.ryu_app_client.OFPClient.assert_called_once_with('127.0.0.1:8080')
|
self.ryu_app_client.OFPClient.assert_called_once_with('127.0.0.1:8080')
|
||||||
|
|
||||||
expected = [mock.call('sudo'),
|
expected = [
|
||||||
|
mock.call('sudo'),
|
||||||
mock.call().device('tap0'),
|
mock.call().device('tap0'),
|
||||||
mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff'),
|
mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff'),
|
||||||
|
mock.call().ensure_namespace('01234567-1234-1234-99'),
|
||||||
|
mock.call().ensure_namespace().add_device_to_namespace(mock.ANY),
|
||||||
mock.call().device().link.set_up()]
|
mock.call().device().link.set_up()]
|
||||||
|
|
||||||
self.ip.assert_has_calls(expected)
|
self.ip.assert_has_calls(expected)
|
||||||
|
Loading…
Reference in New Issue
Block a user