Convert vrouter legacy plugging to os-vif

Before this change, the vrouter VIF type used legacy VIF plugging. This
changeset ports the plugging methods over to an external os-vif plugin,
simplifying the in-tree code.

Miscellaneous notes:

 * There are two "vrouter" Neutron VIF types:
    * "contrail_vrouter" supporting vhostuser plugging, and
    * "vrouter", supporting kernel datapath plugging.
 * The VIFGeneric os-vif type is used for the kernel TAP based
   plugging when the vnic_type is 'normal'.
 * For multiqueue support, the minimum version of libvirt 1.3.1 is
   required. In that case, libvirt creates the TAP device, rather than
   the os-vif plugin. (This is the minimum version for Rocky and later)
   ref: https://bugs.launchpad.net/ubuntu/+source/libvirt/+bug/1574957
 * The corresponding commit on Tungsten Fabric / OpenContrail for this
   work is at:
   ed01d315e5

Change-Id: I047856982251fddc631679fb2dbcea0f3b0db097
Signed-off-by: Jan Gutter <jan.gutter@netronome.com>
blueprint: vrouter-os-vif-conversion
This commit is contained in:
Jan Gutter
2018-06-01 15:24:28 +02:00
parent e3fc005e4e
commit 172855f293
6 changed files with 98 additions and 163 deletions

View File

@@ -426,6 +426,21 @@ def _nova_to_osvif_vif_ivs(vif):
return obj return obj
# VIF_TYPE_VROUTER = 'vrouter'
def _nova_to_osvif_vif_vrouter(vif):
vnic_type = vif.get('vnic_type', model.VNIC_TYPE_NORMAL)
if vnic_type == model.VNIC_TYPE_NORMAL:
obj = _get_vif_instance(
vif,
objects.vif.VIFGeneric,
plugin="vrouter",
vif_name=_get_vif_name(vif)
)
else:
raise NotImplementedError()
return obj
# VIF_TYPE_DVS = 'dvs' # VIF_TYPE_DVS = 'dvs'
def _nova_to_osvif_vif_dvs(vif): def _nova_to_osvif_vif_dvs(vif):
raise NotImplementedError() raise NotImplementedError()
@@ -461,11 +476,6 @@ def _nova_to_osvif_vif_midonet(vif):
raise NotImplementedError() raise NotImplementedError()
# VIF_TYPE_VROUTER = 'vrouter'
def _nova_to_osvif_vif_vrouter(vif):
raise NotImplementedError()
# VIF_TYPE_TAP = 'tap' # VIF_TYPE_TAP = 'tap'
def _nova_to_osvif_vif_tap(vif): def _nova_to_osvif_vif_tap(vif):
raise NotImplementedError() raise NotImplementedError()

View File

@@ -156,38 +156,6 @@ def unplug_plumgrid_vif(dev):
processutils.execute('ifc_ctl', 'gateway', 'del_port', dev) processutils.execute('ifc_ctl', 'gateway', 'del_port', dev)
@nova.privsep.sys_admin_pctxt.entrypoint
def plug_contrail_vif(project_id, vm_id, vm_name, vif_id, net_id, port_type,
dev_name, mac, ip_addr, ip6_addr):
cmd = (
'vrouter-port-control',
'--oper=add',
'--vm_project_uuid=%s' % project_id,
'--instance_uuid=%s' % vm_id,
' --vm_name=%s' % vm_name,
'--uuid=%s' % vif_id,
'--vn_uuid=%s' % net_id,
'--port_type=%s' % port_type,
'--tap_name=%s' % dev_name,
'--mac=%s' % mac,
'--ip_address=%s' % ip_addr,
'--ipv6_address=%s' % ip6_addr,
'--tx_vlan_id=-1',
'--rx_vlan_id=-1',
)
processutils.execute(*cmd)
@nova.privsep.sys_admin_pctxt.entrypoint
def unplug_contrail_vif(port_id):
cmd = (
'vrouter-port-control',
'--oper=delete',
'--uuid=%s' % port_id,
)
processutils.execute(*cmd)
@nova.privsep.sys_admin_pctxt.entrypoint @nova.privsep.sys_admin_pctxt.entrypoint
def readpty(path): def readpty(path):
# TODO(mikal): I'm not a huge fan that we don't enforce a valid pty path # TODO(mikal): I'm not a huge fan that we don't enforce a valid pty path

View File

