[libvirt] [java PATCH 1/1] Add support for Qemu Guest Agent commands

Wido den Hollander wido at widodh.nl
Thu Jul 19 14:09:08 UTC 2018


Through Qemu we can send commands to a Qemu Guest Agent running inside
a domain.

This way we can communicate with a running Domain by asking for it's
network information, requesting a filesystem trim or even execute a
command inside a Domain.

Commands need to be send as JSON Strings, but these have been wrapped
into the QemuCommand class which has a list of pre-defined and the most
commonly used commands.

RAW commands can also be send for more flexibility.

Signed-off-by: Wido den Hollander <wido at widodh.nl>
---
 src/main/java/org/libvirt/Domain.java         |  34 ++++++
 src/main/java/org/libvirt/Library.java        |   3 +
 .../java/org/libvirt/jna/LibvirtQemu.java     |  16 +++
 .../java/org/libvirt/qemu/QemuCommand.java    | 106 ++++++++++++++++++
 .../org/libvirt/qemu/TestQemuCommand.java     |  24 ++++
 5 files changed, 183 insertions(+)
 create mode 100644 src/main/java/org/libvirt/jna/LibvirtQemu.java
 create mode 100644 src/main/java/org/libvirt/qemu/QemuCommand.java
 create mode 100644 src/test/java/org/libvirt/qemu/TestQemuCommand.java

diff --git a/src/main/java/org/libvirt/Domain.java b/src/main/java/org/libvirt/Domain.java
index 83a500c..9138238 100644
--- a/src/main/java/org/libvirt/Domain.java
+++ b/src/main/java/org/libvirt/Domain.java
@@ -8,6 +8,7 @@ import org.libvirt.jna.CString;
 import org.libvirt.jna.DomainPointer;
 import org.libvirt.jna.DomainSnapshotPointer;
 import org.libvirt.jna.Libvirt;
+import org.libvirt.jna.LibvirtQemu;
 import org.libvirt.jna.SizeT;
 import org.libvirt.jna.virDomainBlockInfo;
 import org.libvirt.jna.virDomainBlockStats;
@@ -21,7 +22,9 @@ import org.libvirt.event.RebootListener;
 import org.libvirt.event.LifecycleListener;
 import org.libvirt.event.PMWakeupListener;
 import org.libvirt.event.PMSuspendListener;
+import org.libvirt.qemu.QemuCommand;
 import static org.libvirt.Library.libvirt;
+import static org.libvirt.Library.libvirtqemu;
 import static org.libvirt.ErrorHandler.processError;
 import static org.libvirt.ErrorHandler.processErrorIfZero;
 
@@ -1556,4 +1559,35 @@ public class Domain {
         return processError(libvirt.virDomainUpdateDeviceFlags(VDP, xml, flags));
     }
 
+    /**
+     * Send a Qemu Guest Agent command to a domain
+     *
+     * @param cmd
+     *            The command which has to be send
+     * @param timeout
+     *            The timeout for waiting on an answer. See QemuAgentFlags for more information.
+     * @param flags
+     *            Flags to be send
+     * @return String
+     * @throws LibvirtException
+     */
+    public String qemuAgentCommand(QemuCommand command) throws LibvirtException {
+        return this.qemuAgentCommand(command, 0);
+    }
+
+    /**
+     * Send a Qemu Guest Agent command to a domain
+     * @see <a href="http://wiki.qemu.org/Features/QAPI/GuestAgent">Qemu Documentation</a>
+     * @param command
+     *            The command which has to be send
+     * @param timeout
+     *            The timeout for waiting on an answer. See QemuAgentFlags for more information
+     * @return String
+     * @throws LibvirtException
+     */
+    public String qemuAgentCommand(QemuCommand command, int timeout) throws LibvirtException {
+        CString result = libvirtqemu.virDomainQemuAgentCommand(this.VDP, command.toString(), timeout, 0);
+        processError(result);
+        return result.toString();
+    }
 }
