diff --git a/pkg/cloudprovider/provider/kubevirt/provider.go b/pkg/cloudprovider/provider/kubevirt/provider.go index 388f5504d..021f200c9 100644 --- a/pkg/cloudprovider/provider/kubevirt/provider.go +++ b/pkg/cloudprovider/provider/kubevirt/provider.go @@ -98,6 +98,7 @@ type Config struct { Memory string Namespace string OSImageSource *cdiv1beta1.DataVolumeSource + StorageTarget StorageTarget StorageClassName string StorageAccessType corev1.PersistentVolumeAccessMode PVCSize resource.Quantity @@ -108,6 +109,14 @@ type Config struct { TopologySpreadConstraints []corev1.TopologySpreadConstraint } +// StorageTarget represents targeted storage definition that will be used to provision VirtualMachine volumes. Currently, +// there are two definitions, PVC and Storage. Default value is PVC. +type StorageTarget string + +const ( + Storage StorageTarget = "storage" +) + type AffinityType string const ( @@ -255,6 +264,12 @@ func (p *provider) getConfig(provSpec clusterv1alpha1.ProviderSpec) (*Config, *p return nil, nil, fmt.Errorf(`failed to get value of "osImageSource" field: %w`, err) } + storageTarget, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Template.PrimaryDisk.StorageTarget) + if err != nil { + return nil, nil, fmt.Errorf(`failed to get value of "storageTarget" field: %w`, err) + } + config.StorageTarget = StorageTarget(storageTarget) + pvcSize, err := p.configVarResolver.GetConfigVarStringValue(rawConfig.VirtualMachine.Template.PrimaryDisk.Size) if err != nil { return nil, nil, fmt.Errorf(`failed to get value of "pvcSize" field: %w`, err) @@ -638,6 +653,7 @@ func (p *provider) newVirtualMachine(_ context.Context, c *Config, pc *providerc var ( dataVolumeName = machine.Name annotations = map[string]string{} + dvAnnotations = map[string]string{} ) // Add machineName as prefix to secondaryDisks. addPrefixToSecondaryDisk(c.SecondaryDisks, dataVolumeName) @@ -649,6 +665,11 @@ func (p *provider) newVirtualMachine(_ context.Context, c *Config, pc *providerc annotations["ovn.kubernetes.io/allow_live_migration"] = "true" for k, v := range machine.Annotations { + if strings.HasPrefix(k, "cdi.kubevirt.io") { + dvAnnotations[k] = v + continue + } + annotations[k] = v } @@ -681,8 +702,8 @@ func (p *provider) newVirtualMachine(_ context.Context, c *Config, pc *providerc }, Domain: kubevirtv1.DomainSpec{ Devices: kubevirtv1.Devices{ - Disks: getVMDisks(c), Interfaces: []kubevirtv1.Interface{*defaultBridgeNetwork}, + Disks: getVMDisks(c), }, Resources: resourceRequirements, }, @@ -694,7 +715,7 @@ func (p *provider) newVirtualMachine(_ context.Context, c *Config, pc *providerc TopologySpreadConstraints: getTopologySpreadConstraints(c, map[string]string{machineDeploymentLabelKey: labels[machineDeploymentLabelKey]}), }, }, - DataVolumeTemplates: getDataVolumeTemplates(c, dataVolumeName), + DataVolumeTemplates: getDataVolumeTemplates(c, dataVolumeName, dvAnnotations), }, } return virtualMachine, nil @@ -831,27 +852,43 @@ func getVMVolumes(config *Config, dataVolumeName string, userDataSecretName stri return volumes } -func getDataVolumeTemplates(config *Config, dataVolumeName string) []kubevirtv1.DataVolumeTemplateSpec { +func getDataVolumeTemplates(config *Config, dataVolumeName string, annotations map[string]string) []kubevirtv1.DataVolumeTemplateSpec { pvcRequest := corev1.ResourceList{corev1.ResourceStorage: config.PVCSize} dataVolumeTemplates := []kubevirtv1.DataVolumeTemplateSpec{ { ObjectMeta: metav1.ObjectMeta{ - Name: dataVolumeName, + Name: dataVolumeName, + Annotations: annotations, }, Spec: cdiv1beta1.DataVolumeSpec{ - PVC: &corev1.PersistentVolumeClaimSpec{ - StorageClassName: ptr.To(config.StorageClassName), - AccessModes: []corev1.PersistentVolumeAccessMode{ - config.StorageAccessType, - }, - Resources: corev1.VolumeResourceRequirements{ - Requests: pvcRequest, - }, - }, Source: config.OSImageSource, }, }, } + + switch config.StorageTarget { + case Storage: + dataVolumeTemplates[0].Spec.Storage = &cdiv1beta1.StorageSpec{ + StorageClassName: ptr.To(config.StorageClassName), + AccessModes: []corev1.PersistentVolumeAccessMode{ + config.StorageAccessType, + }, + Resources: corev1.ResourceRequirements{ + Requests: pvcRequest, + }, + } + default: + dataVolumeTemplates[0].Spec.PVC = &corev1.PersistentVolumeClaimSpec{ + StorageClassName: ptr.To(config.StorageClassName), + AccessModes: []corev1.PersistentVolumeAccessMode{ + config.StorageAccessType, + }, + Resources: corev1.VolumeResourceRequirements{ + Requests: pvcRequest, + }, + } + } + for _, sd := range config.SecondaryDisks { dataVolumeTemplates = append(dataVolumeTemplates, kubevirtv1.DataVolumeTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ diff --git a/pkg/cloudprovider/provider/kubevirt/provider_test.go b/pkg/cloudprovider/provider/kubevirt/provider_test.go index 0e69d6626..18bcea9da 100644 --- a/pkg/cloudprovider/provider/kubevirt/provider_test.go +++ b/pkg/cloudprovider/provider/kubevirt/provider_test.go @@ -59,6 +59,7 @@ type kubevirtProviderSpecConf struct { OsImageDV string // if OsImage from DV and not from http source Instancetype *kubevirtv1.InstancetypeMatcher Preference *kubevirtv1.PreferenceMatcher + StorageTarget StorageTarget OperatingSystem string TopologySpreadConstraint bool Affinity bool @@ -123,6 +124,9 @@ func (k kubevirtProviderSpecConf) rawProviderSpec(t *testing.T) []byte { "storageClassName": "longhorn3"}], {{- end }} "primaryDisk": { + {{- if .StorageTarget }} + "storageTarget": "{{ .StorageTarget }}", + {{- end }} {{- if .OsImageDV }} "osImage": "{{ .OsImageDV }}", {{- else }} @@ -217,6 +221,9 @@ func TestNewVirtualMachine(t *testing.T) { { name: "custom-local-disk", specConf: kubevirtProviderSpecConf{OsImageDV: "ns/dvname"}, + }, { + name: "use-storage-as-storage-target", + specConf: kubevirtProviderSpecConf{StorageTarget: Storage}, }, { name: "http-image-source", diff --git a/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml b/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml new file mode 100644 index 000000000..c0246a801 --- /dev/null +++ b/pkg/cloudprovider/provider/kubevirt/testdata/use-storage-as-storage-target.yaml @@ -0,0 +1,80 @@ +apiVersion: kubevirt.io/v1 +kind: VirtualMachine +metadata: + annotations: + labels: + cluster.x-k8s.io/cluster-name: cluster-name + cluster.x-k8s.io/role: worker + kubevirt.io/vm: use-storage-as-storage-target + md: md-name + name: use-storage-as-storage-target + namespace: test-namespace +spec: + dataVolumeTemplates: + - metadata: + name: use-storage-as-storage-target + annotations: {} + spec: + source: + http: + url: "http://x.y.z.t/ubuntu.img" + storage: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: longhorn + runStrategy: Once + template: + metadata: + creationTimestamp: null + annotations: + "ovn.kubernetes.io/allow_live_migration": "true" + labels: + cluster.x-k8s.io/cluster-name: cluster-name + cluster.x-k8s.io/role: worker + kubevirt.io/vm: use-storage-as-storage-target + md: md-name + spec: + affinity: {} + domain: + devices: + disks: + - disk: + bus: virtio + name: datavolumedisk + - disk: + bus: virtio + name: cloudinitdisk + interfaces: + - macAddress: b6:f5:b4:fe:45:1d + name: default + bridge: {} + resources: + limits: + cpu: "2" + memory: 2Gi + requests: + cpu: "2" + memory: 2Gi + networks: + - name: default + pod: {} + terminationGracePeriodSeconds: 30 + topologyspreadconstraints: + - maxskew: 1 + topologykey: kubernetes.io/hostname + whenunsatisfiable: ScheduleAnyway + labelselector: + matchlabels: + md: md-name + volumes: + - dataVolume: + name: use-storage-as-storage-target + name: datavolumedisk + - cloudInitNoCloud: + secretRef: + name: udsn + name: cloudinitdisk + evictionStrategy: External diff --git a/pkg/cloudprovider/provider/kubevirt/types/types.go b/pkg/cloudprovider/provider/kubevirt/types/types.go index a46fc88b3..52a2c2e63 100644 --- a/pkg/cloudprovider/provider/kubevirt/types/types.go +++ b/pkg/cloudprovider/provider/kubevirt/types/types.go @@ -76,6 +76,9 @@ type Template struct { // PrimaryDisk. type PrimaryDisk struct { Disk + // StorageTarget describes which VirtualMachine storage target will be used in the DataVolumeTemplate. + StorageTarget providerconfigtypes.ConfigVarString `json:"storageTarget,omitempty"` + // OsImage describes the OS that will be installed on the VirtualMachine. OsImage providerconfigtypes.ConfigVarString `json:"osImage,omitempty"` // Source describes the VM Disk Image source. Source providerconfigtypes.ConfigVarString `json:"source,omitempty"`