github.com/vmware/govmomi@v0.51.0/vim25/types/configspec.go (about)

     1  // © Broadcom. All Rights Reserved.
     2  // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries.
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package types
     6  
     7  import (
     8  	"fmt"
     9  )
    10  
    11  // EnsureDisksHaveControllers ensures that all disks in the provided
    12  // ConfigSpec point to a controller. If no controller exists, LSILogic SCSI
    13  // controllers are added to the ConfigSpec as necessary for the disks.
    14  //
    15  // Please note the following table for the number of controllers of each type
    16  // that are supported as well as how many disks (per controller) each supports:
    17  //
    18  // SATA
    19  //   - controllers                                    4
    20  //   - disks                                         30
    21  //
    22  // SCSI
    23  //   - controllers                                    4
    24  //   - disks (non-paravirtual)                       16
    25  //   - disks (paravirtual, hardware version <14)     16
    26  //   - disks (paravirtual, hardware version >=14)   256
    27  //
    28  // NVME
    29  //   - controllers                                    4
    30  //   - disks (hardware version <20)                  15
    31  //   - disks (hardware version >=21)                255
    32  func (cs *VirtualMachineConfigSpec) EnsureDisksHaveControllers(
    33  	existingDevices ...BaseVirtualDevice) error {
    34  
    35  	if cs == nil {
    36  		panic("configSpec is nil")
    37  	}
    38  
    39  	var (
    40  		disks           []*VirtualDisk
    41  		newDeviceKey    int32
    42  		pciController   *VirtualPCIController
    43  		diskControllers = ensureDiskControllerData{
    44  			controllerKeys:                map[int32]BaseVirtualController{},
    45  			controllerKeysToAttachedDisks: map[int32]int{},
    46  		}
    47  	)
    48  
    49  	// Inspect the ConfigSpec
    50  	for i := range cs.DeviceChange {
    51  		var (
    52  			bdc BaseVirtualDeviceConfigSpec
    53  			bvd BaseVirtualDevice
    54  			dc  *VirtualDeviceConfigSpec
    55  			d   *VirtualDevice
    56  		)
    57  
    58  		if bdc = cs.DeviceChange[i]; bdc == nil {
    59  			continue
    60  		}
    61  
    62  		if dc = bdc.GetVirtualDeviceConfigSpec(); dc == nil {
    63  			continue
    64  		}
    65  
    66  		if dc.Operation == VirtualDeviceConfigSpecOperationRemove {
    67  			// Do not consider devices being removed.
    68  			continue
    69  		}
    70  
    71  		bvd = dc.Device
    72  		if bvd == nil {
    73  			continue
    74  		}
    75  
    76  		if d = bvd.GetVirtualDevice(); d == nil {
    77  			continue
    78  		}
    79  
    80  		switch tvd := bvd.(type) {
    81  		case *VirtualPCIController:
    82  			pciController = tvd
    83  
    84  		case
    85  			// SCSI
    86  			*ParaVirtualSCSIController,
    87  			*VirtualBusLogicController,
    88  			*VirtualLsiLogicController,
    89  			*VirtualLsiLogicSASController,
    90  			*VirtualSCSIController,
    91  
    92  			// SATA
    93  			*VirtualSATAController,
    94  			*VirtualAHCIController,
    95  
    96  			// NVME
    97  			*VirtualNVMEController:
    98  
    99  			diskControllers.add(bvd)
   100  
   101  		case *VirtualDisk:
   102  
   103  			disks = append(disks, tvd)
   104  
   105  			if controllerKey := d.ControllerKey; controllerKey != 0 {
   106  				// If the disk points to a controller key, then increment
   107  				// the number of devices attached to that controller.
   108  				//
   109  				// Please note that at this point it is not yet known if the
   110  				// controller key is a *valid* controller.
   111  				diskControllers.attach(controllerKey)
   112  			}
   113  		}
   114  
   115  		// Keep track of the smallest device key used. Please note, because
   116  		// device keys in a ConfigSpec are negative numbers, -200 going to be
   117  		// smaller than -1.
   118  		if d.Key < newDeviceKey {
   119  			newDeviceKey = d.Key
   120  		}
   121  	}
   122  
   123  	if len(disks) == 0 {
   124  		// If there are no disks, then go ahead and return early.
   125  		return nil
   126  	}
   127  
   128  	// Categorize any controllers that already exist.
   129  	for i := range existingDevices {
   130  		var (
   131  			d   *VirtualDevice
   132  			bvd = existingDevices[i]
   133  		)
   134  
   135  		if bvd == nil {
   136  			continue
   137  		}
   138  
   139  		if d = bvd.GetVirtualDevice(); d == nil {
   140  			continue
   141  		}
   142  
   143  		switch tvd := bvd.(type) {
   144  		case *VirtualPCIController:
   145  			pciController = tvd
   146  		case
   147  			// SCSI
   148  			*ParaVirtualSCSIController,
   149  			*VirtualBusLogicController,
   150  			*VirtualLsiLogicController,
   151  			*VirtualLsiLogicSASController,
   152  			*VirtualSCSIController,
   153  
   154  			// SATA
   155  			*VirtualSATAController,
   156  			*VirtualAHCIController,
   157  
   158  			// NVME
   159  			*VirtualNVMEController:
   160  
   161  			diskControllers.add(bvd)
   162  
   163  		case *VirtualDisk:
   164  			diskControllers.attach(tvd.ControllerKey)
   165  		}
   166  	}
   167  
   168  	// Decrement the newDeviceKey so the next device has a unique key.
   169  	newDeviceKey--
   170  
   171  	if pciController == nil {
   172  		// Add a PCI controller if one is not present.
   173  		pciController = &VirtualPCIController{
   174  			VirtualController: VirtualController{
   175  				VirtualDevice: VirtualDevice{
   176  					Key: newDeviceKey,
   177  				},
   178  			},
   179  		}
   180  
   181  		// Decrement the newDeviceKey so the next device has a unique key.
   182  		newDeviceKey--
   183  
   184  		// Add the new PCI controller to the ConfigSpec.
   185  		cs.DeviceChange = append(
   186  			cs.DeviceChange,
   187  			&VirtualDeviceConfigSpec{
   188  				Operation: VirtualDeviceConfigSpecOperationAdd,
   189  				Device:    pciController,
   190  			})
   191  	}
   192  
   193  	// Ensure all the recorded controller keys that point to disks are actually
   194  	// valid controller keys.
   195  	diskControllers.validateAttachments()
   196  
   197  	for i := range disks {
   198  		disk := disks[i]
   199  
   200  		// If the disk already points to a controller then skip to the next
   201  		// disk.
   202  		if diskControllers.exists(disk.ControllerKey) {
   203  			continue
   204  		}
   205  
   206  		// The disk does not point to a controller, so try to locate one.
   207  		if ensureDiskControllerFind(disk, &diskControllers) {
   208  			// A controller was located for the disk, so go ahead and skip to
   209  			// the next disk.
   210  			continue
   211  		}
   212  
   213  		// No controller was located for the disk, so a controller must be
   214  		// created.
   215  		if err := ensureDiskControllerCreate(
   216  			cs,
   217  			pciController,
   218  			newDeviceKey,
   219  			&diskControllers); err != nil {
   220  
   221  			return err
   222  		}
   223  
   224  		// Point the disk to the new controller.
   225  		disk.ControllerKey = newDeviceKey
   226  
   227  		// Add the controller key to the map that tracks how many disks are
   228  		// attached to a given controller.
   229  		diskControllers.attach(newDeviceKey)
   230  
   231  		// Decrement the newDeviceKey so the next device has a unique key.
   232  		newDeviceKey--
   233  	}
   234  
   235  	return nil
   236  }
   237  
   238  const (
   239  	maxSCSIControllers                     = 4
   240  	maxSATAControllers                     = 4
   241  	maxNVMEControllers                     = 4
   242  	maxDisksPerSCSIController              = 16
   243  	maxDisksPerPVSCSIControllerHWVersion14 = 256 // TODO(akutz)
   244  	maxDisksPerSATAController              = 30
   245  	maxDisksPerNVMEController              = 15
   246  	maxDisksPerNVMEControllerHWVersion21   = 255 // TODO(akutz)
   247  )
   248  
   249  type ensureDiskControllerBusNumbers struct {
   250  	zero bool
   251  	one  bool
   252  	two  bool
   253  }
   254  
   255  func (d ensureDiskControllerBusNumbers) free() int32 {
   256  	switch {
   257  	case !d.zero:
   258  		return 0
   259  	case !d.one:
   260  		return 1
   261  	case !d.two:
   262  		return 2
   263  	default:
   264  		return 3
   265  	}
   266  }
   267  
   268  func (d *ensureDiskControllerBusNumbers) set(busNumber int32) {
   269  	switch busNumber {
   270  	case 0:
   271  		d.zero = true
   272  	case 1:
   273  		d.one = true
   274  	case 2:
   275  		d.two = true
   276  	}
   277  }
   278  
   279  type ensureDiskControllerData struct {
   280  	// TODO(akutz) Use the hardware version when calculating the max disks for
   281  	//             a given controller type.
   282  	// hardwareVersion int
   283  
   284  	controllerKeys                map[int32]BaseVirtualController
   285  	controllerKeysToAttachedDisks map[int32]int
   286  
   287  	// SCSI
   288  	scsiBusNumbers             ensureDiskControllerBusNumbers
   289  	pvSCSIControllerKeys       []int32
   290  	busLogicSCSIControllerKeys []int32
   291  	lsiLogicControllerKeys     []int32
   292  	lsiLogicSASControllerKeys  []int32
   293  	scsiControllerKeys         []int32
   294  
   295  	// SATA
   296  	sataBusNumbers     ensureDiskControllerBusNumbers
   297  	sataControllerKeys []int32
   298  	ahciControllerKeys []int32
   299  
   300  	// NVME
   301  	nvmeBusNumbers     ensureDiskControllerBusNumbers
   302  	nvmeControllerKeys []int32
   303  }
   304  
   305  func (d ensureDiskControllerData) numSCSIControllers() int {
   306  	return len(d.pvSCSIControllerKeys) +
   307  		len(d.busLogicSCSIControllerKeys) +
   308  		len(d.lsiLogicControllerKeys) +
   309  		len(d.lsiLogicSASControllerKeys) +
   310  		len(d.scsiControllerKeys)
   311  }
   312  
   313  func (d ensureDiskControllerData) numSATAControllers() int {
   314  	return len(d.sataControllerKeys) + len(d.ahciControllerKeys)
   315  }
   316  
   317  func (d ensureDiskControllerData) numNVMEControllers() int {
   318  	return len(d.nvmeControllerKeys)
   319  }
   320  
   321  // validateAttachments ensures the attach numbers are correct by removing any
   322  // keys from controllerKeysToAttachedDisks that do not also exist in
   323  // controllerKeys.
   324  func (d ensureDiskControllerData) validateAttachments() {
   325  	// Remove any invalid controllers from controllerKeyToNumDiskMap.
   326  	for key := range d.controllerKeysToAttachedDisks {
   327  		if _, ok := d.controllerKeys[key]; !ok {
   328  			delete(d.controllerKeysToAttachedDisks, key)
   329  		}
   330  	}
   331  }
   332  
   333  // exists returns true if a controller with the provided key exists.
   334  func (d ensureDiskControllerData) exists(key int32) bool {
   335  	return d.controllerKeys[key] != nil
   336  }
   337  
   338  // add records the provided controller in the map that relates keys to
   339  // controllers as well as appends the key to the list of controllers of that
   340  // given type.
   341  func (d *ensureDiskControllerData) add(controller BaseVirtualDevice) {
   342  
   343  	// Get the controller's device key.
   344  	bvc := controller.(BaseVirtualController)
   345  	key := bvc.GetVirtualController().Key
   346  	busNumber := bvc.GetVirtualController().BusNumber
   347  
   348  	// Record the controller's device key in the controller key map.
   349  	d.controllerKeys[key] = bvc
   350  
   351  	// Record the controller's device key in the list for that type of
   352  	// controller.
   353  	switch controller.(type) {
   354  
   355  	// SCSI
   356  	case *ParaVirtualSCSIController:
   357  		d.pvSCSIControllerKeys = append(d.pvSCSIControllerKeys, key)
   358  		d.scsiBusNumbers.set(busNumber)
   359  	case *VirtualBusLogicController:
   360  		d.busLogicSCSIControllerKeys = append(d.busLogicSCSIControllerKeys, key)
   361  		d.scsiBusNumbers.set(busNumber)
   362  	case *VirtualLsiLogicController:
   363  		d.lsiLogicControllerKeys = append(d.lsiLogicControllerKeys, key)
   364  		d.scsiBusNumbers.set(busNumber)
   365  	case *VirtualLsiLogicSASController:
   366  		d.lsiLogicSASControllerKeys = append(d.lsiLogicSASControllerKeys, key)
   367  		d.scsiBusNumbers.set(busNumber)
   368  	case *VirtualSCSIController:
   369  		d.scsiControllerKeys = append(d.scsiControllerKeys, key)
   370  		d.scsiBusNumbers.set(busNumber)
   371  
   372  	// SATA
   373  	case *VirtualSATAController:
   374  		d.sataControllerKeys = append(d.sataControllerKeys, key)
   375  		d.sataBusNumbers.set(busNumber)
   376  	case *VirtualAHCIController:
   377  		d.ahciControllerKeys = append(d.ahciControllerKeys, key)
   378  		d.sataBusNumbers.set(busNumber)
   379  
   380  	// NVME
   381  	case *VirtualNVMEController:
   382  		d.nvmeControllerKeys = append(d.nvmeControllerKeys, key)
   383  		d.nvmeBusNumbers.set(busNumber)
   384  	}
   385  }
   386  
   387  // attach increments the number of disks attached to the controller identified
   388  // by the provided controller key.
   389  func (d *ensureDiskControllerData) attach(controllerKey int32) {
   390  	d.controllerKeysToAttachedDisks[controllerKey]++
   391  }
   392  
   393  // hasFreeSlot returns whether or not the controller identified by the provided
   394  // controller key has a free slot to attach a disk.
   395  //
   396  // TODO(akutz) Consider the hardware version when calculating these values.
   397  func (d *ensureDiskControllerData) hasFreeSlot(controllerKey int32) bool {
   398  
   399  	var maxDisksForType int
   400  
   401  	switch d.controllerKeys[controllerKey].(type) {
   402  	case
   403  		// SCSI (paravirtual)
   404  		*ParaVirtualSCSIController:
   405  
   406  		maxDisksForType = maxDisksPerSCSIController
   407  
   408  	case
   409  		// SCSI (non-paravirtual)
   410  		*VirtualBusLogicController,
   411  		*VirtualLsiLogicController,
   412  		*VirtualLsiLogicSASController,
   413  		*VirtualSCSIController:
   414  
   415  		maxDisksForType = maxDisksPerSCSIController
   416  
   417  	case
   418  		// SATA
   419  		*VirtualSATAController,
   420  		*VirtualAHCIController:
   421  
   422  		maxDisksForType = maxDisksPerSATAController
   423  
   424  	case
   425  		// NVME
   426  		*VirtualNVMEController:
   427  
   428  		maxDisksForType = maxDisksPerNVMEController
   429  	}
   430  
   431  	return d.controllerKeysToAttachedDisks[controllerKey] < maxDisksForType-1
   432  }
   433  
   434  // ensureDiskControllerFind attempts to locate a controller for the provided
   435  // disk.
   436  //
   437  // Please note this function is written to preserve the order in which
   438  // controllers are located by preferring controller types in the order in which
   439  // they are listed in this function. This prevents the following situation:
   440  //
   441  //   - A ConfigSpec has three controllers in the following order: PVSCSI-1,
   442  //     NVME-1, and PVSCSI-2.
   443  //   - The controller PVSCSI-1 is full while NVME-1 and PVSCSI-2 have free
   444  //     slots.
   445  //   - The *desired* behavior is to look at all, possible PVSCSI controllers
   446  //     before moving onto SATA and then finally NVME controllers.
   447  //   - If the function iterated over the device list in list-order, then the
   448  //     NVME-1 controller would be located first.
   449  //   - Instead, this function iterates over each *type* of controller first
   450  //     before moving onto the next type.
   451  //   - This means that even though NVME-1 has free slots, PVSCSI-2 is checked
   452  //     first.
   453  //
   454  // The order of preference is as follows:
   455  //
   456  // * SCSI
   457  //   - ParaVirtualSCSIController
   458  //   - VirtualBusLogicController
   459  //   - VirtualLsiLogicController
   460  //   - VirtualLsiLogicSASController
   461  //   - VirtualSCSIController
   462  //
   463  // * SATA
   464  //   - VirtualSATAController
   465  //   - VirtualAHCIController
   466  //
   467  // * NVME
   468  //   - VirtualNVMEController
   469  func ensureDiskControllerFind(
   470  	disk *VirtualDisk,
   471  	diskControllers *ensureDiskControllerData) bool {
   472  
   473  	return false ||
   474  		// SCSI
   475  		ensureDiskControllerFindWith(
   476  			disk,
   477  			diskControllers,
   478  			diskControllers.pvSCSIControllerKeys) ||
   479  		ensureDiskControllerFindWith(
   480  			disk,
   481  			diskControllers,
   482  			diskControllers.busLogicSCSIControllerKeys) ||
   483  		ensureDiskControllerFindWith(
   484  			disk,
   485  			diskControllers,
   486  			diskControllers.lsiLogicControllerKeys) ||
   487  		ensureDiskControllerFindWith(
   488  			disk,
   489  			diskControllers,
   490  			diskControllers.lsiLogicSASControllerKeys) ||
   491  		ensureDiskControllerFindWith(
   492  			disk,
   493  			diskControllers,
   494  			diskControllers.scsiControllerKeys) ||
   495  
   496  		// SATA
   497  		ensureDiskControllerFindWith(
   498  			disk,
   499  			diskControllers,
   500  			diskControllers.sataControllerKeys) ||
   501  		ensureDiskControllerFindWith(
   502  			disk,
   503  			diskControllers,
   504  			diskControllers.ahciControllerKeys) ||
   505  
   506  		// NVME
   507  		ensureDiskControllerFindWith(
   508  			disk,
   509  			diskControllers,
   510  			diskControllers.nvmeControllerKeys)
   511  }
   512  
   513  func ensureDiskControllerFindWith(
   514  	disk *VirtualDisk,
   515  	diskControllers *ensureDiskControllerData,
   516  	controllerKeys []int32) bool {
   517  
   518  	for i := range controllerKeys {
   519  		controllerKey := controllerKeys[i]
   520  		if diskControllers.hasFreeSlot(controllerKey) {
   521  			// If the controller has room for another disk, then use this
   522  			// controller for the current disk.
   523  			disk.ControllerKey = controllerKey
   524  			diskControllers.attach(controllerKey)
   525  			return true
   526  		}
   527  	}
   528  	return false
   529  }
   530  
   531  func ensureDiskControllerCreate(
   532  	configSpec *VirtualMachineConfigSpec,
   533  	pciController *VirtualPCIController,
   534  	newDeviceKey int32,
   535  	diskControllers *ensureDiskControllerData) error {
   536  
   537  	var controller BaseVirtualDevice
   538  	switch {
   539  	case diskControllers.numSCSIControllers() < maxSCSIControllers:
   540  		// Prefer creating a new SCSI controller.
   541  		controller = &ParaVirtualSCSIController{
   542  			VirtualSCSIController: VirtualSCSIController{
   543  				VirtualController: VirtualController{
   544  					VirtualDevice: VirtualDevice{
   545  						ControllerKey: pciController.Key,
   546  						Key:           newDeviceKey,
   547  					},
   548  					BusNumber: diskControllers.scsiBusNumbers.free(),
   549  				},
   550  				HotAddRemove: NewBool(true),
   551  				SharedBus:    VirtualSCSISharingNoSharing,
   552  			},
   553  		}
   554  	case diskControllers.numSATAControllers() < maxSATAControllers:
   555  		// If there are no more SCSI controllers, create a SATA
   556  		// controller.
   557  		controller = &VirtualAHCIController{
   558  			VirtualSATAController: VirtualSATAController{
   559  				VirtualController: VirtualController{
   560  					VirtualDevice: VirtualDevice{
   561  						ControllerKey: pciController.Key,
   562  						Key:           newDeviceKey,
   563  					},
   564  					BusNumber: diskControllers.sataBusNumbers.free(),
   565  				},
   566  			},
   567  		}
   568  	case diskControllers.numNVMEControllers() < maxNVMEControllers:
   569  		// If there are no more SATA controllers, create an NVME
   570  		// controller.
   571  		controller = &VirtualNVMEController{
   572  			VirtualController: VirtualController{
   573  				VirtualDevice: VirtualDevice{
   574  					ControllerKey: pciController.Key,
   575  					Key:           newDeviceKey,
   576  				},
   577  				BusNumber: diskControllers.nvmeBusNumbers.free(),
   578  			},
   579  			SharedBus: string(VirtualNVMEControllerSharingNoSharing),
   580  		}
   581  	default:
   582  		return fmt.Errorf("no controllers available")
   583  	}
   584  
   585  	// Add the new controller to the ConfigSpec.
   586  	configSpec.DeviceChange = append(
   587  		configSpec.DeviceChange,
   588  		&VirtualDeviceConfigSpec{
   589  			Operation: VirtualDeviceConfigSpecOperationAdd,
   590  			Device:    controller,
   591  		})
   592  
   593  	// Record the new controller.
   594  	diskControllers.add(controller)
   595  
   596  	return nil
   597  }