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 }