@@ -1040,7 +1040,8 @@ class OSVIFUtilTestCase(test.NoDBTestCase):
subnets=[]),) subnets=[]),)
self.assertIsNone(os_vif_util.nova_to_osvif_vif(vif)) self.assertIsNone(os_vif_util.nova_to_osvif_vif(vif))
def test_nova_to_osvif_vhostuser_vrouter(self): def test_nova_to_osvif_contrail_vrouter(self):
"""Test for the Contrail / Tungsten Fabric DPDK datapath."""
vif = model.VIF( vif = model.VIF(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
type=model.VIF_TYPE_VHOSTUSER, type=model.VIF_TYPE_VHOSTUSER,
@@ -1077,7 +1078,8 @@ class OSVIFUtilTestCase(test.NoDBTestCase):
self.assertObjEqual(expect, actual) self.assertObjEqual(expect, actual)
def test_nova_to_osvif_vhostuser_vrouter_no_socket_path(self): def test_nova_to_osvif_contrail_vrouter_no_socket_path(self):
"""Test for the Contrail / Tungsten Fabric DPDK datapath."""
vif = model.VIF( vif = model.VIF(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536", id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
type=model.VIF_TYPE_VHOSTUSER, type=model.VIF_TYPE_VHOSTUSER,
@@ -1095,3 +1097,34 @@ class OSVIFUtilTestCase(test.NoDBTestCase):
self.assertRaises(exception.VifDetailsMissingVhostuserSockPath, self.assertRaises(exception.VifDetailsMissingVhostuserSockPath,
os_vif_util.nova_to_osvif_vif, os_vif_util.nova_to_osvif_vif,
vif) vif)
def test_nova_to_osvif_vrouter(self):
"""Test for the Contrail / Tungsten Fabric kernel datapath."""
vif = model.VIF(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
type=model.VIF_TYPE_VROUTER,
address="22:52:25:62:e2:aa",
network=model.Network(
id="b82c1929-051e-481d-8110-4669916c7915",
label="Demo Net",
subnets=[]),
)
actual = os_vif_util.nova_to_osvif_vif(vif)
expect = osv_objects.vif.VIFGeneric(
id="dc065497-3c8d-4f44-8fb4-e1d33c16a536",
active=False,
address="22:52:25:62:e2:aa",
plugin="vrouter",
vif_name="nicdc065497-3c",
has_traffic_filtering=False,
preserve_on_delete=False,
network=osv_objects.network.Network(
id="b82c1929-051e-481d-8110-4669916c7915",
bridge_interface=None,
label="Demo Net",
subnets=osv_objects.subnet.SubnetList(
objects=[])))
self.assertObjEqual(expect, actual)

View File

@@ -273,6 +273,15 @@ class LibvirtVifTestCase(test.NoDBTestCase):
type=network_model.VIF_TYPE_VROUTER, type=network_model.VIF_TYPE_VROUTER,
devname='tap-xxx-yyy-zzz') devname='tap-xxx-yyy-zzz')
vif_contrail_vrouter = network_model.VIF(id=uuids.vif,
address='ca:fe:de:ad:be:ef',
network=network_vrouter,
type=network_model.VIF_TYPE_VHOSTUSER,
details={
network_model.VIF_DETAILS_VHOSTUSER_MODE: 'server',
network_model.VIF_DETAILS_VHOSTUSER_SOCKET: '/tmp/usv-xxx-yyy-zzz',
network_model.VIF_DETAILS_VHOSTUSER_VROUTER_PLUG: True})
vif_ib_hostdev = network_model.VIF(id=uuids.vif, vif_ib_hostdev = network_model.VIF(id=uuids.vif,
address='ca:fe:de:ad:be:ef', address='ca:fe:de:ad:be:ef',
network=network_8021, network=network_8021,
@@ -1012,57 +1021,6 @@ class LibvirtVifTestCase(test.NoDBTestCase):
self.vif_iovisor['network']['id'], self.vif_iovisor['network']['id'],
self.instance.project_id)]) self.instance.project_id)])
@mock.patch('nova.privsep.libvirt.unplug_contrail_vif')
def test_unplug_vrouter_with_details(self, mock_unplug_contrail):
d = vif.LibvirtGenericVIFDriver()
d.unplug(self.instance, self.vif_vrouter)
mock_unplug_contrail.assert_called_once_with(self.vif_vrouter['id'])
@mock.patch('nova.privsep.libvirt.plug_contrail_vif')
@mock.patch('nova.privsep.linux_net.set_device_enabled')
def test_plug_vrouter_with_details(self, mock_enabled, mock_plug_contrail):
d = vif.LibvirtGenericVIFDriver()
instance = mock.Mock()
instance.name = 'instance-name'
instance.uuid = '46a4308b-e75a-4f90-a34a-650c86ca18b2'
instance.project_id = 'b168ea26fa0c49c1a84e1566d9565fa5'
instance.display_name = 'instance1'
instance.image_meta = objects.ImageMeta.from_dict({'properties': {}})
with mock.patch.object(utils, 'execute') as execute:
d.plug(instance, self.vif_vrouter)
execute.assert_has_calls([
mock.call('ip', 'tuntap', 'add', 'tap-xxx-yyy-zzz', 'mode',
'tap', run_as_root=True, check_exit_code=[0, 2, 254])])
mock_plug_contrail.called_once_with(
instance.project_id, instance.uuid, instance.display_name,
self.vif_vrouter['id'], self.vif_vrouter['network']['id'],
'NovaVMPort', self.vif_vrouter['devname'],
self.vif_vrouter['address'], '0.0.0.0', None)
mock_enabled.assert_called_once_with('tap-xxx-yyy-zzz')
@mock.patch('nova.network.linux_utils.create_tap_dev')
@mock.patch('nova.privsep.libvirt.plug_contrail_vif')
def test_plug_vrouter_with_details_multiqueue(
self, mock_plug_contrail, mock_create_tap_dev):
d = vif.LibvirtGenericVIFDriver()
instance = mock.Mock()
instance.name = 'instance-name'
instance.uuid = '46a4308b-e75a-4f90-a34a-650c86ca18b2'
instance.project_id = 'b168ea26fa0c49c1a84e1566d9565fa5'
instance.display_name = 'instance1'
instance.image_meta = objects.ImageMeta.from_dict({
'properties': {'hw_vif_multiqueue_enabled': True}})
instance.flavor.vcpus = 2
d.plug(instance, self.vif_vrouter)
mock_create_tap_dev.assert_called_once_with('tap-xxx-yyy-zzz',
multiqueue=True)
mock_plug_contrail.called_once_with(
instance.project_id, instance.uuid, instance.display_name,
self.vif_vrouter['id'], self.vif_vrouter['network']['id'],
'NovaVMPort', self.vif_vrouter['devname'],
self.vif_vrouter['address'], '0.0.0.0', None)
def _check_ovs_virtualport_driver(self, d, vif, want_iface_id): def _check_ovs_virtualport_driver(self, d, vif, want_iface_id):
self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver") self.flags(firewall_driver="nova.virt.firewall.NoopFirewallDriver")
xml = self._get_instance_xml(d, vif) xml = self._get_instance_xml(d, vif)
@@ -1433,6 +1391,33 @@ class LibvirtVifTestCase(test.NoDBTestCase):
script = node.find("script") script = node.find("script")
self.assertIsNone(script) self.assertIsNone(script)
def test_vrouter(self):
"""Test for the Contrail / Tungsten Fabric kernel datapath."""
d = vif.LibvirtGenericVIFDriver()
dev_want = self.vif_vrouter['devname']
xml = self._get_instance_xml(d, self.vif_vrouter)
node = self._get_node(xml)
self._assertTypeAndMacEquals(node, "ethernet", "target", "dev",
self.vif_vrouter, dev_want)
def test_contrail_vrouter(self):
"""Test for the Contrail / Tungsten Fabric DPDK datapath."""
d = vif.LibvirtGenericVIFDriver()
xml = self._get_instance_xml(d,
self.vif_contrail_vrouter)
node = self._get_node(xml)
self.assertEqual(node.get("type"),
network_model.VIF_TYPE_VHOSTUSER)
self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
"source", "mode", "server")
self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
"source", "path", "/tmp/usv-xxx-yyy-zzz")
self._assertTypeEquals(node, network_model.VIF_TYPE_VHOSTUSER,
"source", "type", "unix")
self._assertMacEquals(node, self.vif_contrail_vrouter)
self._assertModel(xml, network_model.VIF_MODEL_VIRTIO)
@mock.patch("nova.network.os_vif_util.nova_to_osvif_instance") @mock.patch("nova.network.os_vif_util.nova_to_osvif_instance")
@mock.patch("nova.network.os_vif_util.nova_to_osvif_vif") @mock.patch("nova.network.os_vif_util.nova_to_osvif_vif")
@mock.patch.object(os_vif, "plug") @mock.patch.object(os_vif, "plug")

