diff --git a/go.sum b/go.sum index bb20e9226..c48857ff1 100644 --- a/go.sum +++ b/go.sum @@ -895,7 +895,6 @@ github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5Fsn github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.2.1 h1:JnMpQc6ppsNgw9QPAGF6Dod479itz7lvlsMzzNayLOI= github.com/prometheus/client_golang v1.2.1/go.mod h1:XMU6Z2MjaRKVu/dC1qupJI9SiNkDYzz3xecMgSW/F+U= -github.com/prometheus/client_golang v1.7.1 h1:NTGy1Ja9pByO+xAeH/qiWnLrKtr3hJPNjaVUwnjpdpA= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= diff --git a/pkg/apis/v2v/v1beta1/resourcemapping_types.go b/pkg/apis/v2v/v1beta1/resourcemapping_types.go index 27711d5bf..23a7b9673 100644 --- a/pkg/apis/v2v/v1beta1/resourcemapping_types.go +++ b/pkg/apis/v2v/v1beta1/resourcemapping_types.go @@ -93,6 +93,8 @@ type StorageResourceMappingItem struct { // +optional VolumeMode *corev1.PersistentVolumeMode `json:"volumeMode,omitempty"` + // +optional + AccessMode *corev1.PersistentVolumeAccessMode `json:"accessMode,omitempty"` } // ResourceMappingStatus defines the observed state of ResourceMapping diff --git a/pkg/apis/v2v/v1beta1/zz_generated.deepcopy.go b/pkg/apis/v2v/v1beta1/zz_generated.deepcopy.go index 60d69a30d..5a7325d2f 100644 --- a/pkg/apis/v2v/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/v2v/v1beta1/zz_generated.deepcopy.go @@ -272,6 +272,11 @@ func (in *StorageResourceMappingItem) DeepCopyInto(out *StorageResourceMappingIt *out = new(v1.PersistentVolumeMode) **out = **in } + if in.AccessMode != nil { + in, out := &in.AccessMode, &out.AccessMode + *out = new(v1.PersistentVolumeAccessMode) + **out = **in + } return } diff --git a/pkg/jobs/jobs.go b/pkg/jobs/jobs.go index 74d4ffb7f..bbff6996a 100644 --- a/pkg/jobs/jobs.go +++ b/pkg/jobs/jobs.go @@ -3,6 +3,7 @@ package jobs import ( "context" "fmt" + "github.com/kubevirt/vm-import-operator/pkg/utils" batchv1 "k8s.io/api/batch/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" diff --git a/pkg/operator/resources/operator/operator.go b/pkg/operator/resources/operator/operator.go index 516ba4ccf..9c36c1452 100644 --- a/pkg/operator/resources/operator/operator.go +++ b/pkg/operator/resources/operator/operator.go @@ -2220,6 +2220,9 @@ func CreateVMImport() *extv1.CustomResourceDefinition { "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source"}, }, @@ -2266,6 +2269,9 @@ func CreateVMImport() *extv1.CustomResourceDefinition { "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source"}, }, @@ -2394,6 +2400,9 @@ NetworkMappings.Source.ID represents the macAddress field of the network adapter "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source"}, }, @@ -2440,6 +2449,9 @@ DiskMappings.Source.ID represents the DiskObjectId or vDiskID of the VirtualDisk "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source"}, }, @@ -2867,6 +2879,9 @@ func CreateResourceMapping() *extv1.CustomResourceDefinition { "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source", "target"}, }, @@ -2913,6 +2928,9 @@ func CreateResourceMapping() *extv1.CustomResourceDefinition { "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source", "target"}, }, @@ -3006,6 +3024,9 @@ NetworkMappings.Source.ID represents the macAddress field of the network adapter "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source", "target"}, }, @@ -3052,6 +3073,9 @@ DiskMappings.Source.ID represents the DiskObjectId or vDiskID of the VirtualDisk "volumeMode": { Type: "string", }, + "accessMode": { + Type: "string", + }, }, Required: []string{"source", "target"}, }, diff --git a/pkg/providers/ovirt/mapper/mapper.go b/pkg/providers/ovirt/mapper/mapper.go index d4ebf71d8..6223c6d2d 100644 --- a/pkg/providers/ovirt/mapper/mapper.go +++ b/pkg/providers/ovirt/mapper/mapper.go @@ -231,9 +231,12 @@ func (o *OvirtMapper) mapDiskInterface(iface ovirtsdk.DiskInterface) string { return string(iface) } -// Set the access mode of the PVC based on the VM's disk read only attribute -// and based on the affinity settings of the VM. -func (o *OvirtMapper) getAccessMode(diskAttachment *ovirtsdk.DiskAttachment) corev1.PersistentVolumeAccessMode { +// If the mapping specifies the access mode return that, otherwise determine the access mode +// of the PVC based on the VM's disk read only attribute and based on the affinity settings of the VM. +func (o *OvirtMapper) getAccessMode(diskAttachment *ovirtsdk.DiskAttachment, mapping *v2vv1.StorageResourceMappingItem) corev1.PersistentVolumeAccessMode { + if mapping != nil && mapping.AccessMode != nil { + return *mapping.AccessMode + } accessMode := corev1.ReadWriteOnce if readOnly, ok := diskAttachment.ReadOnly(); ok && readOnly { accessMode = corev1.ReadOnlyMany @@ -265,9 +268,9 @@ func (o *OvirtMapper) MapDataVolumes(targetVMName *string) (map[string]cdiv1.Dat return dvs, err } quantity, _ := resource.ParseQuantity(diskSizeConverted) - accessMode := o.getAccessMode(diskAttachment) mapping := o.getMapping(disk, o.mappings) + accessMode := o.getAccessMode(diskAttachment, mapping) sdClass := o.getStorageClassForDisk(mapping) volumeMode := o.getVolumeMode(mapping) diff --git a/pkg/providers/vmware/mapper/mapper.go b/pkg/providers/vmware/mapper/mapper.go index 1a29fa8c8..f92a7dd96 100644 --- a/pkg/providers/vmware/mapper/mapper.go +++ b/pkg/providers/vmware/mapper/mapper.go @@ -46,6 +46,11 @@ const ( q35 = "q35" ) +var ( + defaultVolumeMode = corev1.PersistentVolumeFilesystem + defaultAccessMode = corev1.ReadWriteOnce +) + var biosTypeMapping = map[string]*kubevirtv1.Bootloader{ "efi": {EFI: &kubevirtv1.EFI{}}, "bios": {BIOS: &kubevirtv1.BIOS{}}, @@ -222,22 +227,17 @@ func (r *VmwareMapper) buildDisks() error { return nil } -func (r *VmwareMapper) getStorageClassForDisk(disk *disk) *string { +func (r *VmwareMapper) getMappingForDisk(disk disk) *v1beta1.StorageResourceMappingItem { if r.mappings.DiskMappings != nil { for _, mapping := range *r.mappings.DiskMappings { - targetName := mapping.Target.Name if mapping.Source.ID != nil { if disk.id == *mapping.Source.ID { - if targetName != defaultStorageClassTargetName { - return &targetName - } + return &mapping } } if mapping.Source.Name != nil { if disk.name == *mapping.Source.Name { - if targetName != defaultStorageClassTargetName { - return &targetName - } + return &mapping } } } @@ -245,19 +245,14 @@ func (r *VmwareMapper) getStorageClassForDisk(disk *disk) *string { if r.mappings.StorageMappings != nil { for _, mapping := range *r.mappings.StorageMappings { - targetName := mapping.Target.Name if mapping.Source.ID != nil { if disk.datastoreMoRef == *mapping.Source.ID { - if targetName != defaultStorageClassTargetName { - return &targetName - } + return &mapping } } if mapping.Source.Name != nil { if disk.datastoreName == *mapping.Source.Name { - if targetName != defaultStorageClassTargetName { - return &targetName - } + return &mapping } } } @@ -265,6 +260,34 @@ func (r *VmwareMapper) getStorageClassForDisk(disk *disk) *string { return nil } +func (r *VmwareMapper) getStorageClassForDisk(mapping *v1beta1.StorageResourceMappingItem) *string { + if mapping != nil { + targetName := mapping.Target.Name + if targetName != defaultStorageClassTargetName { + return &targetName + } + } + + // Use default storage class: + return nil +} + +func (r *VmwareMapper) getAccessModeForDisk(mapping *v1beta1.StorageResourceMappingItem) corev1.PersistentVolumeAccessMode { + if mapping != nil && mapping.AccessMode != nil { + return *mapping.AccessMode + } + + return defaultAccessMode +} + +func (r *VmwareMapper) getVolumeModeForDisk(mapping *v1beta1.StorageResourceMappingItem) *corev1.PersistentVolumeMode { + if mapping != nil && mapping.VolumeMode != nil { + return mapping.VolumeMode + } + + return &defaultVolumeMode +} + // MapDataVolumes maps the VMware disks to CDI DataVolumes func (r *VmwareMapper) MapDataVolumes(targetVMName *string) (map[string]cdiv1.DataVolume, error) { err := r.buildDisks() @@ -277,6 +300,8 @@ func (r *VmwareMapper) MapDataVolumes(targetVMName *string) (map[string]cdiv1.Da for _, disk := range *r.disks { dvName := buildDataVolumeName(*targetVMName, disk.name) + mapping := r.getMappingForDisk(disk) + dvs[dvName] = cdiv1.DataVolume{ TypeMeta: metav1.TypeMeta{ APIVersion: cdiAPIVersion, @@ -298,20 +323,18 @@ func (r *VmwareMapper) MapDataVolumes(targetVMName *string) (map[string]cdiv1.Da }, PVC: &corev1.PersistentVolumeClaimSpec{ AccessModes: []corev1.PersistentVolumeAccessMode{ - corev1.ReadWriteOnce, + r.getAccessModeForDisk(mapping), }, + VolumeMode: r.getVolumeModeForDisk(mapping), Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{ corev1.ResourceStorage: disk.capacity, }, }, + StorageClassName: r.getStorageClassForDisk(mapping), }, }, } - sdClass := r.getStorageClassForDisk(&disk) - if sdClass != nil { - dvs[dvName].Spec.PVC.StorageClassName = sdClass - } } return dvs, nil } diff --git a/pkg/providers/vmware/mapper/mapper_test.go b/pkg/providers/vmware/mapper/mapper_test.go index 929a2a101..06a506694 100644 --- a/pkg/providers/vmware/mapper/mapper_test.go +++ b/pkg/providers/vmware/mapper/mapper_test.go @@ -3,6 +3,8 @@ package mapper_test import ( "context" + v1 "k8s.io/api/core/v1" + "github.com/kubevirt/vm-import-operator/pkg/apis/v2v/v1beta1" "github.com/kubevirt/vm-import-operator/pkg/providers/vmware/mapper" "github.com/kubevirt/vm-import-operator/pkg/providers/vmware/os" @@ -39,8 +41,15 @@ var ( // disks expectedNumDisks = 2 + diskId1 = "disk-202-0" + diskId2 = "disk-202-1" expectedDiskName1 = "basic-vm-disk-202-0" expectedDiskName2 = "basic-vm-disk-202-1" + + volumeModeBlock = v1.PersistentVolumeBlock + volumeModeFilesystem = v1.PersistentVolumeFilesystem + accessModeRWO = v1.ReadWriteOnce + accessModeRWM = v1.ReadWriteMany ) type mockOsFinder struct{} @@ -250,13 +259,40 @@ var _ = Describe("Test mapping disks", func() { credentials = prepareCredentials(server) }) - It("should map disks", func() { + It("should map datavolumes", func() { + storageClass := "mystorageclass" mappings := createMinimalMapping() + mappings.DiskMappings = &[]v1beta1.StorageResourceMappingItem{ + { + Source: v1beta1.Source{ + Name: &diskId1, + }, + Target: v1beta1.ObjectIdentifier{ + Name: storageClass, + }, + VolumeMode: &volumeModeBlock, + AccessMode: &accessModeRWM, + }, + { + // using defaults + Source: v1beta1.Source{ + Name: &diskId2, + }, + }, + } mapper := mapper.NewVmwareMapper(vm, vmProperties, hostProperties, credentials, mappings, "", osFinder) dvs, _ := mapper.MapDataVolumes(&targetVMName) Expect(dvs).To(HaveLen(expectedNumDisks)) Expect(dvs).To(HaveKey(expectedDiskName1)) Expect(dvs).To(HaveKey(expectedDiskName2)) + // check that mapped options are set correctly + Expect(dvs[expectedDiskName1].Spec.PVC.VolumeMode).To(Equal(&volumeModeBlock)) + Expect(dvs[expectedDiskName1].Spec.PVC.AccessModes[0]).To(Equal(accessModeRWM)) + Expect(dvs[expectedDiskName1].Spec.PVC.StorageClassName).To(Equal(&storageClass)) + // check that defaults are set correctly + Expect(dvs[expectedDiskName2].Spec.PVC.VolumeMode).To(Equal(&volumeModeFilesystem)) + Expect(dvs[expectedDiskName2].Spec.PVC.AccessModes[0]).To(Equal(accessModeRWO)) + Expect(dvs[expectedDiskName2].Spec.PVC.StorageClassName).To(BeNil()) }) }) diff --git a/pkg/providers/vmware/provider_test.go b/pkg/providers/vmware/provider_test.go index c35f92702..fcf6373f1 100644 --- a/pkg/providers/vmware/provider_test.go +++ b/pkg/providers/vmware/provider_test.go @@ -3,6 +3,7 @@ package vmware import ( "context" "encoding/json" + "github.com/onsi/ginkgo/extensions/table" "github.com/ghodss/yaml" diff --git a/pkg/providers/vmware/templates/template-finder.go b/pkg/providers/vmware/templates/template-finder.go index 8b716c70f..5fcb8f18d 100644 --- a/pkg/providers/vmware/templates/template-finder.go +++ b/pkg/providers/vmware/templates/template-finder.go @@ -2,11 +2,12 @@ package templates import ( "fmt" + "sort" + "github.com/kubevirt/vm-import-operator/pkg/providers/vmware/os" "github.com/kubevirt/vm-import-operator/pkg/templates" templatev1 "github.com/openshift/api/template/v1" "github.com/vmware/govmomi/vim25/mo" - "sort" ) const (