[libvirt] [PATCH 4/4] Add support for qemu's guestfwd

Matthew Booth mbooth at redhat.com
Tue Nov 3 16:07:41 UTC 2009


This patch allows the following to be specified in a qemu domain:

<channel type='pipe'>
  <source path='/tmp/guestfwd'/>
  <target type='guestfwd' address='10.0.2.1' port='4600'/>
</channel>

This will output the following on the qemu command line:

-chardev pipe,id=channel0,path=/tmp/guestfwd \
-net user,guestfwd=tcp:10.0.2.1:4600-chardev:channel0

* docs/schemas/domain.rng: Add <channel> and <guestfwd> elements
* proxy/Makefile.am: add network.c as dep of domain_conf.c
* src/conf/domain_conf.[ch]: Add xml parsing/formatting for channel and guestfwd
* src/qemu/qemu_conf.c: Add argument output for guestfwd
* tests/qemuxml2(argv|xml)test.c: Add test for guestfwd domain syntax
---
 docs/schemas/domain.rng                            |   89 ++++++----
 proxy/Makefile.am                                  |    1 +
 src/conf/domain_conf.c                             |  189 ++++++++++++++++++--
 src/conf/domain_conf.h                             |    6 +
 src/qemu/qemu_conf.c                               |   61 +++++++
 .../qemuxml2argv-channel-guestfwd.args             |    1 +
 .../qemuxml2argv-channel-guestfwd.xml              |   26 +++
 tests/qemuxml2argvtest.c                           |    4 +-
 tests/qemuxml2xmltest.c                            |    1 +
 9 files changed, 329 insertions(+), 49 deletions(-)
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args
 create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml

diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng
index 0a6ab61..b75f17e 100644
--- a/docs/schemas/domain.rng
+++ b/docs/schemas/domain.rng
@@ -930,6 +930,19 @@
       definition doesn't fully specify the constraints on this node.
     -->
   <define name="qemucdev">
+    <ref name="qemucdevSrcType"/>
+    <interleave>
+      <ref name="qemucdevSrcDef"/>
+      <optional>
+        <element name="target">
+          <optional>
+            <attribute name="port"/>
+          </optional>
+        </element>
+      </optional>
+    </interleave>
+  </define>
+  <define name="qemucdevSrcType">
     <attribute name="type">
       <choice>
         <value>dev</value>
@@ -944,43 +957,36 @@
         <value>pty</value>
       </choice>
     </attribute>
-    <interleave>
-      <optional>
-        <oneOrMore>
-          <element name="source">
-            <optional>
-              <attribute name="mode"/>
-            </optional>
-            <optional>
-              <attribute name="path"/>
-            </optional>
-            <optional>
-              <attribute name="host"/>
-            </optional>
-            <optional>
-              <attribute name="service"/>
-            </optional>
-            <optional>
-              <attribute name="wiremode"/>
-            </optional>
-          </element>
-        </oneOrMore>
-      </optional>
-      <optional>
-        <element name="protocol">
+  </define>
+  <define name="qemucdevSrcDef">
+    <optional>
+      <oneOrMore>
+        <element name="source">
           <optional>
-            <attribute name="type"/>
+            <attribute name="mode"/>
           </optional>
-        </element>
-      </optional>
-      <optional>
-        <element name="target">
           <optional>
-            <attribute name="port"/>
+            <attribute name="path"/>
+          </optional>
+          <optional>
+            <attribute name="host"/>
+          </optional>
+          <optional>
+            <attribute name="service"/>
+          </optional>
+          <optional>
+            <attribute name="wiremode"/>
           </optional>
         </element>
-      </optional>
-    </interleave>
+      </oneOrMore>
+    </optional>
+    <optional>
+      <element name="protocol">
+        <optional>
+          <attribute name="type"/>
+        </optional>
+      </element>
+    </optional>
   </define>
   <!--
       The description for a console
@@ -1044,6 +1050,24 @@
       <ref name="qemucdev"/>
     </element>
   </define>
+  <define name="guestfwdTarget">
+    <element name="target">
+        <attribute name="type">
+            <value>guestfwd</value>
+        </attribute>
+        <attribute name="address"/>
+        <attribute name="port"/>
+    </element>
+  </define>
+  <define name="channel">
+    <element name="channel">
+      <ref name="qemucdevSrcType"/>
+      <interleave>
+        <ref name="qemucdevSrcDef"/>
+        <ref name="guestfwdTarget"/>
+      </interleave>
+    </element>
+  </define>
   <define name="input">
     <element name="input">
       <attribute name="type">
