summaryrefslogtreecommitdiffstats
path: root/src/main/java/org/openslx/virtualization/disk/DiskImage.java
blob: dfb242a1f17aaaf3bcd81760da4ad07e0c562f15 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
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 = "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 <code>true</code> if image type is supported by the virtualizer; otherwise
		 *         <code>false</code>.
		 */
		public boolean isSupportedbyVirtualizer( List<ImageFormat> supportedImageFormats )
		{
			Predicate<ImageFormat> 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();
		}
	}
}