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

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