View File

@@ -192,10 +192,6 @@ class LibvirtGenericVIFDriver(object):
designer.set_vif_host_backend_hostdev_pci_config(conf, pci_slot) designer.set_vif_host_backend_hostdev_pci_config(conf, pci_slot)
return conf return conf
def _is_multiqueue_enabled(self, image_meta, flavor):
_, vhost_queues = self._get_virtio_mq_settings(image_meta, flavor)
return vhost_queues > 1 if vhost_queues is not None else False
def _get_virtio_mq_settings(self, image_meta, flavor): def _get_virtio_mq_settings(self, image_meta, flavor):
"""A methods to set the number of virtio queues, """A methods to set the number of virtio queues,
if it has been requested in extra specs. if it has been requested in extra specs.
@@ -485,17 +481,6 @@ class LibvirtGenericVIFDriver(object):
inst_type, virt_type, host): inst_type, virt_type, host):
return self.get_base_hostdev_pci_config(vif) return self.get_base_hostdev_pci_config(vif)
def get_config_vrouter(self, instance, vif, image_meta,
inst_type, virt_type, host):
conf = self.get_base_config(instance, vif['address'], image_meta,
inst_type, virt_type, vif['vnic_type'],
host)
dev = self.get_vif_devname(vif)
designer.set_vif_host_backend_ethernet_config(conf, dev, host)
designer.set_vif_bandwidth_config(conf, inst_type)
return conf
def _set_config_VIFGeneric(self, instance, vif, conf, host): def _set_config_VIFGeneric(self, instance, vif, conf, host):
dev = vif.vif_name dev = vif.vif_name
designer.set_vif_host_backend_ethernet_config(conf, dev, host) designer.set_vif_host_backend_ethernet_config(conf, dev, host)
@@ -721,51 +706,6 @@ class LibvirtGenericVIFDriver(object):
def plug_vhostuser(self, instance, vif): def plug_vhostuser(self, instance, vif):
pass pass
def plug_vrouter(self, instance, vif):
"""Plug into Contrail's network port
Bind the vif to a Contrail virtual port.
"""
dev = self.get_vif_devname(vif)
ip_addr = '0.0.0.0'
ip6_addr = None
subnets = vif['network']['subnets']
for subnet in subnets:
if not subnet['ips']:
continue
ips = subnet['ips'][0]
if not ips['address']:
continue
if (ips['version'] == 4):
if ips['address'] is not None:
ip_addr = ips['address']
if (ips['version'] == 6):
if ips['address'] is not None:
ip6_addr = ips['address']
ptype = 'NovaVMPort'
if (CONF.libvirt.virt_type == 'lxc'):
ptype = 'NameSpacePort'
try:
multiqueue = self._is_multiqueue_enabled(instance.image_meta,
instance.flavor)
linux_net_utils.create_tap_dev(dev, multiqueue=multiqueue)
nova.privsep.libvirt.plug_contrail_vif(
instance.project_id,
instance.uuid,
instance.display_name,
vif['id'],
vif['network']['id'],
ptype,
dev,
vif['address'],
ip_addr,
ip6_addr,
)
except processutils.ProcessExecutionError:
LOG.exception(_("Failed while plugging vif"), instance=instance)
def _plug_os_vif(self, instance, vif): def _plug_os_vif(self, instance, vif):
instance_info = os_vif_util.nova_to_osvif_instance(instance) instance_info = os_vif_util.nova_to_osvif_instance(instance)
@@ -880,19 +820,6 @@ class LibvirtGenericVIFDriver(object):
def unplug_vhostuser(self, instance, vif): def unplug_vhostuser(self, instance, vif):
pass pass
def unplug_vrouter(self, instance, vif):
"""Unplug Contrail's network port
Unbind the vif from a Contrail virtual port.
"""
dev = self.get_vif_devname(vif)
port_id = vif['id']
try:
nova.privsep.libvirt.unplug_contrail_vif(port_id)
nova.privsep.linux_net.delete_net_dev(dev)
except processutils.ProcessExecutionError:
LOG.exception(_("Failed while unplugging vif"), instance=instance)
def _unplug_os_vif(self, instance, vif): def _unplug_os_vif(self, instance, vif):
instance_info = os_vif_util.nova_to_osvif_instance(instance) instance_info = os_vif_util.nova_to_osvif_instance(instance)

View File

@@ -0,0 +1,12 @@
---
upgrade:
- |
This release moves the ``vrouter`` VIF plug and unplug code to a
separate package called ``contrail-nova-vif-driver``. This package is a
requirement on compute nodes when using Contrail, OpenContrail or
Tungsten Fabric as a Neutron plugin.
At this time, the reference plugin is hosted on OpenContrail at
https://github.com/Juniper/contrail-nova-vif-driver but is expected to
transition to Tungsten Fabric in the future.
Release ``r5.1.alpha0`` or later of the plugin is required, which will
be included in Tungsten Fabric 5.1.