From 3eb963105f98195d434b08fdf3479be4f3fc880c Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Fri, 3 Oct 2025 16:13:17 +0530 Subject: [PATCH 1/3] Support xz format for template registration --- .../cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java | 5 ++++- .../hypervisor/kvm/storage/MultipathSCSIAdapterBase.java | 5 ++++- .../cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java | 5 ++++- .../cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java | 5 ++++- utils/src/main/java/com/cloud/utils/UriUtils.java | 2 +- .../test/java/com/cloud/utils/UriUtilsParametrizedTest.java | 2 +- utils/src/test/java/com/cloud/utils/UriUtilsTest.java | 1 + 7 files changed, 19 insertions(+), 6 deletions(-) diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index d5119ea55b71..2ab48f39eb4e 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -178,7 +178,8 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S */ public static boolean isTemplateExtractable(String templatePath) { String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") + || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); } /** @@ -193,6 +194,8 @@ public static String getExtractCommandForDownloadedFile(String downloadedTemplat return "bunzip2 -c " + downloadedTemplateFile + " > " + templateUuid; } else if (downloadedTemplateFile.endsWith(".gz")) { return "gunzip -c " + downloadedTemplateFile + " > " + templateUuid; + } else if (downloadedTemplateFile.endsWith(".xz")) { + return "xz -d -c " + downloadedTemplateFile + " > " + templateUuid; } else { throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java index 7ba29ffc26ea..63d310e6a0bb 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java @@ -426,7 +426,8 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, boolean isTemplateExtractable(String templatePath) { ScriptResult result = runScript("file", 5000L, templatePath, "| awk -F' ' '{print $2}'"); String type = result.getResult(); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") + || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); } String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { @@ -436,6 +437,8 @@ String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; } else if (downloadedTemplateFile.endsWith(".gz")) { return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; + } else if (downloadedTemplateFile.endsWith(".xz")) { + return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; } else { throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); } diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index efa8024a34b5..5ebf7a16a431 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -613,7 +613,8 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP private boolean isTemplateExtractable(String templatePath) { String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") + || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); } private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { @@ -623,6 +624,8 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; } else if (downloadedTemplateFile.endsWith(".gz")) { return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; + } else if (downloadedTemplateFile.endsWith(".xz")) { + return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; } else { throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index 4dcc67f3f06b..2471d44bf14b 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -539,7 +539,8 @@ private String extractTemplate(String templateFilePath, File sourceFile, String private boolean isTemplateExtractable(String templatePath) { String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") || type.equalsIgnoreCase("zip"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") + || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); } private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { @@ -549,6 +550,8 @@ private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; } else if (downloadedTemplateFile.endsWith(".gz")) { return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; + } else if (downloadedTemplateFile.endsWith(".xz")) { + return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; } else { throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); } diff --git a/utils/src/main/java/com/cloud/utils/UriUtils.java b/utils/src/main/java/com/cloud/utils/UriUtils.java index 961c121597f5..42603237888c 100644 --- a/utils/src/main/java/com/cloud/utils/UriUtils.java +++ b/utils/src/main/java/com/cloud/utils/UriUtils.java @@ -431,7 +431,7 @@ public static List getMetalinkUrls(String metalinkUrl) { return urls; } - public static final Set COMPRESSION_FORMATS = ImmutableSet.of("zip", "bz2", "gz"); + public static final Set COMPRESSION_FORMATS = ImmutableSet.of("zip", "bz2", "gz", "xz"); public static final Set buildExtensionSet(boolean metalink, String... baseExtensions) { final ImmutableSet.Builder builder = ImmutableSet.builder(); diff --git a/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java b/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java index 3f1e707c6074..2a4c5c34102b 100644 --- a/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java +++ b/utils/src/test/java/com/cloud/utils/UriUtilsParametrizedTest.java @@ -44,7 +44,7 @@ public interface ThrowingBlock { } private static final Set COMMPRESSION_FORMATS = ImmutableSet.of("",".zip", ".bz2", ".gz"); - private static final Set ILLEGAL_COMMPRESSION_FORMATS = ImmutableSet.of(".7z", ".xz"); + private static final Set ILLEGAL_COMMPRESSION_FORMATS = ImmutableSet.of(".7z"); private final static Set FORMATS = ImmutableSet.of( "vhd", "vhdx", diff --git a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java index 47fd389e3aef..8e6408f77835 100644 --- a/utils/src/test/java/com/cloud/utils/UriUtilsTest.java +++ b/utils/src/test/java/com/cloud/utils/UriUtilsTest.java @@ -271,6 +271,7 @@ public void testIsUrlForCompressedFile() { Assert.assertTrue(UriUtils.isUrlForCompressedFile("https://abc.com/xyz.bz2")); Assert.assertTrue(UriUtils.isUrlForCompressedFile("http://abc.com/xyz.zip")); Assert.assertTrue(UriUtils.isUrlForCompressedFile("https://abc.com/xyz.gz")); + Assert.assertTrue(UriUtils.isUrlForCompressedFile("http://abc.com/xyz.xz")); Assert.assertFalse(UriUtils.isUrlForCompressedFile("http://abc.com/xyz.qcow2")); } From 1e5a903a57c0f1854fb0f73822c5e14e0fa558c4 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Tue, 7 Oct 2025 16:33:05 +0530 Subject: [PATCH 2/3] add xz command in shell script --- scripts/installer/createtmplt.sh | 2 ++ scripts/installer/createvolume.sh | 2 ++ scripts/storage/qcow2/createtmplt.sh | 2 ++ scripts/storage/qcow2/createvolume.sh | 2 ++ scripts/storage/secondary/createtmplt.sh | 4 ++++ scripts/storage/secondary/createvolume.sh | 4 ++++ .../development-installation/files/default/createtmplt.sh | 4 ++++ 7 files changed, 20 insertions(+) diff --git a/scripts/installer/createtmplt.sh b/scripts/installer/createtmplt.sh index db3cfd71ee19..b9b403a94bb7 100755 --- a/scripts/installer/createtmplt.sh +++ b/scripts/installer/createtmplt.sh @@ -87,6 +87,8 @@ uncompress() { ;; [zZ][iI][pP]) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/scripts/installer/createvolume.sh b/scripts/installer/createvolume.sh index 52792e9fc3aa..4726404b76a4 100755 --- a/scripts/installer/createvolume.sh +++ b/scripts/installer/createvolume.sh @@ -88,6 +88,8 @@ uncompress() { ;; ZIP) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/scripts/storage/qcow2/createtmplt.sh b/scripts/storage/qcow2/createtmplt.sh index 4bb955e130c7..de7010b0a25e 100755 --- a/scripts/storage/qcow2/createtmplt.sh +++ b/scripts/storage/qcow2/createtmplt.sh @@ -65,6 +65,8 @@ uncompress() { ;; [zZ][iI][pP]) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/scripts/storage/qcow2/createvolume.sh b/scripts/storage/qcow2/createvolume.sh index ff04ee3fe5e0..b2ce249a3d1b 100755 --- a/scripts/storage/qcow2/createvolume.sh +++ b/scripts/storage/qcow2/createvolume.sh @@ -66,6 +66,8 @@ uncompress() { ;; ZIP) unzip -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/scripts/storage/secondary/createtmplt.sh b/scripts/storage/secondary/createtmplt.sh index ed9bbe869e1b..cfc4be28a012 100755 --- a/scripts/storage/secondary/createtmplt.sh +++ b/scripts/storage/secondary/createtmplt.sh @@ -63,6 +63,8 @@ is_compressed() { ;; [zZ][iI][pP]) ctype="zip" ;; + XZ) ctype="xz" + ;; *) echo "File $1 does not appear to be compressed" >&2 return 1 ;; @@ -82,6 +84,8 @@ uncompress() { ;; [zZ][iI][pP]) unzip -q -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/scripts/storage/secondary/createvolume.sh b/scripts/storage/secondary/createvolume.sh index ac69ebe88aba..a47d1fdc2d38 100755 --- a/scripts/storage/secondary/createvolume.sh +++ b/scripts/storage/secondary/createvolume.sh @@ -85,6 +85,8 @@ is_compressed() { ;; ZIP) ctype="zip" ;; + XZ) ctype="xz" + ;; *) echo "File $1 does not appear to be compressed" >&2 return 1 ;; @@ -104,6 +106,8 @@ uncompress() { ;; ZIP) unzip -q -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; diff --git a/tools/devcloud4/common/development-installation/files/default/createtmplt.sh b/tools/devcloud4/common/development-installation/files/default/createtmplt.sh index 2f8f3421350a..ca5022bae4f0 100755 --- a/tools/devcloud4/common/development-installation/files/default/createtmplt.sh +++ b/tools/devcloud4/common/development-installation/files/default/createtmplt.sh @@ -75,6 +75,8 @@ is_compressed() { ;; ZIP) ctype="zip" ;; + XZ) ctype="xz" + ;; *) echo "File $1 does not appear to be compressed" >&2 return 1 ;; @@ -94,6 +96,8 @@ uncompress() { ;; ZIP) unzip -q -p $1 | cat > $tmpfile ;; + XZ) xz -d -c $1 > $tmpfile + ;; *) printf "$1" return 0 ;; From 47db5194b0df28f8d000a08f5ff3438867a062c2 Mon Sep 17 00:00:00 2001 From: Manoj Kumar Date: Mon, 13 Oct 2025 14:47:35 +0530 Subject: [PATCH 3/3] use utility to get extract command --- .../kvm/storage/LibvirtStorageAdaptor.java | 34 +---------- .../kvm/storage/MultipathSCSIAdapterBase.java | 21 ------- .../kvm/storage/ScaleIOStorageAdaptor.java | 26 ++------ .../kvm/storage/LinstorStorageAdaptor.java | 3 +- .../kvm/storage/StorPoolStorageAdaptor.java | 25 +------- .../utils/storage/TemplateDownloaderUtil.java | 61 +++++++++++++++++++ 6 files changed, 73 insertions(+), 97 deletions(-) create mode 100644 utils/src/main/java/com/cloud/utils/storage/TemplateDownloaderUtil.java diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 2ab48f39eb4e..26de4cbdb931 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -79,6 +79,7 @@ import com.cloud.storage.StorageLayer; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; +import com.cloud.utils.storage.TemplateDownloaderUtil; public class LibvirtStorageAdaptor implements StorageAdaptor { protected Logger logger = LogManager.getLogger(getClass()); @@ -172,40 +173,11 @@ public KVMPhysicalDisk createDiskFromTemplateBacking(KVMPhysicalDisk template, S return null; } - /** - * Checks if downloaded template is extractable - * @return true if it should be extracted, false if not - */ - public static boolean isTemplateExtractable(String templatePath) { - String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") - || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); - } - - /** - * Return extract command to execute given downloaded file - * @param downloadedTemplateFile - * @param templateUuid - */ - public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateUuid) { - if (downloadedTemplateFile.endsWith(".zip")) { - return "unzip -p " + downloadedTemplateFile + " | cat > " + templateUuid; - } else if (downloadedTemplateFile.endsWith(".bz2")) { - return "bunzip2 -c " + downloadedTemplateFile + " > " + templateUuid; - } else if (downloadedTemplateFile.endsWith(".gz")) { - return "gunzip -c " + downloadedTemplateFile + " > " + templateUuid; - } else if (downloadedTemplateFile.endsWith(".xz")) { - return "xz -d -c " + downloadedTemplateFile + " > " + templateUuid; - } else { - throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); - } - } - /** * Extract downloaded template into installPath, remove compressed file */ public static void extractDownloadedTemplate(String downloadedTemplateFile, KVMStoragePool destPool, String destinationFile) { - String extractCommand = getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile); + String extractCommand = TemplateDownloaderUtil.getExtractCommandForDownloadedFile(downloadedTemplateFile, destinationFile); Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript("rm -f " + downloadedTemplateFile); } @@ -224,7 +196,7 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP if (destPool.getType() == StoragePoolType.NetworkFilesystem || destPool.getType() == StoragePoolType.Filesystem || destPool.getType() == StoragePoolType.SharedMountPoint) { - if (!Storage.ImageFormat.ISO.equals(format) && isTemplateExtractable(templateFilePath)) { + if (!Storage.ImageFormat.ISO.equals(format) && TemplateDownloaderUtil.isTemplateExtractable(templateFilePath)) { extractDownloadedTemplate(templateFilePath, destPool, destinationFile); } else { Script.runSimpleBashScript("mv " + templateFilePath + " " + destinationFile); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java index 63d310e6a0bb..514e412559cc 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/MultipathSCSIAdapterBase.java @@ -423,27 +423,6 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, throw new UnsupportedOperationException("Unimplemented method 'createPhysicalDisk'"); } - boolean isTemplateExtractable(String templatePath) { - ScriptResult result = runScript("file", 5000L, templatePath, "| awk -F' ' '{print $2}'"); - String type = result.getResult(); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") - || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); - } - - String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { - if (downloadedTemplateFile.endsWith(".zip")) { - return "unzip -p " + downloadedTemplateFile + " | cat > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".bz2")) { - return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".gz")) { - return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".xz")) { - return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; - } else { - throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); - } - } - boolean waitForDiskToBecomeAvailable(AddressInfo address, KVMStoragePool pool, long waitTimeInSec) { LOGGER.debug("Waiting for the volume with id: " + address.getPath() + " of the storage pool: " + pool.getUuid() + " to become available for " + waitTimeInSec + " secs"); diff --git a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java index 5ebf7a16a431..e336e0e5a54d 100644 --- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/ScaleIOStorageAdaptor.java @@ -53,6 +53,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; +import com.cloud.utils.storage.TemplateDownloaderUtil; + import org.apache.commons.lang3.StringUtils; public class ScaleIOStorageAdaptor implements StorageAdaptor { @@ -572,10 +574,10 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP throw new CloudRuntimeException("Failed to find the disk: " + destTemplatePath + " of the storage pool: " + destPool.getUuid()); } - if (isTemplateExtractable(templateFilePath)) { + if (TemplateDownloaderUtil.isTemplateExtractable(templateFilePath)) { srcTemplateFilePath = sourceFile.getParent() + "/" + UUID.randomUUID().toString(); logger.debug("Extract the downloaded template " + templateFilePath + " to " + srcTemplateFilePath); - String extractCommand = getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); + String extractCommand = TemplateDownloaderUtil.getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript("rm -f " + templateFilePath); } @@ -611,26 +613,6 @@ public KVMPhysicalDisk createTemplateFromDirectDownloadFile(String templateFileP return destDisk; } - private boolean isTemplateExtractable(String templatePath) { - String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") - || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); - } - - private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { - if (downloadedTemplateFile.endsWith(".zip")) { - return "unzip -p " + downloadedTemplateFile + " | cat > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".bz2")) { - return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".gz")) { - return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".xz")) { - return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; - } else { - throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); - } - } - public void resizeQcow2ToVolume(String volumePath, QemuImageOptions options, List objects, Integer timeout) throws QemuImgException, LibvirtException { long rawSizeBytes = getPhysicalDiskSize(volumePath); long usableSizeBytes = getUsableBytesFromRawBytes(rawSizeBytes); diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 289285707321..a52eaa762131 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -39,6 +39,7 @@ import org.apache.logging.log4j.LogManager; import org.libvirt.LibvirtException; +import com.cloud.utils.storage.TemplateDownloaderUtil; import com.linbit.linstor.api.ApiClient; import com.linbit.linstor.api.ApiConsts; import com.linbit.linstor.api.ApiException; @@ -730,7 +731,7 @@ private void fileExistsOrThrow(String templateFilePath) { private String getFinalDirectDownloadPath(String templateFilePath, KVMStoragePool destPool) { String finalSourcePath = templateFilePath; - if (LibvirtStorageAdaptor.isTemplateExtractable(templateFilePath)) { + if (TemplateDownloaderUtil.isTemplateExtractable(templateFilePath)) { finalSourcePath = templateFilePath.substring(0, templateFilePath.lastIndexOf('.')); LibvirtStorageAdaptor.extractDownloadedTemplate(templateFilePath, destPool, finalSourcePath); } diff --git a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java index 2471d44bf14b..545f7b33c5fd 100644 --- a/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java +++ b/plugins/storage/volume/storpool/src/main/java/com/cloud/hypervisor/kvm/storage/StorPoolStorageAdaptor.java @@ -26,6 +26,7 @@ import com.cloud.utils.script.OutputInterpreter; import com.cloud.utils.script.Script; +import com.cloud.utils.storage.TemplateDownloaderUtil; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.google.gson.JsonParser; @@ -528,35 +529,15 @@ private static Script createStorPoolRequest(String js, String apiCall, String pa private String extractTemplate(String templateFilePath, File sourceFile, String srcTemplateFilePath, String templateName) { - if (isTemplateExtractable(templateFilePath)) { + if (TemplateDownloaderUtil.isTemplateExtractable(templateFilePath)) { srcTemplateFilePath = sourceFile.getParent() + "/" + templateName; - String extractCommand = getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); + String extractCommand = TemplateDownloaderUtil.getExtractCommandForDownloadedFile(templateFilePath, srcTemplateFilePath); Script.runSimpleBashScript(extractCommand); Script.runSimpleBashScript("rm -f " + templateFilePath); } return srcTemplateFilePath; } - private boolean isTemplateExtractable(String templatePath) { - String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); - return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") - || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); - } - - private String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { - if (downloadedTemplateFile.endsWith(".zip")) { - return "unzip -p " + downloadedTemplateFile + " | cat > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".bz2")) { - return "bunzip2 -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".gz")) { - return "gunzip -c " + downloadedTemplateFile + " > " + templateFile; - } else if (downloadedTemplateFile.endsWith(".xz")) { - return "xz -d -c " + downloadedTemplateFile + " > " + templateFile; - } else { - throw new CloudRuntimeException("Unable to extract template " + downloadedTemplateFile); - } - } - private String getNameFromResponse(String resp, boolean tildeNeeded, boolean isSnapshot) { JsonParser jsonParser = new JsonParser(); JsonObject respObj = (JsonObject) jsonParser.parse(resp); diff --git a/utils/src/main/java/com/cloud/utils/storage/TemplateDownloaderUtil.java b/utils/src/main/java/com/cloud/utils/storage/TemplateDownloaderUtil.java new file mode 100644 index 000000000000..0a7294784683 --- /dev/null +++ b/utils/src/main/java/com/cloud/utils/storage/TemplateDownloaderUtil.java @@ -0,0 +1,61 @@ +// +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +// + +package com.cloud.utils.storage; + +import org.apache.commons.io.FilenameUtils; + +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.utils.script.Script; + +public class TemplateDownloaderUtil { + + private TemplateDownloaderUtil() {} + + /** + * Checks if downloaded template is extractable + * @return true if it should be extracted, false if not + */ + public static boolean isTemplateExtractable(String templatePath) { + String type = Script.runSimpleBashScript("file " + templatePath + " | awk -F' ' '{print $2}'"); + return type.equalsIgnoreCase("bzip2") || type.equalsIgnoreCase("gzip") + || type.equalsIgnoreCase("zip") || type.equalsIgnoreCase("xz"); + } + + /** + * Return extract command to execute given downloaded file + * @param downloadedTemplateFile + * @param templateFile + */ + public static String getExtractCommandForDownloadedFile(String downloadedTemplateFile, String templateFile) { + String extension = FilenameUtils.getExtension(downloadedTemplateFile).toLowerCase(); + switch (extension) { + case "zip": + return String.format("unzip -p '%s' | cat > '%s'", downloadedTemplateFile, templateFile); + case "bz2": + return String.format("bunzip2 -c '%s' > '%s'", downloadedTemplateFile, templateFile); + case "gz": + return String.format("gunzip -c '%s' > '%s'", downloadedTemplateFile, templateFile); + case "xz": + return String.format("xz -d -c '%s' > '%s'", downloadedTemplateFile, templateFile); + default: + throw new CloudRuntimeException("Unable to extract template: " + downloadedTemplateFile + " (unsupported format: ." + extension + ")"); + } + } +}