<VMWare> Allocate free bus for new SCSI controller

There are at most 16 virtual disks that can be attached onto a SCSI
controller, while if the limitation is reached, error occurs
with message "VMwareDriverException: Invalid configuration for
device '0'".

The reason is that when the controller's quota is used up, a new
controller will be created automatically, but the newly-created
one will try to use bus number 0, which is already used my existing
controller.

This change will allocate a new free bus number for the newly-created
controller.

Co-Authored-By: Qiaowei Ren <qiaowei.ren@intel.com>

Closes bug: #1522232

Change-Id: I2ced1358e682cda979d1d5bddb6359253699280b
(cherry picked from commit b36da5dd03)
This commit is contained in:
Feng Xi Yan 2016-01-26 15:13:36 +08:00 committed by Lee Yarwood
parent 5c7741086b
commit be033eef72
4 changed files with 77 additions and 8 deletions

View File

@ -366,8 +366,9 @@ class VirtualIDEController(DataObject):
class VirtualLsiLogicController(DataObject):
"""VirtualLsiLogicController class."""
def __init__(self, key=0, scsiCtlrUnitNumber=0):
def __init__(self, key=0, scsiCtlrUnitNumber=0, busNumber=0):
self.key = key
self.busNumber = busNumber
self.scsiCtlrUnitNumber = scsiCtlrUnitNumber
self.device = []

View File

