001/*
002 * To change this license header, choose License Headers in Project Properties.
003 * To change this template file, choose Tools | Templates
004 * and open the template in the editor.
005 */
006package org.anarres.qemu.image;
007
008import com.fasterxml.jackson.databind.ObjectMapper;
009import com.google.common.base.Preconditions;
010import com.google.common.io.ByteStreams;
011import java.io.File;
012import java.io.IOException;
013import javax.annotation.Nonnegative;
014import javax.annotation.Nonnull;
015
016/**
017 * An abstraction around a QEmu image.
018 * <p>
019 * The underlying image file may or may not exist; this class allows for
020 * simple creation, inspection and deletion.
021 *
022 * @author shevek
023 */
024public class QEmuImage {
025
026    private final File file;
027
028    public QEmuImage(@Nonnull File file) {
029        this.file = Preconditions.checkNotNull(file);
030    }
031
032    public QEmuImage(@Nonnull String path) {
033        this(new File(path));
034    }
035
036    /**
037     * @return the file underlying this QEmuImage.
038     */
039    @Nonnull
040    public File getFile() {
041        return file;
042    }
043
044    @Nonnull
045    public QEmuImageInfo query() throws IOException {
046        ProcessBuilder builder = new ProcessBuilder("qemu-img", "info", "--output=json", file.getAbsolutePath());
047        Process process = builder.start();
048        byte[] data = ByteStreams.toByteArray(process.getInputStream());
049        ObjectMapper mapper = new ObjectMapper();
050        return mapper.readValue(data, QEmuImageInfo.class);
051    }
052
053    /**
054     * Creates this image.
055     *
056     * @param format The image format for the new image.
057     * @param size The virtual size of the new image.
058     */
059    public void create(@Nonnull QEmuImageFormat format, @Nonnegative long size) throws IOException {
060        ProcessBuilder builder = new ProcessBuilder("qemu-img", "create", "-f", format.name(), file.getAbsolutePath(), String.valueOf(size));
061        Process process = builder.start();
062        ByteStreams.copy(process.getInputStream(), System.err);
063    }
064
065    /**
066     * Creates this image.
067     * <p>
068     * The size of the new image is derived from the existing backing file.
069     * <p>
070     * backingFile is referenced by a relative path. If you want it referenced
071     * absolutely, canonicalize the argument with {@link File#getAbsoluteFile()}
072     * before calling this method.
073     *
074     * @param format      The image format for the new image.
075     * @param backingFile The backing file for the new image.
076     */
077    public void create(@Nonnull QEmuImageFormat format, @Nonnull File backingFile) throws IOException {
078        ProcessBuilder builder = new ProcessBuilder("qemu-img", "create", "-f", format.name(), "-b", backingFile.getPath(), file.getAbsolutePath());
079        Process process = builder.start();
080        ByteStreams.copy(process.getInputStream(), System.err);
081    }
082
083    /**
084     * Deletes the file underlying this image, if it exists.
085     */
086    public void delete() throws IOException {
087        if (file.exists())
088            if (!file.delete())
089                throw new IOException("Unable to delete " + file);
090    }
091}