VMware: expand support for Opaque networks

The original support for Opaque networks make use of a
configuration flag indicating that a global integration bridge
was configured on the ESX host. If this global integration
bridge was used then that would be selected as the opaque network.

The next generation NSX neutron plugin will not make use of the
integration bridge. This will require that we make use of the network
ID for the opaque network.

The support for VC 6.0 has specific support for this. For 5.5 we make
use of a DynamicProperty setting to achieve the same goal.

DocImpact

For backward compatible opaque support the setting
'CONF.vmware.integration_bridge' needs to be set.
If this is not set then the network id will be used as the
opaque id.

In addition to this the default value has been set to 'None'.

Implements blueprint vmware-expand-opaque-support

Change-Id: I309ca2bc8186b8eefc0a979d039dd4cd2a6f89f6
This commit is contained in:
Gary Kotton 2015-03-19 02:34:22 -07:00 committed by Matt Riedemann
parent 98af6b148e
commit 8ee49c6aed
5 changed files with 199 additions and 188 deletions

View File

@ -16,7 +16,7 @@
import mock import mock
from oslo_config import cfg from oslo_config import cfg
from oslo_vmware import exceptions as vexc from oslo_vmware import exceptions as vexc
from oslo_vmware import vim_util as vutil from oslo_vmware import vim_util
from nova import exception from nova import exception
from nova.network import model as network_model from nova.network import model as network_model
@ -24,6 +24,7 @@ from nova import test
from nova.tests.unit import matchers from nova.tests.unit import matchers
from nova.tests.unit import utils from nova.tests.unit import utils
from nova.tests.unit.virt.vmwareapi import fake from nova.tests.unit.virt.vmwareapi import fake
from nova.virt.vmwareapi import constants
from nova.virt.vmwareapi import network_util from nova.virt.vmwareapi import network_util
from nova.virt.vmwareapi import vif from nova.virt.vmwareapi import vif
from nova.virt.vmwareapi import vm_util from nova.virt.vmwareapi import vm_util
@ -41,7 +42,7 @@ class VMwareVifTestCase(test.NoDBTestCase):
vlan=3, vlan=3,
bridge_interface='eth0', bridge_interface='eth0',
injected=True) injected=True)
self._network = network
self.vif = network_model.NetworkInfo([ self.vif = network_model.NetworkInfo([
network_model.VIF(id=None, network_model.VIF(id=None,
address='DE:AD:BE:EF:00:00', address='DE:AD:BE:EF:00:00',
@ -132,12 +133,6 @@ class VMwareVifTestCase(test.NoDBTestCase):
create_vlan=False) create_vlan=False)
self.assertThat(ref, matchers.DictMatches(network_ref)) self.assertThat(ref, matchers.DictMatches(network_ref))
def test_get_network_ref_neutron(self):
self.mox.StubOutWithMock(vif, 'get_neutron_network')
vif.get_neutron_network(self.session, 'fa0', self.cluster, self.vif)
self.mox.ReplayAll()
vif.get_network_ref(self.session, self.cluster, self.vif, True)
def test_get_network_ref_flat_dhcp(self): def test_get_network_ref_flat_dhcp(self):
self.mox.StubOutWithMock(vif, 'ensure_vlan_bridge') self.mox.StubOutWithMock(vif, 'ensure_vlan_bridge')
vif.ensure_vlan_bridge(self.session, self.vif, cluster=self.cluster, vif.ensure_vlan_bridge(self.session, self.vif, cluster=self.cluster,
@ -168,110 +163,6 @@ class VMwareVifTestCase(test.NoDBTestCase):
])[0] ])[0]
vif.get_network_ref(self.session, self.cluster, self.vif, False) vif.get_network_ref(self.session, self.cluster, self.vif, False)
def test_get_network_ref_bridge_from_opaque(self):
opaque_networks = [{'opaqueNetworkId': 'bridge_id',
'opaqueNetworkName': 'name',
'opaqueNetworkType': 'OpaqueNetwork'}]
network_ref = vif._get_network_ref_from_opaque(opaque_networks,
'integration_bridge', 'bridge_id')
self.assertEqual('bridge_id', network_ref['network-id'])
def test_get_network_ref_multiple_bridges_from_opaque(self):
opaque_networks = [{'opaqueNetworkId': 'bridge_id1',
'opaqueNetworkName': 'name1',
'opaqueNetworkType': 'OpaqueNetwork'},
{'opaqueNetworkId': 'bridge_id2',
'opaqueNetworkName': 'name2',
'opaqueNetworkType': 'OpaqueNetwork'}]
network_ref = vif._get_network_ref_from_opaque(opaque_networks,
'integration_bridge', 'bridge_id2')
self.assertEqual('bridge_id2', network_ref['network-id'])
def test_get_network_ref_integration(self):
opaque_networks = [{'opaqueNetworkId': 'integration_bridge',
'opaqueNetworkName': 'name',
'opaqueNetworkType': 'OpaqueNetwork'}]
network_ref = vif._get_network_ref_from_opaque(opaque_networks,
'integration_bridge', 'bridge_id')
self.assertEqual('integration_bridge', network_ref['network-id'])
def test_get_network_ref_bridge_none(self):
opaque_networks = [{'opaqueNetworkId': 'bridge_id1',
'opaqueNetworkName': 'name1',
'opaqueNetworkType': 'OpaqueNetwork'},
{'opaqueNetworkId': 'bridge_id2',
'opaqueNetworkName': 'name2',
'opaqueNetworkType': 'OpaqueNetwork'}]
network_ref = vif._get_network_ref_from_opaque(opaque_networks,
'integration_bridge', 'bridge_id')
self.assertIsNone(network_ref)
def test_get_network_ref_integration_multiple(self):
opaque_networks = [{'opaqueNetworkId': 'bridge_id1',
'opaqueNetworkName': 'name1',
'opaqueNetworkType': 'OpaqueNetwork'},
{'opaqueNetworkId': 'integration_bridge',
'opaqueNetworkName': 'name2',
'opaqueNetworkType': 'OpaqueNetwork'}]
network_ref = vif._get_network_ref_from_opaque(opaque_networks,
'integration_bridge', 'bridge_id')
self.assertIsNone(network_ref)
def test_get_neutron_network(self):
self.mox.StubOutWithMock(vm_util, 'get_host_ref')
self.mox.StubOutWithMock(self.session, '_call_method')
self.mox.StubOutWithMock(vif, '_get_network_ref_from_opaque')
vm_util.get_host_ref(self.session,
self.cluster).AndReturn('fake-host')
opaque = fake.DataObject()
opaque.HostOpaqueNetworkInfo = ['fake-network-info']
self.session._call_method(vutil, "get_object_property",
'fake-host', 'config.network.opaqueNetwork').AndReturn(opaque)
vif._get_network_ref_from_opaque(opaque.HostOpaqueNetworkInfo,
CONF.vmware.integration_bridge,
self.vif['network']['id']).AndReturn('fake-network-ref')
self.mox.ReplayAll()
network_ref = vif.get_neutron_network(self.session,
self.vif['network']['id'],
self.cluster,
self.vif)
self.assertEqual(network_ref, 'fake-network-ref')
def test_get_neutron_network_opaque_network_not_found(self):
self.mox.StubOutWithMock(vm_util, 'get_host_ref')
self.mox.StubOutWithMock(self.session, '_call_method')
self.mox.StubOutWithMock(vif, '_get_network_ref_from_opaque')
vm_util.get_host_ref(self.session,
self.cluster).AndReturn('fake-host')
opaque = fake.DataObject()
opaque.HostOpaqueNetworkInfo = ['fake-network-info']
self.session._call_method(vutil, "get_object_property",
'fake-host', 'config.network.opaqueNetwork').AndReturn(opaque)
vif._get_network_ref_from_opaque(opaque.HostOpaqueNetworkInfo,
CONF.vmware.integration_bridge,
self.vif['network']['id']).AndReturn(None)
self.mox.ReplayAll()
self.assertRaises(exception.NetworkNotFoundForBridge,
vif.get_neutron_network, self.session,
self.vif['network']['id'], self.cluster, self.vif)
def test_get_neutron_network_bridge_network_not_found(self):
self.mox.StubOutWithMock(vm_util, 'get_host_ref')
self.mox.StubOutWithMock(self.session, '_call_method')
self.mox.StubOutWithMock(network_util, 'get_network_with_the_name')
vm_util.get_host_ref(self.session,
self.cluster).AndReturn('fake-host')
opaque = fake.DataObject()
opaque.HostOpaqueNetworkInfo = ['fake-network-info']
self.session._call_method(vutil, "get_object_property",
'fake-host', 'config.network.opaqueNetwork').AndReturn(None)
network_util.get_network_with_the_name(self.session, 0,
self.cluster).AndReturn(None)
self.mox.ReplayAll()
self.assertRaises(exception.NetworkNotFoundForBridge,
vif.get_neutron_network, self.session,
self.vif['network']['id'], self.cluster, self.vif)
def test_create_port_group_already_exists(self): def test_create_port_group_already_exists(self):
def fake_call_method(module, method, *args, **kwargs): def fake_call_method(module, method, *args, **kwargs):
if method == 'AddPortGroup': if method == 'AddPortGroup':
@ -304,20 +195,6 @@ class VMwareVifTestCase(test.NoDBTestCase):
'vswitch_name', vlan_id=0, 'vswitch_name', vlan_id=0,
cluster=None) cluster=None)
def test_get_neutron_network_invalid_property(self):
def fake_call_method(module, method, *args, **kwargs):
if method == 'get_object_property':
raise vexc.InvalidPropertyException()
with test.nested(
mock.patch.object(vm_util, 'get_host_ref'),
mock.patch.object(self.session, '_call_method',
fake_call_method),
mock.patch.object(network_util, 'get_network_with_the_name')
) as (_get_host, _call_method, _get_name):
vif.get_neutron_network(self.session, 'network_name',
'cluster', self.vif)
def test_get_vif_info_none(self): def test_get_vif_info_none(self):
vif_info = vif.get_vif_info('fake_session', 'fake_cluster', vif_info = vif.get_vif_info('fake_session', 'fake_cluster',
'is_neutron', 'fake_model', None) 'is_neutron', 'fake_model', None)
@ -339,3 +216,97 @@ class VMwareVifTestCase(test.NoDBTestCase):
'network_ref': 'fake_ref', 'network_ref': 'fake_ref',
'vif_model': 'fake_model'}] 'vif_model': 'fake_model'}]
self.assertEqual(expected, vif_info) self.assertEqual(expected, vif_info)
@mock.patch.object(vif, '_check_ovs_supported_version')
def test_get_neutron_network_ovs_integration_bridge(self,
mock_check):
self.flags(integration_bridge='fake-bridge-id', group='vmware')
vif_info = network_model.NetworkInfo([
network_model.VIF(type=network_model.VIF_TYPE_OVS,
address='DE:AD:BE:EF:00:00',
network=self._network)]
)[0]
network_ref = vif._get_neutron_network('fake-session',
'fake-cluster',
vif_info)
expected_ref = {'type': 'OpaqueNetwork',
'network-id': 'fake-bridge-id',
'network-type': 'opaque',
'use-external-id': False}
self.assertEqual(expected_ref, network_ref)
mock_check.assert_called_once_with('fake-session')
@mock.patch.object(vif, '_check_ovs_supported_version')
def test_get_neutron_network_ovs(self, mock_check):
vif_info = network_model.NetworkInfo([
network_model.VIF(type=network_model.VIF_TYPE_OVS,
address='DE:AD:BE:EF:00:00',
network=self._network)]
)[0]
network_ref = vif._get_neutron_network('fake-session',
'fake-cluster',
vif_info)
expected_ref = {'type': 'OpaqueNetwork',
'network-id': 0,
'network-type': 'nsx.LogicalSwitch',
'use-external-id': True}
self.assertEqual(expected_ref, network_ref)
mock_check.assert_called_once_with('fake-session')
@mock.patch.object(network_util, 'get_network_with_the_name')
def test_get_neutron_network_dvs(self, mock_network_name):
fake_network_obj = {'type': 'DistributedVirtualPortgroup',
'dvpg': 'fake-key',
'dvsw': 'fake-props'}
mock_network_name.return_value = fake_network_obj
vif_info = network_model.NetworkInfo([
network_model.VIF(type=network_model.VIF_TYPE_DVS,
address='DE:AD:BE:EF:00:00',
network=self._network)]
)[0]
network_ref = vif._get_neutron_network('fake-session',
'fake-cluster',
vif_info)
mock_network_name.assert_called_once_with('fake-session',
'fa0',
'fake-cluster')
self.assertEqual(fake_network_obj, network_ref)
@mock.patch.object(network_util, 'get_network_with_the_name',
return_value=None)
def test_get_neutron_network_dvs_no_match(self, mock_network_name):
vif_info = network_model.NetworkInfo([
network_model.VIF(type=network_model.VIF_TYPE_DVS,
address='DE:AD:BE:EF:00:00',
network=self._network)]
)[0]
self.assertRaises(exception.NetworkNotFoundForBridge,
vif._get_neutron_network,
'fake-session',
'fake-cluster',
vif_info)
def test_get_neutron_network_invalid_type(self):
vif_info = network_model.NetworkInfo([
network_model.VIF(address='DE:AD:BE:EF:00:00',
network=self._network)]
)[0]
self.assertRaises(exception.InvalidInput,
vif._get_neutron_network,
'fake-session',
'fake-cluster',
vif_info)
@mock.patch.object(vif.LOG, 'warning')
@mock.patch.object(vim_util, 'get_vc_version',
return_value='5.0.0')
def test_check_invalid_ovs_version(self, mock_version, mock_warning):
vif._check_ovs_supported_version('fake_session')
# assert that the min version is in a warning message
expected_arg = {'version': constants.MIN_VC_OVS_VERSION}
version_arg_found = False
for call in mock_warning.call_args_list:
if call[0][1] == expected_arg:
version_arg_found = True
break
self.assertTrue(version_arg_found)