@ -215,6 +215,13 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual("ns0:ParaVirtualSCSIController",
config_spec.device.obj_name)
def test_create_controller_spec_with_specfic_bus_number(self):
# Test controller spec with specifc bus number rather default 0
config_spec = vm_util.create_controller_spec(fake.FakeFactory(), -101,
adapter_type=constants.ADAPTER_TYPE_LSILOGICSAS,
bus_number=1)
self.assertEqual(1, config_spec.device.busNumber)
def _vmdk_path_and_adapter_type_devices(self, filename, parent=None):
# Test the adapter_type returned for a lsiLogic sas controller
controller_key = 1000
@ -333,6 +340,27 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual([1], taken[201])
self.assertEqual([7], taken[1000])
def test_get_bus_number_for_scsi_controller(self):
devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=7,
busNumber=0),
fake.VirtualLsiLogicController(1002, scsiCtlrUnitNumber=7,
busNumber=2)]
bus_number = vm_util._get_bus_number_for_scsi_controller(devices)
self.assertEqual(1, bus_number)
def test_get_bus_number_for_scsi_controller_buses_used_up(self):
devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=7,
busNumber=0),
fake.VirtualLsiLogicController(1001, scsiCtlrUnitNumber=7,
busNumber=1),
fake.VirtualLsiLogicController(1002, scsiCtlrUnitNumber=7,
busNumber=2),
fake.VirtualLsiLogicController(1003, scsiCtlrUnitNumber=7,
busNumber=3)]
self.assertRaises(vexc.VMwareDriverException,
vm_util._get_bus_number_for_scsi_controller,
devices)
def test_allocate_controller_key_and_unit_number_ide_default(self):
# Test that default IDE controllers are used when there is a free slot
# on them
@ -387,6 +415,23 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertEqual(8, unit_number)
self.assertIsNone(controller_spec)
def test_allocate_controller_key_and_unit_number_scsi_new_controller(self):
# Test that we allocate on existing SCSI controller if there is a free
# slot on it
devices = [fake.VirtualLsiLogicController(1000, scsiCtlrUnitNumber=15)]
for unit_number in range(15):
disk = fake.VirtualDisk(1000, unit_number)
devices.append(disk)
factory = fake.FakeFactory()
(controller_key, unit_number,
controller_spec) = vm_util.allocate_controller_key_and_unit_number(
factory,
devices,
constants.DEFAULT_ADAPTER_TYPE)
self.assertEqual(-101, controller_key)
self.assertEqual(0, unit_number)
self.assertEqual(1, controller_spec.device.busNumber)
def test_get_vnc_config_spec(self):
fake_factory = fake.FakeFactory()
result = vm_util.get_vnc_config_spec(fake_factory,

View File

@ -57,6 +57,9 @@ ADAPTER_TYPE_IDE = "ide"
ADAPTER_TYPE_LSILOGICSAS = "lsiLogicsas"
ADAPTER_TYPE_PARAVIRTUAL = "paraVirtual"
SCSI_ADAPTER_TYPES = [DEFAULT_ADAPTER_TYPE, ADAPTER_TYPE_LSILOGICSAS,
ADAPTER_TYPE_BUSLOGIC, ADAPTER_TYPE_PARAVIRTUAL]
SUPPORTED_FLAT_VARIANTS = ["thin", "preallocated", "thick", "eagerZeroedThick"]
EXTENSION_KEY = 'org.openstack.compute'
@ -66,6 +69,9 @@ EXTENSION_TYPE_INSTANCE = 'instance'
# One adapter has 16 slots but one reserved for controller
SCSI_MAX_CONNECT_NUMBER = 15
# The max number of SCSI adaptors that could be created on one instance.
SCSI_MAX_CONTROLLER_NUMBER = 4
# This list was extracted from the installation iso image for ESX 6.0.
# It is contained in s.v00, which is gzipped. The list was obtained by
# searching for the string 'otherGuest' in the uncompressed contents of that

View File

@ -361,7 +361,8 @@ def get_vm_resize_spec(client_factory, vcpus, memory_mb, extra_specs,
def create_controller_spec(client_factory, key,
adapter_type=constants.DEFAULT_ADAPTER_TYPE):
adapter_type=constants.DEFAULT_ADAPTER_TYPE,
bus_number=0):
"""Builds a Config Spec for the LSI or Bus Logic Controller's addition
which acts as the controller for the virtual hard disk to be attached
to the VM.
@ -383,7 +384,7 @@ def create_controller_spec(client_factory, key,
virtual_controller = client_factory.create(
'ns0:VirtualLsiLogicController')
virtual_controller.key = key
virtual_controller.busNumber = 0
virtual_controller.busNumber = bus_number
virtual_controller.sharedBus = "noSharing"
virtual_device_config.device = virtual_controller
return virtual_device_config
@ -739,6 +740,19 @@ def _find_allocated_slots(devices):
return taken
def _get_bus_number_for_scsi_controller(devices):
"""Return usable bus number when create new SCSI controller."""
# Every SCSI controller will take a unique bus number
taken = [dev.busNumber for dev in devices if _is_scsi_controller(dev)]
# The max bus number for SCSI controllers is 3
for i in range(constants.SCSI_MAX_CONTROLLER_NUMBER):
if i not in taken:
return i
msg = _('Only %d SCSI controllers are allowed to be '
'created on this instance.') % constants.SCSI_MAX_CONTROLLER_NUMBER
raise vexc.VMwareDriverException(msg)
def allocate_controller_key_and_unit_number(client_factory, devices,
adapter_type):
"""This function inspects the current set of hardware devices and returns
@ -754,10 +768,7 @@ def allocate_controller_key_and_unit_number(client_factory, devices,
if adapter_type == constants.ADAPTER_TYPE_IDE:
ide_keys = [dev.key for dev in devices if _is_ide_controller(dev)]
ret = _find_controller_slot(ide_keys, taken, 2)
elif adapter_type in [constants.DEFAULT_ADAPTER_TYPE,
constants.ADAPTER_TYPE_LSILOGICSAS,
constants.ADAPTER_TYPE_BUSLOGIC,
constants.ADAPTER_TYPE_PARAVIRTUAL]:
elif adapter_type in constants.SCSI_ADAPTER_TYPES:
scsi_keys = [dev.key for dev in devices if _is_scsi_controller(dev)]
ret = _find_controller_slot(scsi_keys, taken, 16)
if ret:
@ -765,8 +776,14 @@ def allocate_controller_key_and_unit_number(client_factory, devices,
# create new controller with the specified type and return its spec
controller_key = -101
# Get free bus number for new SCSI controller.
bus_number = 0
if adapter_type in constants.SCSI_ADAPTER_TYPES:
bus_number = _get_bus_number_for_scsi_controller(devices)
controller_spec = create_controller_spec(client_factory, controller_key,
adapter_type)
adapter_type, bus_number)
return controller_key, 0, controller_spec