Skip to content

Commit

Permalink
Added multi disk support
Browse files Browse the repository at this point in the history
  • Loading branch information
vr4manta committed Oct 17, 2024
1 parent a2bac7b commit 13f082b
Show file tree
Hide file tree
Showing 15 changed files with 268 additions and 20 deletions.
1 change: 1 addition & 0 deletions apis/v1alpha3/vspheremachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (src *VSphereMachine) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.DataDisks = restored.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha3/vspheremachinetemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (src *VSphereMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Template.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Template.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Template.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Template.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.Template.Spec.DataDisks = restored.Spec.Template.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha3/vspherevm_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (src *VSphereVM) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.DataDisks = restored.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha3/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions apis/v1alpha4/vspheremachine_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (src *VSphereMachine) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.DataDisks = restored.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha4/vspheremachinetemplate_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ func (src *VSphereMachineTemplate) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Template.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Template.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Template.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Template.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.Template.Spec.DataDisks = restored.Spec.Template.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha4/vspherevm_conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func (src *VSphereVM) ConvertTo(dstRaw conversion.Hub) error {
dst.Spec.Network.Devices[i].DHCP6Overrides = restored.Spec.Network.Devices[i].DHCP6Overrides
dst.Spec.Network.Devices[i].SkipIPAllocation = restored.Spec.Network.Devices[i].SkipIPAllocation
}
dst.Spec.DataDisks = restored.Spec.DataDisks

return nil
}
Expand Down
1 change: 1 addition & 0 deletions apis/v1alpha4/zz_generated.conversion.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions apis/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,16 @@ type VirtualMachineCloneSpec struct {
// Check the compatibility with the ESXi version before setting the value.
// +optional
HardwareVersion string `json:"hardwareVersion,omitempty"`
// DataDisks holds information for additional disks to add to the VM that are not part of the VM's OVA template.
// +optional
DataDisks []VSphereDisk `json:"dataDisks,omitempty"`
}

// VSphereDisk describes additional disks for vSphere to be added to VM that are not part of the VM OVA template.
type VSphereDisk struct {
// SizeGiB is the size of the disk (in GiB).
// +kubebuilder:validation:Required
SizeGiB int32 `json:"sizeGiB"`
}

// VSphereMachineTemplateResource describes the data needed to create a VSphereMachine from a template.
Expand Down
20 changes: 20 additions & 0 deletions apis/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -974,6 +974,21 @@ spec:
CustomVMXKeys is a dictionary of advanced VMX options that can be set on VM
Defaults to empty map
type: object
dataDisks:
description: DataDisks holds information for additional disks to add
to the VM that are not part of the VM's OVA template.
items:
description: VSphereDisk describes additional disks for vSphere
to be added to VM that are not part of the VM OVA template.
properties:
sizeGiB:
description: SizeGiB is the size of the disk (in GiB).
format: int32
type: integer
required:
- sizeGiB
type: object
type: array
datacenter:
description: |-
Datacenter is the name or inventory path of the datacenter in which the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,22 @@ spec:
CustomVMXKeys is a dictionary of advanced VMX options that can be set on VM
Defaults to empty map
type: object
dataDisks:
description: DataDisks holds information for additional disks
to add to the VM that are not part of the VM's OVA template.
items:
description: VSphereDisk describes additional disks for
vSphere to be added to VM that are not part of the VM
OVA template.
properties:
sizeGiB:
description: SizeGiB is the size of the disk (in GiB).
format: int32
type: integer
required:
- sizeGiB
type: object
type: array
datacenter:
description: |-
Datacenter is the name or inventory path of the datacenter in which the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1064,6 +1064,21 @@ spec:
CustomVMXKeys is a dictionary of advanced VMX options that can be set on VM
Defaults to empty map
type: object
dataDisks:
description: DataDisks holds information for additional disks to add
to the VM that are not part of the VM's OVA template.
items:
description: VSphereDisk describes additional disks for vSphere
to be added to VM that are not part of the VM OVA template.
properties:
sizeGiB:
description: SizeGiB is the size of the disk (in GiB).
format: int32
type: integer
required:
- sizeGiB
type: object
type: array
datacenter:
description: |-
Datacenter is the name or inventory path of the datacenter in which the
Expand Down
119 changes: 116 additions & 3 deletions pkg/services/govmomi/vcenter/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
pbmTypes "github.com/vmware/govmomi/pbm/types"
"github.com/vmware/govmomi/vim25/mo"
"github.com/vmware/govmomi/vim25/types"
"k8s.io/klog/v2"
"k8s.io/utils/ptr"
bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -138,10 +139,11 @@ func Clone(ctx context.Context, vmCtx *capvcontext.VMContext, bootstrapData []by

// Only non-linked clones may expand the size of the template's disk.
if snapshotRef == nil {
diskSpecs, err := getDiskSpec(vmCtx, devices)
diskSpecs, err := getDiskSpec(ctx, vmCtx, devices)
if err != nil {
return errors.Wrapf(err, "error getting disk spec for %q", ctx)
}
log.Info("Got the following disks", "disk", diskSpecs)
deviceSpecs = append(deviceSpecs, diskSpecs...)
}

Expand Down Expand Up @@ -334,14 +336,14 @@ func getDiskLocators(disks object.VirtualDeviceList, datastoreRef types.ManagedO
return diskLocators
}

func getDiskSpec(vmCtx *capvcontext.VMContext, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
func getDiskSpec(ctx context.Context, vmCtx *capvcontext.VMContext, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
disks := devices.SelectByType((*types.VirtualDisk)(nil))
if len(disks) == 0 {
return nil, errors.Errorf("Invalid disk count: %d", len(disks))
}

// There is at least one disk
var diskSpecs []types.BaseVirtualDeviceConfigSpec
diskSpecs := []types.BaseVirtualDeviceConfigSpec{}
primaryDisk := disks[0].(*types.VirtualDisk)
primaryCloneCapacityKB := int64(vmCtx.VSphereVM.Spec.DiskGiB) * 1024 * 1024
primaryDiskConfigSpec, err := getDiskConfigSpec(primaryDisk, primaryCloneCapacityKB)
Expand Down Expand Up @@ -369,6 +371,18 @@ func getDiskSpec(vmCtx *capvcontext.VMContext, devices object.VirtualDeviceList)
diskSpecs = append(diskSpecs, additionalDiskConfigSpec)
}
}

// Now if we have increased the disk size of any additional disks that were in the template, we can now add new disks
// that are present in the additionalDisks list.
if len(vmCtx.VSphereVM.Spec.DataDisks) > 0 {
additionalDisks, diskErr := createDataDisks(ctx, vmCtx.VSphereVM.Spec.DataDisks, primaryDisk, devices)
if diskErr != nil {
klog.Errorf("Unable to add additional disks: %v", diskErr)
return nil, diskErr
}
diskSpecs = append(diskSpecs, additionalDisks...)
}

return diskSpecs, nil
}

