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 }