On Wed, Nov 04, 2009 at 04:22:01PM +0000, Matthew Booth wrote: > 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 | 64 +++++++ > .../qemuxml2argv-channel-guestfwd.args | 1 + > .../qemuxml2argv-channel-guestfwd.xml | 26 +++ > tests/qemuxml2argvtest.c | 4 +- > tests/qemuxml2xmltest.c | 1 + > 9 files changed, 332 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 fc70cfd..94bce1e 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 a9f6885..b83df33 100644 > --- a/src/qemu/qemu_conf.c > +++ b/src/qemu/qemu_conf.c > @@ -51,6 +51,7 @@ > #include "xml.h" > #include "nodeinfo.h" > #include "logging.h" > +#include "network.h" > > #define VIR_FROM_THIS VIR_FROM_QEMU > > @@ -1478,6 +1479,29 @@ static void qemudBuildCommandLineChrDevChardevStr(virDomainChrDefPtr dev, > } > } > > +static int qemudBuildCommandLineChrDevTargetStr(virDomainChrDefPtr dev, > + const char *const id, > + virBufferPtr buf) > +{ > + int ret = 0; > + const char *addr = NULL; > + > + int port; > + switch (dev->targetType) { > + case VIR_DOMAIN_CHR_TARGET_TYPE_GUESTFWD: > + addr = virSocketFormatAddr(dev->target.addr); > + port = virSocketGetPort(dev->target.addr); > + > + virBufferVSprintf(buf, "user,guestfwd=tcp:%s:%i-chardev:%s", > + addr, port, id); > + > + VIR_FREE(addr); > + break; > + } > + > + return ret; > +} > + > /* This function outputs an all-in-one character device command line option */ > static int qemudBuildCommandLineChrDevStr(virDomainChrDefPtr dev, > char *buf, > @@ -2156,6 +2180,46 @@ int qemudBuildCommandLine(virConnectPtr conn, > } > } > > + for (i = 0 ; i < def->nchannels ; i++) { > + virBuffer buf = VIR_BUFFER_INITIALIZER; > + const char *argStr; > + 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; > + } > + > + qemudBuildCommandLineChrDevChardevStr(channel, id, &buf); > + argStr = virBufferContentAndReset(&buf); > + if (argStr == NULL) > + goto error; > + > + ADD_ARG_LIT("-chardev"); > + ADD_ARG_LIT(argStr); > + > + VIR_FREE(argStr); > + > + qemudBuildCommandLineChrDevTargetStr(channel, id, &buf); > + argStr = virBufferContentAndReset(&buf); > + if (argStr == NULL) > + goto error; > + > + ADD_ARG_LIT("-net"); > + ADD_ARG_LIT(argStr); > + > + VIR_FREE(argStr); > + } > + } > + > 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"); > -- ACK Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://ovirt.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :| -- Libvir-list mailing list Libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list