[libvirt] [PATCH 00/10] Core support for SPICE configuration

This wires up the core support for SPICE configuration and QXL graphics devices. It also introduces a syntax for setting password expiry times for VNC/SPICE servers, although this isn't yet supported in upstream QEMU. Also not yet included here is seemless migration of SPICE clients, which is also pending QEMU work. docs/schemas/domain.rng | 66 ++++ src/conf/domain_conf.c | 248 +++++++++++++++- src/conf/domain_conf.h | 42 ++ src/esx/esx_vmx.c | 6 src/libvirt_private.syms | 4 src/opennebula/one_conf.c | 4 src/qemu/qemu.conf | 40 ++ src/qemu/qemu_conf.c | 192 +++++++++++- src/qemu/qemu_conf.h | 6 src/qemu/qemu_driver.c | 67 ++-- src/xen/xend_internal.c | 12 src/xen/xm_internal.c | 12 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args | 1 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml | 35 ++ tests/qemuxml2argvtest.c | 9 tests/qemuxml2xmltest.c | 1 16 files changed, 682 insertions(+), 63 deletions(-) Daniel

The comment for the <video> tag was a cut+paste error duplicating info about the <graphics> tag * docs/schemas/domain.rng: Fix comment for <video> tag --- docs/schemas/domain.rng | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index a934a77..aaf9667 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1149,10 +1149,8 @@ </element> </define> <!-- - A graphic description, currently in Xen only 2 types are supported: - - sdl with optional display, xauth and fullscreen - - vnc with a required port and optional listen IP address, password - and keymap + A video adapter description, allowing configuration of device + model, number of virtual heads, and video ram size --> <define name="video"> <element name="video"> -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
The comment for the <video> tag was a cut+paste error duplicating info about the <graphics> tag
* docs/schemas/domain.rng: Fix comment for <video> tag --- docs/schemas/domain.rng | 6 ++---- 1 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index a934a77..aaf9667 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1149,10 +1149,8 @@ </element> </define> <!-- - A graphic description, currently in Xen only 2 types are supported: - - sdl with optional display, xauth and fullscreen - - vnc with a required port and optional listen IP address, password - and keymap + A video adapter description, allowing configuration of device + model, number of virtual heads, and video ram size -->
ACK; practically falls under the trivial rule. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* src/qemu_conf.c: Add dummy entry in enumeration * docs/schemas/domain.rng: Add 'qxl' as a type for the <video> tag * src/domain_conf.c, src/domain_conf.h: Add QXL to video type enumerations --- docs/schemas/domain.rng | 1 + src/conf/domain_conf.c | 3 ++- src/conf/domain_conf.h | 1 + src/qemu/qemu_conf.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index aaf9667..c8beffc 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1163,6 +1163,7 @@ <value>vmvga</value> <value>xen</value> <value>vbox</value> + <value>qxl</value> </choice> </attribute> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index fe93711..8907563 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -243,7 +243,8 @@ VIR_ENUM_IMPL(virDomainVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "cirrus", "vmvga", "xen", - "vbox") + "vbox", + "qxl") VIR_ENUM_IMPL(virDomainInput, VIR_DOMAIN_INPUT_TYPE_LAST, "mouse", diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 5499f28..78f28d0 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -481,6 +481,7 @@ enum virDomainVideoType { VIR_DOMAIN_VIDEO_TYPE_VMVGA, VIR_DOMAIN_VIDEO_TYPE_XEN, VIR_DOMAIN_VIDEO_TYPE_VBOX, + VIR_DOMAIN_VIDEO_TYPE_QXL, VIR_DOMAIN_VIDEO_TYPE_LAST }; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b5c17b5..149dcee 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -92,7 +92,8 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "cirrus", "vmware", "", /* no arg needed for xen */ - "" /* don't support vbox */); + "", /* don't support vbox */ + "", /* Not implemented QXL yet */); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
* src/qemu_conf.c: Add dummy entry in enumeration * docs/schemas/domain.rng: Add 'qxl' as a type for the <video> tag * src/domain_conf.c, src/domain_conf.h: Add QXL to video type enumerations --- docs/schemas/domain.rng | 1 + src/conf/domain_conf.c | 3 ++- src/conf/domain_conf.h | 1 + src/qemu/qemu_conf.c | 3 ++- 4 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index aaf9667..c8beffc 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1163,6 +1163,7 @@ <value>vmvga</value> <value>xen</value> <value>vbox</value> + <value>qxl</value>
Conditional ACK - you also need to mention qxl in docs/formatdomain.html.in. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This adds an element <graphics type='spice' port='5903' tlsPort='5904' autoport='yes' listen='127.0.0.1'/> This is the bare minimum that should be exposed in the guest config for SPICE. Other parameters are better handled as per host level configuration tunables * docs/schemas/domain.rng: Define the SPICE <graphics> schema * src/domain_conf.h, src/domain_conf.c: Add parsing and formatting for SPICE graphics config * src/qemu_conf.c: Complain about unsupported graphics types --- docs/schemas/domain.rng | 38 ++++++++++++++++++++++ src/conf/domain_conf.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 9 +++++ src/qemu/qemu_conf.c | 2 +- 4 files changed, 127 insertions(+), 2 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index c8beffc..3163257 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1090,6 +1090,44 @@ </group> <group> <attribute name="type"> + <value>spice</value> + </attribute> + <optional> + <attribute name="port"> + <ref name="PortNumber"/> + </attribute> + </optional> + <optional> + <attribute name="tlsPort"> + <ref name="PortNumber"/> + </attribute> + </optional> + <optional> + <attribute name="autoport"> + <choice> + <value>yes</value> + <value>no</value> + </choice> + </attribute> + </optional> + <optional> + <attribute name="listen"> + <ref name="addrIP"/> + </attribute> + </optional> + <optional> + <attribute name="passwd"> + <text/> + </attribute> + </optional> + <optional> + <attribute name="keymap"> + <text/> + </attribute> + </optional> + </group> + <group> + <attribute name="type"> <value>rdp</value> </attribute> <optional> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 8907563..a3ea217 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -259,7 +259,8 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "sdl", "vnc", "rdp", - "desktop") + "desktop", + "spice") VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", @@ -446,6 +447,12 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP: VIR_FREE(def->data.desktop.display); break; + + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + VIR_FREE(def->data.spice.listenAddr); + VIR_FREE(def->data.spice.keymap); + VIR_FREE(def->data.spice.passwd); + break; } VIR_FREE(def); @@ -3202,6 +3209,50 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.desktop.fullscreen = 0; def->data.desktop.display = virXMLPropString(node, "display"); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + char *port = virXMLPropString(node, "port"); + char *tlsPort; + char *autoport; + + if (port) { + if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice port %s"), port); + VIR_FREE(port); + goto error; + } + VIR_FREE(port); + } else { + def->data.spice.port = 5900; + } + + tlsPort = virXMLPropString(node, "tlsPort"); + if (tlsPort) { + if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice tlsPort %s"), tlsPort); + VIR_FREE(tlsPort); + goto error; + } + VIR_FREE(tlsPort); + } else { + def->data.spice.tlsPort = 0; + } + + if ((autoport = virXMLPropString(node, "autoport")) != NULL) { + if (STREQ(autoport, "yes")) { + if (flags & VIR_DOMAIN_XML_INACTIVE) { + def->data.spice.port = 0; + def->data.spice.tlsPort = 0; + } + def->data.spice.autoport = 1; + } + VIR_FREE(autoport); + } + + def->data.spice.listenAddr = virXMLPropString(node, "listen"); + def->data.spice.passwd = virXMLPropString(node, "passwd"); + def->data.spice.keymap = virXMLPropString(node, "keymap"); } cleanup: @@ -6355,6 +6406,33 @@ virDomainGraphicsDefFormat(virBufferPtr buf, break; + case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: + if (def->data.spice.port) + virBufferVSprintf(buf, " port='%d'", + def->data.spice.port); + + if (def->data.spice.tlsPort) + virBufferVSprintf(buf, " tlsPort='%d'", + def->data.spice.tlsPort); + + virBufferVSprintf(buf, " autoport='%s'", + def->data.spice.autoport ? "yes" : "no"); + + if (def->data.spice.listenAddr) + virBufferVSprintf(buf, " listen='%s'", + def->data.spice.listenAddr); + + if (def->data.spice.keymap) + virBufferEscapeString(buf, " keymap='%s'", + def->data.spice.keymap); + + if (def->data.spice.passwd && + (flags & VIR_DOMAIN_XML_SECURE)) + virBufferEscapeString(buf, " passwd='%s'", + def->data.spice.passwd); + + break; + } virBufferAddLit(buf, "/>\n"); diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 78f28d0..0238f92 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -511,6 +511,7 @@ enum virDomainGraphicsType { VIR_DOMAIN_GRAPHICS_TYPE_VNC, VIR_DOMAIN_GRAPHICS_TYPE_RDP, VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE, VIR_DOMAIN_GRAPHICS_TYPE_LAST, }; @@ -543,6 +544,14 @@ struct _virDomainGraphicsDef { char *display; unsigned int fullscreen :1; } desktop; + struct { + int port; + int tlsPort; + char *listenAddr; + char *keymap; + char *passwd; + unsigned int autoport :1; + } spice; } data; }; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 149dcee..aa42e04 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4949,7 +4949,7 @@ int qemudBuildCommandLine(virConnectPtr conn, if (def->nvideos) { if (def->nvideos > 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("only one video card is currently supported")); goto error; } -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This adds an element
<graphics type='spice' port='5903' tlsPort='5904' autoport='yes' listen='127.0.0.1'/>
This is the bare minimum that should be exposed in the guest config for SPICE. Other parameters are better handled as per host level configuration tunables
* docs/schemas/domain.rng: Define the SPICE <graphics> schema * src/domain_conf.h, src/domain_conf.c: Add parsing and formatting for SPICE graphics config * src/qemu_conf.c: Complain about unsupported graphics types --- docs/schemas/domain.rng | 38 ++++++++++++++++++++++ src/conf/domain_conf.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 9 +++++ src/qemu/qemu_conf.c | 2 +- 4 files changed, 127 insertions(+), 2 deletions(-)
Also missing docs/formatdomain.html.in changes; but unlike patch 2/10 where the change was a trivial one-word addition, this needs a full paragraph, so I'd like to see this before giving an ack. Rearranging my review a bit...
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 78f28d0..0238f92 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -511,6 +511,7 @@ enum virDomainGraphicsType { VIR_DOMAIN_GRAPHICS_TYPE_VNC, VIR_DOMAIN_GRAPHICS_TYPE_RDP, VIR_DOMAIN_GRAPHICS_TYPE_DESKTOP, + VIR_DOMAIN_GRAPHICS_TYPE_SPICE,
VIR_DOMAIN_GRAPHICS_TYPE_LAST, }; @@ -543,6 +544,14 @@ struct _virDomainGraphicsDef { char *display; unsigned int fullscreen :1; } desktop; + struct { + int port; + int tlsPort;
Should these two be unsigned short, rather than int?
+ char *listenAddr; + char *keymap; + char *passwd; + unsigned int autoport :1;
Or, maybe keep port as int, and use a sentinel of -1 to mean autoport, rather than wasting another 4/8 bytes of struct space for one bit for autoport?
+ } spice; } data; };
diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index c8beffc..3163257 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1090,6 +1090,44 @@ </group> <group> <attribute name="type"> + <value>spice</value> + </attribute>
HALLELUJAH! The stupid Thunderbird bug that corrupted spacing in message replies prior to a word starting with <, >, or & appears to be fixed in the latest F13 build now! (.rng patches were so much harder for me to review when t-bird insisted on left-flushing the entire .rng chunk) :)
+ <optional> + <attribute name="listen"> + <ref name="addrIP"/>
addrIP is hard-coded to IPv4 at the moment; is this something that would need adjustment for IPv6?
@@ -3202,6 +3209,50 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.desktop.fullscreen = 0;
def->data.desktop.display = virXMLPropString(node, "display"); + } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + char *port = virXMLPropString(node, "port"); + char *tlsPort; + char *autoport; + + if (port) { + if (virStrToLong_i(port, NULL, 10, &def->data.spice.port) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice port %s"), port); + VIR_FREE(port); + goto error; + } + VIR_FREE(port); + } else { + def->data.spice.port = 5900; + }
Should we validate that def->data.spice.port < 64k? That is, port='100000' should be rejected.
+ + tlsPort = virXMLPropString(node, "tlsPort"); + if (tlsPort) { + if (virStrToLong_i(tlsPort, NULL, 10, &def->data.spice.tlsPort) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse spice tlsPort %s"), tlsPort); + VIR_FREE(tlsPort); + goto error; + } + VIR_FREE(tlsPort); + } else { + def->data.spice.tlsPort = 0; + }
Likewise for tlsPort.
+ def->data.spice.listenAddr = virXMLPropString(node, "listen"); ... + + if (def->data.spice.listenAddr) + virBufferVSprintf(buf, " listen='%s'", + def->data.spice.listenAddr);
Should we be storing listenAddr as a raw string, or should we be converting it to/from virSocketAddr? Conversion to virSocketAddr would also allow validation
diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 149dcee..aa42e04 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4949,7 +4949,7 @@ int qemudBuildCommandLine(virConnectPtr conn,
if (def->nvideos) { if (def->nvideos > 1) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", _("only one video card is currently supported"));
This seems like an independent change worth putting in at any time. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This supports the '-vga qxl' parameter in upstream QEMU/KVM which has SPICE support added. This isn't particularly useful until you get the next patch for -spice support. Also note that while the libvirt XML supports multiple video devices, this patch only supports a single one. A later patch can add support for 2nd, 3rd, etc PCI devices for QXL * src/qemu/qemu_conf.h: Flag for QXL support * src/qemu/qemu_conf.c: Probe for '-vga qxl' support and implement it * tests/qemuxml2argvtest.c, tests/qemuxml2xmltest.c, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Test case for generating spice args with RHEL6 kvm --- src/qemu/qemu_conf.c | 15 +++++++++- src/qemu/qemu_conf.h | 1 + .../qemuxml2argv-graphics-spice.args | 1 + .../qemuxml2argv-graphics-spice.xml | 27 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 4 +++ tests/qemuxml2xmltest.c | 1 + 6 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index aa42e04..88a330d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -93,7 +93,7 @@ VIR_ENUM_IMPL(qemuVideo, VIR_DOMAIN_VIDEO_TYPE_LAST, "vmware", "", /* no arg needed for xen */ "", /* don't support vbox */ - "", /* Not implemented QXL yet */); + "qxl"); int qemudLoadDriverConfig(struct qemud_driver *driver, const char *filename) { @@ -1202,8 +1202,12 @@ static unsigned long long qemudComputeCmdFlags(const char *help, if (strstr(help, "readonly=")) flags |= QEMUD_CMD_FLAG_DRIVE_READONLY; } - if (strstr(help, "-vga") && !strstr(help, "-std-vga")) + if (strstr(help, "-vga") && !strstr(help, "-std-vga")) { flags |= QEMUD_CMD_FLAG_VGA; + + if (strstr(help, "|qxl")) + flags |= QEMUD_CMD_FLAG_VGA_QXL; + } if (strstr(help, "boot=on")) flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; if (strstr(help, "serial=s")) @@ -4958,6 +4962,13 @@ int qemudBuildCommandLine(virConnectPtr conn, if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { /* nothing - vga has no effect on Xen pvfb */ } else { + if ((def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_QXL) && + !(qemuCmdFlags & QEMUD_CMD_FLAG_VGA_QXL)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("This QEMU does not support QXL graphics adapters")); + goto error; + } + const char *vgastr = qemuVideoTypeToString(def->videos[0]->type); if (!vgastr || STREQ(vgastr, "")) { qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index bbe6887..86f9c8c 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -97,6 +97,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NESTING = (1LL << 41), /* -enable-nesting (SVM/VMX) */ QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ QEMUD_CMD_FLAG_DRIVE_READONLY = (1LL << 43), /* -drive readonly=on|off */ + QEMUD_CMD_FLAG_VGA_QXL = (1LL << 44), /* The 'qxl' arg for '-vga' */ }; /* Main driver state */ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args new file mode 100644 index 0000000..94cd90e --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.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 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml new file mode 100644 index 0000000..031a622 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml @@ -0,0 +1,27 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>c7a5fdbd-edaf-9455-926a-d65c16db1809</uuid> + <memory>219200</memory> + <currentMemory>219200</currentMemory> + <vcpu>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'/> + <address type='drive' controller='0' bus='0' unit='0'/> + </disk> + <controller type='ide' index='0'/> + <video> + <model type='qxl' vram='65536' heads='1'/> + </video> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index f719f12..51d41be 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -323,6 +323,10 @@ mymain(int argc, char **argv) DO_TEST("graphics-sdl", 0, false); DO_TEST("graphics-sdl-fullscreen", 0, false); DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA, false); + DO_TEST("graphics-spice", + QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | + QEMUD_CMD_FLAG_DEVICE, false); + DO_TEST("input-usbmouse", 0, false); DO_TEST("input-usbtablet", 0, false); DO_TEST("input-xen", QEMUD_CMD_FLAG_DOMID, true); diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index cdc4390..326a1f1 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -149,6 +149,7 @@ mymain(int argc, char **argv) DO_TEST("graphics-vnc-tls"); DO_TEST("graphics-sdl"); DO_TEST("graphics-sdl-fullscreen"); + DO_TEST("graphics-spice"); DO_TEST("input-usbmouse"); DO_TEST("input-usbtablet"); DO_TEST("input-xen"); -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This supports the '-vga qxl' parameter in upstream QEMU/KVM which has SPICE support added. This isn't particularly useful until you get the next patch for -spice support. Also note that while the libvirt XML supports multiple video devices, this patch only supports a single one. A later patch can add support for 2nd, 3rd, etc PCI devices for QXL
* src/qemu/qemu_conf.h: Flag for QXL support * src/qemu/qemu_conf.c: Probe for '-vga qxl' support and implement it * tests/qemuxml2argvtest.c, tests/qemuxml2xmltest.c, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args, tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Test case for generating spice args with RHEL6 kvm
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This supports the -spice argument posted for review against the latest upstream QEMU/KVM. This supports the bare minimum config with port, TLS port & listen address. The x509 bits are added in a later patch. * src/qemu_conf.c, src/qemu_conf.h: Add SPICE flag. Check for -spice availability. Format -spice arg for command line * qemuhelptest.c: Add SPICE flag * qemuxml2argvdata/qemuxml2argv-graphics-spice.args: Add <graphics> for spice * qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Add -spice arg * qemuxml2argvtest.c: Add SPICE flag --- src/qemu/qemu_conf.c | 37 ++++++++++++++++++++ src/qemu/qemu_conf.h | 1 + .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 2 + tests/qemuxml2argvtest.c | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 88a330d..b551b8d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -1208,6 +1208,8 @@ static unsigned long long qemudComputeCmdFlags(const char *help, if (strstr(help, "|qxl")) flags |= QEMUD_CMD_FLAG_VGA_QXL; } + if (strstr(help, "-spice")) + flags |= QEMUD_CMD_FLAG_SPICE; if (strstr(help, "boot=on")) flags |= QEMUD_CMD_FLAG_DRIVE_BOOT; if (strstr(help, "serial=s")) @@ -4944,6 +4946,41 @@ int qemudBuildCommandLine(virConnectPtr conn, if (qemuCmdFlags & QEMUD_CMD_FLAG_SDL) ADD_ARG_LIT("-sdl"); + } else if ((def->ngraphics == 1) && + def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + virBuffer opt = VIR_BUFFER_INITIALIZER; + char *optstr; + + if (!(qemuCmdFlags & QEMUD_CMD_FLAG_SPICE)) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("spice graphics are not supported with this QEMU")); + goto error; + } + + virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); + + if (def->graphics[0]->data.spice.tlsPort) + virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); + + if (def->graphics[0]->data.spice.listenAddr) + virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); + + if (virBufferError(&opt)) + goto no_memory; + + optstr = virBufferContentAndReset(&opt); + + ADD_ARG_LIT("-spice"); + ADD_ARG(optstr); + if (def->graphics[0]->data.spice.keymap) { + ADD_ARG_LIT("-k"); + ADD_ARG_LIT(def->graphics[0]->data.spice.keymap); + } + /* SPICE includes native support for tunnelling audio, so we + * set the audio backend to point at SPICE's own driver + */ + ADD_ENV_LIT("QEMU_AUDIO_DRV=spice"); + } else if ((def->ngraphics == 1)) { qemuReportError(VIR_ERR_INTERNAL_ERROR, _("unsupported graphics type '%s'"), diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 86f9c8c..6cfbfe0 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -98,6 +98,7 @@ enum qemud_cmd_flags { QEMUD_CMD_FLAG_NAME_PROCESS = (1LL << 42), /* Is -name process= available */ QEMUD_CMD_FLAG_DRIVE_READONLY = (1LL << 43), /* -drive readonly=on|off */ QEMUD_CMD_FLAG_VGA_QXL = (1LL << 44), /* The 'qxl' arg for '-vga' */ + QEMUD_CMD_FLAG_SPICE = (1LL << 45), /* Is -spice avail */ }; /* Main driver state */ diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 94cd90e..8d195e5 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test /usr/bin/qemu -S -M pc -m 214 -smp 1 -nographic -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1 -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 031a622..08dfb26 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml @@ -20,6 +20,8 @@ <address type='drive' controller='0' bus='0' unit='0'/> </disk> <controller type='ide' index='0'/> + <input type='mouse' bus='ps2'/> + <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'/> <video> <model type='qxl' vram='65536' heads='1'/> </video> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 51d41be..c89f249 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -325,7 +325,7 @@ mymain(int argc, char **argv) DO_TEST("nographics-vga", QEMUD_CMD_FLAG_VGA, false); DO_TEST("graphics-spice", QEMUD_CMD_FLAG_VGA | QEMUD_CMD_FLAG_VGA_QXL | - QEMUD_CMD_FLAG_DEVICE, false); + QEMUD_CMD_FLAG_DEVICE | QEMUD_CMD_FLAG_SPICE, false); DO_TEST("input-usbmouse", 0, false); DO_TEST("input-usbtablet", 0, false); -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This supports the -spice argument posted for review against the latest upstream QEMU/KVM. This supports the bare minimum config with port, TLS port & listen address. The x509 bits are added in a later patch.
* src/qemu_conf.c, src/qemu_conf.h: Add SPICE flag. Check for -spice availability. Format -spice arg for command line * qemuhelptest.c: Add SPICE flag * qemuxml2argvdata/qemuxml2argv-graphics-spice.args: Add <graphics> for spice * qemuxml2argvdata/qemuxml2argv-graphics-spice.xml: Add -spice arg * qemuxml2argvtest.c: Add SPICE flag --- src/qemu/qemu_conf.c | 37 ++++++++++++++++++++ src/qemu/qemu_conf.h | 1 + .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 2 + tests/qemuxml2argvtest.c | 2 +- 5 files changed, 42 insertions(+), 2 deletions(-)
+ + if (def->graphics[0]->data.spice.listenAddr) + virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr);
This may be impacted if you take my comments in 3/10 about using virSocketAddr. Other than that, ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

* src/qemu/qemu_driver.c: Allocate the TCP ports for SPICE before starting guest --- src/qemu/qemu_driver.c | 38 ++++++++++++++++++++++++++------------ 1 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index a7cce6a..fbb0a68 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2949,10 +2949,12 @@ qemuInitPCIAddresses(struct qemud_driver *driver, return ret; } -static int qemudNextFreeVNCPort(struct qemud_driver *driver) { + +static int qemudNextFreePort(struct qemud_driver *driver, + int startPort) { int i; - for (i = QEMU_VNC_PORT_MIN; i < QEMU_VNC_PORT_MAX; i++) { + for (i = startPort ; i < 65535 ; i++) { int fd; int reuse = 1; struct sockaddr_in addr; @@ -3885,17 +3887,29 @@ static int qemudStartVMDaemon(virConnectPtr conn, DEBUG0("Ensuring no historical cgroup is lying around"); qemuRemoveCgroup(driver, vm, 1); - if ((vm->def->ngraphics == 1) && - vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - vm->def->graphics[0]->data.vnc.autoport) { - DEBUG0("Determining VNC port"); - int port = qemudNextFreeVNCPort(driver); - if (port < 0) { - qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to find an unused VNC port")); - goto cleanup; + if (vm->def->ngraphics == 1) { + if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && + vm->def->graphics[0]->data.vnc.autoport) { + int port = qemudNextFreePort(driver, 5900); + if (port < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused VNC port")); + goto cleanup; + } + vm->def->graphics[0]->data.vnc.port = port; + } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && + vm->def->graphics[0]->data.spice.autoport) { + int port = qemudNextFreePort(driver, 5900); + int tlsPort = port == -1 ? -1 : qemudNextFreePort(driver, port + 1); + if (port < 0 || tlsPort < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find unused SPICE ports")); + goto cleanup; + } + + vm->def->graphics[0]->data.spice.port = port; + vm->def->graphics[0]->data.spice.tlsPort = tlsPort; } - vm->def->graphics[0]->data.vnc.port = port; } if (virFileMakePath(driver->logDir) != 0) { -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
* src/qemu/qemu_driver.c: Allocate the TCP ports for SPICE before starting guest --- src/qemu/qemu_driver.c | 38 ++++++++++++++++++++++++++------------ 1 files changed, 26 insertions(+), 12 deletions(-)
+ } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && + vm->def->graphics[0]->data.spice.autoport) { + int port = qemudNextFreePort(driver, 5900); + int tlsPort = port == -1 ? -1 : qemudNextFreePort(driver, port + 1); + if (port < 0 || tlsPort < 0) {
This could be simplified to 'if (tlsPort < 0)', but no big deal. ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

In common with VNC, the QEMU driver configuration file is used specify the host level TLS certificate location and a default password / listen address * src/qemu/qemu.conf: Add spice_listen, spice_tls, spice_tls_x509_cert_dir & spice_password config params * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Parsing of spice config parameters and updating -spice arg generation to use them * tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args, tests/qemuxml2argvtest.c: Expand test case to cover driver level configuration --- src/qemu/qemu.conf | 40 +++++++++++++ src/qemu/qemu_conf.c | 62 +++++++++++++++++++- src/qemu/qemu_conf.h | 4 + src/qemu/qemu_driver.c | 15 ++++- .../qemuxml2argv-graphics-spice.args | 2 +- tests/qemuxml2argvtest.c | 5 ++ 6 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index e2c581e..f4f965e 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -80,6 +80,46 @@ +# SPICE is configured to listen on 127.0.0.1 by default. +# To make it listen on all public interfaces, uncomment +# this next option. +# +# NB, strong recommendation to enable TLS + x509 certificate +# verification when allowing public access +# +# spice_listen = "0.0.0.0" + + +# Enable use of TLS encryption on the SPICE server. +# +# It is necessary to setup CA and issue a server certificate +# before enabling this. +# +# spice_tls = 1 + + +# Use of TLS requires that x509 certificates be issued. The +# default it to keep them in /etc/pki/libvirt-spice. This directory +# must contain +# +# ca-cert.pem - the CA master certificate +# server-cert.pem - the server certificate signed with ca-cert.pem +# server-key.pem - the server private key +# +# This option allows the certificate directory to be changed +# +# spice_tls_x509_cert_dir = "/etc/pki/libvirt-spice" + + +# The default SPICE password. This parameter is only used if the +# per-domain XML config does not already provide a password. To +# allow access without passwords, leave this commented out. An +# empty string will still enable passwords, but be rejected by +# QEMU effectively preventing any use of SPICE. Obviously change +# this example here before you set this +# +# spice_password = "XYZ12345" + # The default security driver is SELinux. If SELinux is disabled # on the host, then the security driver will automatically disable diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index b551b8d..c76893c 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -116,6 +116,15 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, return -1; } + if (!(driver->spiceListen = strdup("127.0.0.1"))) { + virReportOOMError(); + return -1; + } + if (!(driver->spiceTLSx509certdir = strdup(SYSCONF_DIR "/pki/libvirt-spice"))) { + virReportOOMError(); + return -1; + } + #if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R /* For privileged driver, try and find hugepage mount automatically. * Non-privileged driver requires admin to create a dir for the @@ -219,6 +228,43 @@ int qemudLoadDriverConfig(struct qemud_driver *driver, } } + p = virConfGetValue (conf, "spice_tls"); + CHECK_TYPE ("spice_tls", VIR_CONF_LONG); + if (p) driver->spiceTLS = p->l; + + p = virConfGetValue (conf, "spice_tls_x509_cert_dir"); + CHECK_TYPE ("spice_tls_x509_cert_dir", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spiceTLSx509certdir); + if (!(driver->spiceTLSx509certdir = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + + p = virConfGetValue (conf, "spice_listen"); + CHECK_TYPE ("spice_listen", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spiceListen); + if (!(driver->spiceListen = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + + p = virConfGetValue (conf, "spice_password"); + CHECK_TYPE ("spice_password", VIR_CONF_STRING); + if (p && p->str) { + VIR_FREE(driver->spicePassword); + if (!(driver->spicePassword = strdup(p->str))) { + virReportOOMError(); + virConfFree(conf); + return -1; + } + } + p = virConfGetValue (conf, "user"); CHECK_TYPE ("user", VIR_CONF_STRING); if (!(user = strdup(p && p->str ? p->str : QEMU_USER))) { @@ -4959,11 +5005,25 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, "port=%u", def->graphics[0]->data.spice.port); - if (def->graphics[0]->data.spice.tlsPort) + if (driver->spiceTLS && def->graphics[0]->data.spice.tlsPort != -1) virBufferVSprintf(&opt, ",tls-port=%u", def->graphics[0]->data.spice.tlsPort); if (def->graphics[0]->data.spice.listenAddr) virBufferVSprintf(&opt, ",addr=%s", def->graphics[0]->data.spice.listenAddr); + else if (driver->spiceListen) + virBufferVSprintf(&opt, ",addr=%s", driver->spiceListen); + + /* In the password case we set it via monitor command, to avoid + * making it visible on CLI, so there's no use of password=XXX + * in this bit of the code */ + if (!def->graphics[0]->data.spice.passwd && + !driver->spicePassword) + virBufferAddLit(&opt, ",disable-ticketing"); + + if (driver->spiceTLS) + virBufferVSprintf(&opt, ",x509-dir=%s", + driver->spiceTLSx509certdir); + if (virBufferError(&opt)) goto no_memory; diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index 6cfbfe0..7df5794 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -140,6 +140,10 @@ struct qemud_driver { char *vncListen; char *vncPassword; char *vncSASLdir; + unsigned int spiceTLS : 1; + char *spiceTLSx509certdir; + char *spiceListen; + char *spicePassword; char *hugetlbfs_mount; char *hugepage_path; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index fbb0a68..5854c44 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3900,13 +3900,22 @@ static int qemudStartVMDaemon(virConnectPtr conn, } else if (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE && vm->def->graphics[0]->data.spice.autoport) { int port = qemudNextFreePort(driver, 5900); - int tlsPort = port == -1 ? -1 : qemudNextFreePort(driver, port + 1); - if (port < 0 || tlsPort < 0) { + int tlsPort = -1; + if (port < 0) { qemuReportError(VIR_ERR_INTERNAL_ERROR, - "%s", _("Unable to find unused SPICE ports")); + "%s", _("Unable to find an unused SPICE port")); goto cleanup; } + if (driver->spiceTLS) { + tlsPort = qemudNextFreePort(driver, port + 1); + if (tlsPort < 0) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("Unable to find an unused SPICE TLS port")); + goto cleanup; + } + } + vm->def->graphics[0]->data.spice.port = port; vm->def->graphics[0]->data.spice.tlsPort = tlsPort; } diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 8d195e5..e412fdb 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1 -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index c89f249..9a6f78a 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -214,6 +214,11 @@ mymain(int argc, char **argv) return EXIT_FAILURE; if ((driver.hugepage_path = strdup("/dev/hugepages/libvirt/qemu")) == NULL) return EXIT_FAILURE; + driver.spiceTLS = 1; + if (!(driver.spiceTLSx509certdir = strdup("/etc/pki/libvirt-spice"))) + return EXIT_FAILURE; + if (!(driver.spicePassword = strdup("123456"))) + return EXIT_FAILURE; # define DO_TEST_FULL(name, extraFlags, migrateFrom, expectError) \ do { \ -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
In common with VNC, the QEMU driver configuration file is used specify the host level TLS certificate location and a default password / listen address
* src/qemu/qemu.conf: Add spice_listen, spice_tls, spice_tls_x509_cert_dir & spice_password config params * src/qemu/qemu_conf.c, src/qemu/qemu_conf.h: Parsing of spice config parameters and updating -spice arg generation to use them * tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-rhel6.args, tests/qemuxml2argvtest.c: Expand test case to cover driver level configuration --- src/qemu/qemu.conf | 40 +++++++++++++ src/qemu/qemu_conf.c | 62 +++++++++++++++++++- src/qemu/qemu_conf.h | 4 + src/qemu/qemu_driver.c | 15 ++++- .../qemuxml2argv-graphics-spice.args | 2 +- tests/qemuxml2argvtest.c | 5 ++ 6 files changed, 123 insertions(+), 5 deletions(-)
index 6cfbfe0..7df5794 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -140,6 +140,10 @@ struct qemud_driver { char *vncListen; char *vncPassword; char *vncSASLdir; + unsigned int spiceTLS : 1; + char *spiceTLSx509certdir; + char *spiceListen;
Should this be virSocketAddr rather than a raw string? Other than that, ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This extends the XML syntax for <graphics> to allow a password expiry time to be set eg <graphics type='vnc' port='5900' autoport='yes' keymap='en-us' passwd='12345' passwdValidTo='2010-04-09T15:51:00'/> The timestamp is in UTC. * src/conf/domain_conf.h: Pull passwd out into separate struct virDomainGraphicsAuthDef to allow sharing between VNC & SPICE * src/conf/domain_conf.c: Add parsing/formatting of new passwdValidTo argument * src/opennebula/one_conf.c, src/qemu/qemu_conf.c, src/qemu/qemu_driver.c, src/xen/xend_internal.c, src/xen/xm_internal.c: Update for changed struct containing VNC password --- src/conf/domain_conf.c | 102 +++++++++++++++++++++++++++++++++++++++----- src/conf/domain_conf.h | 13 +++++- src/esx/esx_vmx.c | 6 +- src/opennebula/one_conf.c | 4 +- src/qemu/qemu_conf.c | 4 +- src/qemu/qemu_driver.c | 20 ++++---- src/xen/xend_internal.c | 12 +++--- src/xen/xm_internal.c | 12 +++--- 8 files changed, 130 insertions(+), 43 deletions(-) diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index a3ea217..04829e9 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -423,6 +423,17 @@ virDomainObjPtr virDomainFindByName(const virDomainObjListPtr doms, #endif /* !PROXY */ +static void +virDomainGraphicsAuthDefClear(virDomainGraphicsAuthDefPtr def) +{ + if (!def) + return; + + VIR_FREE(def->passwd); + + /* Don't free def */ +} + void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) { if (!def) @@ -432,7 +443,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_VNC: VIR_FREE(def->data.vnc.listenAddr); VIR_FREE(def->data.vnc.keymap); - VIR_FREE(def->data.vnc.passwd); + virDomainGraphicsAuthDefClear(&def->data.vnc.auth); break; case VIR_DOMAIN_GRAPHICS_TYPE_SDL: @@ -451,7 +462,7 @@ void virDomainGraphicsDefFree(virDomainGraphicsDefPtr def) case VIR_DOMAIN_GRAPHICS_TYPE_SPICE: VIR_FREE(def->data.spice.listenAddr); VIR_FREE(def->data.spice.keymap); - VIR_FREE(def->data.spice.passwd); + virDomainGraphicsAuthDefClear(&def->data.spice.auth); break; } @@ -3068,6 +3079,56 @@ error: goto cleanup; } + +static int +virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def) +{ + char *validTo = NULL; + + def->passwd = virXMLPropString(node, "passwd"); + + if (!def->passwd) + return 0; + + validTo = virXMLPropString(node, "passwdValidTo"); + if (validTo) { + char *tmp; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ + if (/* year */ + virStrToLong_i(validTo, &tmp, 10, &tm.tm_year) < 0 || *tmp != '-' || + /* month */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mon) < 0 || *tmp != '-' || + /* day */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mday) < 0 || *tmp != 'T' || + /* hour */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_hour) < 0 || *tmp != ':' || + /* minute */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_min) < 0 || *tmp != ':' || + /* second */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_sec) < 0 || *tmp != '\0') { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse password validity time '%s', expect YYYY-MM-DDTHH:MM:SS"), + validTo); + VIR_FREE(validTo); + VIR_FREE(def->passwd); + return -1; + } + VIR_FREE(validTo); + + tm.tm_year -= 1900; /* Human epoch starts at 0 BC, not 1900BC */ + tm.tm_mon--; /* Humans start months at 1, computers at 0 */ + + /* XXX this is broken it needs to be UTC not localtime */ + def->validTo = timegm(&tm); + def->expires = 1; + } + + return 0; +} + + /* Parse the XML definition for a graphics device */ static virDomainGraphicsDefPtr virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { @@ -3126,8 +3187,10 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.vnc.listenAddr = virXMLPropString(node, "listen"); - def->data.vnc.passwd = virXMLPropString(node, "passwd"); def->data.vnc.keymap = virXMLPropString(node, "keymap"); + + if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) + goto error; } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SDL) { char *fullscreen = virXMLPropString(node, "fullscreen"); @@ -3251,8 +3314,9 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { } def->data.spice.listenAddr = virXMLPropString(node, "listen"); - def->data.spice.passwd = virXMLPropString(node, "passwd"); def->data.spice.keymap = virXMLPropString(node, "keymap"); + if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) + goto error; } cleanup: @@ -6320,6 +6384,24 @@ virDomainTimerDefFormat(virBufferPtr buf, return 0; } +static void +virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, + virDomainGraphicsAuthDefPtr def) +{ + if (!def->passwd) + return; + + virBufferEscapeString(buf, " passwd='%s'", + def->passwd); + if (def->expires) { + char strbuf[100]; + struct tm tmbuf, *tm; + tm = gmtime_r(&def->validTo, &tmbuf); + strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm); + virBufferVSprintf(buf, " passwdValidTo='%s'", strbuf); + } +} + static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, @@ -6355,10 +6437,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.vnc.keymap); - if (def->data.vnc.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.vnc.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth); break; @@ -6426,10 +6506,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.spice.keymap); - if (def->data.spice.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.spice.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.spice.auth); break; diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 0238f92..34a3904 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -516,6 +516,15 @@ enum virDomainGraphicsType { VIR_DOMAIN_GRAPHICS_TYPE_LAST, }; +typedef struct _virDomainGraphicsAuthDef virDomainGraphicsAuthDef; +typedef virDomainGraphicsAuthDef *virDomainGraphicsAuthDefPtr; +struct _virDomainGraphicsAuthDef { + char *passwd; + unsigned int expires: 1; /* Whether there is an expiry time set */ + time_t validTo; /* seconds since epoch */ +}; + + typedef struct _virDomainGraphicsDef virDomainGraphicsDef; typedef virDomainGraphicsDef *virDomainGraphicsDefPtr; struct _virDomainGraphicsDef { @@ -526,7 +535,7 @@ struct _virDomainGraphicsDef { unsigned int autoport :1; char *listenAddr; char *keymap; - char *passwd; + virDomainGraphicsAuthDef auth; } vnc; struct { char *display; @@ -549,7 +558,7 @@ struct _virDomainGraphicsDef { int tlsPort; char *listenAddr; char *keymap; - char *passwd; + virDomainGraphicsAuthDef auth; unsigned int autoport :1; } spice; } data; diff --git a/src/esx/esx_vmx.c b/src/esx/esx_vmx.c index 0a26614..fcaf5e9 100644 --- a/src/esx/esx_vmx.c +++ b/src/esx/esx_vmx.c @@ -1431,7 +1431,7 @@ esxVMX_ParseVNC(virConfPtr conf, virDomainGraphicsDefPtr *def) esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.keymap", &(*def)->data.vnc.keymap, true) < 0 || esxUtil_GetConfigString(conf, "RemoteDisplay.vnc.password", - &(*def)->data.vnc.passwd, true) < 0) { + &(*def)->data.vnc.auth.passwd, true) < 0) { goto failure; } @@ -2831,9 +2831,9 @@ esxVMX_FormatVNC(virDomainGraphicsDefPtr def, virBufferPtr buffer) def->data.vnc.keymap); } - if (def->data.vnc.passwd != NULL) { + if (def->data.vnc.auth.passwd != NULL) { virBufferVSprintf(buffer, "RemoteDisplay.vnc.password = \"%s\"\n", - def->data.vnc.passwd); + def->data.vnc.auth.passwd); } return 0; diff --git a/src/opennebula/one_conf.c b/src/opennebula/one_conf.c index 2079c51..0b0a08a 100644 --- a/src/opennebula/one_conf.c +++ b/src/opennebula/one_conf.c @@ -262,9 +262,9 @@ char* xmlOneTemplate(virDomainDefPtr def) virBufferVSprintf(&buf,",\n port = \"%d\"", def->graphics[i]->data.vnc.port); - if (def->graphics[i]->data.vnc.passwd != NULL) + if (def->graphics[i]->data.vnc.auth.passwd != NULL) virBufferVSprintf(&buf,",\n passwd = \"%s\"", - def->graphics[i]->data.vnc.passwd); + def->graphics[i]->data.vnc.auth.passwd); virBufferAddLit(&buf," ]\n"); diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index c76893c..f0b4296 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -4906,7 +4906,7 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, ":%d", def->graphics[0]->data.vnc.port - 5900); - if (def->graphics[0]->data.vnc.passwd || + if (def->graphics[0]->data.vnc.auth.passwd || driver->vncPassword) virBufferAddLit(&opt, ",password"); @@ -5016,7 +5016,7 @@ int qemudBuildCommandLine(virConnectPtr conn, /* In the password case we set it via monitor command, to avoid * making it visible on CLI, so there's no use of password=XXX * in this bit of the code */ - if (!def->graphics[0]->data.spice.passwd && + if (!def->graphics[0]->data.spice.auth.passwd && !driver->spicePassword) virBufferAddLit(&opt, ",disable-ticketing"); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 5854c44..24e2367 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -2515,12 +2515,12 @@ qemuInitPasswords(virConnectPtr conn, if ((vm->def->ngraphics == 1) && vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_VNC && - (vm->def->graphics[0]->data.vnc.passwd || driver->vncPassword)) { + (vm->def->graphics[0]->data.vnc.auth.passwd || driver->vncPassword)) { qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetVNCPassword(priv->mon, - vm->def->graphics[0]->data.vnc.passwd ? - vm->def->graphics[0]->data.vnc.passwd : + vm->def->graphics[0]->data.vnc.auth.passwd ? + vm->def->graphics[0]->data.vnc.auth.passwd : driver->vncPassword); qemuDomainObjExitMonitorWithDriver(driver, vm); } @@ -8829,19 +8829,19 @@ qemuDomainChangeGraphics(struct qemud_driver *driver, return -1; } - if (STRNEQ_NULLABLE(olddev->data.vnc.passwd, dev->data.vnc.passwd)) { - VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.passwd, driver->vncPassword); + if (STRNEQ_NULLABLE(olddev->data.vnc.auth.passwd, dev->data.vnc.auth.passwd)) { + VIR_DEBUG("Updating password on VNC server %p %p", dev->data.vnc.auth.passwd, driver->vncPassword); qemuDomainObjEnterMonitorWithDriver(driver, vm); ret = qemuMonitorSetVNCPassword(priv->mon, - dev->data.vnc.passwd ? - dev->data.vnc.passwd : + dev->data.vnc.auth.passwd ? + dev->data.vnc.auth.passwd : driver->vncPassword); qemuDomainObjExitMonitorWithDriver(driver, vm); /* Steal the new dev's char * reference */ - VIR_FREE(olddev->data.vnc.passwd); - olddev->data.vnc.passwd = dev->data.vnc.passwd; - dev->data.vnc.passwd = NULL; + VIR_FREE(olddev->data.vnc.auth.passwd); + olddev->data.vnc.auth.passwd = dev->data.vnc.auth.passwd; + dev->data.vnc.auth.passwd = NULL; } else { ret = 0; } diff --git a/src/xen/xend_internal.c b/src/xen/xend_internal.c index 614c036..ee7f0e1 100644 --- a/src/xen/xend_internal.c +++ b/src/xen/xend_internal.c @@ -1808,7 +1808,7 @@ xenDaemonParseSxprGraphicsOld(virConnectPtr conn, goto no_memory; if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) + !(graphics->data.vnc.auth.passwd = strdup(vncPasswd))) goto no_memory; if (keymap && @@ -1930,7 +1930,7 @@ xenDaemonParseSxprGraphicsNew(virConnectPtr conn, goto no_memory; if (vncPasswd && - !(graphics->data.vnc.passwd = strdup(vncPasswd))) + !(graphics->data.vnc.auth.passwd = strdup(vncPasswd))) goto no_memory; if (keymap && @@ -5251,8 +5251,8 @@ xenDaemonFormatSxprGraphicsNew(virDomainGraphicsDefPtr def, if (def->data.vnc.listenAddr) virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.auth.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd); if (def->data.vnc.keymap) virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); } @@ -5294,8 +5294,8 @@ xenDaemonFormatSxprGraphicsOld(virDomainGraphicsDefPtr def, if (def->data.vnc.listenAddr) virBufferVSprintf(buf, "(vnclisten '%s')", def->data.vnc.listenAddr); - if (def->data.vnc.passwd) - virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.passwd); + if (def->data.vnc.auth.passwd) + virBufferVSprintf(buf, "(vncpasswd '%s')", def->data.vnc.auth.passwd); if (def->data.vnc.keymap) virBufferVSprintf(buf, "(keymap '%s')", def->data.vnc.keymap); diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 6c5df0f..4dd0641 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -1304,7 +1304,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { } if (xenXMConfigCopyStringOpt(conf, "vnclisten", &graphics->data.vnc.listenAddr) < 0) goto cleanup; - if (xenXMConfigCopyStringOpt(conf, "vncpasswd", &graphics->data.vnc.passwd) < 0) + if (xenXMConfigCopyStringOpt(conf, "vncpasswd", &graphics->data.vnc.auth.passwd) < 0) goto cleanup; if (xenXMConfigCopyStringOpt(conf, "keymap", &graphics->data.vnc.keymap) < 0) goto cleanup; @@ -1376,7 +1376,7 @@ xenXMDomainConfigParse(virConnectPtr conn, virConfPtr conf) { if (!(graphics->data.vnc.listenAddr = strdup(key + 10))) goto no_memory; } else if (STRPREFIX(key, "vncpasswd=")) { - if (!(graphics->data.vnc.passwd = strdup(key + 10))) + if (!(graphics->data.vnc.auth.passwd = strdup(key + 10))) goto no_memory; } else if (STRPREFIX(key, "keymap=")) { if (!(graphics->data.vnc.keymap = strdup(key + 7))) @@ -2538,9 +2538,9 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, xenXMConfigSetString(conf, "vnclisten", def->graphics[0]->data.vnc.listenAddr) < 0) goto no_memory; - if (def->graphics[0]->data.vnc.passwd && + if (def->graphics[0]->data.vnc.auth.passwd && xenXMConfigSetString(conf, "vncpasswd", - def->graphics[0]->data.vnc.passwd) < 0) + def->graphics[0]->data.vnc.auth.passwd) < 0) goto no_memory; if (def->graphics[0]->data.vnc.keymap && xenXMConfigSetString(conf, "keymap", @@ -2569,9 +2569,9 @@ virConfPtr xenXMDomainConfigFormat(virConnectPtr conn, if (def->graphics[0]->data.vnc.listenAddr) virBufferVSprintf(&buf, ",vnclisten=%s", def->graphics[0]->data.vnc.listenAddr); - if (def->graphics[0]->data.vnc.passwd) + if (def->graphics[0]->data.vnc.auth.passwd) virBufferVSprintf(&buf, ",vncpasswd=%s", - def->graphics[0]->data.vnc.passwd); + def->graphics[0]->data.vnc.auth.passwd); if (def->graphics[0]->data.vnc.keymap) virBufferVSprintf(&buf, ",keymap=%s", def->graphics[0]->data.vnc.keymap); -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This extends the XML syntax for <graphics> to allow a password expiry time to be set
eg
<graphics type='vnc' port='5900' autoport='yes' keymap='en-us' passwd='12345' passwdValidTo='2010-04-09T15:51:00'/>
The timestamp is in UTC.
* src/conf/domain_conf.h: Pull passwd out into separate struct virDomainGraphicsAuthDef to allow sharing between VNC & SPICE * src/conf/domain_conf.c: Add parsing/formatting of new passwdValidTo argument * src/opennebula/one_conf.c, src/qemu/qemu_conf.c, src/qemu/qemu_driver.c, src/xen/xend_internal.c, src/xen/xm_internal.c: Update for changed struct containing VNC password --- src/conf/domain_conf.c | 102 +++++++++++++++++++++++++++++++++++++++----- src/conf/domain_conf.h | 13 +++++- src/esx/esx_vmx.c | 6 +- src/opennebula/one_conf.c | 4 +- src/qemu/qemu_conf.c | 4 +- src/qemu/qemu_driver.c | 20 ++++---- src/xen/xend_internal.c | 12 +++--- src/xen/xm_internal.c | 12 +++--- 8 files changed, 130 insertions(+), 43 deletions(-)
Where's the changes to docs/schemas/domain.rng and docs/formatdomain.html.in? Is passwdValidTo any better off as seconds since the Epoch (date +%s) rather than an ISO time (date +%FT%T)? It boils down to a question of which format is easier for machines to handle. Or maybe we should support both formats, as it's pretty easy to tell them apart?
@@ -3068,6 +3079,56 @@ error: goto cleanup; }
+ +static int +virDomainGraphicsAuthDefParseXML(xmlNodePtr node, virDomainGraphicsAuthDefPtr def) +{ + char *validTo = NULL; + + def->passwd = virXMLPropString(node, "passwd"); + + if (!def->passwd) + return 0; + + validTo = virXMLPropString(node, "passwdValidTo"); + if (validTo) { + char *tmp; + struct tm tm; + memset(&tm, 0, sizeof(tm)); + /* Expect: YYYY-MM-DDTHH:MM:SS (%d-%d-%dT%d:%d:%d) eg 2010-11-28T14:29:01 */ + if (/* year */ + virStrToLong_i(validTo, &tmp, 10, &tm.tm_year) < 0 || *tmp != '-' || + /* month */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mon) < 0 || *tmp != '-' || + /* day */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_mday) < 0 || *tmp != 'T' || + /* hour */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_hour) < 0 || *tmp != ':' || + /* minute */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_min) < 0 || *tmp != ':' || + /* second */ + virStrToLong_i(tmp+1, &tmp, 10, &tm.tm_sec) < 0 || *tmp != '\0') { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("cannot parse password validity time '%s', expect YYYY-MM-DDTHH:MM:SS"), + validTo); + VIR_FREE(validTo); + VIR_FREE(def->passwd); + return -1; + } + VIR_FREE(validTo); + + tm.tm_year -= 1900; /* Human epoch starts at 0 BC, not 1900BC */ + tm.tm_mon--; /* Humans start months at 1, computers at 0 */ + + /* XXX this is broken it needs to be UTC not localtime */ + def->validTo = timegm(&tm);
Is that XXX comment still correct, or are we using UTC time by virtue of the timegm() call?
+static void +virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, + virDomainGraphicsAuthDefPtr def) +{ + if (!def->passwd) + return; + + virBufferEscapeString(buf, " passwd='%s'", + def->passwd);
Should this depend on whether VIR_DOMAIN_XML_SECURE is in effect...
+ if (def->expires) { + char strbuf[100]; + struct tm tmbuf, *tm; + tm = gmtime_r(&def->validTo, &tmbuf); + strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm); + virBufferVSprintf(buf, " passwdValidTo='%s'", strbuf); + } +} + static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, @@ -6355,10 +6437,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.vnc.keymap);
- if (def->data.vnc.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.vnc.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth);
rather than here, since it makes sense to include password expiry in the XML even if the password itself is not included? -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

On Tue, Nov 02, 2010 at 05:12:43PM -0600, Eric Blake wrote:
On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This extends the XML syntax for <graphics> to allow a password expiry time to be set
eg
<graphics type='vnc' port='5900' autoport='yes' keymap='en-us' passwd='12345' passwdValidTo='2010-04-09T15:51:00'/>
The timestamp is in UTC.
* src/conf/domain_conf.h: Pull passwd out into separate struct virDomainGraphicsAuthDef to allow sharing between VNC & SPICE * src/conf/domain_conf.c: Add parsing/formatting of new passwdValidTo argument * src/opennebula/one_conf.c, src/qemu/qemu_conf.c, src/qemu/qemu_driver.c, src/xen/xend_internal.c, src/xen/xm_internal.c: Update for changed struct containing VNC password --- src/conf/domain_conf.c | 102 +++++++++++++++++++++++++++++++++++++++----- src/conf/domain_conf.h | 13 +++++- src/esx/esx_vmx.c | 6 +- src/opennebula/one_conf.c | 4 +- src/qemu/qemu_conf.c | 4 +- src/qemu/qemu_driver.c | 20 ++++---- src/xen/xend_internal.c | 12 +++--- src/xen/xm_internal.c | 12 +++--- 8 files changed, 130 insertions(+), 43 deletions(-)
Where's the changes to docs/schemas/domain.rng and docs/formatdomain.html.in?
Is passwdValidTo any better off as seconds since the Epoch (date +%s) rather than an ISO time (date +%FT%T)? It boils down to a question of which format is easier for machines to handle. Or maybe we should support both formats, as it's pretty easy to tell them apart?
I wanted it to be clear that this is an absolute time, not relative to the time you passed in the XML, so I decided that the ISO style time was better.
+ VIR_FREE(validTo); + + tm.tm_year -= 1900; /* Human epoch starts at 0 BC, not 1900BC */ + tm.tm_mon--; /* Humans start months at 1, computers at 0 */ + + /* XXX this is broken it needs to be UTC not localtime */ + def->validTo = timegm(&tm);
Is that XXX comment still correct, or are we using UTC time by virtue of the timegm() call?
No, that's an old comment.
+static void +virDomainGraphicsAuthDefFormatAttr(virBufferPtr buf, + virDomainGraphicsAuthDefPtr def) +{ + if (!def->passwd) + return; + + virBufferEscapeString(buf, " passwd='%s'", + def->passwd);
Should this depend on whether VIR_DOMAIN_XML_SECURE is in effect...
+ if (def->expires) { + char strbuf[100]; + struct tm tmbuf, *tm; + tm = gmtime_r(&def->validTo, &tmbuf); + strftime(strbuf, sizeof(strbuf), "%Y-%m-%dT%H:%M:%S", tm); + virBufferVSprintf(buf, " passwdValidTo='%s'", strbuf); + } +} + static int virDomainGraphicsDefFormat(virBufferPtr buf, virDomainGraphicsDefPtr def, @@ -6355,10 +6437,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, virBufferEscapeString(buf, " keymap='%s'", def->data.vnc.keymap);
- if (def->data.vnc.passwd && - (flags & VIR_DOMAIN_XML_SECURE)) - virBufferEscapeString(buf, " passwd='%s'", - def->data.vnc.passwd); + if (flags & VIR_DOMAIN_XML_SECURE) + virDomainGraphicsAuthDefFormatAttr(buf, &def->data.vnc.auth);
rather than here, since it makes sense to include password expiry in the XML even if the password itself is not included?
I guess that's reasonable Daniel -- |: Red Hat, Engineering, London -o- http://people.redhat.com/berrange/ :| |: http://libvirt.org -o- http://virt-manager.org -o- http://deltacloud.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: GnuPG: 7D3B9505 -o- F3C9 553F A1DA 4AC2 5648 23C1 B3DF F742 7D3B 9505 :|

QEMU crashes & burns if you try multiple Cirrus video cards, but QXL copes fine. Adapt QEMU config code to allow multiple QXL video cards * src/qemu/qemu_conf.c: Support multiple QXL video cards --- src/qemu/qemu_conf.c | 64 +++++++++++++++++-- .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 3 + 3 files changed, 61 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index f0b4296..5b50b27 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -3249,6 +3249,36 @@ error: } +static char * +qemuBuildVideoDevStr(virDomainVideoDefPtr video) +{ + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *model = qemuVideoTypeToString(video->type); + + if (!model) { + qemuReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("invalid video model")); + goto error; + } + + virBufferVSprintf(&buf, "%s", model); + virBufferVSprintf(&buf, ",id=%s", video->info.alias); + if (qemuBuildDeviceAddressStr(&buf, &video->info) < 0) + goto error; + + if (virBufferError(&buf)) { + virReportOOMError(); + goto error; + } + + return virBufferContentAndReset(&buf); + +error: + virBufferFreeAndReset(&buf); + return NULL; +} + + int qemudOpenPCIConfig(virDomainHostdevDefPtr dev) { @@ -5048,13 +5078,7 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } - if (def->nvideos) { - if (def->nvideos > 1) { - qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, - "%s", _("only one video card is currently supported")); - goto error; - } - + if (def->nvideos > 0) { if (qemuCmdFlags & QEMUD_CMD_FLAG_VGA) { if (def->videos[0]->type == VIR_DOMAIN_VIDEO_TYPE_XEN) { /* nothing - vga has no effect on Xen pvfb */ @@ -5100,6 +5124,32 @@ int qemudBuildCommandLine(virConnectPtr conn, goto error; } } + + if (def->nvideos > 1) { + if (qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) { + for (i = 1 ; i < def->nvideos ; i++) { + char *str; + if (def->videos[i]->type != VIR_DOMAIN_VIDEO_TYPE_QXL) { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("video type %s is only valid as primary video card"), + virDomainVideoTypeToString(def->videos[0]->type)); + goto error; + } + + ADD_ARG_LIT("-device"); + + if (!(str = qemuBuildVideoDevStr(def->videos[i]))) + goto error; + + ADD_ARG(str); + } + } else { + qemuReportError(VIR_ERR_CONFIG_UNSUPPORTED, + "%s", _("only one video card is currently supported")); + goto error; + } + } + } else { /* If we have -device, then we set -nodefault already */ if (!(qemuCmdFlags & QEMUD_CMD_FLAG_DEVICE) && diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index e412fdb..44809b0 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 08dfb26..6fe9a60 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml @@ -25,5 +25,8 @@ <video> <model type='qxl' vram='65536' heads='1'/> </video> + <video> + <model type='qxl' vram='65536' heads='1'/> + </video> </devices> </domain> -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
QEMU crashes & burns if you try multiple Cirrus video cards, but QXL copes fine. Adapt QEMU config code to allow multiple QXL video cards
* src/qemu/qemu_conf.c: Support multiple QXL video cards --- src/qemu/qemu_conf.c | 64 +++++++++++++++++-- .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 3 + 3 files changed, 61 insertions(+), 8 deletions(-)
ACK. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org

This extends the SPICE XML to allow channel security options <graphics type='spice' port='5901' tlsPort='-1' autoport='yes'> <channel name='main' mode='secure'/> <channel name='record' mode='insecure'/> </graphics> Any non-specified channel uses the default, which allows both secure & insecure usage * src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add XML syntax for specifying per channel security options for spice;. * src/qemu/qemu_conf.c: Configure channel security with spice --- docs/schemas/domain.rng | 21 ++++++ src/conf/domain_conf.c | 75 +++++++++++++++++++- src/conf/domain_conf.h | 21 ++++++ src/libvirt_private.syms | 4 + src/qemu/qemu_conf.c | 13 ++++ .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 5 +- 7 files changed, 138 insertions(+), 3 deletions(-) diff --git a/docs/schemas/domain.rng b/docs/schemas/domain.rng index 3163257..b79aafd 100644 --- a/docs/schemas/domain.rng +++ b/docs/schemas/domain.rng @@ -1087,6 +1087,27 @@ <text/> </attribute> </optional> + <zeroOrMore> + <element name="channel"> + <attribute name="name"> + <choice> + <value>main</value> + <value>display</value> + <value>inputs</value> + <value>cursor</value> + <value>playback</value> + <value>record</value> + </choice> + </attribute> + <attribute name="mode"> + <choice> + <value>any</value> + <value>secure</value> + <value>insecure</value> + </choice> + </attribute> + </element> + </zeroOrMore> </group> <group> <attribute name="type"> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 04829e9..3655209 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -262,6 +262,21 @@ VIR_ENUM_IMPL(virDomainGraphics, VIR_DOMAIN_GRAPHICS_TYPE_LAST, "desktop", "spice") +VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelName, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST, + "main", + "display", + "inputs", + "cursor", + "playback", + "record"); + +VIR_ENUM_IMPL(virDomainGraphicsSpiceChannelMode, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST, + "any", + "secure", + "insecure"); + VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", "capabilities") @@ -3273,6 +3288,7 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.desktop.display = virXMLPropString(node, "display"); } else if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + xmlNodePtr cur; char *port = virXMLPropString(node, "port"); char *tlsPort; char *autoport; @@ -3317,6 +3333,40 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, int flags) { def->data.spice.keymap = virXMLPropString(node, "keymap"); if (virDomainGraphicsAuthDefParseXML(node, &def->data.vnc.auth) < 0) goto error; + + cur = node->children; + while (cur != NULL) { + if (cur->type == XML_ELEMENT_NODE) { + if (xmlStrEqual(cur->name, BAD_CAST "channel")) { + const char *name, *mode; + int nameval, modeval; + name = virXMLPropString(cur, "name"); + mode = virXMLPropString(cur, "mode"); + + if (!name || !mode) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("spice channel missing name/mode")); + goto error; + } + + if ((nameval = virDomainGraphicsSpiceChannelNameTypeFromString(name)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown spice channel name %s"), + name); + goto error; + } + if ((modeval = virDomainGraphicsSpiceChannelModeTypeFromString(mode)) < 0) { + virDomainReportError(VIR_ERR_INTERNAL_ERROR, + _("unknown spice channel mode %s"), + mode); + goto error; + } + + def->data.spice.channels[nameval] = modeval; + } + } + cur = cur->next; + } } cleanup: @@ -6408,6 +6458,8 @@ virDomainGraphicsDefFormat(virBufferPtr buf, int flags) { const char *type = virDomainGraphicsTypeToString(def->type); + int children = 0; + int i; if (!type) { virDomainReportError(VIR_ERR_INTERNAL_ERROR, @@ -6513,7 +6565,28 @@ virDomainGraphicsDefFormat(virBufferPtr buf, } - virBufferAddLit(buf, "/>\n"); + if (def->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) { + for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = def->data.spice.channels[i]; + if (mode == VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY) + continue; + + if (!children) { + virBufferAddLit(buf, ">\n"); + children = 1; + } + + virBufferVSprintf(buf, " <channel name='%s' mode='%s'/>\n", + virDomainGraphicsSpiceChannelNameTypeToString(i), + virDomainGraphicsSpiceChannelModeTypeToString(mode)); + } + } + + if (children) { + virBufferAddLit(buf, " </graphics>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } return 0; } diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 34a3904..3062122 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -524,6 +524,24 @@ struct _virDomainGraphicsAuthDef { time_t validTo; /* seconds since epoch */ }; +enum virDomainGraphicsSpiceChannelName { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MAIN, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_DISPLAY, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_INPUT, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_CURSOR, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_PLAYBACK, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_RECORD, + + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST +}; + +enum virDomainGraphicsSpiceChannelMode { + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_ANY, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE, + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE, + + VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_LAST +}; typedef struct _virDomainGraphicsDef virDomainGraphicsDef; typedef virDomainGraphicsDef *virDomainGraphicsDefPtr; @@ -560,6 +578,7 @@ struct _virDomainGraphicsDef { char *keymap; virDomainGraphicsAuthDef auth; unsigned int autoport :1; + int channels[VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST]; } spice; } data; }; @@ -1222,6 +1241,8 @@ VIR_ENUM_DECL(virDomainHostdevSubsys) VIR_ENUM_DECL(virDomainInput) VIR_ENUM_DECL(virDomainInputBus) VIR_ENUM_DECL(virDomainGraphics) +VIR_ENUM_DECL(virDomainGraphicsSpiceChannelName) +VIR_ENUM_DECL(virDomainGraphicsSpiceChannelMode) /* from libvirt.h */ VIR_ENUM_DECL(virDomainState) VIR_ENUM_DECL(virDomainSeclabel) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index cf64bd3..c23c342 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -204,6 +204,10 @@ virDomainFindByName; virDomainFindByUUID; virDomainGetRootFilesystem; virDomainGraphicsDefFree; +virDomainGraphicsSpiceChannelNameTypeFromString; +virDomainGraphicsSpiceChannelNameTypeToString; +virDomainGraphicsSpiceChannelModeTypeFromString; +virDomainGraphicsSpiceChannelModeTypeToString; virDomainGraphicsTypeFromString; virDomainGraphicsTypeToString; virDomainHostdevDefFree; diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 5b50b27..3e9f21d 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -5054,6 +5054,19 @@ int qemudBuildCommandLine(virConnectPtr conn, virBufferVSprintf(&opt, ",x509-dir=%s", driver->spiceTLSx509certdir); + for (i = 0 ; i < VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_LAST ; i++) { + int mode = def->graphics[0]->data.spice.channels[i]; + switch (mode) { + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_SECURE: + virBufferVSprintf(&opt, ",tls-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + case VIR_DOMAIN_GRAPHICS_SPICE_CHANNEL_MODE_INSECURE: + virBufferVSprintf(&opt, ",plaintext-channel=%s", + virDomainGraphicsSpiceChannelNameTypeToString(i)); + break; + } + } if (virBufferError(&opt)) goto no_memory; diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args index 44809b0..87b8c06 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.args @@ -1 +1 @@ -LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 +LC_ALL=C PATH=/bin HOME=/home/test USER=test LOGNAME=test QEMU_AUDIO_DRV=spice /usr/bin/qemu -S -M pc -m 214 -smp 1 -nodefaults -monitor unix:/tmp/test-monitor,server,nowait -no-acpi -boot c -hda /dev/HostVG/QEMUGuest1 -usb -spice port=5903,tls-port=5904,addr=127.0.0.1,x509-dir=/etc/pki/libvirt-spice,tls-channel=main,plaintext-channel=inputs -vga qxl -device qxl,id=video1,bus=pci.0,addr=0x4 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml index 6fe9a60..bdce04b 100644 --- a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice.xml @@ -21,7 +21,10 @@ </disk> <controller type='ide' index='0'/> <input type='mouse' bus='ps2'/> - <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'/> + <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'> + <channel name='main' mode='secure'/> + <channel name='inputs' mode='insecure'/> + </graphics> <video> <model type='qxl' vram='65536' heads='1'/> </video> -- 1.7.2.3

On 11/01/2010 12:17 PM, Daniel P. Berrange wrote:
This extends the SPICE XML to allow channel security options
<graphics type='spice' port='5901' tlsPort='-1' autoport='yes'> <channel name='main' mode='secure'/> <channel name='record' mode='insecure'/> </graphics>
Any non-specified channel uses the default, which allows both secure & insecure usage
* src/conf/domain_conf.c, src/conf/domain_conf.h, src/libvirt_private.syms: Add XML syntax for specifying per channel security options for spice;.
s/;//
* src/qemu/qemu_conf.c: Configure channel security with spice --- docs/schemas/domain.rng | 21 ++++++ src/conf/domain_conf.c | 75 +++++++++++++++++++- src/conf/domain_conf.h | 21 ++++++ src/libvirt_private.syms | 4 + src/qemu/qemu_conf.c | 13 ++++ .../qemuxml2argv-graphics-spice.args | 2 +- .../qemuxml2argv-graphics-spice.xml | 5 +- 7 files changed, 138 insertions(+), 3 deletions(-)
Again, we need something in docs/formatdomain.html.in.
--- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -204,6 +204,10 @@ virDomainFindByName; virDomainFindByUUID; virDomainGetRootFilesystem; virDomainGraphicsDefFree; +virDomainGraphicsSpiceChannelNameTypeFromString; +virDomainGraphicsSpiceChannelNameTypeToString; +virDomainGraphicsSpiceChannelModeTypeFromString; +virDomainGraphicsSpiceChannelModeTypeToString;
Swap lines to alphabetical order. -- Eric Blake eblake@redhat.com +1-801-349-2682 Libvirt virtualization library http://libvirt.org
participants (2)
-
Daniel P. Berrange
-
Eric Blake