diff --git a/src/main/java/org/libvirt/Library.java b/src/main/java/org/libvirt/Library.java
index 8e054c6..30f15be 100644
--- a/src/main/java/org/libvirt/Library.java
+++ b/src/main/java/org/libvirt/Library.java
@@ -2,6 +2,7 @@ package org.libvirt;
 
 import org.libvirt.jna.Libvirt;
 import org.libvirt.jna.Libvirt.VirEventTimeoutCallback;
+import org.libvirt.jna.LibvirtQemu;
 import org.libvirt.jna.CString;
 import static org.libvirt.ErrorHandler.processError;
 
@@ -34,6 +35,7 @@ public final class Library {
         };
 
     final static Libvirt libvirt;
+    final static LibvirtQemu libvirtqemu;
 
     // an empty string array constant
     // prefer this over creating empty arrays dynamically.
@@ -47,6 +49,7 @@ public final class Library {
         } catch (Exception e) {
             e.printStackTrace();
         }
+        libvirtqemu = LibvirtQemu.INSTANCE;
     }
 
     private Library() {}
diff --git a/src/main/java/org/libvirt/jna/LibvirtQemu.java b/src/main/java/org/libvirt/jna/LibvirtQemu.java
new file mode 100644
index 0000000..82213e9
--- /dev/null
+++ b/src/main/java/org/libvirt/jna/LibvirtQemu.java
@@ -0,0 +1,16 @@
+package org.libvirt.jna;
+
+import com.sun.jna.Library;
+import com.sun.jna.Native;
+import com.sun.jna.Platform;
+
+/**
+ * The libvirt Qemu interface which is exposed via JNA
+ */
+
+public interface LibvirtQemu extends Library {
+
+    LibvirtQemu INSTANCE = (LibvirtQemu) Native.loadLibrary(Platform.isWindows() ? "virt-qemu-0" : "virt-qemu", LibvirtQemu.class);
+
+    CString virDomainQemuAgentCommand(DomainPointer virDomainPtr, String cmd, int timeout, int flags);
+}
diff --git a/src/main/java/org/libvirt/qemu/QemuCommand.java b/src/main/java/org/libvirt/qemu/QemuCommand.java
new file mode 100644
index 0000000..5b28c81
--- /dev/null
+++ b/src/main/java/org/libvirt/qemu/QemuCommand.java
@@ -0,0 +1,106 @@
+package org.libvirt.qemu;
+
+import java.lang.IllegalArgumentException;
+
+public class QemuCommand {
+
+    public enum Command {
+
+        GUEST_INFO("guest-info", false),
+        GUEST_SYNC_DELIMITED("guest-sync-delimited", true),
+        GUEST_SYNC("guest-sync", true),
+        GUEST_PING("guest-ping", false),
+        GUEST_GET_TIME("guest-get-time", false),
+        GUEST_SET_TIME("guest-set-time", true),
+        GUEST_SHUTDOWN("guest-shutdown", false),
+        GUEST_FILE_OPEN("guest-file-open", true),
+        GUEST_FILE_CLOSE("guest-file-close", true),
+        GUEST_FILE_READ("guest-file-read", true),
+        GUEST_FILE_WRITE("guest-file-write", true),
+        GUEST_FILE_SEEK("guest-file-seek", true),
+        GUEST_FILE_FLUSH("guest-file-flush", true),
+        GUEST_FSFREEZE_STATUS("guest-fsfreeze-status", false),
+        GUEST_FSFREEZE_FREEZE("guest-fsfreeze-freeze", false),
+        GUEST_FSFREEZE_LIST("guest-fsfreeze-freeze-list", true),
+        GUEST_FSFREEZE_THAW("guest-fsfreeze-thaw", false),
+        GUEST_FSTRIM("guest-fstrim", true),
+        GUEST_SUSPEND_DISK("guest-suspend-disk", false),
+        GUEST_SUSPEND_RAM("guest-suspend-ram", false),
+        GUEST_SUSPEND_HYBRID("guest-suspend-hybrid", false),
+        GUEST_GET_NETWORK_INTERFACES("guest-network-get-interfaces", false),
+        GUEST_GET_VCPUS("guest-get-vcpus", false),
+        GUEST_SET_VCPUS("guest-set-vcpus", true),
+        GUEST_GET_FSINFO("guest-get-fsinfo", false),
+        GUEST_SET_USER_PASSWORD("guest-set-user-password", true),
+        GUEST_GET_MEMORY_BLOCKS("guest-get-memory-blocks", false),
+        GUEST_SET_MEMORY_BLOCKS("guest-set-memory-blocks", true),
+        GUEST_GET_MEMORY_BLOCK_INFO("guest-get-memory-block-info", false),
+        GUEST_EXEC_STATUS("guest-exec-status", false),
+        GUEST_EXEC("guest-exec", true);
+
+        private final String command;
+        private final Boolean requiresArguments;
+
+        Command(String command, Boolean requiresArguments) {
+            this.command = command;
+            this.requiresArguments = requiresArguments;
+        }
+
+        private String getCommand() {
+            return command;
+        }
+
+        private Boolean requiresArguments() {
+            return requiresArguments;
+        }
+    }
+
+    Command command;
+    String arguments;
+    String raw;
+
+    public static QemuCommand create(Command cmd) {
+        return new QemuCommand(cmd, null);
+    }
+
+    public static QemuCommand create(Command cmd, String args) {
+        return new QemuCommand(cmd, args);
+    }
+
+    public static QemuCommand raw(String jsonObject) {
+        return new QemuCommand(jsonObject);
+    }
+
+    private QemuCommand(Command command, String arguments) {
+        if (command.requiresArguments() && arguments == null) {
+            throw new IllegalArgumentException(command.getCommand() + " requires a argument");
+        } else if(!command.requiresArguments() && arguments != null) {
+            throw new IllegalArgumentException(command.getCommand() + " does not take a argument");
+        }
+
+        this.command = command;
+        this.arguments = arguments;
+    }
+
+    private QemuCommand(String raw) {
+        this.raw = raw;
+    }
+
+    public String toString() {
+        if (this.raw != null) {
+            return this.raw;
+        }
+
+        String json = "";
+
+        json += "{";
+        json += "\"execute\": \"" + command.getCommand() + "\"";
+
+        if (arguments != null && arguments.length() > 0) {
+            json += "\"arguments\": " + arguments + "\"";
+        }
+
+        json += "}";
+        return json;
+    }
+}
diff --git a/src/test/java/org/libvirt/qemu/TestQemuCommand.java b/src/test/java/org/libvirt/qemu/TestQemuCommand.java
new file mode 100644
index 0000000..f372310
--- /dev/null
+++ b/src/test/java/org/libvirt/qemu/TestQemuCommand.java
@@ -0,0 +1,24 @@
+package org.libvirt.qemu;
+
+import junit.framework.TestCase;
+
+public final class TestQemuCommand extends TestCase {
+
+    public void testCommandWithoutArguments() {
+        QemuCommand command = QemuCommand.create(QemuCommand.Command.GUEST_INFO);
+        assertEquals("{\"execute\": \"guest-info\"}", command.toString());
+    }
+
+    public void testCommandWithoutNonRequiredArguments() {
+        try {
+            QemuCommand command = QemuCommand.create(QemuCommand.Command.GUEST_INFO, "I am Not Required");
+            fail("Expected a IllegalArgumentException which was not thrown");
+        } catch(IllegalArgumentException e) {}
+    }
+
+    public void testRawCommand() {
+        String rawcommand = "this is a raw string";
+        QemuCommand command = QemuCommand.raw(rawcommand);
+        assertEquals(rawcommand, command.toString());
+    }
+}
-- 
2.17.1




More information about the libvir-list mailing list