Expand All @@ -390,6 +404,105 @@ func getDiskConfigSpec(disk *types.VirtualDisk, diskCloneCapacityKB int64) (type
}, nil
}

// createDataDisks parses through the list of VSphereDisk objects and generates the VirtualDeviceConfigSpec for each disk needing to be created during clone process.
func createDataDisks(ctx context.Context, disks []infrav1.VSphereDisk, primaryDisk *types.VirtualDisk, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
log := ctrl.LoggerFrom(ctx)
additionalDisks := []types.BaseVirtualDeviceConfigSpec{}
unit := int32(1)

for i, dataDisk := range disks {
log.Info("Adding disk", "spec", dataDisk)

// Get controller. Only supporting using same controller as primary disk at this time
controller, ok := devices.FindByKey(primaryDisk.ControllerKey).(types.BaseVirtualController)
if !ok {
return nil, errors.Errorf("unable to find controller with key=%v", primaryDisk.ControllerKey)
}

dev := &types.VirtualDisk{
VirtualDevice: types.VirtualDevice{
Key: devices.NewKey() - int32(i),
Backing: &types.VirtualDiskFlatVer2BackingInfo{
DiskMode: string(types.VirtualDiskModePersistent),
ThinProvisioned: types.NewBool(true),
VirtualDeviceFileBackingInfo: types.VirtualDeviceFileBackingInfo{
FileName: "",
},
},
ControllerKey: controller.GetVirtualController().Key,
},
CapacityInKB: int64(dataDisk.SizeGiB) * 1024 * 1024,
}

// Assign unit number to next slot on controller
assignUnitNumber(ctx, dev, devices, additionalDisks, controller, unit)
unit = *dev.UnitNumber

diskConfigSpec := types.VirtualDeviceConfigSpec{
Device: dev,
Operation: types.VirtualDeviceConfigSpecOperationAdd,
FileOperation: types.VirtualDeviceConfigSpecFileOperationCreate,
}

log.Info("Generated device", "dev", dev)

additionalDisks = append(additionalDisks, &diskConfigSpec)
}

return additionalDisks, nil
}

// assignUnitNumber assigns a controller unit number to a device.
func assignUnitNumber(ctx context.Context, device types.BaseVirtualDevice, existingDevices object.VirtualDeviceList, newDevices []types.BaseVirtualDeviceConfigSpec, controller types.BaseVirtualController, offset int32) {
log := ctrl.LoggerFrom(ctx)
vd := device.GetVirtualDevice()
vd.ControllerKey = controller.GetVirtualController().Key
vd.UnitNumber = &offset

units := make([]bool, 30)

for i := 0; i < int(offset); i++ {
units[i] = true
}

sc, ok := controller.(types.BaseVirtualSCSIController)
if ok {
// The SCSI controller sits on its own bus
log.V(4).Info("Marking SCSI Controller's unit number: %d", sc.GetVirtualSCSIController().ScsiCtlrUnitNumber)
units[sc.GetVirtualSCSIController().ScsiCtlrUnitNumber] = true
}

key := controller.GetVirtualController().Key

// Check all existing devices
for _, device := range existingDevices {
d := device.GetVirtualDevice()

if d.ControllerKey == key && d.UnitNumber != nil {
units[int(*d.UnitNumber)] = true
}
}

// Check new devices
for _, device := range newDevices {
d := device.GetVirtualDeviceConfigSpec().Device.GetVirtualDevice()

if d.ControllerKey == key && d.UnitNumber != nil {
units[int(*d.UnitNumber)] = true
}
}

// Assign first unused unit number
for unit, used := range units {
if !used {
unit32 := int32(unit)
vd.UnitNumber = &unit32
log.V(4).Info("Determined next available unit number", "unitNumber", unit32)
break
}
}
}

const ethCardType = "vmxnet3"

func getNetworkSpecs(ctx context.Context, vmCtx *capvcontext.VMContext, devices object.VirtualDeviceList) ([]types.BaseVirtualDeviceConfigSpec, error) {
Expand Down
Loading

0 comments on commit 13f082b

Please sign in to comment.