@@ -1158,6 +1182,7 @@
             <ref name="console"/>
             <ref name="parallel"/>
             <ref name="serial"/>
+            <ref name="channel"/>
           </choice>
         </zeroOrMore>
         <optional>
diff --git a/proxy/Makefile.am b/proxy/Makefile.am
index 3e0050b..42f6a81 100644
--- a/proxy/Makefile.am
+++ b/proxy/Makefile.am
@@ -17,6 +17,7 @@ libvirt_proxy_SOURCES = libvirt_proxy.c \
             @top_srcdir@/src/util/buf.c \
 	    @top_srcdir@/src/util/logging.c \
             @top_srcdir@/src/util/memory.c \
+            @top_srcdir@/src/util/network.c \
 	    @top_srcdir@/src/util/threads.c  \
             @top_srcdir@/src/util/util.c \
 	    @top_srcdir@/src/util/uuid.c \
diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index e050453..4689bac 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -40,6 +40,7 @@
 #include "buf.h"
 #include "c-ctype.h"
 #include "logging.h"
+#include "network.h"
 
 #define VIR_FROM_THIS VIR_FROM_DOMAIN
 
@@ -132,7 +133,8 @@ VIR_ENUM_IMPL(virDomainChrTarget, VIR_DOMAIN_CHR_TARGET_TYPE_LAST,
               "monitor",
               "parallel",
               "serial",
-              "console")
+              "console",
+              "guestfwd")
 
 VIR_ENUM_IMPL(virDomainChr, VIR_DOMAIN_CHR_TYPE_LAST,
               "null",
@@ -412,6 +414,12 @@ void virDomainChrDefFree(virDomainChrDefPtr def)
     if (!def)
         return;
 
+    switch (def->targetType) {
+    case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+        VIR_FREE(def->target.addr);
+        break;
+    }
+
     switch (def->type) {
     case VIR_DOMAIN_CHR_TYPE_PTY:
     case VIR_DOMAIN_CHR_TYPE_DEV:
@@ -541,6 +549,7 @@ void virDomainDefFree(virDomainDefPtr def)
     for (i = 0 ; i < def->nnets ; i++)
         virDomainNetDefFree(def->nets[i]);
     VIR_FREE(def->nets);
+
     for (i = 0 ; i < def->nserials ; i++)
         virDomainChrDefFree(def->serials[i]);
     VIR_FREE(def->serials);
@@ -549,6 +558,10 @@ void virDomainDefFree(virDomainDefPtr def)
         virDomainChrDefFree(def->parallels[i]);
     VIR_FREE(def->parallels);
 
+    for (i = 0 ; i < def->nchannels ; i++)
+        virDomainChrDefFree(def->channels[i]);
+    VIR_FREE(def->channels);
+
     virDomainChrDefFree(def->console);
 
     for (i = 0 ; i < def->nsounds ; i++)
@@ -1332,7 +1345,10 @@ virDomainChrDefParseXML(virConnectPtr conn,
     char *path = NULL;
     char *mode = NULL;
     char *protocol = NULL;
+    const char *nodeName;
     const char *targetType = NULL;
+    const char *addrStr = NULL;
+    const char *portStr = NULL;
     virDomainChrDefPtr def;
 
     if (VIR_ALLOC(def) < 0) {
@@ -1346,18 +1362,15 @@ virDomainChrDefParseXML(virConnectPtr conn,
     else if ((def->type = virDomainChrTypeFromString(type)) < 0)
         def->type = VIR_DOMAIN_CHR_TYPE_NULL;
 
-    targetType = (const char *) node->name;
-    if (targetType == NULL) {
-        /* Shouldn't be possible */
-        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                             "node->name is NULL at %s:%i",
-                             __FILE__, __LINE__);
-        return NULL;
-    }
-    if ((def->targetType = virDomainChrTargetTypeFromString(targetType)) < 0) {
-        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
-                             _("unknown target type for character device: %s"),
-                             targetType);
+    nodeName = (const char *) node->name;
+    if ((def->targetType = virDomainChrTargetTypeFromString(nodeName)) < 0) {
+        /* channel is handled below */
+        if(STRNEQ(nodeName, "channel")) {
+            virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                              _("unknown target type for character device: %s"),
+                                 nodeName);
+            return NULL;
+        }
         def->targetType = VIR_DOMAIN_CHR_TARGET_TYPE_NULL;
     }
 
@@ -1406,6 +1419,89 @@ virDomainChrDefParseXML(virConnectPtr conn,
             } else if (xmlStrEqual(cur->name, BAD_CAST "protocol")) {
                 if (protocol == NULL)
                     protocol = virXMLPropString(cur, "type");
+            } else if (xmlStrEqual(cur->name, BAD_CAST "target")) {
+                /* If target type isn't set yet, expect it to be set here */
+                if(def->targetType == VIR_DOMAIN_CHR_TARGET_TYPE_NULL) {
+                    targetType = virXMLPropString(cur, "type");
+                    if(targetType == NULL) {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, "%s",
+                                             _("character device target does "
+                                               "not define a type"));
+                        goto error;
+                    }
+                    if ((def->targetType =
+                        virDomainChrTargetTypeFromString(targetType)) < 0)
+                    {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                                             _("unknown target type for "
+                                               "character device: %s"),
+                                             targetType);
+                        goto error;
+                    }
+                }
+
+                unsigned int port;
+                switch (def->targetType) {
+                case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL:
+                case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL:
+                case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE:
+                    portStr = virXMLPropString(cur, "port");
+                    if(portStr == NULL) {
+                        /* Not required. It will be assigned automatically
+                         * later */
+                        break;
+                    }
+
+                    if(virStrToLong_ui(portStr, NULL, 10, &port) < 0) {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                                             _("Invalid port number: %s"),
+                                             portStr);
+                        goto error;
+                    }
+                    break;
+
+                case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+                    addrStr = virXMLPropString(cur, "address");
+                    portStr = virXMLPropString(cur, "port");
+
+                    if(addrStr == NULL) {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, "%s",
+                                             _("guestfwd channel does not "
+                                               "define a target address"));
+                        goto error;
+                    }
+                    if(VIR_ALLOC(def->target.addr) < 0) {
+                        virReportOOMError(conn);
+                        goto error;
+                    }
+                    if(virSocketParseAddr(addrStr, def->target.addr, 0) < 0)
+                    {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                                             _("%s is not a valid address"),
+                                             addrStr);
+                        goto error;
+                    }
+
+                    if(portStr == NULL) {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN, "%s",
+                                             _("guestfwd channel does "
+                                               "not define a target port"));
+                        goto error;
+                    }
+                    if(virStrToLong_ui(portStr, NULL, 10, &port) < 0) {
+                        virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                                             _("Invalid port number: %s"),
+                                             portStr);
+                        goto error;
+                    }
+                    virSocketSetPort(def->target.addr, port);
+                    break;
+
+                default:
+                    virDomainReportError(conn, VIR_ERR_INVALID_DOMAIN,
+                                         _("unexpected target type type %u"),
+                                         def->targetType);
+                }
             }
         }
         cur = cur->next;
