Merge "Linux Agent improvements for L3"
This commit is contained in:
commit
636648d037
@ -22,6 +22,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
import netaddr
|
||||||
from sqlalchemy.ext import sqlsoup
|
from sqlalchemy.ext import sqlsoup
|
||||||
|
|
||||||
from quantum.agent.common import config
|
from quantum.agent.common import config
|
||||||
@ -243,8 +244,17 @@ class DeviceManager(object):
|
|||||||
self.driver.plug(network.id,
|
self.driver.plug(network.id,
|
||||||
port.id,
|
port.id,
|
||||||
interface_name,
|
interface_name,
|
||||||
port.mac_address)
|
port.mac_address,
|
||||||
self.driver.init_l3(port, interface_name)
|
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):
|
def destroy(self, network):
|
||||||
self.driver.unplug(self.get_interface_name(network))
|
self.driver.unplug(self.get_interface_name(network))
|
||||||
|
@ -50,22 +50,21 @@ class LinuxInterfaceDriver(object):
|
|||||||
def __init__(self, conf):
|
def __init__(self, conf):
|
||||||
self.conf = conf
|
self.conf = conf
|
||||||
|
|
||||||
def init_l3(self, port, device_name):
|
def init_l3(self, device_name, ip_cidrs, namespace=None):
|
||||||
"""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,
|
ip_cidrs: list of 'X.X.X.X/YY' strings
|
||||||
self.conf.root_helper,
|
"""
|
||||||
port.network.id)
|
device = ip_lib.IPDevice(device_name, self.conf.root_helper,
|
||||||
|
namespace=namespace)
|
||||||
|
|
||||||
previous = {}
|
previous = {}
|
||||||
for address in device.addr.list(scope='global', filters=['permanent']):
|
for address in device.addr.list(scope='global', filters=['permanent']):
|
||||||
previous[address['cidr']] = address['ip_version']
|
previous[address['cidr']] = address['ip_version']
|
||||||
|
|
||||||
# add new addresses
|
# add new addresses
|
||||||
for fixed_ip in port.fixed_ips:
|
for ip_cidr in ip_cidrs:
|
||||||
subnet = fixed_ip.subnet
|
|
||||||
net = netaddr.IPNetwork(subnet.cidr)
|
|
||||||
ip_cidr = '%s/%s' % (fixed_ip.ip_address, net.prefixlen)
|
|
||||||
|
|
||||||
|
net = netaddr.IPNetwork(ip_cidr)
|
||||||
if ip_cidr in previous:
|
if ip_cidr in previous:
|
||||||
del previous[ip_cidr]
|
del previous[ip_cidr]
|
||||||
continue
|
continue
|
||||||
@ -78,40 +77,44 @@ class LinuxInterfaceDriver(object):
|
|||||||
|
|
||||||
def check_bridge_exists(self, bridge):
|
def check_bridge_exists(self, bridge):
|
||||||
if not ip_lib.device_exists(bridge):
|
if not ip_lib.device_exists(bridge):
|
||||||
raise exception.BridgeDoesNotExist(bridge=bridge)
|
raise exceptions.BridgeDoesNotExist(bridge=bridge)
|
||||||
|
|
||||||
def get_device_name(self, port):
|
def get_device_name(self, port):
|
||||||
return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
|
return (self.DEV_NAME_PREFIX + port.id)[:self.DEV_NAME_LEN]
|
||||||
|
|
||||||
@abc.abstractmethod
|
@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."""
|
"""Plug in the interface."""
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def unplug(self, device_name):
|
def unplug(self, device_name, bridge=None):
|
||||||
"""Unplug the interface."""
|
"""Unplug the interface."""
|
||||||
|
|
||||||
|
|
||||||
class NullDriver(LinuxInterfaceDriver):
|
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
|
pass
|
||||||
|
|
||||||
def unplug(self, device_name):
|
def unplug(self, device_name, bridge=None):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class OVSInterfaceDriver(LinuxInterfaceDriver):
|
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."""
|
"""Plug in the interface."""
|
||||||
bridge = self.conf.ovs_integration_bridge
|
if not bridge:
|
||||||
|
bridge = self.conf.ovs_integration_bridge
|
||||||
|
|
||||||
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,
|
self.conf.root_helper,
|
||||||
namespace=network_id):
|
namespace=namespace):
|
||||||
|
|
||||||
utils.execute(['ovs-vsctl',
|
utils.execute(['ovs-vsctl',
|
||||||
'--', '--may-exist', 'add-port', bridge,
|
'--', '--may-exist', 'add-port', bridge,
|
||||||
@ -127,24 +130,24 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
mac_address],
|
mac_address],
|
||||||
self.conf.root_helper)
|
self.conf.root_helper)
|
||||||
|
|
||||||
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
ip = ip_lib.IPWrapper(self.conf.root_helper)
|
||||||
device = ip.device(device_name)
|
device = ip.device(device_name)
|
||||||
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)
|
if namespace:
|
||||||
namespace.add_device_to_namespace(device)
|
namespace_obj = ip.ensure_namespace(namespace)
|
||||||
device.link.set_up()
|
namespace_obj.add_device_to_namespace(device)
|
||||||
else:
|
device.link.set_up()
|
||||||
LOG.error(_('Device %s already exists') % device)
|
|
||||||
|
|
||||||
def unplug(self, device_name):
|
def unplug(self, device_name, bridge=None):
|
||||||
"""Unplug the interface."""
|
"""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)
|
self.check_bridge_exists(bridge)
|
||||||
bridge = ovs_lib.OVSBridge(bridge_name, self.conf.root_helper)
|
bridge = ovs_lib.OVSBridge(bridge, self.conf.root_helper)
|
||||||
bridge.delete_port(device_name)
|
bridge.delete_port(device_name)
|
||||||
|
|
||||||
|
|
||||||
@ -153,19 +156,21 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
|
|
||||||
DEV_NAME_PREFIX = 'dhc'
|
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."""
|
"""Plugin the interface."""
|
||||||
if not ip_lib.device_exists(device_name,
|
if not ip_lib.device_exists(device_name,
|
||||||
self.conf.root_helper,
|
self.conf.root_helper,
|
||||||
namespace=network_id):
|
namespace=namespace):
|
||||||
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)
|
if namespace:
|
||||||
namespace.add_device_to_namespace(dhcp_veth)
|
namespace_obj = ip.ensure_namespace(namespace)
|
||||||
|
namespace_obj.add_device_to_namespace(dhcp_veth)
|
||||||
|
|
||||||
root_veth.link.set_up()
|
root_veth.link.set_up()
|
||||||
dhcp_veth.link.set_up()
|
dhcp_veth.link.set_up()
|
||||||
@ -173,7 +178,7 @@ class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
else:
|
else:
|
||||||
LOG.warn(_("Device %s already exists") % device_name)
|
LOG.warn(_("Device %s already exists") % device_name)
|
||||||
|
|
||||||
def unplug(self, device_name):
|
def unplug(self, device_name, bridge=None):
|
||||||
"""Unplug the interface."""
|
"""Unplug the interface."""
|
||||||
device = ip_lib.IPDevice(device_name, self.conf.root_helper)
|
device = ip_lib.IPDevice(device_name, self.conf.root_helper)
|
||||||
try:
|
try:
|
||||||
@ -194,15 +199,17 @@ class RyuInterfaceDriver(OVSInterfaceDriver):
|
|||||||
LOG.debug('ryu rest host %s', self.conf.ryu_api_host)
|
LOG.debug('ryu rest host %s', self.conf.ryu_api_host)
|
||||||
self.ryu_client = OFPClient(self.conf.ryu_api_host)
|
self.ryu_client = OFPClient(self.conf.ryu_api_host)
|
||||||
|
|
||||||
self.check_bridge_exists(self.conf.ovs_integration_bridge)
|
def plug(self, network_id, port_id, device_name, mac_address,
|
||||||
self.ovs_br = ovs_lib.OVSBridge(self.conf.ovs_integration_bridge,
|
bridge=None, namespace=None):
|
||||||
self.conf.root_helper)
|
|
||||||
self.datapath_id = self.ovs_br.get_datapath_id()
|
|
||||||
|
|
||||||
def plug(self, network_id, port_id, device_name, mac_address):
|
|
||||||
"""Plug in the interface."""
|
"""Plug in the interface."""
|
||||||
super(RyuInterfaceDriver, self).plug(network_id, port_id, device_name,
|
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.check_bridge_exists(bridge)
|
||||||
self.ryu_client.create_port(network_id, self.datapath_id, port_no)
|
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)
|
||||||
|
@ -99,7 +99,7 @@ class IPWrapper(SubProcessBase):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_namespaces(cls, root_helper):
|
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')]
|
return [l.strip() for l in output.split('\n')]
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ class IptablesManager(object):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, _execute=None, state_less=False,
|
def __init__(self, _execute=None, state_less=False,
|
||||||
root_helper=None, use_ipv6=False):
|
root_helper=None, use_ipv6=False, namespace=None):
|
||||||
if _execute:
|
if _execute:
|
||||||
self.execute = _execute
|
self.execute = _execute
|
||||||
else:
|
else:
|
||||||
@ -202,6 +202,7 @@ class IptablesManager(object):
|
|||||||
|
|
||||||
self.use_ipv6 = use_ipv6
|
self.use_ipv6 = use_ipv6
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
|
self.namespace = namespace
|
||||||
|
|
||||||
self.ipv4 = {'filter': IptablesTable()}
|
self.ipv4 = {'filter': IptablesTable()}
|
||||||
self.ipv6 = {'filter': IptablesTable()}
|
self.ipv6 = {'filter': IptablesTable()}
|
||||||
@ -276,12 +277,18 @@ class IptablesManager(object):
|
|||||||
|
|
||||||
for cmd, tables in s:
|
for cmd, tables in s:
|
||||||
for table in tables:
|
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))
|
root_helper=self.root_helper))
|
||||||
current_lines = current_table.split('\n')
|
current_lines = current_table.split('\n')
|
||||||
new_filter = self._modify_rules(current_lines,
|
new_filter = self._modify_rules(current_lines,
|
||||||
tables[table])
|
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),
|
process_input='\n'.join(new_filter),
|
||||||
root_helper=self.root_helper)
|
root_helper=self.root_helper)
|
||||||
LOG.debug(("IPTablesManager.apply completed with success"))
|
LOG.debug(("IPTablesManager.apply completed with success"))
|
||||||
|
@ -89,7 +89,7 @@ class OVSBridge:
|
|||||||
def _build_flow_expr_arr(self, **kwargs):
|
def _build_flow_expr_arr(self, **kwargs):
|
||||||
flow_expr_arr = []
|
flow_expr_arr = []
|
||||||
is_delete_expr = kwargs.get('delete', False)
|
is_delete_expr = kwargs.get('delete', False)
|
||||||
print "kwargs = %s" % kwargs
|
|
||||||
if not is_delete_expr:
|
if not is_delete_expr:
|
||||||
prefix = ("hard_timeout=%s,idle_timeout=%s,priority=%s" %
|
prefix = ("hard_timeout=%s,idle_timeout=%s,priority=%s" %
|
||||||
(kwargs.get('hard_timeout', '0'),
|
(kwargs.get('hard_timeout', '0'),
|
||||||
|
@ -34,6 +34,23 @@ class FakeModel:
|
|||||||
return str(self.__dict__)
|
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):
|
class TestDhcpAgent(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.conf = config.setup_conf()
|
self.conf = config.setup_conf()
|
||||||
@ -384,17 +401,22 @@ class TestDeviceManager(unittest.TestCase):
|
|||||||
self.client_cls_p.stop()
|
self.client_cls_p.stop()
|
||||||
|
|
||||||
def test_setup(self):
|
def test_setup(self):
|
||||||
|
port_id = '12345678-1234-aaaa-1234567890ab'
|
||||||
|
network_id = '12345678-1234-5678-1234567890ab'
|
||||||
fake_subnets = [FakeModel('12345678-aaaa-aaaa-1234567890ab'),
|
fake_subnets = [FakeModel('12345678-aaaa-aaaa-1234567890ab'),
|
||||||
FakeModel('12345678-bbbb-bbbb-1234567890ab')]
|
FakeModel('12345678-bbbb-bbbb-1234567890ab')]
|
||||||
|
|
||||||
fake_network = FakeModel('12345678-1234-5678-1234567890ab',
|
fake_network = FakeModel(network_id,
|
||||||
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
tenant_id='aaaaaaaa-aaaa-aaaa-aaaaaaaaaaaa',
|
||||||
subnets=fake_subnets)
|
subnets=fake_subnets)
|
||||||
|
|
||||||
fake_port = FakeModel('12345678-1234-aaaa-1234567890ab',
|
fake_port = FakePortModel(port_id, mac_address='aa:bb:cc:dd:ee:ff',
|
||||||
mac_address='aa:bb:cc:dd:ee:ff')
|
network_id=network_id,
|
||||||
|
allocations=[])
|
||||||
port_dict = dict(mac_address='aa:bb:cc:dd:ee:ff', allocations=[], id=1)
|
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.client_inst.create_port.return_value = dict(port=port_dict)
|
||||||
self.device_exists.return_value = False
|
self.device_exists.return_value = False
|
||||||
@ -427,11 +449,14 @@ class TestDeviceManager(unittest.TestCase):
|
|||||||
mock.call.create_port(mock.ANY)])
|
mock.call.create_port(mock.ANY)])
|
||||||
|
|
||||||
self.mock_driver.assert_has_calls([
|
self.mock_driver.assert_has_calls([
|
||||||
mock.call.plug('12345678-1234-5678-1234567890ab',
|
mock.call.get_device_name(mock.ANY),
|
||||||
'12345678-1234-aaaa-1234567890ab',
|
mock.call.plug(network_id,
|
||||||
|
port_id,
|
||||||
'tap12345678-12',
|
'tap12345678-12',
|
||||||
'aa:bb:cc:dd:ee:ff'),
|
'aa:bb:cc:dd:ee:ff',
|
||||||
mock.call.init_l3(mock.ANY, 'tap12345678-12')]
|
namespace=network_id),
|
||||||
|
mock.call.init_l3('tap12345678-12', ['172.9.9.9/24'],
|
||||||
|
namespace=network_id)]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_destroy(self):
|
def test_destroy(self):
|
||||||
|
@ -92,21 +92,37 @@ class TestABCDriver(TestBase):
|
|||||||
self.ip_dev().addr.list = mock.Mock(return_value=addresses)
|
self.ip_dev().addr.list = mock.Mock(return_value=addresses)
|
||||||
|
|
||||||
bc = BaseChild(self.conf)
|
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(
|
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.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')])
|
||||||
|
|
||||||
|
|
||||||
class TestOVSInterfaceDriver(TestBase):
|
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):
|
def device_exists(dev, root_helper=None, namespace=None):
|
||||||
return dev == 'br-int'
|
return dev == bridge
|
||||||
|
|
||||||
vsctl_cmd = ['ovs-vsctl', '--', '--may-exist', 'add-port',
|
vsctl_cmd = ['ovs-vsctl', '--', '--may-exist', 'add-port',
|
||||||
'br-int', 'tap0', '--', 'set', 'Interface', 'tap0',
|
bridge, 'tap0', '--', 'set', 'Interface', 'tap0',
|
||||||
'type=internal', '--', 'set', 'Interface', 'tap0',
|
'type=internal', '--', 'set', 'Interface', 'tap0',
|
||||||
'external-ids:iface-id=port-1234', '--', 'set',
|
'external-ids:iface-id=port-1234', '--', 'set',
|
||||||
'Interface', 'tap0',
|
'Interface', 'tap0',
|
||||||
@ -120,29 +136,35 @@ class TestOVSInterfaceDriver(TestBase):
|
|||||||
ovs.plug('01234567-1234-1234-99',
|
ovs.plug('01234567-1234-1234-99',
|
||||||
'port-1234',
|
'port-1234',
|
||||||
'tap0',
|
'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')
|
execute.assert_called_once_with(vsctl_cmd, 'sudo')
|
||||||
|
|
||||||
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')]
|
||||||
expected.extend(additional_expectation)
|
expected.extend(additional_expectation)
|
||||||
expected.extend(
|
if namespace:
|
||||||
[mock.call().ensure_namespace('01234567-1234-1234-99'),
|
expected.extend(
|
||||||
mock.call().ensure_namespace().add_device_to_namespace(mock.ANY),
|
[mock.call().ensure_namespace(namespace),
|
||||||
mock.call().device().link.set_up()])
|
mock.call().ensure_namespace().add_device_to_namespace(
|
||||||
|
mock.ANY)])
|
||||||
|
expected.extend([mock.call().device().link.set_up()])
|
||||||
|
|
||||||
self.ip.assert_has_calls(expected)
|
self.ip.assert_has_calls(expected)
|
||||||
|
|
||||||
def test_plug_mtu(self):
|
def test_plug_mtu(self):
|
||||||
self.conf.set_override('network_device_mtu', 9000)
|
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:
|
with mock.patch('quantum.agent.linux.ovs_lib.OVSBridge') as ovs_br:
|
||||||
ovs = interface.OVSInterfaceDriver(self.conf)
|
ovs = interface.OVSInterfaceDriver(self.conf)
|
||||||
ovs.unplug('tap0')
|
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')])
|
mock.call().delete_port('tap0')])
|
||||||
|
|
||||||
|
|
||||||
@ -152,7 +174,13 @@ class TestBridgeInterfaceDriver(TestBase):
|
|||||||
device_name = br.get_device_name(FakePort())
|
device_name = br.get_device_name(FakePort())
|
||||||
self.assertEqual('dhcabcdef01-12', device_name)
|
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):
|
def device_exists(device, root_helper=None, namespace=None):
|
||||||
return device.startswith('brq')
|
return device.startswith('brq')
|
||||||
|
|
||||||
@ -172,13 +200,17 @@ class TestBridgeInterfaceDriver(TestBase):
|
|||||||
br.plug('01234567-1234-1234-99',
|
br.plug('01234567-1234-1234-99',
|
||||||
'port-1234',
|
'port-1234',
|
||||||
'dhc0',
|
'dhc0',
|
||||||
'aa:bb:cc:dd:ee:ff')
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
namespace=namespace)
|
||||||
|
|
||||||
self.ip.assert_has_calls(
|
ip_calls = [mock.call('sudo'), mock.call().add_veth('tap0', 'dhc0')]
|
||||||
[mock.call('sudo'),
|
if namespace:
|
||||||
mock.call().add_veth('tap0', 'dhc0'),
|
ip_calls.extend([
|
||||||
mock.call().ensure_namespace('01234567-1234-1234-99'),
|
mock.call().ensure_namespace('01234567-1234-1234-99'),
|
||||||
mock.call().ensure_namespace().add_device_to_namespace(ns_veth)])
|
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()])
|
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()])
|
||||||
@ -240,25 +272,24 @@ class TestRyuInterfaceDriver(TestBase):
|
|||||||
self.ryu_mod_p.stop()
|
self.ryu_mod_p.stop()
|
||||||
super(TestRyuInterfaceDriver, self).tearDown()
|
super(TestRyuInterfaceDriver, self).tearDown()
|
||||||
|
|
||||||
@staticmethod
|
def test_plug_no_ns(self):
|
||||||
def _device_exists(dev, root_helper=None, namespace=None):
|
self._test_plug()
|
||||||
return dev == 'br-int'
|
|
||||||
|
|
||||||
_vsctl_cmd_init = ['ovs-vsctl', '--timeout=2',
|
def test_plug_with_ns(self):
|
||||||
'get', 'Bridge', 'br-int', 'datapath_id']
|
self._test_plug(namespace='01234567-1234-1234-99')
|
||||||
|
|
||||||
def test_init(self):
|
def test_plug_alt_bridge(self):
|
||||||
with mock.patch.object(utils, 'execute') as execute:
|
self._test_plug(bridge='br-foo')
|
||||||
self.device_exists.side_effect = self._device_exists
|
|
||||||
interface.RyuInterfaceDriver(self.conf)
|
|
||||||
execute.assert_called_once_with(self._vsctl_cmd_init,
|
|
||||||
root_helper='sudo')
|
|
||||||
|
|
||||||
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',
|
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',
|
'type=internal', '--', 'set', 'Interface', 'tap0',
|
||||||
'external-ids:iface-id=port-1234', '--', 'set',
|
'external-ids:iface-id=port-1234', '--', 'set',
|
||||||
'Interface', 'tap0',
|
'Interface', 'tap0',
|
||||||
@ -267,17 +298,21 @@ class TestRyuInterfaceDriver(TestBase):
|
|||||||
'external-ids:attached-mac=aa:bb:cc:dd:ee:ff']
|
'external-ids:attached-mac=aa:bb:cc:dd:ee:ff']
|
||||||
vsctl_cmd_ofport = ['ovs-vsctl', '--timeout=2',
|
vsctl_cmd_ofport = ['ovs-vsctl', '--timeout=2',
|
||||||
'get', 'Interface', 'tap0', 'ofport']
|
'get', 'Interface', 'tap0', 'ofport']
|
||||||
|
vsctl_cmd_dp = ['ovs-vsctl', '--timeout=2',
|
||||||
|
'get', 'Bridge', bridge, 'datapath_id']
|
||||||
|
|
||||||
with mock.patch.object(utils, 'execute') as execute:
|
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 = interface.RyuInterfaceDriver(self.conf)
|
||||||
|
|
||||||
ryu.plug('01234567-1234-1234-99',
|
ryu.plug('01234567-1234-1234-99',
|
||||||
'port-1234',
|
'port-1234',
|
||||||
'tap0',
|
'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')])
|
root_helper='sudo')])
|
||||||
execute.assert_has_calls([mock.call(vsctl_cmd_plug, 'sudo')])
|
execute.assert_has_calls([mock.call(vsctl_cmd_plug, 'sudo')])
|
||||||
execute.assert_has_calls([mock.call(vsctl_cmd_ofport,
|
execute.assert_has_calls([mock.call(vsctl_cmd_ofport,
|
||||||
@ -288,9 +323,12 @@ class TestRyuInterfaceDriver(TestBase):
|
|||||||
expected = [
|
expected = [
|
||||||
mock.call('sudo'),
|
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'),
|
if namespace:
|
||||||
mock.call().ensure_namespace().add_device_to_namespace(mock.ANY),
|
expected.extend([
|
||||||
mock.call().device().link.set_up()]
|
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)
|
self.ip.assert_has_calls(expected)
|
||||||
|
@ -149,7 +149,7 @@ class TestIpWrapper(unittest.TestCase):
|
|||||||
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
'bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb',
|
||||||
'cccccccc-cccc-cccc-cccc-cccccccccccc'])
|
'cccccccc-cccc-cccc-cccc-cccccccccccc'])
|
||||||
|
|
||||||
self.execute.assert_called_once_with('netns', ('list',),
|
self.execute.assert_called_once_with('', 'netns', ('list',),
|
||||||
root_helper='sudo')
|
root_helper='sudo')
|
||||||
|
|
||||||
def test_add_tuntap(self):
|
def test_add_tuntap(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user