From 2cdee4f4023ec003fe65cfa85ebb654f1446ff59 Mon Sep 17 00:00:00 2001 From: Manuel Bentele Date: Wed, 19 May 2021 09:48:37 +0200 Subject: Rename 'vm.disk' package to 'virtualization.disk' --- .../org/openslx/virtualization/disk/DiskImage.java | 253 +++++++++++++++++++ .../virtualization/disk/DiskImageException.java | 25 ++ .../virtualization/disk/DiskImageQcow2.java | 232 +++++++++++++++++ .../virtualization/disk/DiskImageUtils.java | 144 +++++++++++ .../openslx/virtualization/disk/DiskImageVdi.java | 105 ++++++++ .../openslx/virtualization/disk/DiskImageVmdk.java | 281 +++++++++++++++++++++ .../virtualization/virtualizer/Virtualizer.java | 2 +- .../virtualizer/VirtualizerDocker.java | 4 +- .../virtualizer/VirtualizerQemu.java | 4 +- .../virtualizer/VirtualizerVirtualBox.java | 4 +- .../virtualizer/VirtualizerVmware.java | 4 +- src/main/java/org/openslx/vm/disk/DiskImage.java | 253 ------------------- .../org/openslx/vm/disk/DiskImageException.java | 25 -- .../java/org/openslx/vm/disk/DiskImageQcow2.java | 232 ----------------- .../java/org/openslx/vm/disk/DiskImageUtils.java | 144 ----------- .../java/org/openslx/vm/disk/DiskImageVdi.java | 105 -------- .../java/org/openslx/vm/disk/DiskImageVmdk.java | 281 --------------------- .../VirtualizationConfigurationQemuTest.java | 6 +- .../VirtualizationConfigurationVirtualBoxTest.java | 2 +- ...urationLogicDozModServerToDozModClientTest.java | 2 +- .../virtualization/disk/DiskImageQcow2Test.java | 221 ++++++++++++++++ .../openslx/virtualization/disk/DiskImageTest.java | 19 ++ .../disk/DiskImageTestResources.java | 16 ++ .../virtualization/disk/DiskImageVdiTest.java | 44 ++++ .../virtualization/disk/DiskImageVmdkTest.java | 111 ++++++++ .../org/openslx/vm/disk/DiskImageQcow2Test.java | 221 ---------------- .../java/org/openslx/vm/disk/DiskImageTest.java | 19 -- .../openslx/vm/disk/DiskImageTestResources.java | 16 -- .../java/org/openslx/vm/disk/DiskImageVdiTest.java | 44 ---- .../org/openslx/vm/disk/DiskImageVmdkTest.java | 111 -------- 30 files changed, 1465 insertions(+), 1465 deletions(-) create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImage.java create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImageException.java create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImageQcow2.java create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImageUtils.java create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImageVdi.java create mode 100644 src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImage.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImageException.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImageQcow2.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImageUtils.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImageVdi.java delete mode 100644 src/main/java/org/openslx/vm/disk/DiskImageVmdk.java create mode 100644 src/test/java/org/openslx/virtualization/disk/DiskImageQcow2Test.java create mode 100644 src/test/java/org/openslx/virtualization/disk/DiskImageTest.java create mode 100644 src/test/java/org/openslx/virtualization/disk/DiskImageTestResources.java create mode 100644 src/test/java/org/openslx/virtualization/disk/DiskImageVdiTest.java create mode 100644 src/test/java/org/openslx/virtualization/disk/DiskImageVmdkTest.java delete mode 100644 src/test/java/org/openslx/vm/disk/DiskImageQcow2Test.java delete mode 100644 src/test/java/org/openslx/vm/disk/DiskImageTest.java delete mode 100644 src/test/java/org/openslx/vm/disk/DiskImageTestResources.java delete mode 100644 src/test/java/org/openslx/vm/disk/DiskImageVdiTest.java delete mode 100644 src/test/java/org/openslx/vm/disk/DiskImageVmdkTest.java diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImage.java b/src/main/java/org/openslx/virtualization/disk/DiskImage.java new file mode 100644 index 0000000..5271e6e --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImage.java @@ -0,0 +1,253 @@ +package org.openslx.virtualization.disk; + +import java.io.Closeable; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.util.List; +import java.util.function.Predicate; + +import org.openslx.bwlp.thrift.iface.Virtualizer; +import org.openslx.thrifthelper.TConst; +import org.openslx.util.Util; +import org.openslx.virtualization.Version; + +/** + * Disk image for virtual machines. + * + * @implNote This class is the abstract base class to implement various specific disk images (like + * QCOW2 or VMDK). + * + * @author Manuel Bentele + * @version 1.0 + */ +public abstract class DiskImage implements Closeable +{ + /** + * Stores the image file of the disk. + */ + private RandomAccessFile diskImage = null; + + /** + * Creates a new disk image from an existing image file with a known disk image format. + * + * @param diskImage file to a disk storing the image content. + * + * @implNote Do not use this constructor to create a new disk image from an image file with + * unknown disk image format. Instead, use the factory method + * {@link #newInstance(File)} to probe unknown disk image files before creation. + */ + protected DiskImage( RandomAccessFile diskImage ) + { + this.diskImage = diskImage; + } + + /** + * Returns the disk image file. + * + * @return the disk image file. + */ + protected RandomAccessFile getDiskImage() + { + return this.diskImage; + } + + /** + * Checks whether disk image is standalone and do not depend on other files (e.g. snapshot + * files). + * + * @return state whether disk image is standalone or not. + * + * @throws DiskImageException unable to check if disk image is standalone. + */ + public abstract boolean isStandalone() throws DiskImageException; + + /** + * Checks whether disk image is compressed. + * + * @return state whether disk image is compressed or not. + * + * @throws DiskImageException unable to check whether disk image is compressed. + */ + public abstract boolean isCompressed() throws DiskImageException; + + /** + * Checks whether disk image is a snapshot. + * + * @return state whether disk image is a snapshot or not. + * + * @throws DiskImageException unable to check whether disk image is a snapshot. + */ + public abstract boolean isSnapshot() throws DiskImageException; + + /** + * Returns the version of the disk image format. + * + * @return version of the disk image format. + * + * @throws DiskImageException unable to obtain version of the disk image format. + */ + public abstract Version getVersion() throws DiskImageException; + + /** + * Returns the disk image description. + * + * @return description of the disk image. + * + * @throws DiskImageException unable to obtain description of the disk image. + */ + public abstract String getDescription() throws DiskImageException; + + /** + * Returns the format of the disk image. + * + * @return format of the disk image. + */ + public abstract ImageFormat getFormat(); + + /** + * Creates a new disk image from an existing image file with an unknown disk image format. + * + * @param diskImagePath file to a disk storing the image content. + * @return concrete disk image instance. + * + * @throws FileNotFoundException cannot find specified disk image file. + * @throws IOException cannot access the content of the disk image file. + * @throws DiskImageException disk image file has an invalid and unknown disk image format. + */ + public static DiskImage newInstance( File diskImagePath ) + throws FileNotFoundException, IOException, DiskImageException + { + // Make sure this doesn't escape the scope, in case instantiation fails - we can't know when the GC + // would come along and close this file, which is problematic on Windows (blocking rename/delete) + final RandomAccessFile fileHandle = new RandomAccessFile( diskImagePath, "r" ); + + try { + if ( DiskImageQcow2.probe( fileHandle ) ) { + return new DiskImageQcow2( fileHandle ); + } else if ( DiskImageVdi.probe( fileHandle ) ) { + return new DiskImageVdi( fileHandle ); + } else if ( DiskImageVmdk.probe( fileHandle ) ) { + return new DiskImageVmdk( fileHandle ); + } + } catch ( Exception e ) { + Util.safeClose( fileHandle ); + throw e; + } + Util.safeClose( fileHandle ); + final String errorMsg = new String( "File '" + diskImagePath.getAbsolutePath() + "' is not a valid disk image!" ); + throw new DiskImageException( errorMsg ); + } + + @Override + public void close() throws IOException + { + Util.safeClose( diskImage ); + } + + @Override + protected void finalize() throws Throwable + { + close(); + } + + /** + * Format of a disk image. + * + * @author Manuel Bentele + * @version 1.0 + */ + public enum ImageFormat + { + // @formatter:off + NONE ( "none" ), + QCOW2( "qcow2" ), + VDI ( "vdi" ), + VMDK ( "vmdk" ); + // @formatter:on + + /** + * Stores filename extension of the disk image format. + */ + public final String extension; + + /** + * Create new disk image format. + * + * @param extension filename extension of the disk image format. + */ + ImageFormat( String extension ) + { + this.extension = extension; + } + + /** + * Returns filename extension of the disk image. + * + * @return filename extension of the disk image. + */ + public String getExtension() + { + return this.extension; + } + + /** + * Checks if the disk image format is supported by a virtualizer. + * + * @param supportedImageFormats list of supported disk image formats of a virtualizer. + * @return true if image type is supported by the virtualizer; otherwise + * false. + */ + public boolean isSupportedbyVirtualizer( List supportedImageFormats ) + { + Predicate matchDiskFormat = supportedImageFormat -> supportedImageFormat.toString() + .equalsIgnoreCase( this.toString() ); + return supportedImageFormats.stream().anyMatch( matchDiskFormat ); + } + + /** + * Returns default (preferred) disk image format for the specified virtualizer. + * + * @param virt virtualizer for that the default disk image should be determined. + * @return default (preferred) disk image format. + */ + public static ImageFormat defaultForVirtualizer( Virtualizer virt ) + { + if ( virt == null ) { + return null; + } else { + return ImageFormat.defaultForVirtualizer( virt.virtId ); + } + } + + /** + * Returns default (preferred) disk image format for the specified virtualizer. + * + * @param virtId ID of a virtualizer for that the default disk image should be determined. + * @return default (preferred) disk image format. + */ + public static ImageFormat defaultForVirtualizer( String virtId ) + { + ImageFormat imgFormat = null; + + if ( TConst.VIRT_DOCKER.equals( virtId ) ) { + imgFormat = NONE; + } else if ( TConst.VIRT_QEMU.equals( virtId ) ) { + imgFormat = QCOW2; + } else if ( TConst.VIRT_VIRTUALBOX.equals( virtId ) ) { + imgFormat = VDI; + } else if ( TConst.VIRT_VMWARE.equals( virtId ) ) { + imgFormat = VMDK; + } + + return imgFormat; + } + + @Override + public String toString() + { + return this.getExtension(); + } + } +} diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageException.java b/src/main/java/org/openslx/virtualization/disk/DiskImageException.java new file mode 100644 index 0000000..db62917 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImageException.java @@ -0,0 +1,25 @@ +package org.openslx.virtualization.disk; + +/** + * An exception for faulty disk image handling. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class DiskImageException extends Exception +{ + /** + * Version number for serialization. + */ + private static final long serialVersionUID = 5464286488698331909L; + + /** + * Creates a disk image exception including an error message. + * + * @param errorMsg message to describe a disk image error. + */ + public DiskImageException( String errorMsg ) + { + super( errorMsg ); + } +} diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageQcow2.java b/src/main/java/org/openslx/virtualization/disk/DiskImageQcow2.java new file mode 100644 index 0000000..a007e1c --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImageQcow2.java @@ -0,0 +1,232 @@ +package org.openslx.virtualization.disk; + +import java.io.RandomAccessFile; + +import org.openslx.virtualization.Version; + +/** + * QCOW2 disk image for virtual machines. + * + * A QCOW2 disk image consists of a header, one L1 table and several L2 tables used for lookup data + * clusters in the file via a two-level lookup. The QCOW2 header contains the following fields: + * + *
+ *   QCOW2 (version 2 and 3) header format:
+ *   
+ *   magic                   (4 byte)
+ *   version                 (4 byte)
+ *   backing_file_offset     (8 byte)
+ *   backing_file_size       (4 byte)
+ *   cluster_bits            (4 byte)
+ *   size                    (8 byte)
+ *   crypt_method            (4 byte)
+ *   l1_size                 (4 byte)
+ *   l1_table_offset         (8 byte)
+ *   refcount_table_offset   (8 byte)
+ *   refcount_table_clusters (4 byte)
+ *   nb_snapshots            (4 byte)
+ *   snapshots_offset        (8 byte)
+ *   incompatible_features   (8 byte)  [*]
+ *   compatible_features     (8 byte)  [*]
+ *   autoclear_features      (8 byte)  [*]
+ *   refcount_order          (8 byte)  [*]
+ *   header_length           (4 byte)  [*]
+ *
+ *   [*] these fields are only available in the QCOW2 version 3 header format
+ * 
+ * + * @author Manuel Bentele + * @version 1.0 + */ +public class DiskImageQcow2 extends DiskImage +{ + /** + * Big endian representation of the big endian QCOW2 magic bytes QFI\xFB. + */ + private static final int QCOW2_MAGIC = 0x514649fb; + + /** + * Creates a new QCOW2 disk image from an existing QCOW2 image file. + * + * @param diskImage file to a QCOW2 disk storing the image content. + */ + DiskImageQcow2( RandomAccessFile diskImage ) + { + super( diskImage ); + } + + /** + * Probe specified file with unknown format to be a QCOW2 disk image file. + * + * @param diskImage file with unknown format that should be probed. + * @return state whether file is a QCOW2 disk image or not. + * + * @throws DiskImageException cannot probe specified file with unknown format. + */ + public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException + { + final boolean isQcow2ImageFormat; + + // goto the beginning of the disk image to read the disk image + final int diskImageMagic = DiskImageUtils.readInt( diskImage, 0 ); + + // check if disk image's magic bytes can be found + if ( diskImageMagic == DiskImageQcow2.QCOW2_MAGIC ) { + isQcow2ImageFormat = true; + } else { + isQcow2ImageFormat = false; + } + + return isQcow2ImageFormat; + } + + @Override + public boolean isStandalone() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final long qcowBackingFileOffset = DiskImageUtils.readLong( diskFile, 8 ); + final boolean qcowStandalone; + + // check if QCOW2 image does not refer to any backing file + if ( qcowBackingFileOffset == 0 ) { + qcowStandalone = true; + } else { + qcowStandalone = false; + } + + return qcowStandalone; + } + + @Override + public boolean isCompressed() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final boolean qcowUseExtendedL2; + boolean qcowCompressed = false; + + // check if QCOW2 image uses extended L2 tables + // extended L2 tables are only possible in QCOW2 version 3 header format + if ( this.getVersion().getMajor() >= Short.valueOf( "3" ) ) { + // read incompatible feature bits + final long qcowIncompatibleFeatures = DiskImageUtils.readLong( diskFile, 72 ); + + // support for extended L2 tables is enabled if bit 4 is set + qcowUseExtendedL2 = ( ( ( qcowIncompatibleFeatures & 0x000000000010 ) >>> 4 ) == 1 ); + } else { + qcowUseExtendedL2 = false; + } + + // get number of entries in L1 table + final int qcowL1TableSize = DiskImageUtils.readInt( diskFile, 36 ); + + // check if a valid L1 table is present + if ( qcowL1TableSize > 0 ) { + // QCOW2 image contains active L1 table with more than 0 entries: l1_size > 0 + // search for first L2 table and its first entry to get compression bit + + // get cluster bits to calculate the cluster size + final int qcowClusterBits = DiskImageUtils.readInt( diskFile, 20 ); + final int qcowClusterSize = ( 1 << qcowClusterBits ); + + // entries of a L1 table have always the size of 8 byte (64 bit) + final int qcowL1TableEntrySize = 8; + + // entries of a L2 table have either the size of 8 or 16 byte (64 or 128 bit) + final int qcowL2TableEntrySize = ( qcowUseExtendedL2 ) ? 16 : 8; + + // calculate number of L2 table entries + final int qcowL2TableSize = qcowClusterSize / qcowL2TableEntrySize; + + // get offset of L1 table + final long qcowL1TableOffset = DiskImageUtils.readLong( diskFile, 40 ); + + // check for each L2 table referenced from an L1 table its entries + // until a compressed cluster descriptor is found + for ( long i = 0; i < qcowL1TableSize; i++ ) { + // get offset of current L2 table from the current L1 table entry + final long qcowL1TableEntryOffset = qcowL1TableOffset + ( i * qcowL1TableEntrySize ); + final long qcowL1TableEntry = DiskImageUtils.readLong( diskFile, qcowL1TableEntryOffset ); + + // extract offset (bits 9 - 55) from L1 table entry + final long qcowL2TableOffset = ( qcowL1TableEntry & 0x00fffffffffffe00L ); + + if ( qcowL2TableOffset == 0 ) { + // L2 table and all clusters described by this L2 table are unallocated + continue; + } + + // get each L2 table entry and check if it is a compressed cluster descriptor + for ( long j = 0; j < qcowL2TableSize; j++ ) { + // get current L2 table entry + final long qcowL2TableEntryOffset = qcowL2TableOffset + ( j * qcowL2TableEntrySize ); + final long qcowL2TableEntry = DiskImageUtils.readLong( diskFile, qcowL2TableEntryOffset ); + + // extract cluster type (standard or compressed) (bit 62) + boolean qcowClusterCompressed = ( ( ( qcowL2TableEntry & 0x4000000000000000L ) >>> 62 ) == 1 ); + + // check if QCOW2 disk image contains at least one compressed cluster descriptor + if ( qcowClusterCompressed ) { + qcowCompressed = true; + break; + } + } + + // terminate if one compressed cluster descriptor is already found + if ( qcowCompressed ) { + break; + } + } + } else { + // QCOW2 image does not contain an active L1 table with any entry: l1_size = 0 + qcowCompressed = false; + } + + return qcowCompressed; + } + + @Override + public boolean isSnapshot() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final int qcowNumSnapshots = DiskImageUtils.readInt( diskFile, 56 ); + final boolean qcowSnapshot; + + // check if QCOW2 image contains at least one snapshot + if ( qcowNumSnapshots == 0 ) { + qcowSnapshot = true; + } else { + qcowSnapshot = false; + } + + return qcowSnapshot; + } + + @Override + public Version getVersion() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final int qcowVersion = DiskImageUtils.readInt( diskFile, 4 ); + + // check QCOW2 file format version + if ( qcowVersion < 2 || qcowVersion > 3 ) { + // QCOW2 disk image does not contain a valid QCOW2 version + final String errorMsg = new String( "Invalid QCOW2 version in header found!" ); + throw new DiskImageException( errorMsg ); + } + + return new Version( Integer.valueOf( qcowVersion ).shortValue() ); + } + + @Override + public String getDescription() throws DiskImageException + { + // QCOW2 disk image format does not support any disk description + return null; + } + + @Override + public ImageFormat getFormat() + { + return ImageFormat.QCOW2; + } +} diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageUtils.java b/src/main/java/org/openslx/virtualization/disk/DiskImageUtils.java new file mode 100644 index 0000000..dc67548 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImageUtils.java @@ -0,0 +1,144 @@ +package org.openslx.virtualization.disk; + +import java.io.IOException; +import java.io.RandomAccessFile; + +/** + * Utilities to parse disk image format elements and control versions of disk images. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class DiskImageUtils +{ + /** + * Returns the size of a specified disk image file. + * + * @param diskImage file to a disk storing the image content. + * @return size of the disk image file in bytes. + * + * @throws DiskImageException unable to obtain the size of the disk image file. + */ + public static long getImageSize( RandomAccessFile diskImage ) throws DiskImageException + { + final long imageSize; + + try { + imageSize = diskImage.length(); + } catch ( IOException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + + return imageSize; + } + + /** + * Reads two bytes ({@link Short}) at a given offset from the specified disk image + * file. + * + * @param diskImage file to a disk storing the image content. + * @param offset offset in bytes for reading the two bytes. + * @return value of the two bytes from the disk image file as {@link Short}. + * + * @throws DiskImageException unable to read two bytes from the disk image file. + */ + public static short readShort( RandomAccessFile diskImage, long offset ) throws DiskImageException + { + final long imageSize = DiskImageUtils.getImageSize( diskImage ); + short value = 0; + + if ( imageSize >= ( offset + Short.BYTES ) ) { + try { + diskImage.seek( offset ); + value = diskImage.readShort(); + } catch ( IOException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + } + + return value; + } + + /** + * Reads four bytes ({@link Integer}) at a given offset from the specified disk + * image file. + * + * @param diskImage file to a disk storing the image content. + * @param offset offset in bytes for reading the four bytes. + * @return value of the four bytes from the disk image file as {@link Integer}. + * + * @throws DiskImageException unable to read four bytes from the disk image file. + */ + public static int readInt( RandomAccessFile diskImage, long offset ) throws DiskImageException + { + final long imageSize = DiskImageUtils.getImageSize( diskImage ); + int value = 0; + + if ( imageSize >= ( offset + Integer.BYTES ) ) { + try { + diskImage.seek( offset ); + value = diskImage.readInt(); + } catch ( IOException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + } + + return value; + } + + /** + * Reads eight bytes ({@link Long}) at a given offset from the specified disk image + * file. + * + * @param diskImage file to a disk storing the image content. + * @param offset offset in bytes for reading the eight bytes. + * @return value of the eight bytes from the disk image file as {@link Long}. + * + * @throws DiskImageException unable to read eight bytes from the disk image file. + */ + public static long readLong( RandomAccessFile diskImage, long offset ) throws DiskImageException + { + final long imageSize = DiskImageUtils.getImageSize( diskImage ); + long value = 0; + + if ( imageSize >= ( offset + Long.BYTES ) ) { + try { + diskImage.seek( offset ); + value = diskImage.readLong(); + } catch ( IOException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + } + + return value; + } + + /** + * Reads a variable number of bytes (numBytes) at a given offset from the specified disk image file. + * + * @param diskImage file to a disk storing the image content. + * @param offset offset in bytes for reading numBytes bytes. + * @param numBytes number of bytes to read at offset. + * @return read bytes from the disk image file as {@link String}. + * + * @throws DiskImageException unable to read two bytes from the disk image file. + */ + public static String readBytesAsString( RandomAccessFile diskImage, long offset, int numBytes ) + throws DiskImageException + { + final long imageSize = DiskImageUtils.getImageSize( diskImage ); + byte values[] = {}; + + if ( imageSize >= ( offset + numBytes ) ) { + try { + diskImage.seek( offset ); + values = new byte[ numBytes ]; + diskImage.readFully( values ); + } catch ( IOException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + } + + return new String( values ); + } +} diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageVdi.java b/src/main/java/org/openslx/virtualization/disk/DiskImageVdi.java new file mode 100644 index 0000000..c564112 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImageVdi.java @@ -0,0 +1,105 @@ +package org.openslx.virtualization.disk; + +import java.io.RandomAccessFile; + +import org.openslx.virtualization.Version; + +/** + * VDI disk image for virtual machines. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class DiskImageVdi extends DiskImage +{ + /** + * Big endian representation of the little endian VDI magic bytes (signature). + */ + private static final int VDI_MAGIC = 0x7f10dabe; + + /** + * Creates a new VDI disk image from an existing VDI image file. + * + * @param diskImage file to a VDI disk storing the image content. + */ + DiskImageVdi( RandomAccessFile diskImage ) + { + super( diskImage ); + } + + /** + * Probe specified file with unknown format to be a VDI disk image file. + * + * @param diskImage file with unknown format that should be probed. + * @return state whether file is a VDI disk image or not. + * + * @throws DiskImageException cannot probe specified file with unknown format. + */ + public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException + { + final boolean isVdiImageFormat; + + // goto the beginning of the disk image to read the magic bytes + // skip first 64 bytes (opening tag) + final int diskImageMagic = DiskImageUtils.readInt( diskImage, 64 ); + + // check if disk image's magic bytes can be found + if ( diskImageMagic == DiskImageVdi.VDI_MAGIC ) { + isVdiImageFormat = true; + } else { + isVdiImageFormat = false; + } + + return isVdiImageFormat; + } + + @Override + public boolean isStandalone() throws DiskImageException + { + // VDI does not seem to support split VDI files, so VDI files are always standalone + return true; + } + + @Override + public boolean isCompressed() throws DiskImageException + { + // compression is done by sparsifying the disk files, there is no flag for it + return false; + } + + @Override + public boolean isSnapshot() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + + // if parent UUID is set, the VDI file is a snapshot + final String parentUuid = DiskImageUtils.readBytesAsString( diskFile, 440, 16 ); + final String zeroUuid = new String( new byte[ 16 ] ); + + return !zeroUuid.equals( parentUuid ); + } + + @Override + public Version getVersion() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + + final short vdiVersionMajor = Short.reverseBytes( DiskImageUtils.readShort( diskFile, 68 ) ); + final short vdiVersionMinor = Short.reverseBytes( DiskImageUtils.readShort( diskFile, 70 ) ); + + return new Version( vdiVersionMajor, vdiVersionMinor ); + } + + @Override + public String getDescription() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + return DiskImageUtils.readBytesAsString( diskFile, 84, 256 ); + } + + @Override + public ImageFormat getFormat() + { + return ImageFormat.VDI; + } +} diff --git a/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java b/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java new file mode 100644 index 0000000..720ec37 --- /dev/null +++ b/src/main/java/org/openslx/virtualization/disk/DiskImageVmdk.java @@ -0,0 +1,281 @@ +package org.openslx.virtualization.disk; + +import java.io.RandomAccessFile; + +import org.openslx.util.Util; +import org.openslx.virtualization.configuration.VirtualizationConfigurationVmwareFileFormat; +import org.openslx.virtualization.Version; +import org.openslx.virtualization.configuration.VirtualizationConfigurationException; + +/** + * VMDK (sparse extent) disk image for virtual machines. + * + * @author Manuel Bentele + * @version 1.0 + */ +public class DiskImageVmdk extends DiskImage +{ + /** + * Big endian representation of the little endian magic bytes KDMV. + */ + private static final int VMDK_MAGIC = 0x4b444d56; + + /** + * Size of a VMDK disk image data cluster in bytes. + */ + private static final int VMDK_SECTOR_SIZE = 512; + + /** + * Default hardware version of a VMDK disk image. + */ + private static final int VMDK_DEFAULT_HW_VERSION = 10; + + /** + * Stores disk configuration if VMDK disk image contains an embedded descriptor file. + */ + private final VirtualizationConfigurationVmwareFileFormat vmdkConfig; + + /** + * Creates a new VMDK disk image from an existing VMDK image file. + * + * @param diskImage file to a VMDK disk storing the image content. + * + * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed. + */ + DiskImageVmdk( RandomAccessFile diskImage ) throws DiskImageException + { + super( diskImage ); + + this.vmdkConfig = this.parseVmdkConfig(); + } + + /** + * Probe specified file with unknown format to be a VMDK disk image file. + * + * @param diskImage file with unknown format that should be probed. + * @return state whether file is a VMDK disk image or not. + * + * @throws DiskImageException cannot probe specified file with unknown format. + */ + public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException + { + final boolean isVmdkImageFormat; + + // goto the beginning of the disk image to read the magic bytes + final int diskImageMagic = DiskImageUtils.readInt( diskImage, 0 ); + + // check if disk image's magic bytes can be found + if ( diskImageMagic == DiskImageVmdk.VMDK_MAGIC ) { + isVmdkImageFormat = true; + } else { + isVmdkImageFormat = false; + } + + return isVmdkImageFormat; + } + + /** + * Returns the creation type from the VMDK's embedded descriptor file. + * + * @return creation type from the VMDK's embedded descriptor file. + */ + private String getCreationType() + { + final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); + final String vmdkCreationType; + + if ( vmdkConfig == null ) { + // VMDK disk image does not contain any descriptor file + // assume that the file is not stand alone + vmdkCreationType = null; + } else { + // VMDK disk image contains a descriptor file + // get creation type from the content of the descriptor file + vmdkCreationType = this.vmdkConfig.get( "createType" ); + } + + return vmdkCreationType; + } + + /** + * Parse the configuration of the VMDK's embedded descriptor file. + * + * @return parsed configuration of the VMDK's embedded descriptor file. + * + * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed. + */ + protected VirtualizationConfigurationVmwareFileFormat parseVmdkConfig() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final VirtualizationConfigurationVmwareFileFormat vmdkConfig; + + // get offset and size of descriptor file embedded into the VMDK disk image + final long vmdkDescriptorSectorOffset = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 28 ) ); + final long vmdkDescriptorSectorSize = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 36 ) ); + + if ( vmdkDescriptorSectorOffset > 0 ) { + // get content of descriptor file embedded into the VMDK disk image + final long vmdkDescriptorOffset = vmdkDescriptorSectorOffset * DiskImageVmdk.VMDK_SECTOR_SIZE; + final long vmdkDescriptorSizeMax = vmdkDescriptorSectorSize * DiskImageVmdk.VMDK_SECTOR_SIZE; + final String descriptorStr = DiskImageUtils.readBytesAsString( diskFile, vmdkDescriptorOffset, + Long.valueOf( vmdkDescriptorSizeMax ).intValue() ); + + // get final length of the content within the sectors to be able to trim all 'zero' characters + final int vmdkDescriptorSize = descriptorStr.indexOf( 0 ); + + // if final length of the content is invalid, throw an exception + if ( vmdkDescriptorSize > vmdkDescriptorSizeMax || vmdkDescriptorSize < 0 ) { + final String errorMsg = new String( "Embedded descriptor size in VMDK disk image is invalid!" ); + throw new DiskImageException( errorMsg ); + } + + // trim all 'zero' characters at the end of the descriptor content to avoid errors during parsing + final String configStr = descriptorStr.substring( 0, vmdkDescriptorSize ); + + // create configuration instance from content of the descriptor file + try { + vmdkConfig = new VirtualizationConfigurationVmwareFileFormat( configStr.getBytes(), vmdkDescriptorSize ); + } catch ( VirtualizationConfigurationException e ) { + throw new DiskImageException( e.getLocalizedMessage() ); + } + } else { + // there is no descriptor file embedded into the VMDK disk image + vmdkConfig = null; + } + + return vmdkConfig; + } + + /** + * Returns parsed configuration of the VMDK's embedded descriptor file. + * + * @return parsed configuration of the VMDK's embedded descriptor file. + */ + protected VirtualizationConfigurationVmwareFileFormat getVmdkConfig() + { + return this.vmdkConfig; + } + + /** + * Returns the hardware version from the VMDK's embedded descriptor file. + * + * If the VMDK's embedded descriptor file does not contain any hardware version configuration + * entry, the default hardware version (see {@link #VMDK_DEFAULT_HW_VERSION}) is returned. + * + * @return hardware version from the VMDK's embedded descriptor file. + * + * @throws DiskImageException unable to obtain the VMDK's hardware version of the disk image + * format. + */ + public Version getHwVersion() throws DiskImageException + { + final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); + final Version hwVersion; + + if ( vmdkConfig != null ) { + // VMDK image contains a hardware version, so return parsed hardware version + // if hardware version cannot be parsed, return default hardware version + final String hwVersionStr = vmdkConfig.get( "ddb.virtualHWVersion" ); + + final int hwVersionMajor = Util.parseInt( hwVersionStr, DiskImageVmdk.VMDK_DEFAULT_HW_VERSION ); + hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() ); + } else { + // VMDK image does not contain any hardware version, so return default hardware version + final int hwVersionMajor = DiskImageVmdk.VMDK_DEFAULT_HW_VERSION; + hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() ); + } + + return hwVersion; + } + + @Override + public boolean isStandalone() throws DiskImageException + { + final String vmdkCreationType = this.getCreationType(); + final boolean vmdkStandalone; + + if ( vmdkCreationType != null ) { + // creation type is defined, so check if VMDK disk image is a snapshot + if ( this.isSnapshot() ) { + // VMDK disk image is a snapshot and not stand alone + vmdkStandalone = false; + } else { + // VMDK disk image is not a snapshot + // determine stand alone disk image property + vmdkStandalone = vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) || + vmdkCreationType.equalsIgnoreCase( "monolithicSparse" ); + } + } else { + // creation type is not defined + // assume that the file is not stand alone + vmdkStandalone = false; + } + + return vmdkStandalone; + } + + @Override + public boolean isCompressed() throws DiskImageException + { + final String vmdkCreationType = this.getCreationType(); + final boolean vmdkCompressed; + + if ( vmdkCreationType != null && vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) ) { + // creation type is defined, and VMDK disk image is compressed + vmdkCompressed = true; + } else { + // creation type for compression is not defined + // assume that the file is not compressed + vmdkCompressed = false; + } + + return vmdkCompressed; + } + + @Override + public boolean isSnapshot() throws DiskImageException + { + final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); + final boolean vmdkSnapshot; + + if ( vmdkConfig == null ) { + // VMDK disk image does not contain any descriptor file + // assume that the file is not a snapshot + vmdkSnapshot = false; + } else { + // get parent CID to determine snapshot disk image property + final String parentCid = vmdkConfig.get( "parentCID" ); + + if ( parentCid != null && !parentCid.equalsIgnoreCase( "ffffffff" ) ) { + // link to parent content identifier is defined, so VMDK disk image is a snapshot + vmdkSnapshot = true; + } else { + // link to parent content identifier is not defined, so VMDK disk image is not a snapshot + vmdkSnapshot = false; + } + } + + return vmdkSnapshot; + } + + @Override + public Version getVersion() throws DiskImageException + { + final RandomAccessFile diskFile = this.getDiskImage(); + final int vmdkVersion = Integer.reverseBytes( DiskImageUtils.readInt( diskFile, 4 ) ); + + return new Version( Integer.valueOf( vmdkVersion ).shortValue() ); + } + + @Override + public String getDescription() throws DiskImageException + { + return null; + } + + @Override + public ImageFormat getFormat() + { + return ImageFormat.VMDK; + } +} diff --git a/src/main/java/org/openslx/virtualization/virtualizer/Virtualizer.java b/src/main/java/org/openslx/virtualization/virtualizer/Virtualizer.java index ac3a4ec..ff24862 100644 --- a/src/main/java/org/openslx/virtualization/virtualizer/Virtualizer.java +++ b/src/main/java/org/openslx/virtualization/virtualizer/Virtualizer.java @@ -3,7 +3,7 @@ package org.openslx.virtualization.virtualizer; import java.util.List; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; /** * Representation of a virtualization system. diff --git a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerDocker.java b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerDocker.java index 673447b..06ac24e 100644 --- a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerDocker.java +++ b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerDocker.java @@ -6,8 +6,8 @@ import java.util.List; import org.openslx.thrifthelper.TConst; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; /** * Representation of the Docker virtualizer for application containers. diff --git a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerQemu.java b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerQemu.java index fcce392..d950111 100644 --- a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerQemu.java +++ b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerQemu.java @@ -6,8 +6,8 @@ import java.util.List; import org.openslx.thrifthelper.TConst; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; /** * Representation of the QEMU virtualizer for virtual machines. diff --git a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVirtualBox.java b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVirtualBox.java index 6be7cbf..ebb3b3f 100644 --- a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVirtualBox.java +++ b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVirtualBox.java @@ -6,8 +6,8 @@ import java.util.List; import org.openslx.thrifthelper.TConst; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; /** * Representation of the VirtualBox virtualizer for virtual machines. diff --git a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVmware.java b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVmware.java index 6e676f3..9fe31a5 100644 --- a/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVmware.java +++ b/src/main/java/org/openslx/virtualization/virtualizer/VirtualizerVmware.java @@ -6,8 +6,8 @@ import java.util.List; import org.openslx.thrifthelper.TConst; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; /** * Representation of the VMware virtualizer for virtual machines. diff --git a/src/main/java/org/openslx/vm/disk/DiskImage.java b/src/main/java/org/openslx/vm/disk/DiskImage.java deleted file mode 100644 index cf7df83..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImage.java +++ /dev/null @@ -1,253 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.Closeable; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.List; -import java.util.function.Predicate; - -import org.openslx.bwlp.thrift.iface.Virtualizer; -import org.openslx.thrifthelper.TConst; -import org.openslx.util.Util; -import org.openslx.virtualization.Version; - -/** - * Disk image for virtual machines. - * - * @implNote This class is the abstract base class to implement various specific disk images (like - * QCOW2 or VMDK). - * - * @author Manuel Bentele - * @version 1.0 - */ -public abstract class DiskImage implements Closeable -{ - /** - * Stores the image file of the disk. - */ - private RandomAccessFile diskImage = null; - - /** - * Creates a new disk image from an existing image file with a known disk image format. - * - * @param diskImage file to a disk storing the image content. - * - * @implNote Do not use this constructor to create a new disk image from an image file with - * unknown disk image format. Instead, use the factory method - * {@link #newInstance(File)} to probe unknown disk image files before creation. - */ - protected DiskImage( RandomAccessFile diskImage ) - { - this.diskImage = diskImage; - } - - /** - * Returns the disk image file. - * - * @return the disk image file. - */ - protected RandomAccessFile getDiskImage() - { - return this.diskImage; - } - - /** - * Checks whether disk image is standalone and do not depend on other files (e.g. snapshot - * files). - * - * @return state whether disk image is standalone or not. - * - * @throws DiskImageException unable to check if disk image is standalone. - */ - public abstract boolean isStandalone() throws DiskImageException; - - /** - * Checks whether disk image is compressed. - * - * @return state whether disk image is compressed or not. - * - * @throws DiskImageException unable to check whether disk image is compressed. - */ - public abstract boolean isCompressed() throws DiskImageException; - - /** - * Checks whether disk image is a snapshot. - * - * @return state whether disk image is a snapshot or not. - * - * @throws DiskImageException unable to check whether disk image is a snapshot. - */ - public abstract boolean isSnapshot() throws DiskImageException; - - /** - * Returns the version of the disk image format. - * - * @return version of the disk image format. - * - * @throws DiskImageException unable to obtain version of the disk image format. - */ - public abstract Version getVersion() throws DiskImageException; - - /** - * Returns the disk image description. - * - * @return description of the disk image. - * - * @throws DiskImageException unable to obtain description of the disk image. - */ - public abstract String getDescription() throws DiskImageException; - - /** - * Returns the format of the disk image. - * - * @return format of the disk image. - */ - public abstract ImageFormat getFormat(); - - /** - * Creates a new disk image from an existing image file with an unknown disk image format. - * - * @param diskImagePath file to a disk storing the image content. - * @return concrete disk image instance. - * - * @throws FileNotFoundException cannot find specified disk image file. - * @throws IOException cannot access the content of the disk image file. - * @throws DiskImageException disk image file has an invalid and unknown disk image format. - */ - public static DiskImage newInstance( File diskImagePath ) - throws FileNotFoundException, IOException, DiskImageException - { - // Make sure this doesn't escape the scope, in case instantiation fails - we can't know when the GC - // would come along and close this file, which is problematic on Windows (blocking rename/delete) - final RandomAccessFile fileHandle = new RandomAccessFile( diskImagePath, "r" ); - - try { - if ( DiskImageQcow2.probe( fileHandle ) ) { - return new DiskImageQcow2( fileHandle ); - } else if ( DiskImageVdi.probe( fileHandle ) ) { - return new DiskImageVdi( fileHandle ); - } else if ( DiskImageVmdk.probe( fileHandle ) ) { - return new DiskImageVmdk( fileHandle ); - } - } catch ( Exception e ) { - Util.safeClose( fileHandle ); - throw e; - } - Util.safeClose( fileHandle ); - final String errorMsg = new String( "File '" + diskImagePath.getAbsolutePath() + "' is not a valid disk image!" ); - throw new DiskImageException( errorMsg ); - } - - @Override - public void close() throws IOException - { - Util.safeClose( diskImage ); - } - - @Override - protected void finalize() throws Throwable - { - close(); - } - - /** - * Format of a disk image. - * - * @author Manuel Bentele - * @version 1.0 - */ - public enum ImageFormat - { - // @formatter:off - NONE ( "none" ), - QCOW2( "qcow2" ), - VDI ( "vdi" ), - VMDK ( "vmdk" ); - // @formatter:on - - /** - * Stores filename extension of the disk image format. - */ - public final String extension; - - /** - * Create new disk image format. - * - * @param extension filename extension of the disk image format. - */ - ImageFormat( String extension ) - { - this.extension = extension; - } - - /** - * Returns filename extension of the disk image. - * - * @return filename extension of the disk image. - */ - public String getExtension() - { - return this.extension; - } - - /** - * Checks if the disk image format is supported by a virtualizer. - * - * @param supportedImageFormats list of supported disk image formats of a virtualizer. - * @return true if image type is supported by the virtualizer; otherwise - * false. - */ - public boolean isSupportedbyVirtualizer( List supportedImageFormats ) - { - Predicate matchDiskFormat = supportedImageFormat -> supportedImageFormat.toString() - .equalsIgnoreCase( this.toString() ); - return supportedImageFormats.stream().anyMatch( matchDiskFormat ); - } - - /** - * Returns default (preferred) disk image format for the specified virtualizer. - * - * @param virt virtualizer for that the default disk image should be determined. - * @return default (preferred) disk image format. - */ - public static ImageFormat defaultForVirtualizer( Virtualizer virt ) - { - if ( virt == null ) { - return null; - } else { - return ImageFormat.defaultForVirtualizer( virt.virtId ); - } - } - - /** - * Returns default (preferred) disk image format for the specified virtualizer. - * - * @param virtId ID of a virtualizer for that the default disk image should be determined. - * @return default (preferred) disk image format. - */ - public static ImageFormat defaultForVirtualizer( String virtId ) - { - ImageFormat imgFormat = null; - - if ( TConst.VIRT_DOCKER.equals( virtId ) ) { - imgFormat = NONE; - } else if ( TConst.VIRT_QEMU.equals( virtId ) ) { - imgFormat = QCOW2; - } else if ( TConst.VIRT_VIRTUALBOX.equals( virtId ) ) { - imgFormat = VDI; - } else if ( TConst.VIRT_VMWARE.equals( virtId ) ) { - imgFormat = VMDK; - } - - return imgFormat; - } - - @Override - public String toString() - { - return this.getExtension(); - } - } -} diff --git a/src/main/java/org/openslx/vm/disk/DiskImageException.java b/src/main/java/org/openslx/vm/disk/DiskImageException.java deleted file mode 100644 index a98f963..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImageException.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.openslx.vm.disk; - -/** - * An exception for faulty disk image handling. - * - * @author Manuel Bentele - * @version 1.0 - */ -public class DiskImageException extends Exception -{ - /** - * Version number for serialization. - */ - private static final long serialVersionUID = 5464286488698331909L; - - /** - * Creates a disk image exception including an error message. - * - * @param errorMsg message to describe a disk image error. - */ - public DiskImageException( String errorMsg ) - { - super( errorMsg ); - } -} diff --git a/src/main/java/org/openslx/vm/disk/DiskImageQcow2.java b/src/main/java/org/openslx/vm/disk/DiskImageQcow2.java deleted file mode 100644 index e569708..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImageQcow2.java +++ /dev/null @@ -1,232 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.RandomAccessFile; - -import org.openslx.virtualization.Version; - -/** - * QCOW2 disk image for virtual machines. - * - * A QCOW2 disk image consists of a header, one L1 table and several L2 tables used for lookup data - * clusters in the file via a two-level lookup. The QCOW2 header contains the following fields: - * - *
- *   QCOW2 (version 2 and 3) header format:
- *   
- *   magic                   (4 byte)
- *   version                 (4 byte)
- *   backing_file_offset     (8 byte)
- *   backing_file_size       (4 byte)
- *   cluster_bits            (4 byte)
- *   size                    (8 byte)
- *   crypt_method            (4 byte)
- *   l1_size                 (4 byte)
- *   l1_table_offset         (8 byte)
- *   refcount_table_offset   (8 byte)
- *   refcount_table_clusters (4 byte)
- *   nb_snapshots            (4 byte)
- *   snapshots_offset        (8 byte)
- *   incompatible_features   (8 byte)  [*]
- *   compatible_features     (8 byte)  [*]
- *   autoclear_features      (8 byte)  [*]
- *   refcount_order          (8 byte)  [*]
- *   header_length           (4 byte)  [*]
- *
- *   [*] these fields are only available in the QCOW2 version 3 header format
- * 
- * - * @author Manuel Bentele - * @version 1.0 - */ -public class DiskImageQcow2 extends DiskImage -{ - /** - * Big endian representation of the big endian QCOW2 magic bytes QFI\xFB. - */ - private static final int QCOW2_MAGIC = 0x514649fb; - - /** - * Creates a new QCOW2 disk image from an existing QCOW2 image file. - * - * @param diskImage file to a QCOW2 disk storing the image content. - */ - DiskImageQcow2( RandomAccessFile diskImage ) - { - super( diskImage ); - } - - /** - * Probe specified file with unknown format to be a QCOW2 disk image file. - * - * @param diskImage file with unknown format that should be probed. - * @return state whether file is a QCOW2 disk image or not. - * - * @throws DiskImageException cannot probe specified file with unknown format. - */ - public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException - { - final boolean isQcow2ImageFormat; - - // goto the beginning of the disk image to read the disk image - final int diskImageMagic = DiskImageUtils.readInt( diskImage, 0 ); - - // check if disk image's magic bytes can be found - if ( diskImageMagic == DiskImageQcow2.QCOW2_MAGIC ) { - isQcow2ImageFormat = true; - } else { - isQcow2ImageFormat = false; - } - - return isQcow2ImageFormat; - } - - @Override - public boolean isStandalone() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final long qcowBackingFileOffset = DiskImageUtils.readLong( diskFile, 8 ); - final boolean qcowStandalone; - - // check if QCOW2 image does not refer to any backing file - if ( qcowBackingFileOffset == 0 ) { - qcowStandalone = true; - } else { - qcowStandalone = false; - } - - return qcowStandalone; - } - - @Override - public boolean isCompressed() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final boolean qcowUseExtendedL2; - boolean qcowCompressed = false; - - // check if QCOW2 image uses extended L2 tables - // extended L2 tables are only possible in QCOW2 version 3 header format - if ( this.getVersion().getMajor() >= Short.valueOf( "3" ) ) { - // read incompatible feature bits - final long qcowIncompatibleFeatures = DiskImageUtils.readLong( diskFile, 72 ); - - // support for extended L2 tables is enabled if bit 4 is set - qcowUseExtendedL2 = ( ( ( qcowIncompatibleFeatures & 0x000000000010 ) >>> 4 ) == 1 ); - } else { - qcowUseExtendedL2 = false; - } - - // get number of entries in L1 table - final int qcowL1TableSize = DiskImageUtils.readInt( diskFile, 36 ); - - // check if a valid L1 table is present - if ( qcowL1TableSize > 0 ) { - // QCOW2 image contains active L1 table with more than 0 entries: l1_size > 0 - // search for first L2 table and its first entry to get compression bit - - // get cluster bits to calculate the cluster size - final int qcowClusterBits = DiskImageUtils.readInt( diskFile, 20 ); - final int qcowClusterSize = ( 1 << qcowClusterBits ); - - // entries of a L1 table have always the size of 8 byte (64 bit) - final int qcowL1TableEntrySize = 8; - - // entries of a L2 table have either the size of 8 or 16 byte (64 or 128 bit) - final int qcowL2TableEntrySize = ( qcowUseExtendedL2 ) ? 16 : 8; - - // calculate number of L2 table entries - final int qcowL2TableSize = qcowClusterSize / qcowL2TableEntrySize; - - // get offset of L1 table - final long qcowL1TableOffset = DiskImageUtils.readLong( diskFile, 40 ); - - // check for each L2 table referenced from an L1 table its entries - // until a compressed cluster descriptor is found - for ( long i = 0; i < qcowL1TableSize; i++ ) { - // get offset of current L2 table from the current L1 table entry - final long qcowL1TableEntryOffset = qcowL1TableOffset + ( i * qcowL1TableEntrySize ); - final long qcowL1TableEntry = DiskImageUtils.readLong( diskFile, qcowL1TableEntryOffset ); - - // extract offset (bits 9 - 55) from L1 table entry - final long qcowL2TableOffset = ( qcowL1TableEntry & 0x00fffffffffffe00L ); - - if ( qcowL2TableOffset == 0 ) { - // L2 table and all clusters described by this L2 table are unallocated - continue; - } - - // get each L2 table entry and check if it is a compressed cluster descriptor - for ( long j = 0; j < qcowL2TableSize; j++ ) { - // get current L2 table entry - final long qcowL2TableEntryOffset = qcowL2TableOffset + ( j * qcowL2TableEntrySize ); - final long qcowL2TableEntry = DiskImageUtils.readLong( diskFile, qcowL2TableEntryOffset ); - - // extract cluster type (standard or compressed) (bit 62) - boolean qcowClusterCompressed = ( ( ( qcowL2TableEntry & 0x4000000000000000L ) >>> 62 ) == 1 ); - - // check if QCOW2 disk image contains at least one compressed cluster descriptor - if ( qcowClusterCompressed ) { - qcowCompressed = true; - break; - } - } - - // terminate if one compressed cluster descriptor is already found - if ( qcowCompressed ) { - break; - } - } - } else { - // QCOW2 image does not contain an active L1 table with any entry: l1_size = 0 - qcowCompressed = false; - } - - return qcowCompressed; - } - - @Override - public boolean isSnapshot() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final int qcowNumSnapshots = DiskImageUtils.readInt( diskFile, 56 ); - final boolean qcowSnapshot; - - // check if QCOW2 image contains at least one snapshot - if ( qcowNumSnapshots == 0 ) { - qcowSnapshot = true; - } else { - qcowSnapshot = false; - } - - return qcowSnapshot; - } - - @Override - public Version getVersion() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final int qcowVersion = DiskImageUtils.readInt( diskFile, 4 ); - - // check QCOW2 file format version - if ( qcowVersion < 2 || qcowVersion > 3 ) { - // QCOW2 disk image does not contain a valid QCOW2 version - final String errorMsg = new String( "Invalid QCOW2 version in header found!" ); - throw new DiskImageException( errorMsg ); - } - - return new Version( Integer.valueOf( qcowVersion ).shortValue() ); - } - - @Override - public String getDescription() throws DiskImageException - { - // QCOW2 disk image format does not support any disk description - return null; - } - - @Override - public ImageFormat getFormat() - { - return ImageFormat.QCOW2; - } -} diff --git a/src/main/java/org/openslx/vm/disk/DiskImageUtils.java b/src/main/java/org/openslx/vm/disk/DiskImageUtils.java deleted file mode 100644 index ccb053f..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImageUtils.java +++ /dev/null @@ -1,144 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.IOException; -import java.io.RandomAccessFile; - -/** - * Utilities to parse disk image format elements and control versions of disk images. - * - * @author Manuel Bentele - * @version 1.0 - */ -public class DiskImageUtils -{ - /** - * Returns the size of a specified disk image file. - * - * @param diskImage file to a disk storing the image content. - * @return size of the disk image file in bytes. - * - * @throws DiskImageException unable to obtain the size of the disk image file. - */ - public static long getImageSize( RandomAccessFile diskImage ) throws DiskImageException - { - final long imageSize; - - try { - imageSize = diskImage.length(); - } catch ( IOException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - - return imageSize; - } - - /** - * Reads two bytes ({@link Short}) at a given offset from the specified disk image - * file. - * - * @param diskImage file to a disk storing the image content. - * @param offset offset in bytes for reading the two bytes. - * @return value of the two bytes from the disk image file as {@link Short}. - * - * @throws DiskImageException unable to read two bytes from the disk image file. - */ - public static short readShort( RandomAccessFile diskImage, long offset ) throws DiskImageException - { - final long imageSize = DiskImageUtils.getImageSize( diskImage ); - short value = 0; - - if ( imageSize >= ( offset + Short.BYTES ) ) { - try { - diskImage.seek( offset ); - value = diskImage.readShort(); - } catch ( IOException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - } - - return value; - } - - /** - * Reads four bytes ({@link Integer}) at a given offset from the specified disk - * image file. - * - * @param diskImage file to a disk storing the image content. - * @param offset offset in bytes for reading the four bytes. - * @return value of the four bytes from the disk image file as {@link Integer}. - * - * @throws DiskImageException unable to read four bytes from the disk image file. - */ - public static int readInt( RandomAccessFile diskImage, long offset ) throws DiskImageException - { - final long imageSize = DiskImageUtils.getImageSize( diskImage ); - int value = 0; - - if ( imageSize >= ( offset + Integer.BYTES ) ) { - try { - diskImage.seek( offset ); - value = diskImage.readInt(); - } catch ( IOException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - } - - return value; - } - - /** - * Reads eight bytes ({@link Long}) at a given offset from the specified disk image - * file. - * - * @param diskImage file to a disk storing the image content. - * @param offset offset in bytes for reading the eight bytes. - * @return value of the eight bytes from the disk image file as {@link Long}. - * - * @throws DiskImageException unable to read eight bytes from the disk image file. - */ - public static long readLong( RandomAccessFile diskImage, long offset ) throws DiskImageException - { - final long imageSize = DiskImageUtils.getImageSize( diskImage ); - long value = 0; - - if ( imageSize >= ( offset + Long.BYTES ) ) { - try { - diskImage.seek( offset ); - value = diskImage.readLong(); - } catch ( IOException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - } - - return value; - } - - /** - * Reads a variable number of bytes (numBytes) at a given offset from the specified disk image file. - * - * @param diskImage file to a disk storing the image content. - * @param offset offset in bytes for reading numBytes bytes. - * @param numBytes number of bytes to read at offset. - * @return read bytes from the disk image file as {@link String}. - * - * @throws DiskImageException unable to read two bytes from the disk image file. - */ - public static String readBytesAsString( RandomAccessFile diskImage, long offset, int numBytes ) - throws DiskImageException - { - final long imageSize = DiskImageUtils.getImageSize( diskImage ); - byte values[] = {}; - - if ( imageSize >= ( offset + numBytes ) ) { - try { - diskImage.seek( offset ); - values = new byte[ numBytes ]; - diskImage.readFully( values ); - } catch ( IOException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - } - - return new String( values ); - } -} diff --git a/src/main/java/org/openslx/vm/disk/DiskImageVdi.java b/src/main/java/org/openslx/vm/disk/DiskImageVdi.java deleted file mode 100644 index 37e45c1..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImageVdi.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.RandomAccessFile; - -import org.openslx.virtualization.Version; - -/** - * VDI disk image for virtual machines. - * - * @author Manuel Bentele - * @version 1.0 - */ -public class DiskImageVdi extends DiskImage -{ - /** - * Big endian representation of the little endian VDI magic bytes (signature). - */ - private static final int VDI_MAGIC = 0x7f10dabe; - - /** - * Creates a new VDI disk image from an existing VDI image file. - * - * @param diskImage file to a VDI disk storing the image content. - */ - DiskImageVdi( RandomAccessFile diskImage ) - { - super( diskImage ); - } - - /** - * Probe specified file with unknown format to be a VDI disk image file. - * - * @param diskImage file with unknown format that should be probed. - * @return state whether file is a VDI disk image or not. - * - * @throws DiskImageException cannot probe specified file with unknown format. - */ - public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException - { - final boolean isVdiImageFormat; - - // goto the beginning of the disk image to read the magic bytes - // skip first 64 bytes (opening tag) - final int diskImageMagic = DiskImageUtils.readInt( diskImage, 64 ); - - // check if disk image's magic bytes can be found - if ( diskImageMagic == DiskImageVdi.VDI_MAGIC ) { - isVdiImageFormat = true; - } else { - isVdiImageFormat = false; - } - - return isVdiImageFormat; - } - - @Override - public boolean isStandalone() throws DiskImageException - { - // VDI does not seem to support split VDI files, so VDI files are always standalone - return true; - } - - @Override - public boolean isCompressed() throws DiskImageException - { - // compression is done by sparsifying the disk files, there is no flag for it - return false; - } - - @Override - public boolean isSnapshot() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - - // if parent UUID is set, the VDI file is a snapshot - final String parentUuid = DiskImageUtils.readBytesAsString( diskFile, 440, 16 ); - final String zeroUuid = new String( new byte[ 16 ] ); - - return !zeroUuid.equals( parentUuid ); - } - - @Override - public Version getVersion() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - - final short vdiVersionMajor = Short.reverseBytes( DiskImageUtils.readShort( diskFile, 68 ) ); - final short vdiVersionMinor = Short.reverseBytes( DiskImageUtils.readShort( diskFile, 70 ) ); - - return new Version( vdiVersionMajor, vdiVersionMinor ); - } - - @Override - public String getDescription() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - return DiskImageUtils.readBytesAsString( diskFile, 84, 256 ); - } - - @Override - public ImageFormat getFormat() - { - return ImageFormat.VDI; - } -} diff --git a/src/main/java/org/openslx/vm/disk/DiskImageVmdk.java b/src/main/java/org/openslx/vm/disk/DiskImageVmdk.java deleted file mode 100644 index 75a2bac..0000000 --- a/src/main/java/org/openslx/vm/disk/DiskImageVmdk.java +++ /dev/null @@ -1,281 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.RandomAccessFile; - -import org.openslx.util.Util; -import org.openslx.virtualization.configuration.VirtualizationConfigurationVmwareFileFormat; -import org.openslx.virtualization.Version; -import org.openslx.virtualization.configuration.VirtualizationConfigurationException; - -/** - * VMDK (sparse extent) disk image for virtual machines. - * - * @author Manuel Bentele - * @version 1.0 - */ -public class DiskImageVmdk extends DiskImage -{ - /** - * Big endian representation of the little endian magic bytes KDMV. - */ - private static final int VMDK_MAGIC = 0x4b444d56; - - /** - * Size of a VMDK disk image data cluster in bytes. - */ - private static final int VMDK_SECTOR_SIZE = 512; - - /** - * Default hardware version of a VMDK disk image. - */ - private static final int VMDK_DEFAULT_HW_VERSION = 10; - - /** - * Stores disk configuration if VMDK disk image contains an embedded descriptor file. - */ - private final VirtualizationConfigurationVmwareFileFormat vmdkConfig; - - /** - * Creates a new VMDK disk image from an existing VMDK image file. - * - * @param diskImage file to a VMDK disk storing the image content. - * - * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed. - */ - DiskImageVmdk( RandomAccessFile diskImage ) throws DiskImageException - { - super( diskImage ); - - this.vmdkConfig = this.parseVmdkConfig(); - } - - /** - * Probe specified file with unknown format to be a VMDK disk image file. - * - * @param diskImage file with unknown format that should be probed. - * @return state whether file is a VMDK disk image or not. - * - * @throws DiskImageException cannot probe specified file with unknown format. - */ - public static boolean probe( RandomAccessFile diskImage ) throws DiskImageException - { - final boolean isVmdkImageFormat; - - // goto the beginning of the disk image to read the magic bytes - final int diskImageMagic = DiskImageUtils.readInt( diskImage, 0 ); - - // check if disk image's magic bytes can be found - if ( diskImageMagic == DiskImageVmdk.VMDK_MAGIC ) { - isVmdkImageFormat = true; - } else { - isVmdkImageFormat = false; - } - - return isVmdkImageFormat; - } - - /** - * Returns the creation type from the VMDK's embedded descriptor file. - * - * @return creation type from the VMDK's embedded descriptor file. - */ - private String getCreationType() - { - final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); - final String vmdkCreationType; - - if ( vmdkConfig == null ) { - // VMDK disk image does not contain any descriptor file - // assume that the file is not stand alone - vmdkCreationType = null; - } else { - // VMDK disk image contains a descriptor file - // get creation type from the content of the descriptor file - vmdkCreationType = this.vmdkConfig.get( "createType" ); - } - - return vmdkCreationType; - } - - /** - * Parse the configuration of the VMDK's embedded descriptor file. - * - * @return parsed configuration of the VMDK's embedded descriptor file. - * - * @throws DiskImageException parsing of the VMDK's embedded descriptor file failed. - */ - protected VirtualizationConfigurationVmwareFileFormat parseVmdkConfig() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final VirtualizationConfigurationVmwareFileFormat vmdkConfig; - - // get offset and size of descriptor file embedded into the VMDK disk image - final long vmdkDescriptorSectorOffset = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 28 ) ); - final long vmdkDescriptorSectorSize = Long.reverseBytes( DiskImageUtils.readLong( diskFile, 36 ) ); - - if ( vmdkDescriptorSectorOffset > 0 ) { - // get content of descriptor file embedded into the VMDK disk image - final long vmdkDescriptorOffset = vmdkDescriptorSectorOffset * DiskImageVmdk.VMDK_SECTOR_SIZE; - final long vmdkDescriptorSizeMax = vmdkDescriptorSectorSize * DiskImageVmdk.VMDK_SECTOR_SIZE; - final String descriptorStr = DiskImageUtils.readBytesAsString( diskFile, vmdkDescriptorOffset, - Long.valueOf( vmdkDescriptorSizeMax ).intValue() ); - - // get final length of the content within the sectors to be able to trim all 'zero' characters - final int vmdkDescriptorSize = descriptorStr.indexOf( 0 ); - - // if final length of the content is invalid, throw an exception - if ( vmdkDescriptorSize > vmdkDescriptorSizeMax || vmdkDescriptorSize < 0 ) { - final String errorMsg = new String( "Embedded descriptor size in VMDK disk image is invalid!" ); - throw new DiskImageException( errorMsg ); - } - - // trim all 'zero' characters at the end of the descriptor content to avoid errors during parsing - final String configStr = descriptorStr.substring( 0, vmdkDescriptorSize ); - - // create configuration instance from content of the descriptor file - try { - vmdkConfig = new VirtualizationConfigurationVmwareFileFormat( configStr.getBytes(), vmdkDescriptorSize ); - } catch ( VirtualizationConfigurationException e ) { - throw new DiskImageException( e.getLocalizedMessage() ); - } - } else { - // there is no descriptor file embedded into the VMDK disk image - vmdkConfig = null; - } - - return vmdkConfig; - } - - /** - * Returns parsed configuration of the VMDK's embedded descriptor file. - * - * @return parsed configuration of the VMDK's embedded descriptor file. - */ - protected VirtualizationConfigurationVmwareFileFormat getVmdkConfig() - { - return this.vmdkConfig; - } - - /** - * Returns the hardware version from the VMDK's embedded descriptor file. - * - * If the VMDK's embedded descriptor file does not contain any hardware version configuration - * entry, the default hardware version (see {@link #VMDK_DEFAULT_HW_VERSION}) is returned. - * - * @return hardware version from the VMDK's embedded descriptor file. - * - * @throws DiskImageException unable to obtain the VMDK's hardware version of the disk image - * format. - */ - public Version getHwVersion() throws DiskImageException - { - final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); - final Version hwVersion; - - if ( vmdkConfig != null ) { - // VMDK image contains a hardware version, so return parsed hardware version - // if hardware version cannot be parsed, return default hardware version - final String hwVersionStr = vmdkConfig.get( "ddb.virtualHWVersion" ); - - final int hwVersionMajor = Util.parseInt( hwVersionStr, DiskImageVmdk.VMDK_DEFAULT_HW_VERSION ); - hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() ); - } else { - // VMDK image does not contain any hardware version, so return default hardware version - final int hwVersionMajor = DiskImageVmdk.VMDK_DEFAULT_HW_VERSION; - hwVersion = new Version( Integer.valueOf( hwVersionMajor ).shortValue() ); - } - - return hwVersion; - } - - @Override - public boolean isStandalone() throws DiskImageException - { - final String vmdkCreationType = this.getCreationType(); - final boolean vmdkStandalone; - - if ( vmdkCreationType != null ) { - // creation type is defined, so check if VMDK disk image is a snapshot - if ( this.isSnapshot() ) { - // VMDK disk image is a snapshot and not stand alone - vmdkStandalone = false; - } else { - // VMDK disk image is not a snapshot - // determine stand alone disk image property - vmdkStandalone = vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) || - vmdkCreationType.equalsIgnoreCase( "monolithicSparse" ); - } - } else { - // creation type is not defined - // assume that the file is not stand alone - vmdkStandalone = false; - } - - return vmdkStandalone; - } - - @Override - public boolean isCompressed() throws DiskImageException - { - final String vmdkCreationType = this.getCreationType(); - final boolean vmdkCompressed; - - if ( vmdkCreationType != null && vmdkCreationType.equalsIgnoreCase( "streamOptimized" ) ) { - // creation type is defined, and VMDK disk image is compressed - vmdkCompressed = true; - } else { - // creation type for compression is not defined - // assume that the file is not compressed - vmdkCompressed = false; - } - - return vmdkCompressed; - } - - @Override - public boolean isSnapshot() throws DiskImageException - { - final VirtualizationConfigurationVmwareFileFormat vmdkConfig = this.getVmdkConfig(); - final boolean vmdkSnapshot; - - if ( vmdkConfig == null ) { - // VMDK disk image does not contain any descriptor file - // assume that the file is not a snapshot - vmdkSnapshot = false; - } else { - // get parent CID to determine snapshot disk image property - final String parentCid = vmdkConfig.get( "parentCID" ); - - if ( parentCid != null && !parentCid.equalsIgnoreCase( "ffffffff" ) ) { - // link to parent content identifier is defined, so VMDK disk image is a snapshot - vmdkSnapshot = true; - } else { - // link to parent content identifier is not defined, so VMDK disk image is not a snapshot - vmdkSnapshot = false; - } - } - - return vmdkSnapshot; - } - - @Override - public Version getVersion() throws DiskImageException - { - final RandomAccessFile diskFile = this.getDiskImage(); - final int vmdkVersion = Integer.reverseBytes( DiskImageUtils.readInt( diskFile, 4 ) ); - - return new Version( Integer.valueOf( vmdkVersion ).shortValue() ); - } - - @Override - public String getDescription() throws DiskImageException - { - return null; - } - - @Override - public ImageFormat getFormat() - { - return ImageFormat.VMDK; - } -} diff --git a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java index 1cc7841..fa2ed13 100644 --- a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java +++ b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationQemuTest.java @@ -40,9 +40,9 @@ import org.openslx.virtualization.configuration.VirtualizationConfiguration.Ethe import org.openslx.virtualization.configuration.VirtualizationConfiguration.SoundCardType; import org.openslx.virtualization.configuration.VirtualizationConfiguration.UsbSpeed; import org.openslx.virtualization.configuration.logic.ConfigurationLogicTestUtils; -import org.openslx.vm.disk.DiskImage; -import org.openslx.vm.disk.DiskImageTestResources; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage; +import org.openslx.virtualization.disk.DiskImageTestResources; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; public class VirtualizationConfigurationQemuTest { diff --git a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBoxTest.java b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBoxTest.java index 597fffb..1689007 100644 --- a/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBoxTest.java +++ b/src/test/java/org/openslx/virtualization/configuration/VirtualizationConfigurationVirtualBoxTest.java @@ -20,7 +20,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage.ImageFormat; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; public class VirtualizationConfigurationVirtualBoxTest { diff --git a/src/test/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToDozModClientTest.java b/src/test/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToDozModClientTest.java index 96180ed..cb5cbb3 100644 --- a/src/test/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToDozModClientTest.java +++ b/src/test/java/org/openslx/virtualization/configuration/logic/ConfigurationLogicDozModServerToDozModClientTest.java @@ -15,7 +15,7 @@ import org.openslx.libvirt.xml.LibvirtXmlTestResources; import org.openslx.virtualization.configuration.VirtualizationConfiguration; import org.openslx.virtualization.configuration.data.ConfigurationDataDozModServerToDozModClient; import org.openslx.virtualization.configuration.transformation.TransformationException; -import org.openslx.vm.disk.DiskImageTestResources; +import org.openslx.virtualization.disk.DiskImageTestResources; public class ConfigurationLogicDozModServerToDozModClientTest { diff --git a/src/test/java/org/openslx/virtualization/disk/DiskImageQcow2Test.java b/src/test/java/org/openslx/virtualization/disk/DiskImageQcow2Test.java new file mode 100644 index 0000000..c6a294c --- /dev/null +++ b/src/test/java/org/openslx/virtualization/disk/DiskImageQcow2Test.java @@ -0,0 +1,221 @@ +package org.openslx.virtualization.disk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.virtualization.Version; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; + +public class DiskImageQcow2Test +{ + @Test + @DisplayName( "Test detection of default QCOW2 disk image" ) + public void testQcow2DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2Compressed16384DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2Compressed16384DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2NonCompressed16384DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2NonCompressed16384DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2Compressed65536DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2Compressed65536DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2NonCompressed65536DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2NonCompressed65536DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2Compressed2097152DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2Compressed2097152DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" ) + public void testQcow2DetectionL2NonCompressed2097152DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-on.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" ) + public void testQcow2DetectionNonL2NonCompressed2097152DiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage + .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-off.qcow2" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + + assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + } +} diff --git a/src/test/java/org/openslx/virtualization/disk/DiskImageTest.java b/src/test/java/org/openslx/virtualization/disk/DiskImageTest.java new file mode 100644 index 0000000..9293a90 --- /dev/null +++ b/src/test/java/org/openslx/virtualization/disk/DiskImageTest.java @@ -0,0 +1,19 @@ +package org.openslx.virtualization.disk; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class DiskImageTest +{ + @Test + @DisplayName( "Test of invalid disk image" ) + public void testInvalidDiskImage() throws IOException + { + Assertions.assertThrows( DiskImageException.class, () -> { + DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.invalid" ) ); + } ); + } +} diff --git a/src/test/java/org/openslx/virtualization/disk/DiskImageTestResources.java b/src/test/java/org/openslx/virtualization/disk/DiskImageTestResources.java new file mode 100644 index 0000000..ab97bbd --- /dev/null +++ b/src/test/java/org/openslx/virtualization/disk/DiskImageTestResources.java @@ -0,0 +1,16 @@ +package org.openslx.virtualization.disk; + +import java.io.File; +import java.net.URL; + +public class DiskImageTestResources +{ + private static final String DISK_PREFIX_PATH = File.separator + "disk"; + + public static File getDiskFile( String diskFileName ) + { + String diskPath = DiskImageTestResources.DISK_PREFIX_PATH + File.separator + diskFileName; + URL disk = DiskImageTestResources.class.getResource( diskPath ); + return new File( disk.getFile() ); + } +} diff --git a/src/test/java/org/openslx/virtualization/disk/DiskImageVdiTest.java b/src/test/java/org/openslx/virtualization/disk/DiskImageVdiTest.java new file mode 100644 index 0000000..06307fa --- /dev/null +++ b/src/test/java/org/openslx/virtualization/disk/DiskImageVdiTest.java @@ -0,0 +1,44 @@ +package org.openslx.virtualization.disk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import java.io.IOException; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.virtualization.Version; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; + +public class DiskImageVdiTest +{ + @Test + @DisplayName( "Test detection of default VDI disk image" ) + public void testVdiDiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.vdi" ) ); + final Version imageVersion = new Version( Short.valueOf( "1" ), Short.valueOf( "1" ) ); + + assertEquals( ImageFormat.VDI.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNotNull( image.getDescription() ); + } + + @Test + @DisplayName( "Test detection of VDI disk image snapshot" ) + public void testVdiDiskImageSnapshot() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default_snapshot.vdi" ) ); + final Version imageVersion = new Version( Short.valueOf( "1" ), Short.valueOf( "1" ) ); + + assertEquals( ImageFormat.VDI.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( true, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNotNull( image.getDescription() ); + } +} diff --git a/src/test/java/org/openslx/virtualization/disk/DiskImageVmdkTest.java b/src/test/java/org/openslx/virtualization/disk/DiskImageVmdkTest.java new file mode 100644 index 0000000..5b3cccf --- /dev/null +++ b/src/test/java/org/openslx/virtualization/disk/DiskImageVmdkTest.java @@ -0,0 +1,111 @@ +package org.openslx.virtualization.disk; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.openslx.virtualization.Version; +import org.openslx.virtualization.disk.DiskImage.ImageFormat; + +public class DiskImageVmdkTest +{ + @Test + @DisplayName( "Test detection of default VMDK disk image" ) + public void testVmdkDiskImage() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.vmdk" ) ); + final Version imageVersion = new Version( Short.valueOf( "1" ) ); + final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); + + assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + + // test special features of the VMDK disk image format + final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); + assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 0: single growable virtual disk)" ) + public void testVmdkDiskImageType0() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t0.vmdk" ) ); + final Version imageVersion = new Version( Short.valueOf( "1" ) ); + final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); + + assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( false, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + + // test special features of the VMDK disk image format + final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); + assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 1: growable virtual disk split into multiple files)" ) + public void testVmdkDiskImageType1() throws DiskImageException, IOException + { + Assertions.assertThrows( DiskImageException.class, () -> { + DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t1.vmdk" ) ); + } ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 2: preallocated virtual disk)" ) + public void testVmdkDiskImageType2() throws DiskImageException, IOException + { + Assertions.assertThrows( DiskImageException.class, () -> { + DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t2.vmdk" ) ); + } ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 3: preallocated virtual disk split into multiple files)" ) + public void testVmdkDiskImageType3() throws DiskImageException, IOException + { + Assertions.assertThrows( DiskImageException.class, () -> { + DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t3.vmdk" ) ); + } ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 4: preallocated ESX-type virtual disk)" ) + public void testVmdkDiskImageType4() throws DiskImageException, IOException + { + Assertions.assertThrows( DiskImageException.class, () -> { + DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t4.vmdk" ) ); + } ); + } + + @Test + @DisplayName( "Test detection of VMDK disk image (type 5: compressed disk optimized for streaming)" ) + public void testVmdkDiskImageType5() throws DiskImageException, IOException + { + final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t5.vmdk" ) ); + final Version imageVersion = new Version( Short.valueOf( "3" ) ); + final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); + + assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); + assertEquals( true, image.isStandalone() ); + assertEquals( false, image.isSnapshot() ); + assertEquals( true, image.isCompressed() ); + assertEquals( imageVersion, image.getVersion() ); + assertNull( image.getDescription() ); + + // test special features of the VMDK disk image format + final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); + assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); + } +} diff --git a/src/test/java/org/openslx/vm/disk/DiskImageQcow2Test.java b/src/test/java/org/openslx/vm/disk/DiskImageQcow2Test.java deleted file mode 100644 index 7804d7d..0000000 --- a/src/test/java/org/openslx/vm/disk/DiskImageQcow2Test.java +++ /dev/null @@ -1,221 +0,0 @@ -package org.openslx.vm.disk; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.IOException; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage.ImageFormat; - -public class DiskImageQcow2Test -{ - @Test - @DisplayName( "Test detection of default QCOW2 disk image" ) - public void testQcow2DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2Compressed16384DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2Compressed16384DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-on_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2NonCompressed16384DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 16384 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2NonCompressed16384DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-16384_cp-off_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2Compressed65536DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2Compressed65536DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-on_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2NonCompressed65536DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 65536 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2NonCompressed65536DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-65536_cp-off_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2Compressed2097152DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2Compressed2097152DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-on_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image with extended L2 tables" ) - public void testQcow2DetectionL2NonCompressed2097152DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-on.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of non-compressed, 2097152 byte cluster QCOW2 disk image without extended L2 tables" ) - public void testQcow2DetectionNonL2NonCompressed2097152DiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage - .newInstance( DiskImageTestResources.getDiskFile( "image_cs-2097152_cp-off_l2-off.qcow2" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - - assertEquals( ImageFormat.QCOW2.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - } -} diff --git a/src/test/java/org/openslx/vm/disk/DiskImageTest.java b/src/test/java/org/openslx/vm/disk/DiskImageTest.java deleted file mode 100644 index 2572c58..0000000 --- a/src/test/java/org/openslx/vm/disk/DiskImageTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.IOException; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -public class DiskImageTest -{ - @Test - @DisplayName( "Test of invalid disk image" ) - public void testInvalidDiskImage() throws IOException - { - Assertions.assertThrows( DiskImageException.class, () -> { - DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.invalid" ) ); - } ); - } -} diff --git a/src/test/java/org/openslx/vm/disk/DiskImageTestResources.java b/src/test/java/org/openslx/vm/disk/DiskImageTestResources.java deleted file mode 100644 index 2ec2e05..0000000 --- a/src/test/java/org/openslx/vm/disk/DiskImageTestResources.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.openslx.vm.disk; - -import java.io.File; -import java.net.URL; - -public class DiskImageTestResources -{ - private static final String DISK_PREFIX_PATH = File.separator + "disk"; - - public static File getDiskFile( String diskFileName ) - { - String diskPath = DiskImageTestResources.DISK_PREFIX_PATH + File.separator + diskFileName; - URL disk = DiskImageTestResources.class.getResource( diskPath ); - return new File( disk.getFile() ); - } -} diff --git a/src/test/java/org/openslx/vm/disk/DiskImageVdiTest.java b/src/test/java/org/openslx/vm/disk/DiskImageVdiTest.java deleted file mode 100644 index 85112cc..0000000 --- a/src/test/java/org/openslx/vm/disk/DiskImageVdiTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package org.openslx.vm.disk; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.io.IOException; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage.ImageFormat; - -public class DiskImageVdiTest -{ - @Test - @DisplayName( "Test detection of default VDI disk image" ) - public void testVdiDiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.vdi" ) ); - final Version imageVersion = new Version( Short.valueOf( "1" ), Short.valueOf( "1" ) ); - - assertEquals( ImageFormat.VDI.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNotNull( image.getDescription() ); - } - - @Test - @DisplayName( "Test detection of VDI disk image snapshot" ) - public void testVdiDiskImageSnapshot() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default_snapshot.vdi" ) ); - final Version imageVersion = new Version( Short.valueOf( "1" ), Short.valueOf( "1" ) ); - - assertEquals( ImageFormat.VDI.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( true, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNotNull( image.getDescription() ); - } -} diff --git a/src/test/java/org/openslx/vm/disk/DiskImageVmdkTest.java b/src/test/java/org/openslx/vm/disk/DiskImageVmdkTest.java deleted file mode 100644 index 4c8be82..0000000 --- a/src/test/java/org/openslx/vm/disk/DiskImageVmdkTest.java +++ /dev/null @@ -1,111 +0,0 @@ -package org.openslx.vm.disk; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNull; - -import java.io.IOException; - -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.openslx.virtualization.Version; -import org.openslx.vm.disk.DiskImage.ImageFormat; - -public class DiskImageVmdkTest -{ - @Test - @DisplayName( "Test detection of default VMDK disk image" ) - public void testVmdkDiskImage() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image-default.vmdk" ) ); - final Version imageVersion = new Version( Short.valueOf( "1" ) ); - final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); - - assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - - // test special features of the VMDK disk image format - final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); - assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 0: single growable virtual disk)" ) - public void testVmdkDiskImageType0() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t0.vmdk" ) ); - final Version imageVersion = new Version( Short.valueOf( "1" ) ); - final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); - - assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( false, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - - // test special features of the VMDK disk image format - final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); - assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 1: growable virtual disk split into multiple files)" ) - public void testVmdkDiskImageType1() throws DiskImageException, IOException - { - Assertions.assertThrows( DiskImageException.class, () -> { - DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t1.vmdk" ) ); - } ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 2: preallocated virtual disk)" ) - public void testVmdkDiskImageType2() throws DiskImageException, IOException - { - Assertions.assertThrows( DiskImageException.class, () -> { - DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t2.vmdk" ) ); - } ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 3: preallocated virtual disk split into multiple files)" ) - public void testVmdkDiskImageType3() throws DiskImageException, IOException - { - Assertions.assertThrows( DiskImageException.class, () -> { - DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t3.vmdk" ) ); - } ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 4: preallocated ESX-type virtual disk)" ) - public void testVmdkDiskImageType4() throws DiskImageException, IOException - { - Assertions.assertThrows( DiskImageException.class, () -> { - DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t4.vmdk" ) ); - } ); - } - - @Test - @DisplayName( "Test detection of VMDK disk image (type 5: compressed disk optimized for streaming)" ) - public void testVmdkDiskImageType5() throws DiskImageException, IOException - { - final DiskImage image = DiskImage.newInstance( DiskImageTestResources.getDiskFile( "image_t5.vmdk" ) ); - final Version imageVersion = new Version( Short.valueOf( "3" ) ); - final Version imageHwVersion = new Version( Short.valueOf( "18" ) ); - - assertEquals( ImageFormat.VMDK.toString(), image.getFormat().toString() ); - assertEquals( true, image.isStandalone() ); - assertEquals( false, image.isSnapshot() ); - assertEquals( true, image.isCompressed() ); - assertEquals( imageVersion, image.getVersion() ); - assertNull( image.getDescription() ); - - // test special features of the VMDK disk image format - final DiskImageVmdk vmdkImage = DiskImageVmdk.class.cast( image ); - assertEquals( imageHwVersion, vmdkImage.getHwVersion() ); - } -} -- cgit v1.2.3-55-g7522