@@ -1535,6 +1631,9 @@ cleanup:
     VIR_FREE(connectHost);
     VIR_FREE(connectService);
     VIR_FREE(path);
+    VIR_FREE(targetType);
+    VIR_FREE(addrStr);
+    VIR_FREE(portStr);
 
     return def;
 
@@ -3007,6 +3106,25 @@ static virDomainDefPtr virDomainDefParseXML(virConnectPtr conn,
         }
     }
 
+    if ((n = virXPathNodeSet(conn, "./devices/channel", ctxt, &nodes)) < 0) {
+        virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
+                             "%s", _("cannot extract channel devices"));
+        goto error;
+    }
+    if (n && VIR_ALLOC_N(def->channels, n) < 0)
+        goto no_memory;
+
+    for (i = 0 ; i < n ; i++) {
+        virDomainChrDefPtr chr = virDomainChrDefParseXML(conn,
+                                                         nodes[i],
+                                                         flags);
+        if (!chr)
+            goto error;
+
+        def->channels[def->nchannels++] = chr;
+    }
+    VIR_FREE(nodes);
+
 
     /* analysis of the input devices */
     if ((n = virXPathNodeSet(conn, "./devices/input", ctxt, &nodes)) < 0) {
@@ -3992,13 +4110,26 @@ virDomainChrDefFormat(virConnectPtr conn,
 {
     const char *type = virDomainChrTypeToString(def->type);
     const char *targetName = virDomainChrTargetTypeToString(def->targetType);
+    const char *elementName;
+
+    const char *addr = NULL;
+    int ret = 0;
+
+    switch (def->targetType) {
+    /* channel types are in a common channel element */
+    case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+        elementName = "channel";
+        break;
 
-    const char *elementName = targetName; /* Currently always the same */
+    default:
+        elementName = targetName;
+    }
 
     if (!type) {
         virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR,
                              _("unexpected char type %d"), def->type);
-        return -1;
+        ret = -1;
+        goto cleanup;
     }
 
     /* Compat with legacy  <console tty='/dev/pts/5'/> syntax */
@@ -4080,6 +4211,25 @@ virDomainChrDefFormat(virConnectPtr conn,
     }
 
     switch (def->targetType) {
+    case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+        addr = virSocketFormatAddr(def->target.addr);
+        if (addr == NULL) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                                 _("Unable to format guestfwd address"));
+            ret = -1;
+            goto cleanup;
+        }
+        int port = virSocketGetPort(def->target.addr);
+        if (port < 0) {
+            virDomainReportError(conn, VIR_ERR_INTERNAL_ERROR, "%s",
+                                 _("Unable to format guestfwd port"));
+            ret = -1;
+            goto cleanup;
+        }
+        virBufferVSprintf(buf, "      <target type='guestfwd' address='%s' port='%d'/>\n",
+                          addr, port);
+        break;
+
     case VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL:
     case VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL:
     case VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE:
@@ -4097,7 +4247,10 @@ virDomainChrDefFormat(virConnectPtr conn,
     virBufferVSprintf(buf, "    </%s>\n",
                       elementName);
 
-    return 0;
+cleanup:
+    VIR_FREE(addr);
+
+    return ret;
 }
 
 static int
@@ -4563,6 +4716,10 @@ char *virDomainDefFormat(virConnectPtr conn,
             goto cleanup;
     }
 
+    for (n = 0 ; n < def->nchannels ; n++)
+        if (virDomainChrDefFormat(conn, &buf, def->channels[n], flags) < 0)
+            goto cleanup;
+
     for (n = 0 ; n < def->ninputs ; n++)
         if (def->inputs[n]->bus == VIR_DOMAIN_INPUT_BUS_USB &&
             virDomainInputDefFormat(conn, &buf, def->inputs[n]) < 0)
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index 7bd8c63..e826cc7 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -34,6 +34,7 @@
 #include "util.h"
 #include "threads.h"
 #include "hash.h"
+#include "network.h"
 
 /* Private component of virDomainXMLFlags */
 typedef enum {
@@ -217,6 +218,7 @@ enum virDomainChrTargetType {
     VIR_DOMAIN_CHR_TARGET_TYPE_PARALLEL,
     VIR_DOMAIN_CHR_TARGET_TYPE_SERIAL,
     VIR_DOMAIN_CHR_TARGET_TYPE_CONSOLE,
+    VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD,
 
     VIR_DOMAIN_CHR_TARGET_TYPE_LAST
 };
@@ -249,6 +251,7 @@ struct _virDomainChrDef {
     int targetType;
     union {
         int port; /* parallel, serial, console */
+        virSocketAddrPtr addr; /* guestfwd */
     } target;
 
     int type;
@@ -623,6 +626,9 @@ struct _virDomainDef {
     int nparallels;
     virDomainChrDefPtr *parallels;
 
+    int nchannels;
+    virDomainChrDefPtr *channels;
+
     /* Only 1 */
     virDomainChrDefPtr console;
     virSecurityLabelDef seclabel;
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c
index 3e37c66..0fbc190 100644
--- a/src/qemu/qemu_conf.c
+++ b/src/qemu/qemu_conf.c
@@ -50,6 +50,7 @@
 #include "xml.h"
 #include "nodeinfo.h"
 #include "logging.h"
+#include "network.h"
 
 #define VIR_FROM_THIS VIR_FROM_QEMU
 
@@ -1472,6 +1473,35 @@ static int qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev,
     return 0;
 }
 
+static int qemudBuildCommandLineChrDevTargetStr(virDomainChrDefPtr dev,
+                                                const char *const id,
+                                                char *buf,
+                                                int buflen)
+{
+    int ret = 0;
+    const char *addr = NULL;
+
+    switch (dev->targetType) {
+    case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+        {
+            addr = virSocketFormatAddr(dev->target.addr);
+            int port = virSocketGetPort(dev->target.addr);
+
+            if (snprintf(buf, buflen, "user,guestfwd=tcp:%s:%i-chardev:%s",
+                                      addr, port, id) >= buflen) {
+                ret = -1;
+                goto cleanup;
+            }
+            break;
+        }
+    }
+
+cleanup:
+    VIR_FREE(addr);
+
+    return ret;
+}
+
 /* This function outputs an all-in-one character device command line option */
 static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev,
                                           char *buf,
@@ -2150,6 +2180,37 @@ int qemudBuildCommandLine(virConnectPtr conn,
         }
     }
 
+    for (i = 0 ; i < def->nchannels ; i++) {
+        char buf[4096];
+        char id[16];
+
+        virDomainChrDefPtr channel = def->channels[i];
+
+        if (snprintf(id, sizeof(id), "channel%i", i) > sizeof(id))
+            goto error;
+
+        switch(channel->targetType) {
+        case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD:
+            if (!(qemuCmdFlags & QEMUD_CMD_FLAG_CHARDEV)) {
+                qemudReportError(conn, NULL, NULL, VIR_ERR_NO_SUPPORT,
+                     "%s", _("guestfwd requires QEMU to support -chardev"));
+                goto error;
+            }
+
+            if (qemudBuildCommandLineChrDevChardevStr(channel, id,
+                                                      buf, sizeof(buf)) < 0)
+                goto error;
+            ADD_ARG_LIT("-chardev");
+            ADD_ARG_LIT(buf);
+
+            if (qemudBuildCommandLineChrDevTargetStr(channel, id,
+                                                     buf, sizeof(buf)) < 0)
+                goto error;
+            ADD_ARG_LIT("-net");
+            ADD_ARG_LIT(buf);
+        }
+    }
+
     ADD_ARG_LIT("-usb");
     for (i = 0 ; i < def->ninputs ; i++) {
         virDomainInputDefPtr input = def->inputs[i];
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args
new file mode 100644
index 0000000..b5bb46d
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.args
@@ -0,0 +1 @@
+LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -net none -serial none -parallel none -chardev pipe,id=channel0,path=/tmp/guestfwd -net user,guestfwd=tcp:10.0.2.1:4600-chardev:channel0 -usb
diff --git a/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml
new file mode 100644
index 0000000..51a0c1e
--- /dev/null
+++ b/tests/qemuxml2argvdata/qemuxml2argv-channel-guestfwd.xml
@@ -0,0 +1,26 @@
+<domain type='qemu'>
+  <name>QEMUGuest1</name>
+  <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid>
+  <memory>219200</memory>
+  <currentMemory>219200</currentMemory>
+  <vcpu cpuset='1-4,8-20,525'>1</vcpu>
+  <os>
+    <type arch='i686' machine='pc'>hvm</type>
+    <boot dev='hd'/>
+  </os>
+  <clock offset='utc'/>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <devices>
+    <emulator>/usr/bin/qemu</emulator>
+    <disk type='block' device='disk'>
+      <source dev='/dev/HostVG/QEMUGuest1'/>
+      <target dev='hda' bus='ide'/>
+    </disk>
+    <channel type='pipe'>
+      <source path='/tmp/guestfwd'/>
+      <target type='guestfwd' address='10.0.2.1' port='4600'/>
+    </channel>
+  </devices>
+</domain>
diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c
index 3255146..c948379 100644
--- a/tests/qemuxml2argvtest.c
+++ b/tests/qemuxml2argvtest.c
@@ -268,11 +268,13 @@ mymain(int argc, char **argv)
     DO_TEST("serial-many", 0);
     DO_TEST("parallel-tcp", 0);
     DO_TEST("console-compat", 0);
+
+    DO_TEST("channel-guestfwd", QEMUD_CMD_FLAG_CHARDEV);
+
     DO_TEST("sound", 0);
 
     DO_TEST("hostdev-usb-product", 0);
     DO_TEST("hostdev-usb-address", 0);
-
     DO_TEST("hostdev-pci-address", QEMUD_CMD_FLAG_PCIDEVICE);
 
     DO_TEST_FULL("restore-v1", QEMUD_CMD_FLAG_MIGRATE_KVM_STDIO, "stdio");
diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c
index 2cba47b..25ef2ce 100644
--- a/tests/qemuxml2xmltest.c
+++ b/tests/qemuxml2xmltest.c
@@ -129,6 +129,7 @@ mymain(int argc, char **argv)
     DO_TEST("serial-many");
     DO_TEST("parallel-tcp");
     DO_TEST("console-compat");
+    DO_TEST("channel-guestfwd");
 
     DO_TEST("hostdev-usb-product");
     DO_TEST("hostdev-usb-address");
-- 
1.6.2.5




More information about the libvir-list mailing list