-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(vd): support filesystem mode (#327)
Support filesystem mode. Support qcow2 images. --------- Signed-off-by: yaroslavborbat <[email protected]>
- Loading branch information
1 parent
621baf2
commit 754c4a7
Showing
22 changed files
with
529 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
6 changes: 0 additions & 6 deletions
6
api/pkg/apiserver/api/generated/openapi/zz_generated.openapi.go
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
images/cdi-artifact/patches/012-add-caps-for-deckhouse-provisioners.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
diff --git a/pkg/storagecapabilities/storagecapabilities.go b/pkg/storagecapabilities/storagecapabilities.go | ||
index 24bbac64b..fccdd0c08 100644 | ||
--- a/pkg/storagecapabilities/storagecapabilities.go | ||
+++ b/pkg/storagecapabilities/storagecapabilities.go | ||
@@ -8,9 +8,10 @@ import ( | ||
|
||
v1 "k8s.io/api/core/v1" | ||
storagev1 "k8s.io/api/storage/v1" | ||
- "kubevirt.io/containerized-data-importer/pkg/util" | ||
"sigs.k8s.io/controller-runtime/pkg/client" | ||
|
||
+ "kubevirt.io/containerized-data-importer/pkg/util" | ||
+ | ||
cdiv1 "kubevirt.io/containerized-data-importer-api/pkg/apis/core/v1beta1" | ||
) | ||
|
||
@@ -102,6 +103,9 @@ var CapabilitiesByProvisionerKey = map[string][]StorageCapabilities{ | ||
"manila.csi.openstack.org": {{rwx, file}}, | ||
// ovirt csi | ||
"csi.ovirt.org": createRWOBlockAndFilesystemCapabilities(), | ||
+ // Deckhouse | ||
+ "replicated.csi.storage.deckhouse.io": createLinstorCapabilities(), | ||
+ "local.csi.storage.deckhouse.io": createTopoLVMCapabilities(), | ||
} | ||
|
||
// SourceFormatsByProvisionerKey defines the advised data import cron source format |
131 changes: 131 additions & 0 deletions
131
images/cdi-artifact/patches/013-converting-images-in-filesystem-to-qcow2.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
diff --git a/pkg/common/format.go b/pkg/common/format.go | ||
new file mode 100644 | ||
index 000000000..06497e582 | ||
--- /dev/null | ||
+++ b/pkg/common/format.go | ||
@@ -0,0 +1,22 @@ | ||
+package common | ||
+ | ||
+import "os" | ||
+ | ||
+func GetFormat(path string) (string, error) { | ||
+ const ( | ||
+ formatQcow2 = "qcow2" | ||
+ formatRaw = "raw" | ||
+ ) | ||
+ info, err := os.Stat(path) | ||
+ if err != nil { | ||
+ if os.IsNotExist(err) { | ||
+ return formatQcow2, nil | ||
+ } | ||
+ return "", err | ||
+ } | ||
+ mode := info.Mode() | ||
+ if mode&os.ModeDevice != 0 { | ||
+ return formatRaw, nil | ||
+ } | ||
+ return formatQcow2, nil | ||
+} | ||
diff --git a/pkg/image/qemu.go b/pkg/image/qemu.go | ||
index 651fb5fc8..adcfe8824 100644 | ||
--- a/pkg/image/qemu.go | ||
+++ b/pkg/image/qemu.go | ||
@@ -61,6 +61,7 @@ type ImgInfo struct { | ||
// QEMUOperations defines the interface for executing qemu subprocesses | ||
type QEMUOperations interface { | ||
ConvertToRawStream(*url.URL, string, bool) error | ||
+ ConvertToFormatStream(url *url.URL, format, dest string, preallocate bool) error | ||
Resize(string, resource.Quantity, bool) error | ||
Info(url *url.URL) (*ImgInfo, error) | ||
Validate(*url.URL, int64) error | ||
@@ -114,6 +115,36 @@ func NewQEMUOperations() QEMUOperations { | ||
return &qemuOperations{} | ||
} | ||
|
||
+func convertTo(format, src, dest string, preallocate bool) error { | ||
+ switch format { | ||
+ case "qcow2", "raw": | ||
+ // Do nothing. | ||
+ default: | ||
+ return errors.Errorf("unknown format: %s", format) | ||
+ } | ||
+ args := []string{"convert", "-t", "writeback", "-p", "-O", format, src, dest} | ||
+ var err error | ||
+ | ||
+ if preallocate { | ||
+ err = addPreallocation(args, convertPreallocationMethods, func(args []string) ([]byte, error) { | ||
+ return qemuExecFunction(nil, reportProgress, "qemu-img", args...) | ||
+ }) | ||
+ } else { | ||
+ klog.V(1).Infof("Running qemu-img with args: %v", args) | ||
+ _, err = qemuExecFunction(nil, reportProgress, "qemu-img", args...) | ||
+ } | ||
+ if err != nil { | ||
+ os.Remove(dest) | ||
+ errorMsg := fmt.Sprintf("could not convert image to %s", format) | ||
+ if nbdkitLog, err := os.ReadFile(common.NbdkitLogPath); err == nil { | ||
+ errorMsg += " " + string(nbdkitLog) | ||
+ } | ||
+ return errors.Wrap(err, errorMsg) | ||
+ } | ||
+ | ||
+ return nil | ||
+} | ||
+ | ||
func convertToRaw(src, dest string, preallocate bool) error { | ||
args := []string{"convert", "-t", "writeback", "-p", "-O", "raw", src, dest} | ||
var err error | ||
@@ -145,6 +176,13 @@ func (o *qemuOperations) ConvertToRawStream(url *url.URL, dest string, prealloca | ||
return convertToRaw(url.String(), dest, preallocate) | ||
} | ||
|
||
+func (o *qemuOperations) ConvertToFormatStream(url *url.URL, format, dest string, preallocate bool) error { | ||
+ if len(url.Scheme) > 0 && url.Scheme != "nbd+unix" { | ||
+ return fmt.Errorf("not valid schema %s", url.Scheme) | ||
+ } | ||
+ return convertTo(format, url.String(), dest, preallocate) | ||
+} | ||
+ | ||
// convertQuantityToQemuSize translates a quantity string into a Qemu compatible string. | ||
func convertQuantityToQemuSize(size resource.Quantity) string { | ||
int64Size, asInt := size.AsInt64() | ||
@@ -274,13 +312,17 @@ func CreateBlankImage(dest string, size resource.Quantity, preallocate bool) err | ||
|
||
// CreateBlankImage creates a raw image with a given size | ||
func (o *qemuOperations) CreateBlankImage(dest string, size resource.Quantity, preallocate bool) error { | ||
+ format, err := common.GetFormat(dest) | ||
+ if err != nil { | ||
+ return err | ||
+ } | ||
klog.V(3).Infof("image size is %s", size.String()) | ||
- args := []string{"create", "-f", "raw", dest, convertQuantityToQemuSize(size)} | ||
+ args := []string{"create", "-f", format, dest, convertQuantityToQemuSize(size)} | ||
if preallocate { | ||
klog.V(1).Infof("Added preallocation") | ||
args = append(args, []string{"-o", "preallocation=falloc"}...) | ||
} | ||
- _, err := qemuExecFunction(nil, nil, "qemu-img", args...) | ||
+ _, err = qemuExecFunction(nil, nil, "qemu-img", args...) | ||
if err != nil { | ||
os.Remove(dest) | ||
return errors.Wrap(err, fmt.Sprintf("could not create raw image with size %s in %s", size.String(), dest)) | ||
diff --git a/pkg/importer/data-processor.go b/pkg/importer/data-processor.go | ||
index ca7b2e853..ec24b3b64 100644 | ||
--- a/pkg/importer/data-processor.go | ||
+++ b/pkg/importer/data-processor.go | ||
@@ -276,8 +276,13 @@ func (dp *DataProcessor) convert(url *url.URL) (ProcessingPhase, error) { | ||
if err != nil { | ||
return ProcessingPhaseError, err | ||
} | ||
- klog.V(3).Infoln("Converting to Raw") | ||
- err = qemuOperations.ConvertToRawStream(url, dp.dataFile, dp.preallocation) | ||
+ format, err := common.GetFormat(dp.dataFile) | ||
+ if err != nil { | ||
+ return ProcessingPhaseError, errors.Wrap(err, "Unable to get format") | ||
+ } | ||
+ | ||
+ klog.V(3).Infoln("Converting to", "format", format) | ||
+ err = qemuOperations.ConvertToFormatStream(url, format, dp.dataFile, dp.preallocation) | ||
if err != nil { | ||
return ProcessingPhaseError, errors.Wrap(err, "Conversion to Raw failed") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
128 changes: 128 additions & 0 deletions
128
images/virt-artifact/patches/021-support-qcow2-for-filesystem.patch
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
diff --git a/pkg/host-disk/host-disk.go b/pkg/host-disk/host-disk.go | ||
index 7ada596d87..5be737e557 100644 | ||
--- a/pkg/host-disk/host-disk.go | ||
+++ b/pkg/host-disk/host-disk.go | ||
@@ -22,6 +22,7 @@ package hostdisk | ||
import ( | ||
"fmt" | ||
"os" | ||
+ "os/exec" | ||
"path" | ||
"path/filepath" | ||
"syscall" | ||
@@ -171,6 +172,15 @@ func createSparseRaw(fullPath string, size int64) (err error) { | ||
return nil | ||
} | ||
|
||
+func createQcow2(fullPath string, size int64) (err error) { | ||
+ log.Log.Infof("Create %s with qcow2 format", fullPath) | ||
+ cmd := exec.Command("qemu-img", "create", "-f", "qcow2", fullPath, fmt.Sprintf("%db", size)) | ||
+ if err = cmd.Run(); err != nil { | ||
+ return fmt.Errorf("failed to create qcow2: %w", err) | ||
+ } | ||
+ return nil | ||
+} | ||
+ | ||
func getPVCDiskImgPath(volumeName string, diskName string) string { | ||
return path.Join(pvcBaseDir, volumeName, diskName) | ||
} | ||
@@ -236,7 +246,7 @@ func (hdc *DiskImgCreator) mountHostDiskAndSetOwnership(vmi *v1.VirtualMachineIn | ||
return err | ||
} | ||
if !fileExists { | ||
- if err := hdc.handleRequestedSizeAndCreateSparseRaw(vmi, diskDir, diskPath, hostDisk); err != nil { | ||
+ if err := hdc.handleRequestedSizeAndCreateQcow2(vmi, diskDir, diskPath, hostDisk); err != nil { | ||
return err | ||
} | ||
} | ||
@@ -248,7 +258,7 @@ func (hdc *DiskImgCreator) mountHostDiskAndSetOwnership(vmi *v1.VirtualMachineIn | ||
return nil | ||
} | ||
|
||
-func (hdc *DiskImgCreator) handleRequestedSizeAndCreateSparseRaw(vmi *v1.VirtualMachineInstance, diskDir string, diskPath string, hostDisk *v1.HostDisk) error { | ||
+func (hdc *DiskImgCreator) handleRequestedSizeAndCreateQcow2(vmi *v1.VirtualMachineInstance, diskDir string, diskPath string, hostDisk *v1.HostDisk) error { | ||
size, err := hdc.dirBytesAvailableFunc(diskDir, hdc.minimumPVCReserveBytes) | ||
availableSize := int64(size) | ||
if err != nil { | ||
@@ -261,9 +271,9 @@ func (hdc *DiskImgCreator) handleRequestedSizeAndCreateSparseRaw(vmi *v1.Virtual | ||
return err | ||
} | ||
} | ||
- err = createSparseRaw(diskPath, requestedSize) | ||
+ err = createQcow2(diskPath, requestedSize) | ||
if err != nil { | ||
- log.Log.Reason(err).Errorf("Couldn't create a sparse raw file for disk path: %s, error: %v", diskPath, err) | ||
+ log.Log.Reason(err).Errorf("Couldn't create a qcow2 file for disk path: %s, error: %v", diskPath, err) | ||
return err | ||
} | ||
return nil | ||
diff --git a/pkg/virt-launcher/virtwrap/converter/converter.go b/pkg/virt-launcher/virtwrap/converter/converter.go | ||
index c64b315d8f..71bbe50037 100644 | ||
--- a/pkg/virt-launcher/virtwrap/converter/converter.go | ||
+++ b/pkg/virt-launcher/virtwrap/converter/converter.go | ||
@@ -590,7 +590,6 @@ func Add_Agent_To_api_Channel() (channel api.Channel) { | ||
} | ||
|
||
func Convert_v1_Volume_To_api_Disk(source *v1.Volume, disk *api.Disk, c *ConverterContext, diskIndex int) error { | ||
- | ||
if source.ContainerDisk != nil { | ||
return Convert_v1_ContainerDiskSource_To_api_Disk(source.Name, source.ContainerDisk, disk, c, diskIndex) | ||
} | ||
@@ -733,7 +732,7 @@ func Convert_v1_Hotplug_DataVolume_To_api_Disk(name string, disk *api.Disk, c *C | ||
// Convert_v1_FilesystemVolumeSource_To_api_Disk takes a FS source and builds the domain Disk representation | ||
func Convert_v1_FilesystemVolumeSource_To_api_Disk(volumeName string, disk *api.Disk, volumesDiscardIgnore []string) error { | ||
disk.Type = "file" | ||
- disk.Driver.Type = "raw" | ||
+ disk.Driver.Type = "qcow2" | ||
disk.Driver.ErrorPolicy = "stop" | ||
disk.Source.File = GetFilesystemVolumePath(volumeName) | ||
if !contains(volumesDiscardIgnore, volumeName) { | ||
@@ -745,7 +744,7 @@ func Convert_v1_FilesystemVolumeSource_To_api_Disk(volumeName string, disk *api. | ||
// Convert_v1_Hotplug_FilesystemVolumeSource_To_api_Disk takes a FS source and builds the KVM Disk representation | ||
func Convert_v1_Hotplug_FilesystemVolumeSource_To_api_Disk(volumeName string, disk *api.Disk, volumesDiscardIgnore []string) error { | ||
disk.Type = "file" | ||
- disk.Driver.Type = "raw" | ||
+ disk.Driver.Type = "qcow2" | ||
disk.Driver.ErrorPolicy = "stop" | ||
if !contains(volumesDiscardIgnore, volumeName) { | ||
disk.Driver.Discard = "unmap" | ||
@@ -779,13 +778,37 @@ func Convert_v1_Hotplug_BlockVolumeSource_To_api_Disk(volumeName string, disk *a | ||
} | ||
|
||
func Convert_v1_HostDisk_To_api_Disk(volumeName string, path string, disk *api.Disk) error { | ||
+ file := hostdisk.GetMountedHostDiskPath(volumeName, path) | ||
+ format, err := getFormat(file) | ||
+ if err != nil { | ||
+ return fmt.Errorf("cannot detect host disk format: %w", err) | ||
+ } | ||
disk.Type = "file" | ||
- disk.Driver.Type = "raw" | ||
+ disk.Driver.Type = format | ||
disk.Driver.ErrorPolicy = "stop" | ||
- disk.Source.File = hostdisk.GetMountedHostDiskPath(volumeName, path) | ||
+ disk.Source.File = file | ||
return nil | ||
} | ||
|
||
+func getFormat(path string) (string, error) { | ||
+ const ( | ||
+ formatQcow2 = "qcow2" | ||
+ formatRaw = "raw" | ||
+ ) | ||
+ info, err := os.Stat(path) | ||
+ if err != nil { | ||
+ if errors.Is(err, os.ErrNotExist) { | ||
+ return formatQcow2, nil | ||
+ } | ||
+ return "", err | ||
+ } | ||
+ mode := info.Mode() | ||
+ if mode&os.ModeDevice != 0 { | ||
+ return formatRaw, nil | ||
+ } | ||
+ return formatQcow2, nil | ||
+} | ||
+ | ||
func Convert_v1_SysprepSource_To_api_Disk(volumeName string, disk *api.Disk) error { | ||
if disk.Type == "lun" { | ||
return fmt.Errorf(deviceTypeNotCompatibleFmt, disk.Alias.GetName()) |
Oops, something went wrong.