Merge "Adds SR-IOV support"

This commit is contained in:
Zuul 2017-11-21 11:16:18 +00:00 committed by Gerrit Code Review
commit 6ab9f17bfe
7 changed files with 214 additions and 4 deletions

View File

@ -73,6 +73,7 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
self.netutils._switch_ports = {}
self.netutils._vlan_sds = {}
self.netutils._profile_sds = {}
self.netutils._hw_offload_sds = {}
self.netutils._vsid_sds = {}
self.netutils._bandwidth_sds = {}
conn = self.netutils._conn
@ -94,6 +95,8 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
mock_bad_sd, mock_sd]
conn.Msvm_EthernetSwitchPortBandwidthSettingData.return_value = [
mock_bad_sd, mock_sd]
conn.Msvm_EthernetSwitchPortOffloadSettingData.return_value = [
mock_bad_sd, mock_sd]
self.netutils.init_caches()
@ -106,6 +109,8 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
self.assertEqual([mock_sd], list(self.netutils._vsid_sds.values()))
self.assertEqual([mock_sd],
list(self.netutils._bandwidth_sds.values()))
self.assertEqual([mock_sd],
list(self.netutils._hw_offload_sds.values()))
def test_update_cache_disabled(self):
self.netutils._enable_cache = False
@ -643,6 +648,36 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
self.assertFalse(self.netutils._jobutils.add_virt_feature.called)
@mock.patch.object(networkutils.NetworkUtils,
'_get_hw_offload_sd_from_port_alloc')
def test_set_vswitch_port_sriov_already_set(self, mock_get_hw_offload_sd):
mock_port_alloc = self._mock_get_switch_port_alloc()
mock_hw_offload_sd = mock_get_hw_offload_sd.return_value
mock_hw_offload_sd.IOVOffloadWeight = self.netutils._IOV_ENABLED
self.netutils.set_vswitch_port_sriov(mock.sentinel.port_name,
True)
mock_get_hw_offload_sd.assert_called_once_with(mock_port_alloc)
self.netutils._jobutils.modify_virt_feature.assert_not_called()
@ddt.data(True, False)
@mock.patch.object(networkutils.NetworkUtils,
'_get_hw_offload_sd_from_port_alloc')
def test_set_vswitch_port_sriov(self, state, mock_get_hw_offload_sd):
mock_port_alloc = self._mock_get_switch_port_alloc()
mock_hw_offload_sd = mock_get_hw_offload_sd.return_value
self.netutils.set_vswitch_port_sriov(mock.sentinel.port_name,
state)
mock_get_hw_offload_sd.assert_called_once_with(mock_port_alloc)
self.netutils._jobutils.modify_virt_feature.assert_called_with(
mock_hw_offload_sd)
desired_state = (self.netutils._IOV_ENABLED if state else
self.netutils._IOV_DISABLED)
self.assertEqual(desired_state, mock_hw_offload_sd.IOVOffloadWeight)
@mock.patch.object(networkutils.NetworkUtils,
'_get_setting_data_from_port_alloc')
def test_get_profile_setting_data_from_port_alloc(self, mock_get_sd):
@ -677,6 +712,17 @@ class NetworkUtilsTestCase(test_base.OsWinBaseTestCase):
mock_port, self.netutils._vsid_sds,
self.netutils._PORT_SECURITY_SET_DATA)
@mock.patch.object(networkutils.NetworkUtils,
'_get_setting_data_from_port_alloc')
def test_get_hw_offload_sd_from_port_alloc(self, mock_get_sd):
mock_port = mock.MagicMock()
result = self.netutils._get_hw_offload_sd_from_port_alloc(mock_port)
self.assertEqual(mock_get_sd.return_value, result)
mock_get_sd.assert_called_once_with(
mock_port, self.netutils._hw_offload_sds,
self.netutils._PORT_HW_OFFLOAD_SET_DATA)
@mock.patch.object(networkutils.NetworkUtils,
'_get_setting_data_from_port_alloc')
def test_get_bandwidth_setting_data_from_port_alloc(self, mock_get_sd):

View File

