Merge "Adds support for the Indigo Virtual Switch (IVS)"
This commit is contained in:
commit
a8c7ab6a3c
@ -40,3 +40,9 @@ servers=localhost:8080
|
|||||||
#server_ssl=True
|
#server_ssl=True
|
||||||
#sync_data=True
|
#sync_data=True
|
||||||
#server_timeout=10
|
#server_timeout=10
|
||||||
|
|
||||||
|
[NOVA]
|
||||||
|
# Specify the VIF_TYPE that will be controlled on the Nova compute instances
|
||||||
|
# options: ivs or ovs
|
||||||
|
# default: ovs
|
||||||
|
# vif_type = ovs
|
||||||
|
@ -21,6 +21,7 @@ kill_dnsmasq_usr: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
|
|||||||
# dhcp-agent uses cat
|
# dhcp-agent uses cat
|
||||||
cat: RegExpFilter, cat, root, cat, /proc/\d+/cmdline
|
cat: RegExpFilter, cat, root, cat, /proc/\d+/cmdline
|
||||||
ovs-vsctl: CommandFilter, ovs-vsctl, root
|
ovs-vsctl: CommandFilter, ovs-vsctl, root
|
||||||
|
ivs-ctl: CommandFilter, ivs-ctl, root
|
||||||
|
|
||||||
# metadata proxy
|
# metadata proxy
|
||||||
metadata_proxy: CommandFilter, quantum-ns-metadata-proxy, root
|
metadata_proxy: CommandFilter, quantum-ns-metadata-proxy, root
|
||||||
|
@ -219,6 +219,69 @@ class OVSInterfaceDriver(LinuxInterfaceDriver):
|
|||||||
device_name)
|
device_name)
|
||||||
|
|
||||||
|
|
||||||
|
class IVSInterfaceDriver(LinuxInterfaceDriver):
|
||||||
|
"""Driver for creating an internal interface on an IVS bridge."""
|
||||||
|
|
||||||
|
DEV_NAME_PREFIX = 'tap'
|
||||||
|
|
||||||
|
def __init__(self, conf):
|
||||||
|
super(IVSInterfaceDriver, self).__init__(conf)
|
||||||
|
self.DEV_NAME_PREFIX = 'ns-'
|
||||||
|
|
||||||
|
def _get_tap_name(self, dev_name, prefix=None):
|
||||||
|
dev_name = dev_name.replace(prefix or self.DEV_NAME_PREFIX, 'tap')
|
||||||
|
return dev_name
|
||||||
|
|
||||||
|
def _ivs_add_port(self, device_name, port_id, mac_address):
|
||||||
|
cmd = ['ivs-ctl', 'add-port', device_name]
|
||||||
|
utils.execute(cmd, self.root_helper)
|
||||||
|
|
||||||
|
def plug(self, network_id, port_id, device_name, mac_address,
|
||||||
|
bridge=None, namespace=None, prefix=None):
|
||||||
|
"""Plug in the interface."""
|
||||||
|
if not ip_lib.device_exists(device_name,
|
||||||
|
self.root_helper,
|
||||||
|
namespace=namespace):
|
||||||
|
|
||||||
|
ip = ip_lib.IPWrapper(self.root_helper)
|
||||||
|
tap_name = self._get_tap_name(device_name, prefix)
|
||||||
|
|
||||||
|
root_dev, ns_dev = ip.add_veth(tap_name, device_name)
|
||||||
|
|
||||||
|
self._ivs_add_port(tap_name, port_id, mac_address)
|
||||||
|
|
||||||
|
ns_dev = ip.device(device_name)
|
||||||
|
ns_dev.link.set_address(mac_address)
|
||||||
|
|
||||||
|
if self.conf.network_device_mtu:
|
||||||
|
ns_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||||
|
root_dev.link.set_mtu(self.conf.network_device_mtu)
|
||||||
|
|
||||||
|
if namespace:
|
||||||
|
namespace_obj = ip.ensure_namespace(namespace)
|
||||||
|
namespace_obj.add_device_to_namespace(ns_dev)
|
||||||
|
|
||||||
|
ns_dev.link.set_up()
|
||||||
|
root_dev.link.set_up()
|
||||||
|
else:
|
||||||
|
LOG.warn(_("Device %s already exists"), device_name)
|
||||||
|
|
||||||
|
def unplug(self, device_name, bridge=None, namespace=None, prefix=None):
|
||||||
|
"""Unplug the interface."""
|
||||||
|
tap_name = self._get_tap_name(device_name, prefix)
|
||||||
|
try:
|
||||||
|
cmd = ['ivs-ctl', 'del-port', tap_name]
|
||||||
|
utils.execute(cmd, self.root_helper)
|
||||||
|
device = ip_lib.IPDevice(device_name,
|
||||||
|
self.root_helper,
|
||||||
|
namespace)
|
||||||
|
device.link.delete()
|
||||||
|
LOG.debug(_("Unplugged interface '%s'"), device_name)
|
||||||
|
except RuntimeError:
|
||||||
|
LOG.error(_("Failed unplugging interface '%s'"),
|
||||||
|
device_name)
|
||||||
|
|
||||||
|
|
||||||
class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
class BridgeInterfaceDriver(LinuxInterfaceDriver):
|
||||||
"""Driver for creating bridge interfaces."""
|
"""Driver for creating bridge interfaces."""
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ CAP_PORT_FILTER = 'port_filter'
|
|||||||
VIF_TYPE_UNBOUND = 'unbound'
|
VIF_TYPE_UNBOUND = 'unbound'
|
||||||
VIF_TYPE_BINDING_FAILED = 'binding_failed'
|
VIF_TYPE_BINDING_FAILED = 'binding_failed'
|
||||||
VIF_TYPE_OVS = 'ovs'
|
VIF_TYPE_OVS = 'ovs'
|
||||||
|
VIF_TYPE_IVS = 'ivs'
|
||||||
VIF_TYPE_BRIDGE = 'bridge'
|
VIF_TYPE_BRIDGE = 'bridge'
|
||||||
VIF_TYPE_802_QBG = '802.1qbg'
|
VIF_TYPE_802_QBG = '802.1qbg'
|
||||||
VIF_TYPE_802_QBH = '802.1qbh'
|
VIF_TYPE_802_QBH = '802.1qbh'
|
||||||
|
@ -104,6 +104,16 @@ restproxy_opts = [
|
|||||||
cfg.CONF.register_opts(restproxy_opts, "RESTPROXY")
|
cfg.CONF.register_opts(restproxy_opts, "RESTPROXY")
|
||||||
|
|
||||||
|
|
||||||
|
nova_opts = [
|
||||||
|
cfg.StrOpt('vif_type', default='ovs',
|
||||||
|
help=_("Virtual interface type to configure on "
|
||||||
|
"Nova compute nodes")),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
cfg.CONF.register_opts(nova_opts, "NOVA")
|
||||||
|
|
||||||
|
|
||||||
# The following are used to invoke the API on the external controller
|
# The following are used to invoke the API on the external controller
|
||||||
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
||||||
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
||||||
@ -1267,7 +1277,16 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def _extend_port_dict_binding(self, context, port):
|
def _extend_port_dict_binding(self, context, port):
|
||||||
port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS
|
cfg_vif_type = cfg.CONF.NOVA.vif_type.lower()
|
||||||
|
if not cfg_vif_type in (portbindings.VIF_TYPE_OVS,
|
||||||
|
portbindings.VIF_TYPE_IVS):
|
||||||
|
LOG.warning(_("Unrecognized vif_type in configuration "
|
||||||
|
"[%s]. Defaulting to ovs. "),
|
||||||
|
cfg_vif_type)
|
||||||
|
cfg_vif_type = portbindings.VIF_TYPE_OVS
|
||||||
|
|
||||||
|
port[portbindings.VIF_TYPE] = cfg_vif_type
|
||||||
|
|
||||||
port[portbindings.CAPABILITIES] = {
|
port[portbindings.CAPABILITIES] = {
|
||||||
portbindings.CAP_PORT_FILTER:
|
portbindings.CAP_PORT_FILTER:
|
||||||
'security-group' in self.supported_extension_aliases}
|
'security-group' in self.supported_extension_aliases}
|
||||||
|
@ -24,3 +24,10 @@ reconnect_interval = 2
|
|||||||
servers=localhost:8899
|
servers=localhost:8899
|
||||||
serverssl=False
|
serverssl=False
|
||||||
#serverauth=username:password
|
#serverauth=username:password
|
||||||
|
|
||||||
|
[NOVA]
|
||||||
|
# Specify the VIF_TYPE that will be controlled on the Nova compute instances
|
||||||
|
# options: ivs or ovs
|
||||||
|
# default: ovs
|
||||||
|
vif_type = ovs
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from mock import patch
|
from mock import patch
|
||||||
|
from oslo.config import cfg
|
||||||
|
|
||||||
import quantum.common.test_lib as test_lib
|
import quantum.common.test_lib as test_lib
|
||||||
from quantum.extensions import portbindings
|
from quantum.extensions import portbindings
|
||||||
@ -93,6 +94,18 @@ class TestBigSwitchProxyPortsV2(test_plugin.TestPortsV2,
|
|||||||
HAS_PORT_FILTER = False
|
HAS_PORT_FILTER = False
|
||||||
|
|
||||||
|
|
||||||
|
class TestBigSwitchProxyPortsV2IVS(test_plugin.TestPortsV2,
|
||||||
|
BigSwitchProxyPluginV2TestCase,
|
||||||
|
test_bindings.PortBindingsTestCase):
|
||||||
|
VIF_TYPE = portbindings.VIF_TYPE_IVS
|
||||||
|
HAS_PORT_FILTER = False
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestBigSwitchProxyPortsV2IVS,
|
||||||
|
self).setUp()
|
||||||
|
cfg.CONF.set_override('vif_type', 'ivs', 'NOVA')
|
||||||
|
|
||||||
|
|
||||||
class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2,
|
class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2,
|
||||||
BigSwitchProxyPluginV2TestCase):
|
BigSwitchProxyPluginV2TestCase):
|
||||||
|
|
||||||
|
@ -394,3 +394,79 @@ class TestMetaInterfaceDriver(TestBase):
|
|||||||
namespace=namespace)
|
namespace=namespace)
|
||||||
self.ip_dev.assert_has_calls(expected)
|
self.ip_dev.assert_has_calls(expected)
|
||||||
self.assertEquals('fake1', plugin_tag1)
|
self.assertEquals('fake1', plugin_tag1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestIVSInterfaceDriver(TestBase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestIVSInterfaceDriver, self).setUp()
|
||||||
|
|
||||||
|
def test_get_device_name(self):
|
||||||
|
br = interface.IVSInterfaceDriver(self.conf)
|
||||||
|
device_name = br.get_device_name(FakePort())
|
||||||
|
self.assertEqual('ns-abcdef01-12', device_name)
|
||||||
|
|
||||||
|
def test_plug_with_prefix(self):
|
||||||
|
self._test_plug(devname='qr-0', prefix='qr-')
|
||||||
|
|
||||||
|
def _test_plug(self, devname=None, namespace=None,
|
||||||
|
prefix=None, mtu=None):
|
||||||
|
|
||||||
|
if not devname:
|
||||||
|
devname = 'ns-0'
|
||||||
|
|
||||||
|
def device_exists(dev, root_helper=None, namespace=None):
|
||||||
|
return dev == 'indigo'
|
||||||
|
|
||||||
|
ivs = interface.IVSInterfaceDriver(self.conf)
|
||||||
|
self.device_exists.side_effect = device_exists
|
||||||
|
|
||||||
|
root_dev = mock.Mock()
|
||||||
|
_ns_dev = mock.Mock()
|
||||||
|
ns_dev = mock.Mock()
|
||||||
|
self.ip().add_veth = mock.Mock(return_value=(root_dev, _ns_dev))
|
||||||
|
self.ip().device = mock.Mock(return_value=(ns_dev))
|
||||||
|
expected = [mock.call('sudo'), mock.call().add_veth('tap0', devname),
|
||||||
|
mock.call().device(devname)]
|
||||||
|
|
||||||
|
ivsctl_cmd = ['ivs-ctl', 'add-port', 'tap0']
|
||||||
|
|
||||||
|
with mock.patch.object(utils, 'execute') as execute:
|
||||||
|
ivs.plug('01234567-1234-1234-99',
|
||||||
|
'port-1234',
|
||||||
|
devname,
|
||||||
|
'aa:bb:cc:dd:ee:ff',
|
||||||
|
namespace=namespace,
|
||||||
|
prefix=prefix)
|
||||||
|
execute.assert_called_once_with(ivsctl_cmd, 'sudo')
|
||||||
|
|
||||||
|
ns_dev.assert_has_calls(
|
||||||
|
[mock.call.link.set_address('aa:bb:cc:dd:ee:ff')])
|
||||||
|
if mtu:
|
||||||
|
ns_dev.assert_has_calls([mock.call.link.set_mtu(mtu)])
|
||||||
|
root_dev.assert_has_calls([mock.call.link.set_mtu(mtu)])
|
||||||
|
if namespace:
|
||||||
|
expected.extend(
|
||||||
|
[mock.call().ensure_namespace(namespace),
|
||||||
|
mock.call().ensure_namespace().add_device_to_namespace(
|
||||||
|
mock.ANY)])
|
||||||
|
|
||||||
|
self.ip.assert_has_calls(expected)
|
||||||
|
root_dev.assert_has_calls([mock.call.link.set_up()])
|
||||||
|
ns_dev.assert_has_calls([mock.call.link.set_up()])
|
||||||
|
|
||||||
|
def test_plug_mtu(self):
|
||||||
|
self.conf.set_override('network_device_mtu', 9000)
|
||||||
|
self._test_plug(mtu=9000)
|
||||||
|
|
||||||
|
def test_plug_namespace(self):
|
||||||
|
self._test_plug(namespace='mynamespace')
|
||||||
|
|
||||||
|
def test_unplug(self):
|
||||||
|
ivs = interface.IVSInterfaceDriver(self.conf)
|
||||||
|
ivsctl_cmd = ['ivs-ctl', 'del-port', 'tap0']
|
||||||
|
with mock.patch.object(utils, 'execute') as execute:
|
||||||
|
ivs.unplug('ns-0')
|
||||||
|
execute.assert_called_once_with(ivsctl_cmd, 'sudo')
|
||||||
|
self.ip_dev.assert_has_calls([mock.call('ns-0', 'sudo', None),
|
||||||
|
mock.call().link.delete()])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user