View File

@ -1012,14 +1012,13 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
_wait_for_task.assert_called_once_with( _wait_for_task.assert_called_once_with(
'fake_reconfigure_task') 'fake_reconfigure_task')
def test_get_network_attach_config_spec_opaque(self): def _get_network_attach_config_spec_opaque(self, network_ref,
vif_info = {'network_name': 'br-int', vc6_onwards=False):
'mac_address': '00:00:00:ca:fe:01', vif_info = {'network_name': 'fake-name',
'network_ref': {'type': 'OpaqueNetwork', 'mac_address': '00:00:00:ca:fe:01',
'network-id': 'fake-network-id', 'network_ref': network_ref,
'network-type': 'opaque'}, 'iface_id': 7,
'iface_id': 7, 'vif_model': 'VirtualE1000'}
'vif_model': 'VirtualE1000'}
fake_factory = fake.FakeFactory() fake_factory = fake.FakeFactory()
result = vm_util.get_network_attach_config_spec( result = vm_util.get_network_attach_config_spec(
fake_factory, vif_info, 1) fake_factory, vif_info, 1)
@ -1038,6 +1037,14 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
device = fake_factory.create('ns0:VirtualE1000') device = fake_factory.create('ns0:VirtualE1000')
device.macAddress = vif_info['mac_address'] device.macAddress = vif_info['mac_address']
if network_ref['use-external-id']:
if vc6_onwards:
device.externalId = vif_info['iface_id']
else:
dp = fake_factory.create('ns0:DynamicProperty')
dp.name = '__externalId__'
dp.val = vif_info['iface_id']
device.dynamicProperty = [dp]
device.addressType = 'manual' device.addressType = 'manual'
connectable = fake_factory.create('ns0:VirtualDeviceConnectInfo') connectable = fake_factory.create('ns0:VirtualDeviceConnectInfo')
connectable.allowGuestControl = True connectable.allowGuestControl = True
@ -1055,6 +1062,37 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual(expected, result) self.assertEqual(expected, result)
def test_get_network_attach_config_spec_opaque_integration_bridge(self):
network_ref = {'type': 'OpaqueNetwork',
'network-id': 'fake-network-id',
'network-type': 'opaque',
'use-external-id': False}
self._get_network_attach_config_spec_opaque(network_ref)
def test_get_network_attach_config_spec_opaque(self):
network_ref = {'type': 'OpaqueNetwork',
'network-id': 'fake-network-id',
'network-type': 'nsx.LogicalSwitch',
'use-external-id': True}
self._get_network_attach_config_spec_opaque(network_ref)
@mock.patch.object(fake, 'DataObject')
def test_get_network_attach_config_spec_opaque_vc6_onwards(self,
mock_object):
# Add new attribute externalId supported from VC6
class FakeVirtualE1000(fake.DataObject):
def __init__(self):
super(FakeVirtualE1000, self).__init__()
self.externalId = None
mock_object.return_value = FakeVirtualE1000
network_ref = {'type': 'OpaqueNetwork',
'network-id': 'fake-network-id',
'network-type': 'nsx.LogicalSwitch',
'use-external-id': True}
self._get_network_attach_config_spec_opaque(network_ref,
vc6_onwards=True)
def test_get_network_attach_config_spec_dvs(self): def test_get_network_attach_config_spec_dvs(self):
vif_info = {'network_name': 'br100', vif_info = {'network_name': 'br100',
'mac_address': '00:00:00:ca:fe:01', 'mac_address': '00:00:00:ca:fe:01',

View File

@ -19,6 +19,8 @@ Shared constants across the VMware driver
from nova.network import model as network_model from nova.network import model as network_model
MIN_VC_VERSION = '5.1.0' MIN_VC_VERSION = '5.1.0'
# The minimum VC version for Neutron 'ovs' port type support
MIN_VC_OVS_VERSION = '5.5.0'
DISK_FORMAT_ISO = 'iso' DISK_FORMAT_ISO = 'iso'
DISK_FORMAT_VMDK = 'vmdk' DISK_FORMAT_VMDK = 'vmdk'

View File

@ -17,12 +17,13 @@
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
from oslo_vmware import exceptions as vexc from oslo_vmware import vim_util
from oslo_vmware import vim_util as vutil
from nova import exception from nova import exception
from nova.i18n import _LW from nova.i18n import _, _LW
from nova.network import model from nova.network import model
from nova import utils
from nova.virt.vmwareapi import constants
from nova.virt.vmwareapi import network_util from nova.virt.vmwareapi import network_util
from nova.virt.vmwareapi import vm_util from nova.virt.vmwareapi import vm_util
@ -34,8 +35,11 @@ vmwareapi_vif_opts = [
default='vmnic0', default='vmnic0',
help='Physical ethernet adapter name for vlan networking'), help='Physical ethernet adapter name for vlan networking'),
cfg.StrOpt('integration_bridge', cfg.StrOpt('integration_bridge',
default='br-int', help='This option should be configured only when using the '
help='Name of Integration Bridge'), 'NSX-MH Neutron plugin. This is the name of the '
'integration bridge on the ESXi. This should not be set '
'for any other Neutron plugin. Hence the default value '
'is not set.'),
] ]
CONF.register_opts(vmwareapi_vif_opts, 'vmware') CONF.register_opts(vmwareapi_vif_opts, 'vmware')
@ -99,63 +103,49 @@ def ensure_vlan_bridge(session, vif, cluster=None, create_vlan=True):
return network_ref return network_ref
def _is_valid_opaque_network_id(opaque_id, bridge_id, integration_bridge, def _check_ovs_supported_version(session):
num_networks): # The port type 'ovs' is only support by the VC version 5.5 onwards
return (opaque_id == bridge_id or min_version = utils.convert_version_to_int(
(num_networks == 1 and constants.MIN_VC_OVS_VERSION)
opaque_id == integration_bridge)) vc_version = utils.convert_version_to_int(
vim_util.get_vc_version(session))
if vc_version < min_version:
LOG.warning(_LW('VMware vCenter version less than %(version)s '
'does not support the \'ovs\' port type.'),
{'version': constants.MIN_VC_OVS_VERSION})
def _get_network_ref_from_opaque(opaque_networks, integration_bridge, bridge): def _get_neutron_network(session, cluster, vif):
num_networks = len(opaque_networks) if vif['type'] == model.VIF_TYPE_OVS:
for network in opaque_networks: _check_ovs_supported_version(session)
if _is_valid_opaque_network_id(network['opaqueNetworkId'], bridge, # Check if this is the NSX-MH plugin is used
integration_bridge, num_networks): if CONF.vmware.integration_bridge:
return {'type': 'OpaqueNetwork', net_id = CONF.vmware.integration_bridge
'network-id': network['opaqueNetworkId'], use_external_id = False
'network-name': network['opaqueNetworkName'], network_type = 'opaque'
'network-type': network['opaqueNetworkType']} else:
LOG.warning(_LW("No valid network found in %(opaque)s, from %(bridge)s " net_id = vif['network']['id']
"or %(integration_bridge)s"), use_external_id = True
{'opaque': opaque_networks, 'bridge': bridge, network_type = 'nsx.LogicalSwitch'
'integration_bridge': integration_bridge}) network_ref = {'type': 'OpaqueNetwork',
'network-id': net_id,
'network-type': network_type,
def _get_opaque_network(session, cluster): 'use-external-id': use_external_id}
host = vm_util.get_host_ref(session, cluster) elif vif['type'] == model.VIF_TYPE_DVS:
try: network_id = vif['network']['bridge']
opaque = session._call_method(vutil,
"get_object_property",
host,
"config.network.opaqueNetwork")
except vexc.InvalidPropertyException:
opaque = None
return opaque
def get_neutron_network(session, network_name, cluster, vif):
opaque = None
if vif['type'] != model.VIF_TYPE_DVS:
opaque = _get_opaque_network(session, cluster)
if opaque:
bridge = vif['network']['id']
opaque_networks = opaque.HostOpaqueNetworkInfo
network_ref = _get_network_ref_from_opaque(opaque_networks,
CONF.vmware.integration_bridge, bridge)
else:
bridge = network_name
network_ref = network_util.get_network_with_the_name( network_ref = network_util.get_network_with_the_name(
session, network_name, cluster) session, network_id, cluster)
if not network_ref: if not network_ref:
raise exception.NetworkNotFoundForBridge(bridge=bridge) raise exception.NetworkNotFoundForBridge(bridge=network_id)
else:
reason = _('vif type %s not supported') % vif['type']
raise exception.InvalidInput(reason=reason)
return network_ref return network_ref
def get_network_ref(session, cluster, vif, is_neutron): def get_network_ref(session, cluster, vif, is_neutron):
if is_neutron: if is_neutron:
network_name = (vif['network']['bridge'] or network_ref = _get_neutron_network(session, cluster, vif)
CONF.vmware.integration_bridge)
network_ref = get_neutron_network(session, network_name, cluster, vif)
else: else:
create_vlan = vif['network'].get_meta('should_create_vlan', False) create_vlan = vif['network'].get_meta('should_create_vlan', False)
network_ref = ensure_vlan_bridge(session, vif, cluster=cluster, network_ref = ensure_vlan_bridge(session, vif, cluster=cluster,

View File

@ -428,6 +428,16 @@ def _create_vif_spec(client_factory, vif_info):
'ns0:VirtualEthernetCardOpaqueNetworkBackingInfo') 'ns0:VirtualEthernetCardOpaqueNetworkBackingInfo')
backing.opaqueNetworkId = network_ref['network-id'] backing.opaqueNetworkId = network_ref['network-id']
backing.opaqueNetworkType = network_ref['network-type'] backing.opaqueNetworkType = network_ref['network-type']
# Configure externalId
if network_ref['use-external-id']:
# externalId is only supported from vCenter 6.0 onwards
if hasattr(net_device, 'externalId'):
net_device.externalId = vif_info['iface_id']
else:
dp = client_factory.create('ns0:DynamicProperty')
dp.name = "__externalId__"
dp.val = vif_info['iface_id']
net_device.dynamicProperty = [dp]
elif (network_ref and elif (network_ref and
network_ref['type'] == "DistributedVirtualPortgroup"): network_ref['type'] == "DistributedVirtualPortgroup"):
backing = client_factory.create( backing = client_factory.create(