Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions api/v1alpha5/virtualmachine_hardware_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,19 @@ const (
VirtualControllerTypeSATA VirtualControllerType = "SATA"
)

// MaxCount returns the maximum number of controllers per VM.
func (t VirtualControllerType) MaxCount() int32 {
switch t {
case VirtualControllerTypeIDE:
return 2
case VirtualControllerTypeNVME,
VirtualControllerTypeSATA,
VirtualControllerTypeSCSI:
return 4
}
return 0
}

type VirtualControllerSharingMode string

const (
Expand Down Expand Up @@ -54,6 +67,21 @@ type IDEControllerSpec struct {
BusNumber int32 `json:"busNumber"`
}

// MaxSlots returns the maximum number of slots per IDE controller.
func (c IDEControllerSpec) MaxSlots() int32 {
return 2
}

// MaxCount returns the maximum number of IDE controllers per VM.
func (c IDEControllerSpec) MaxCount() int32 {
return VirtualControllerTypeIDE.MaxCount()
}

// ReservedUnitNumber returns any reserved unit numbers or negative one.
func (c IDEControllerSpec) ReservedUnitNumber() int32 {
return -1
}

type NVMEControllerSpec struct {
// +required
// +kubebuilder:validation:Minimum=0
Expand Down Expand Up @@ -81,6 +109,21 @@ type NVMEControllerSpec struct {
SharingMode VirtualControllerSharingMode `json:"sharingMode,omitempty"`
}

// MaxSlots returns the maximum number of slots per NVME controller.
func (c NVMEControllerSpec) MaxSlots() int32 {
return 64
}

// MaxCount returns the maximum number of NVME controllers per VM.
func (c NVMEControllerSpec) MaxCount() int32 {
return VirtualControllerTypeNVME.MaxCount()
}

// ReservedUnitNumber returns any reserved unit numbers or negative one.
func (c NVMEControllerSpec) ReservedUnitNumber() int32 {
return -1
}

type SATAControllerSpec struct {
// +required
// +kubebuilder:validation:Minimum=0
Expand All @@ -99,6 +142,21 @@ type SATAControllerSpec struct {
PCISlotNumber *int32 `json:"pciSlotNumber,omitempty"`
}

// MaxSlots returns the maximum number of slots per SATA controller.
func (c SATAControllerSpec) MaxSlots() int32 {
return 30
}

// MaxCount returns the maximum number of SATA controllers per VM.
func (c SATAControllerSpec) MaxCount() int32 {
return VirtualControllerTypeSATA.MaxCount()
}

// ReservedUnitNumber returns any reserved unit numbers or negative one.
func (c SATAControllerSpec) ReservedUnitNumber() int32 {
return -1
}

type SCSIControllerSpec struct {
// +required
// +kubebuilder:validation:Minimum=0
Expand Down Expand Up @@ -134,6 +192,33 @@ type SCSIControllerSpec struct {
Type SCSIControllerType `json:"type,omitempty"`
}

// MaxSlots returns the maximum number of devices per SCSI controller type.
// The controller itself occupies one slot (unit number seven).
//
// For all these sub-types, the max slots is one less than the capacity
// since unit number seven is reserved for the SCSI controller itself.
func (c SCSIControllerSpec) MaxSlots() int32 {
switch c.Type {
case SCSIControllerTypeParaVirtualSCSI:
return 63 // 64 targets - 1 for controller
case SCSIControllerTypeBusLogic,
SCSIControllerTypeLsiLogic,
SCSIControllerTypeLsiLogicSAS:
return 15 // 16 targets - 1 for controller
}
return 0
}

// MaxCount returns the maximum number of SCSI controllers per VM.
func (c SCSIControllerSpec) MaxCount() int32 {
return VirtualControllerTypeSCSI.MaxCount()
}

// ReservedUnitNumber returns any reserved unit numbers or negative one.
func (c SCSIControllerSpec) ReservedUnitNumber() int32 {
return 7
}

type VirtualDeviceStatus struct {
// +required

Expand Down
1 change: 0 additions & 1 deletion api/v1alpha5/virtualmachine_storage_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ type PersistentVolumeClaimVolumeSource struct {
ControllerBusNumber *int32 `json:"controllerBusNumber,omitempty"`

// +optional
// +kubebuilder:default=SCSI

// ControllerType describes the type of the controller to which this volume
// should be attached.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7444,7 +7444,6 @@ spec:
format: int32
type: integer
controllerType:
default: SCSI
description: |-
ControllerType describes the type of the controller to which this volume
should be attached.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12135,7 +12135,6 @@ spec:
format: int32
type: integer
controllerType:
default: SCSI
description: |-
ControllerType describes the type of the controller to which this volume
should be attached.
Expand Down
182 changes: 182 additions & 0 deletions pkg/util/vmopv1/hardware.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// © Broadcom. All Rights Reserved.
// The term "Broadcom" refers to Broadcom Inc. and/or its subsidiaries.
// SPDX-License-Identifier: Apache-2.0

package vmopv1

import (
vmopv1 "github.com/vmware-tanzu/vm-operator/api/v1alpha5"
pkgutil "github.com/vmware-tanzu/vm-operator/pkg/util"
"k8s.io/apimachinery/pkg/util/sets"
)

// ControllerSpec is an interface describing a controller specification.
type ControllerSpec interface {
// MaxSlots returns the maximum number of slots per controller type.
MaxSlots() int32

// MaxCount returns the maximum number of controllers per VM.
MaxCount() int32

// ReservedUnitNumber returns any reserved unit numbers or negative one
// if no reserved unit numbers are present.
ReservedUnitNumber() int32
}

// NextAvailableUnitNumber returns the first available unit number for the
// specified controller. The occupiedSlots parameter should contain all unit
// numbers that are already in use on the specified bus.
// Returns the first available unit number, or negative one if no slots are
// available.
func NextAvailableUnitNumber(
controller ControllerSpec,
occupiedSlots sets.Set[int32],
) int32 {
for unitNumber := int32(0); unitNumber < controller.MaxSlots(); unitNumber++ {
if _, exists := occupiedSlots[unitNumber]; !exists &&
unitNumber != controller.ReservedUnitNumber() {
return unitNumber
}
}
return -1
}

// GenerateControllerID generates a controller ID from a controller specification.
// Returns a ControllerID with BusNumber set to negative one if the controller
// type is not supported.
func GenerateControllerID(
controller any,
) pkgutil.ControllerID {

if controller == nil {
return pkgutil.ControllerID{BusNumber: -1}
}

switch c := controller.(type) {
case vmopv1.SCSIControllerSpec:
return pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeSCSI,
BusNumber: c.BusNumber,
}
case vmopv1.SATAControllerSpec:
return pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeSATA,
BusNumber: c.BusNumber,
}
case vmopv1.NVMEControllerSpec:
return pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeNVME,
BusNumber: c.BusNumber,
}
case vmopv1.IDEControllerSpec:
return pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeIDE,
BusNumber: c.BusNumber,
}
default:
return pkgutil.ControllerID{BusNumber: -1}
}
}

// GetControllerSharingMode returns the sharing mode for a controller.
// Returns the sharing mode from the controller if supported, or None otherwise.
func GetControllerSharingMode(
controller any,
) vmopv1.VirtualControllerSharingMode {
switch c := controller.(type) {
case vmopv1.SCSIControllerSpec:
return c.SharingMode
case vmopv1.NVMEControllerSpec:
return c.SharingMode
}
return vmopv1.VirtualControllerSharingModeNone
}

// CreateNewController creates a new controller with the specified
// controller type, bus number, and sharing mode.
// For SCSI controller type, the type is ParaVirtualSCSI by default.
func CreateNewController(
controllerID pkgutil.ControllerID,
sharingMode vmopv1.VirtualControllerSharingMode,
) ControllerSpec {
switch controllerID.ControllerType {
case vmopv1.VirtualControllerTypeSCSI:
return vmopv1.SCSIControllerSpec{
BusNumber: controllerID.BusNumber,
Type: vmopv1.SCSIControllerTypeParaVirtualSCSI,
SharingMode: sharingMode,
}
case vmopv1.VirtualControllerTypeSATA:
return vmopv1.SATAControllerSpec{
BusNumber: controllerID.BusNumber,
}
case vmopv1.VirtualControllerTypeNVME:
return vmopv1.NVMEControllerSpec{
BusNumber: controllerID.BusNumber,
SharingMode: sharingMode,
}
case vmopv1.VirtualControllerTypeIDE:
return vmopv1.IDEControllerSpec{
BusNumber: controllerID.BusNumber,
}
default:
return nil
}
}

// BuildVMControllersMap builds a map of controller ID to controller specification
// from the specified VM's spec.hardware.controllers.
func BuildVMControllersMap(
vm *vmopv1.VirtualMachine,
) map[pkgutil.ControllerID]ControllerSpec {

if vm.Spec.Hardware == nil {
vm.Spec.Hardware = &vmopv1.VirtualMachineHardwareSpec{}
}

existingControllers := make(map[pkgutil.ControllerID]ControllerSpec)

for _, controller := range vm.Spec.Hardware.SCSIControllers {
existingControllers[pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeSCSI,
BusNumber: controller.BusNumber,
}] = controller
}
for _, controller := range vm.Spec.Hardware.SATAControllers {
existingControllers[pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeSATA,
BusNumber: controller.BusNumber,
}] = controller
}
for _, controller := range vm.Spec.Hardware.NVMEControllers {
existingControllers[pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeNVME,
BusNumber: controller.BusNumber,
}] = controller
}
for _, controller := range vm.Spec.Hardware.IDEControllers {
existingControllers[pkgutil.ControllerID{
ControllerType: vmopv1.VirtualControllerTypeIDE,
BusNumber: controller.BusNumber,
}] = controller
}

return existingControllers
}

// GetManagedVolumesWithPVC returns all volumes from the VM spec that have
// a PersistentVolumeClaim and are managed (not instance storage).
func GetManagedVolumesWithPVC(
vm vmopv1.VirtualMachine,
) []vmopv1.VirtualMachineVolume {

volumes := []vmopv1.VirtualMachineVolume{}
for _, v := range vm.Spec.Volumes {
if v.PersistentVolumeClaim != nil &&
v.PersistentVolumeClaim.UnmanagedVolumeClaim == nil {
volumes = append(volumes, v)
}
}

return volumes
}
Loading