import time
import uuid
+import netaddr
from sqlalchemy.ext import sqlsoup
from quantum.agent.common import config
self.driver.plug(network.id,
port.id,
interface_name,
- port.mac_address)
- self.driver.init_l3(port, interface_name)
+ port.mac_address,
+ namespace=network.id)
+ ip_cidrs = []
+ for fixed_ip in port.fixed_ips:
+ subnet = fixed_ip.subnet
+ net = netaddr.IPNetwork(subnet.cidr)
+ ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
+ ip_cidrs.append(ip_cidr)
+
+ self.driver.init_l3(interface_name, ip_cidrs,
+ namespace=network.id)
def destroy(self, network):
self.driver.unplug(self.get_interface_name(network))
def __init__(self, conf):
self.conf = conf
- 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,
- port.network.id)
+ def init_l3(self, device_name, ip_cidrs, namespace=None):
+ """Set the L3 settings for the interface using data from the port.
+ ip_cidrs: list of 'X.X.X.X/YY' strings
+ """
+ device = ip_lib.IPDevice(device_name, self.conf.root_helper,
+ namespace=namespace)
previous = {}
for address in device.addr.list(scope='global', filters=['permanent']):
previous[address['cidr']] = address['ip_version']
# add new addresses
- for fixed_ip in port.fixed_ips:
- subnet = fixed_ip.subnet
- net = netaddr.IPNetwork(subnet.cidr)
- ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
+ for ip_cidr in ip_cidrs:
+ net = netaddr.IPNetwork(ip_cidr)
if ip_cidr in previous:
del previous[ip_cidr]
continue
def check_bridge_exists(self, bridge):
if not ip_lib.device_exists(bridge):
- raise exception.BridgeDoesNotExist(bridge=bridge)
+ raise exceptions.BridgeDoesNotExist(bridge=bridge)
def get_device_name(self, port):
return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
@abc.abstractmethod
- def plug(self, network_id, port_id, device_name, mac_address):
+ def plug(self, network_id, port_id, device_name, mac_address,
+ bridge=None, namespace=None):
"""Plug in the interface."""
@abc.abstractmethod
- def unplug(self, device_name):
+ def unplug(self, device_name, bridge=None):
"""Unplug the interface."""
class NullDriver(LinuxInterfaceDriver):
- def plug(self, network_id, port_id, device_name, mac_address):
+ def plug(self, network_id, port_id, device_name, mac_address,
+ bridge=None, namespace=None):
pass
- def unplug(self, device_name):
+ def unplug(self, device_name, bridge=None):
pass
class OVSInterfaceDriver(LinuxInterfaceDriver):
- """Driver for creating an OVS interface."""
+ """Driver for creating an internal interface on an OVS bridge."""
- def plug(self, network_id, port_id, device_name, mac_address):
+ def plug(self, network_id, port_id, device_name, mac_address,
+ bridge=None, namespace=None):
"""Plug in the interface."""
- bridge = self.conf.ovs_integration_bridge
+ if not bridge:
+ bridge = self.conf.ovs_integration_bridge
self.check_bridge_exists(bridge)
if not ip_lib.device_exists(device_name,
self.conf.root_helper,
- namespace=network_id):
+ namespace=namespace):
utils.execute(['ovs-vsctl',
'--', '--may-exist', 'add-port', bridge,
mac_address],
self.conf.root_helper)
- ip = ip_lib.IPWrapper(self.conf.root_helper)
- device = ip.device(device_name)
- 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)
+ ip = ip_lib.IPWrapper(self.conf.root_helper)
+ device = ip.device(device_name)
+ device.link.set_address(mac_address)
+ if self.conf.network_device_mtu:
+ device.link.set_mtu(self.conf.network_device_mtu)
+
+ if namespace:
+ namespace_obj = ip.ensure_namespace(namespace)
+ namespace_obj.add_device_to_namespace(device)
+ device.link.set_up()
- def unplug(self, device_name):
+ def unplug(self, device_name, bridge=None):
"""Unplug the interface."""
- bridge_name = self.conf.ovs_integration_bridge
+ if not bridge:
+ bridge = self.conf.ovs_integration_bridge
- self.check_bridge_exists(bridge_name)
- bridge = ovs_lib.OVSBridge(bridge_name, self.conf.root_helper)
+ self.check_bridge_exists(bridge)
+ bridge = ovs_lib.OVSBridge(bridge, self.conf.root_helper)
bridge.delete_port(device_name)
DEV_NAME_PREFIX = 'dhc'
- def plug(self, network_id, port_id, device_name, mac_address):
+ def plug(self, network_id, port_id, device_name, mac_address,
+ bridge=None, namespace=None):
"""Plugin the interface."""
if not ip_lib.device_exists(device_name,
self.conf.root_helper,
- namespace=network_id):
+ namespace=namespace):
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(dhcp_veth)
+ if namespace:
+ namespace_obj = ip.ensure_namespace(namespace)
+ namespace_obj.add_device_to_namespace(dhcp_veth)
root_veth.link.set_up()
dhcp_veth.link.set_up()
else:
LOG.warn(_("Device %s already exists") % device_name)
- def unplug(self, device_name):
+ def unplug(self, device_name, bridge=None):
"""Unplug the interface."""
device = ip_lib.IPDevice(device_name, self.conf.root_helper)
try:
LOG.debug('ryu rest host %s', self.conf.ryu_api_host)
self.ryu_client = OFPClient(self.conf.ryu_api_host)
- self.check_bridge_exists(self.conf.ovs_integration_bridge)
- self.ovs_br = ovs_lib.OVSBridge(self.conf.ovs_integration_bridge,
- self.conf.root_helper)
- self.datapath_id = self.ovs_br.get_datapath_id()
-
- def plug(self, network_id, port_id, device_name, mac_address):
+ def plug(self, network_id, port_id, device_name, mac_address,
+ bridge=None, namespace=None):
"""Plug in the interface."""
super(RyuInterfaceDriver, self).plug(network_id, port_id, device_name,
- mac_address)
+ mac_address, bridge=bridge,
+ namespace=namespace)
+ if not bridge:
+ bridge = self.conf.ovs_integration_bridge
- port_no = self.ovs_br.get_port_ofport(device_name)
- self.ryu_client.create_port(network_id, self.datapath_id, port_no)
+ self.check_bridge_exists(bridge)
+ ovs_br = ovs_lib.OVSBridge(bridge, self.conf.root_helper)
+ datapath_id = ovs_br.get_datapath_id()
+ port_no = ovs_br.get_port_ofport(device_name)
+ self.ryu_client.create_port(network_id, datapath_id, port_no)
@classmethod
def get_namespaces(cls, root_helper):
- output = cls._execute('netns', ('list',), root_helper=root_helper)
+ output = cls._execute('', 'netns', ('list',), root_helper=root_helper)
return [l.strip() for l in output.split('\n')]
"""
def __init__(self, _execute=None, state_less=False,
- root_helper=None, use_ipv6=False):
+ root_helper=None, use_ipv6=False, namespace=None):
if _execute:
self.execute = _execute
else:
self.use_ipv6 = use_ipv6
self.root_helper = root_helper
+ self.namespace = namespace
self.ipv4 = {'filter': IptablesTable()}
self.ipv6 = {'filter': IptablesTable()}
for cmd, tables in s:
for table in tables:
- current_table = (self.execute(['%s-save' % cmd, '-t', table],
+ args = ['%s-save' % cmd, '-t', table]
+ if self.namespace:
+ args = ['ip', 'netns', 'exec', self.namespace] + args
+ current_table = (self.execute(args,
root_helper=self.root_helper))
current_lines = current_table.split('\n')
new_filter = self._modify_rules(current_lines,
tables[table])
- self.execute(['%s-restore' % (cmd)],
+ args = ['%s-restore' % (cmd)]
+ if self.namespace:
+ args = ['ip', 'netns', 'exec', self.namespace] + args
+ self.execute(args,
process_input='\n'.join(new_filter),
root_helper=self.root_helper)
LOG.debug(("IPTablesManager.apply completed with success"))
def _build_flow_expr_arr(self, **kwargs):
flow_expr_arr = []
is_delete_expr = kwargs.get('delete', False)
- print "kwargs = %s" % kwargs
+
if not is_delete_expr:
prefix = ("hard_timeout=%s,idle_timeout=%s,priority=%s" %
(kwargs.get('hard_timeout', '0'),
return str(self.__dict__)
+class FakePortModel(FakeModel):
+ fixed_ips = []
+
+
+class FakeFixedIPModel(object):
+
+ def __init__(self, ip_address, cidr):
+ self.subnet = FakeSubnetModel(cidr)
+ self.ip_address = ip_address
+
+
+class FakeSubnetModel(object):
+
+ def __init__(self, cidr):
+ self.cidr = cidr
+
+
class TestDhcpAgent(unittest.TestCase):
def setUp(self):
self.conf = config.setup_conf()
self.client_cls_p.stop()
def test_setup(self):
+ port_id = '12345678-1234-aaaa-1234567890ab'
+ network_id = '12345678-1234-5678-1234567890ab'
fake_subnets = [FakeModel('12345678-aaaa-aaaa-1234567890ab'),
FakeModel('12345678-bbbb-bbbb-1234567890ab')]
- fake_network = FakeModel('12345678-1234-5678-1234567890ab',
+ fake_network = FakeModel(network_id,
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
subnets=fake_subnets)
- fake_port = FakeModel('12345678-1234-aaaa-1234567890ab',
- mac_address='aa:bb:cc:dd:ee:ff')
-
- port_dict = dict(mac_address='aa:bb:cc:dd:ee:ff', allocations=[], id=1)
+ fake_port = FakePortModel(port_id, mac_address='aa:bb:cc:dd:ee:ff',
+ network_id=network_id,
+ allocations=[])
+ fake_port.fixed_ips.append(FakeFixedIPModel('172.9.9.9',
+ '172.9.9.0/24'))
+ port_dict = dict(mac_address='aa:bb:cc:dd:ee:ff',
+ allocations=[], id=1)
self.client_inst.create_port.return_value = dict(port=port_dict)
self.device_exists.return_value = False
mock.call.create_port(mock.ANY)])
self.mock_driver.assert_has_calls([
- mock.call.plug('12345678-1234-5678-1234567890ab',
- '12345678-1234-aaaa-1234567890ab',
+ mock.call.get_device_name(mock.ANY),
+ mock.call.plug(network_id,
+ port_id,
'tap12345678-12',
- 'aa:bb:cc:dd:ee:ff'),
- mock.call.init_l3(mock.ANY, 'tap12345678-12')]
+ 'aa:bb:cc:dd:ee:ff',
+ namespace=network_id),
+ mock.call.init_l3('tap12345678-12', ['172.9.9.9/24'],
+ namespace=network_id)]
)
def test_destroy(self):
self.ip_dev().addr.list = mock.Mock(return_value=addresses)
bc = BaseChild(self.conf)
- bc.init_l3(FakePort(), 'tap0')
+ ns = '12345678-1234-5678-90ab-ba0987654321'
+ bc.init_l3('tap0', ['192.168.1.2/24'], namespace=ns)
self.ip_dev.assert_has_calls(
- [mock.call('tap0', 'sudo', '12345678-1234-5678-90ab-ba0987654321'),
+ [mock.call('tap0', 'sudo', namespace=ns),
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')])
class TestOVSInterfaceDriver(TestBase):
- def test_plug(self, additional_expectation=[]):
+
+ def test_plug_no_ns(self):
+ self._test_plug()
+
+ def test_plug_with_ns(self):
+ self._test_plug(namespace='01234567-1234-1234-99')
+
+ def test_plug_alt_bridge(self):
+ self._test_plug(bridge='br-foo')
+
+ def _test_plug(self, additional_expectation=[], bridge=None,
+ namespace=None):
+
+ if not bridge:
+ bridge = 'br-int'
+
def device_exists(dev, root_helper=None, namespace=None):
- return dev == 'br-int'
+ return dev == bridge
vsctl_cmd = ['ovs-vsctl', '--', '--may-exist', 'add-port',
- 'br-int', 'tap0', '--', 'set', 'Interface', 'tap0',
+ bridge, 'tap0', '--', 'set', 'Interface', 'tap0',
'type=internal', '--', 'set', 'Interface', 'tap0',
'external-ids:iface-id=port-1234', '--', 'set',
'Interface', 'tap0',
ovs.plug('01234567-1234-1234-99',
'port-1234',
'tap0',
- 'aa:bb:cc:dd:ee:ff')
+ 'aa:bb:cc:dd:ee:ff',
+ bridge=bridge,
+ namespace=namespace)
execute.assert_called_once_with(vsctl_cmd, 'sudo')
expected = [mock.call('sudo'),
mock.call().device('tap0'),
mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')]
expected.extend(additional_expectation)
- 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()])
+ if namespace:
+ expected.extend(
+ [mock.call().ensure_namespace(namespace),
+ mock.call().ensure_namespace().add_device_to_namespace(
+ mock.ANY)])
+ expected.extend([mock.call().device().link.set_up()])
self.ip.assert_has_calls(expected)
def test_plug_mtu(self):
self.conf.set_override('network_device_mtu', 9000)
- self.test_plug([mock.call().device().link.set_mtu(9000)])
+ self._test_plug([mock.call().device().link.set_mtu(9000)])
- def test_unplug(self):
+ def test_unplug(self, bridge=None):
+ if not bridge:
+ bridge = 'br-int'
with mock.patch('quantum.agent.linux.ovs_lib.OVSBridge') as ovs_br:
ovs = interface.OVSInterfaceDriver(self.conf)
ovs.unplug('tap0')
- ovs_br.assert_has_calls([mock.call('br-int', 'sudo'),
+ ovs_br.assert_has_calls([mock.call(bridge, 'sudo'),
mock.call().delete_port('tap0')])
device_name = br.get_device_name(FakePort())
self.assertEqual('dhcabcdef01-12', device_name)
- def test_plug(self):
+ def test_plug_no_ns(self):
+ self._test_plug()
+
+ def test_plug_with_ns(self):
+ self._test_plug(namespace='01234567-1234-1234-99')
+
+ def _test_plug(self, namespace=None):
def device_exists(device, root_helper=None, namespace=None):
return device.startswith('brq')
br.plug('01234567-1234-1234-99',
'port-1234',
'dhc0',
- 'aa:bb:cc:dd:ee:ff')
+ 'aa:bb:cc:dd:ee:ff',
+ namespace=namespace)
+
+ ip_calls = [mock.call('sudo'), mock.call().add_veth('tap0', 'dhc0')]
+ if namespace:
+ ip_calls.extend([
+ mock.call().ensure_namespace('01234567-1234-1234-99'),
+ mock.call().ensure_namespace().add_device_to_namespace(
+ ns_veth)])
- self.ip.assert_has_calls(
- [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(ns_veth)])
+ self.ip.assert_has_calls(ip_calls)
root_veth.assert_has_calls([mock.call.link.set_up()])
ns_veth.assert_has_calls([mock.call.link.set_up()])
self.ryu_mod_p.stop()
super(TestRyuInterfaceDriver, self).tearDown()
- @staticmethod
- def _device_exists(dev, root_helper=None, namespace=None):
- return dev == 'br-int'
+ def test_plug_no_ns(self):
+ self._test_plug()
- _vsctl_cmd_init = ['ovs-vsctl', '--timeout=2',
- 'get', 'Bridge', 'br-int', 'datapath_id']
+ def test_plug_with_ns(self):
+ self._test_plug(namespace='01234567-1234-1234-99')
- def test_init(self):
- with mock.patch.object(utils, 'execute') as execute:
- self.device_exists.side_effect = self._device_exists
- interface.RyuInterfaceDriver(self.conf)
- execute.assert_called_once_with(self._vsctl_cmd_init,
- root_helper='sudo')
+ def test_plug_alt_bridge(self):
+ self._test_plug(bridge='br-foo')
- self.ryu_app_client.OFPClient.assert_called_once_with('127.0.0.1:8080')
+ def _test_plug(self, namespace=None, bridge=None):
+ if not bridge:
+ bridge = 'br-int'
+
+ def _device_exists(dev, root_helper=None, namespace=None):
+ return dev == bridge
- def test_plug(self):
vsctl_cmd_plug = ['ovs-vsctl', '--', '--may-exist', 'add-port',
- 'br-int', 'tap0', '--', 'set', 'Interface', 'tap0',
+ bridge, 'tap0', '--', 'set', 'Interface', 'tap0',
'type=internal', '--', 'set', 'Interface', 'tap0',
'external-ids:iface-id=port-1234', '--', 'set',
'Interface', 'tap0',
'external-ids:attached-mac=aa:bb:cc:dd:ee:ff']
vsctl_cmd_ofport = ['ovs-vsctl', '--timeout=2',
'get', 'Interface', 'tap0', 'ofport']
+ vsctl_cmd_dp = ['ovs-vsctl', '--timeout=2',
+ 'get', 'Bridge', bridge, 'datapath_id']
with mock.patch.object(utils, 'execute') as execute:
- self.device_exists.side_effect = self._device_exists
+ self.device_exists.side_effect = _device_exists
ryu = interface.RyuInterfaceDriver(self.conf)
ryu.plug('01234567-1234-1234-99',
'port-1234',
'tap0',
- 'aa:bb:cc:dd:ee:ff')
+ 'aa:bb:cc:dd:ee:ff',
+ bridge=bridge,
+ namespace=namespace)
- execute.assert_has_calls([mock.call(self._vsctl_cmd_init,
+ execute.assert_has_calls([mock.call(vsctl_cmd_dp,
root_helper='sudo')])
execute.assert_has_calls([mock.call(vsctl_cmd_plug, 'sudo')])
execute.assert_has_calls([mock.call(vsctl_cmd_ofport,
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()]
+ mock.call().device().link.set_address('aa:bb:cc:dd:ee:ff')]
+ if namespace:
+ expected.extend([
+ mock.call().ensure_namespace(namespace),
+ mock.call().ensure_namespace().add_device_to_namespace(
+ mock.ANY)])
+ expected.extend([mock.call().device().link.set_up()])
self.ip.assert_has_calls(expected)
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
'cccccccc-cccc-cccc-cccc-cccccccccccc'])
- self.execute.assert_called_once_with('netns', ('list',),
+ self.execute.assert_called_once_with('', 'netns', ('list',),
root_helper='sudo')
def test_add_tuntap(self):