@ -47,10 +47,21 @@ class HostUtilsTestCase(test_base.OsWinBaseTestCase):
def setUp(self):
self._hostutils = hostutils.HostUtils()
self._hostutils._conn_cimv2 = mock.MagicMock()
self._hostutils._conn_scimv2 = mock.MagicMock()
self._hostutils._conn_attr = mock.MagicMock()
self._hostutils._netutils_prop = mock.MagicMock()
self._conn = self._hostutils._conn
self._conn_scimv2 = self._hostutils._conn_scimv2
self._netutils = self._hostutils._netutils
super(HostUtilsTestCase, self).setUp()
@mock.patch('os_win.utilsfactory.get_networkutils')
def test_netutils(self, mock_get_networkutils):
self._hostutils._netutils_prop = None
self.assertEqual(self._hostutils._netutils,
mock_get_networkutils.return_value)
@mock.patch('os_win.utils.hostutils.kernel32')
def test_get_host_tick_count64(self, mock_kernel32):
tick_count64 = "100"
@ -175,6 +186,37 @@ class HostUtilsTestCase(test_base.OsWinBaseTestCase):
mock_sv_feature_cls.assert_called_once_with(
ID=mock.sentinel.feature_id)
def test_get_nic_sriov_vfs(self):
mock_vswitch_sd = mock.Mock()
mock_hw_offload_sd_bad = mock.Mock(IovVfCapacity=0)
mock_hw_offload_sd_ok = mock.Mock()
vswitch_sds_class = self._conn.Msvm_VirtualEthernetSwitchSettingData
vswitch_sds_class.return_value = [mock_vswitch_sd] * 3
self._conn.Msvm_EthernetSwitchHardwareOffloadData.side_effect = [
[mock_hw_offload_sd_bad], [mock_hw_offload_sd_ok],
[mock_hw_offload_sd_ok]]
self._netutils.get_vswitch_external_network_name.side_effect = [
None, mock.sentinel.nic_name]
mock_nic = mock.Mock()
self._conn_scimv2.MSFT_NetAdapter.return_value = [mock_nic]
vfs = self._hostutils.get_nic_sriov_vfs()
expected = {
'vswitch_name': mock_vswitch_sd.ElementName,
'device_id': mock_nic.PnPDeviceID,
'total_vfs': mock_hw_offload_sd_ok.IovVfCapacity,
'used_vfs': mock_hw_offload_sd_ok.IovVfUsage,
}
self.assertEqual([expected], vfs)
vswitch_sds_class.assert_called_once_with(IOVPreferred=True)
self._conn.Msvm_EthernetSwitchHardwareOffloadData.assert_has_calls([
mock.call(SystemName=mock_vswitch_sd.VirtualSystemIdentifier)] * 3)
self._netutils.get_vswitch_external_network_name.assert_has_calls([
mock.call(mock_vswitch_sd.ElementName)] * 2)
self._conn_scimv2.MSFT_NetAdapter.assert_called_once_with(
InterfaceDescription=mock.sentinel.nic_name)
def _check_get_numa_nodes_missing_info(self):
numa_node = mock.MagicMock()
self._hostutils._conn.Msvm_NumaNode.return_value = [

View File

@ -238,6 +238,11 @@ class JobUtilsTestCase(test_base.OsWinBaseTestCase):
True, mock.sentinel.vm_path,
[mock.sentinel.res_data])
def test_modify_virt_feature(self):
self._test_virt_method('ModifyFeatureSettings', 3,
'modify_virt_feature', False,
FeatureSettings=[mock.sentinel.res_data])
def test_remove_virt_feature(self):
self._test_virt_method('RemoveFeatureSettings', 2,
'remove_virt_feature', False,

View File

@ -652,17 +652,24 @@ class VMUtils(baseutils.BaseUtilsVirt):
raise exceptions.HyperVvNicNotFound(vnic_name=name)
def create_nic(self, vm_name, nic_name, mac_address):
"""Create a (synthetic) nic and attach it to the vm."""
def create_nic(self, vm_name, nic_name, mac_address=None):
"""Create a (synthetic) nic and attach it to the vm.
:param vm_name: The VM name to which the NIC will be attached to.
:param nic_name: The name of the NIC to be attached.
:param mac_address: The VM NIC's MAC address. If None, a Dynamic MAC
address will be used instead.
"""
# Create a new nic
new_nic_data = self._get_new_setting_data(
self._SYNTHETIC_ETHERNET_PORT_SETTING_DATA_CLASS)
# Configure the nic
new_nic_data.ElementName = nic_name
new_nic_data.Address = mac_address.replace(':', '')
new_nic_data.StaticMacAddress = 'True'
new_nic_data.VirtualSystemIdentifiers = ['{' + str(uuid.uuid4()) + '}']
if mac_address:
new_nic_data.Address = mac_address.replace(':', '')
new_nic_data.StaticMacAddress = 'True'
# Add the new nic to the vm
vmsettings = self._lookup_vm_check(vm_name)

View File

@ -47,11 +47,25 @@ class HostUtils(baseutils.BaseUtilsVirt):
FEATURE_MPIO = 57
_wmi_cimv2_namespace = '//./root/cimv2'
_wmi_standard_cimv2_namespace = '//./root/StandardCimv2'
def __init__(self, host='.'):
super(HostUtils, self).__init__(host)
self._conn_cimv2 = self._get_wmi_conn(self._wmi_cimv2_namespace,
privileges=["Shutdown"])
self._conn_scimv2 = self._get_wmi_conn(
self._wmi_standard_cimv2_namespace)
self._netutils_prop = None
@property
def _netutils(self):
if not self._netutils_prop:
# NOTE(claudiub): we're importing utilsfactory here in order to
# avoid circular dependencies.
from os_win import utilsfactory
self._netutils_prop = utilsfactory.get_networkutils()
return self._netutils_prop
def get_cpus_info(self):
"""Returns dictionary containing information about the host's CPUs."""
@ -167,6 +181,54 @@ class HostUtils(baseutils.BaseUtilsVirt):
"""Checks if the given feature exists on the host."""
return len(self._conn_cimv2.Win32_ServerFeature(ID=feature_id)) > 0
def get_nic_sriov_vfs(self):
"""Get host's NIC SR-IOV VFs.
This method will ignore the vSwitches which do not have SR-IOV enabled,
or which are poorly configured (the NIC does not support SR-IOV).
:returns: a list of dictionaries, containing the following fields:
- 'vswitch_name': the vSwtch name.
- 'total_vfs': the vSwitch's maximum number of VFs. (> 0)
- 'used_vfs': the vSwitch's number of used VFs. (<= 'total_vfs')
"""
vfs = []
# NOTE(claudiub): A vSwitch will have to be configured to enable
# SR-IOV, otherwise its IOVPreferred flag will be False.
vswitch_sds = self._conn.Msvm_VirtualEthernetSwitchSettingData(
IOVPreferred=True)
for vswitch_sd in vswitch_sds:
hw_offload = self._conn.Msvm_EthernetSwitchHardwareOffloadData(
SystemName=vswitch_sd.VirtualSystemIdentifier)[0]
if not hw_offload.IovVfCapacity:
LOG.warning("VSwitch %s has SR-IOV enabled, but it is not "
"supported by the NIC or by the OS.",
vswitch_sd.ElementName)
continue
nic_name = self._netutils.get_vswitch_external_network_name(
vswitch_sd.ElementName)
if not nic_name:
# NOTE(claudiub): This can happen if the vSwitch is not
# external.
LOG.warning("VSwitch %s is not external.",
vswitch_sd.ElementName)
continue
nic = self._conn_scimv2.MSFT_NetAdapter(
InterfaceDescription=nic_name)[0]
vfs.append({
'vswitch_name': vswitch_sd.ElementName,
'device_id': nic.PnPDeviceID,
'total_vfs': hw_offload.IovVfCapacity,
'used_vfs': hw_offload.IovVfUsage,
})
return vfs
def get_numa_nodes(self):
"""Returns the host's list of NUMA nodes.

View File

@ -215,6 +215,14 @@ class JobUtils(baseutils.BaseUtilsVirt):
parent.path_(), [f.GetText_(1) for f in virt_features])
self.check_ret_val(ret_val, job_path)
@_utils.not_found_decorator()
@_utils.retry_decorator(exceptions=exceptions.HyperVException)
def modify_virt_feature(self, virt_feature):
(job_path, out_set_data,
ret_val) = self._vs_man_svc.ModifyFeatureSettings(
FeatureSettings=[virt_feature.GetText_(1)])
self.check_ret_val(ret_val, job_path)
def remove_virt_feature(self, virt_feature):
self.remove_multiple_virt_features([virt_feature])

View File

@ -62,6 +62,7 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
_PORT_VLAN_SET_DATA = 'Msvm_EthernetSwitchPortVlanSettingData'
_PORT_PROFILE_SET_DATA = 'Msvm_EthernetSwitchPortProfileSettingData'
_PORT_SECURITY_SET_DATA = 'Msvm_EthernetSwitchPortSecuritySettingData'
_PORT_HW_OFFLOAD_SET_DATA = 'Msvm_EthernetSwitchPortOffloadSettingData'
_PORT_ALLOC_ACL_SET_DATA = 'Msvm_EthernetSwitchPortAclSettingData'
_PORT_BANDWIDTH_SET_DATA = 'Msvm_EthernetSwitchPortBandwidthSettingData'
_PORT_EXT_ACL_SET_DATA = _PORT_ALLOC_ACL_SET_DATA
@ -72,6 +73,9 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
_VM_SUMMARY_ENABLED_STATE = 100
_HYPERV_VM_STATE_ENABLED = 2
_IOV_ENABLED = 100
_IOV_DISABLED = 0
_ACL_DIR_IN = 1
_ACL_DIR_OUT = 2
@ -103,6 +107,7 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
_switch_ports = {}
_vlan_sds = {}
_profile_sds = {}
_hw_offload_sds = {}
_vsid_sds = {}
_sg_acl_sds = {}
_bandwidth_sds = {}
@ -159,6 +164,14 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
if match:
self._bandwidth_sds[match.group()] = bandwidth_sd
# map between switch port's InstanceID and their HW offload setting
# data WMI objects.
hw_offloads = self._conn.Msvm_EthernetSwitchPortOffloadSettingData()
for hw_offload_sd in hw_offloads:
match = switch_port_id_regex.match(hw_offload_sd.InstanceID)
if match:
self._hw_offload_sds[match.group()] = hw_offload_sd
def update_cache(self):
if not self._enable_cache:
return
@ -359,6 +372,7 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
self._vlan_sds.pop(sw_port.InstanceID, None)
self._vsid_sds.pop(sw_port.InstanceID, None)
self._bandwidth_sds.pop(sw_port.InstanceID, None)
self._hw_offload_sds.pop(sw_port.InstanceID, None)
def set_vswitch_port_profile_id(self, switch_port_name, profile_id,
profile_data, profile_name, vendor_name,
@ -559,6 +573,28 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
raise exceptions.HyperVException(
_('Port Security Settings not found: %s') % switch_port_name)
def set_vswitch_port_sriov(self, switch_port_name, enabled):
"""Enables / Disables SR-IOV for the given port.
:param switch_port_name: the name of the port which will have SR-IOV
enabled or disabled.
:param enabled: boolean, if SR-IOV should be turned on or off.
"""
port_alloc = self._get_switch_port_allocation(switch_port_name)[0]
# NOTE(claudiub): All ports have a HW offload SD.
hw_offload_sd = self._get_hw_offload_sd_from_port_alloc(port_alloc)
desired_state = self._IOV_ENABLED if enabled else self._IOV_DISABLED
if hw_offload_sd.IOVOffloadWeight == desired_state:
# already in the desired state. noop.
return
hw_offload_sd.IOVOffloadWeight = desired_state
# NOTE(claudiub): The HW offload SD can simply be modified. No need to
# remove it and create a new one.
self._jobutils.modify_virt_feature(hw_offload_sd)
def _get_profile_setting_data_from_port_alloc(self, port_alloc):
return self._get_setting_data_from_port_alloc(
port_alloc, self._profile_sds, self._PORT_PROFILE_SET_DATA)
@ -571,6 +607,10 @@ class NetworkUtils(baseutils.BaseUtilsVirt):
return self._get_setting_data_from_port_alloc(
port_alloc, self._vsid_sds, self._PORT_SECURITY_SET_DATA)
def _get_hw_offload_sd_from_port_alloc(self, port_alloc):
return self._get_setting_data_from_port_alloc(
port_alloc, self._hw_offload_sds, self._PORT_HW_OFFLOAD_SET_DATA)
def _get_bandwidth_setting_data_from_port_alloc(self, port_alloc):
return self._get_setting_data_from_port_alloc(
port_alloc, self._bandwidth_sds, self._PORT_BANDWIDTH_SET_DATA)