diff --git a/quantum/agent/dhcp_agent.py b/quantum/agent/dhcp_agent.py index 8ab0c92540..b8e203434d 100644 --- a/quantum/agent/dhcp_agent.py +++ b/quantum/agent/dhcp_agent.py @@ -231,7 +231,9 @@ class DeviceManager(object): port = self._get_or_create_port(network) 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: raise exceptions.PreexistingDeviceFailure( dev_name=interface_name) diff --git a/quantum/agent/linux/dhcp.py b/quantum/agent/linux/dhcp.py index 5ed015867b..5792f9a71f 100644 --- a/quantum/agent/linux/dhcp.py +++ b/quantum/agent/linux/dhcp.py @@ -24,6 +24,7 @@ import tempfile import netaddr +from quantum.agent.linux import ip_lib from quantum.agent.linux import utils from quantum.openstack.common import cfg from quantum.openstack.common import importutils @@ -110,7 +111,10 @@ class DhcpLocalProcess(DhcpBase): pid = self.pid 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) elif pid: 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.""" interface_name = self.device_delegate.get_interface_name(self.network) cmd = [ - 'NETWORK_ID=%s' % self.network.id, # TODO (mark): this is dhcpbridge script we'll need to know # when an IP address has been released 'dnsmasq', @@ -219,7 +222,9 @@ class Dnsmasq(DhcpLocalProcess): if 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): """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.""" self._output_hosts_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) def _output_hosts_file(self): diff --git a/quantum/agent/linux/interface.py b/quantum/agent/linux/interface.py index 4f7d9bec63..03ed071dfe 100644 --- a/quantum/agent/linux/interface.py +++ b/quantum/agent/linux/interface.py @@ -52,7 +52,9 @@ class LinuxInterfaceDriver(object): def init_l3(self, port, device_name): """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 = {} for address in device.addr.list(scope='global', filters=['permanent']): @@ -107,7 +109,10 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): 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', '--', '--may-exist', 'add-port', bridge, device_name, @@ -127,6 +132,9 @@ class OVSInterfaceDriver(LinuxInterfaceDriver): device.link.set_address(mac_address) if 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() else: 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): """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) tap_name = device_name.replace(self.DEV_NAME_PREFIX, 'tap') root_veth, dhcp_veth = ip.add_veth(tap_name, device_name) 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() dhcp_veth.link.set_up() + else: LOG.warn(_("Device %s already exists") % device_name) diff --git a/quantum/tests/unit/test_dhcp_agent.py b/quantum/tests/unit/test_dhcp_agent.py index 0eeb7eb75c..3e2312ea83 100644 --- a/quantum/tests/unit/test_dhcp_agent.py +++ b/quantum/tests/unit/test_dhcp_agent.py @@ -360,6 +360,7 @@ class TestDeviceManager(unittest.TestCase): self.conf.register_opts(dhcp_agent.DeviceManager.OPTS) self.conf.set_override('interface_driver', 'quantum.agent.linux.interface.NullDriver') + self.conf.root_helper = 'sudo' self.client_cls_p = mock.patch('quantumclient.v2_0.client.Client') client_cls = self.client_cls_p.start() diff --git a/quantum/tests/unit/test_linux_dhcp.py b/quantum/tests/unit/test_linux_dhcp.py index e20e3a392c..2686253812 100644 --- a/quantum/tests/unit/test_linux_dhcp.py +++ b/quantum/tests/unit/test_linux_dhcp.py @@ -280,7 +280,9 @@ class TestDhcpLocalProcess(TestBase): lp.disable() 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): with mock.patch('__builtin__.open') as mock_open: @@ -311,7 +313,10 @@ class TestDnsmasq(TestBase): return '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/%s' % kind expected = [ - 'NETWORK_ID=cccccccc-cccc-cccc-cccc-cccccccccccc', + 'ip', + 'netns', + 'exec', + 'cccccccc-cccc-cccc-cccc-cccccccccccc', 'dnsmasq', '--no-hosts', '--no-resolv', @@ -347,7 +352,7 @@ class TestDnsmasq(TestBase): device_delegate=delegate) dm.spawn_process() 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): self._test_spawn([]) @@ -388,6 +393,8 @@ class TestDnsmasq(TestBase): """.lstrip() exp_opt_name = '/dhcp/cccccccc-cccc-cccc-cccc-cccccccccccc/opts' 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: isdir.return_value = True @@ -398,4 +405,4 @@ class TestDnsmasq(TestBase): self.safe.assert_has_calls([mock.call(exp_host_name, exp_host_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') diff --git a/quantum/tests/unit/test_linux_interface.py b/quantum/tests/unit/test_linux_interface.py index 5b70b0f942..a9d9cd6387 100644 --- a/quantum/tests/unit/test_linux_interface.py +++ b/quantum/tests/unit/test_linux_interface.py @@ -94,7 +94,7 @@ class TestABCDriver(TestBase): bc = BaseChild(self.conf) bc.init_l3(FakePort(), 'tap0') 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.add(4, '192.168.1.2/24', '192.168.1.255'), mock.call().addr.delete(4, '172.16.77.240/24')]) @@ -127,7 +127,10 @@ class TestOVSInterfaceDriver(TestBase): mock.call().device('tap0'), mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')] 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) @@ -172,9 +175,10 @@ class TestBridgeInterfaceDriver(TestBase): 'aa:bb:cc:dd:ee:ff') self.ip.assert_has_calls( - [mock.call(), - mock.call('sudo'), - mock.call().add_veth('tap0', 'dhc0')]) + [mock.call('sudo'), + 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()]) ns_veth.assert_has_calls([mock.call.link.set_up()]) @@ -237,7 +241,7 @@ class TestRyuInterfaceDriver(TestBase): super(TestRyuInterfaceDriver, self).tearDown() @staticmethod - def _device_exists(dev, root_helper=None): + def _device_exists(dev, root_helper=None, namespace=None): return dev == 'br-int' _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') - expected = [mock.call('sudo'), - mock.call().device('tap0'), - mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff'), - mock.call().device().link.set_up()] + expected = [ + mock.call('sudo'), + mock.call().device('tap0'), + 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()] + self.ip.assert_has_calls(expected)