conf: add qemu-vdagent channel

Add the ability to configure a qemu-vdagent in guest domains. This
device is similar to the spice vdagent channel except that qemu handles
the spice-vdagent protocol messages itself rather than routing them over
a spice protocol channel.

The qemu-vdagent device has two notable configuration options which
determine whether qemu will handle particular vdagent features:
'clipboard' and 'mouse'.

The 'clipboard' option allows qemu to synchronize its internal clipboard
manager with the guest clipboard, which enables client<->guest clipboard
synchronization for non-spice guests such as vnc.

The 'mouse' option allows absolute mouse positioning to be sent over the
vdagent channel rather than using a usb or virtio tablet device.

Sample configuration:
  <channel type='qemu-vdagent'>
    <target type='virtio' name='com.redhat.spice.0'/>
    <source>
      <clipboard copypaste='yes'/>
      <mouse mode='client'/>
    </source>
  </channel>

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Jonathon Jongsma 2022-03-18 15:51:39 -05:00
parent f135fdabab
commit 05b09f039e
12 changed files with 140 additions and 28 deletions

View File

@ -6608,6 +6608,29 @@ types have different ``target`` attributes.
``name='com.redhat.spice.0'``. The optional ``address`` element can tie the
channel to a particular ``type='virtio-serial'`` controller. :since:`Since
0.8.8`
``qemu-vdagent``
Paravirtualized qemu vdagent channel. This channel implements the SPICE
vdagent protocol, but is handled internally by qemu and therefore does not
require a SPICE graphics device. Like the spicevmc channel, the ``target``
element must be present, with attribute ``type='virtio'``; an optional
attribute ``name`` controls how the guest will have access to the channel,
and defaults to ``name='com.redhat.spice.0'``. The optional ``address``
element can tie the channel to a particular ``type='virtio-serial'``
controller. Certain vdagent protocol features can by enabled or disabled
using the ``source`` element.
Copy & Paste functionality is set by the ``clipboard`` element. It is
disabled by default, and can be enabled by setting the ``copypaste``
property to ``yes``. This allows the guest's clipboard to be synchronized
with the qemu clipboard manager. This can enable copy and paste between a
guest and a client when using a VNC `graphics device <#elementsGraphics>`__
(when using a VNC client that supports the copy/paste feature) or other
graphics types that support the qemu clipboard manager.
Mouse mode is set by the ``mouse`` element, setting its ``mode`` attribute
to one of ``server`` or ``client``. If no mode is specified, the qemu
default will be used (client mode).
:since:`Since 8.2.0`
:anchor:`<a id="elementsCharHostInterface"/>`

View File

@ -715,6 +715,7 @@ VIR_ENUM_IMPL(virDomainChr,
"spicevmc",
"spiceport",
"nmdm",
"qemu-vdagent",
);
VIR_ENUM_IMPL(virDomainChrTcpProtocol,
@ -2702,6 +2703,7 @@ virDomainChrSourceDefGetPath(virDomainChrSourceDef *chr)
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
return NULL;
}
@ -2811,6 +2813,11 @@ virDomainChrSourceDefCopy(virDomainChrSourceDef *dest,
dest->data.spiceport.channel = g_strdup(src->data.spiceport.channel);
break;
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
dest->data.qemuVdagent.clipboard = src->data.qemuVdagent.clipboard;
dest->data.qemuVdagent.mouse = src->data.qemuVdagent.mouse;
break;
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_STDIO:
@ -2892,6 +2899,10 @@ virDomainChrSourceDefIsEqual(const virDomainChrSourceDef *src,
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
return src->data.spicevmc == tgt->data.spicevmc;
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
return src->data.qemuVdagent.clipboard == tgt->data.qemuVdagent.clipboard &&
src->data.qemuVdagent.mouse == tgt->data.qemuVdagent.mouse;
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_STDIO:
@ -11220,6 +11231,33 @@ virDomainChrSourceDefParseLog(virDomainChrSourceDef *def,
}
static int
virDomainChrSourceDefParseQemuVdagent(virDomainChrSourceDef *def,
xmlNodePtr source,
xmlXPathContextPtr ctxt)
{
xmlNodePtr cur;
VIR_XPATH_NODE_AUTORESTORE(ctxt)
ctxt->node = source;
if ((cur = virXPathNode("./clipboard", ctxt))) {
if (virXMLPropTristateBool(cur, "copypaste",
VIR_XML_PROP_REQUIRED,
&def->data.qemuVdagent.clipboard) < 0)
return -1;
}
if ((cur = virXPathNode("./mouse", ctxt))) {
if (virXMLPropEnum(cur, "mode",
virDomainMouseModeTypeFromString,
VIR_XML_PROP_REQUIRED | VIR_XML_PROP_NONZERO,
&def->data.qemuVdagent.mouse) < 0)
return -1;
}
return 0;
}
/* Parse the source half of the XML definition for a character device,
* where node is the first element of node->children of the parent
* element. def->type must already be valid.
@ -11301,6 +11339,12 @@ virDomainChrSourceDefParseXML(virDomainChrSourceDef *def,
def->data.nmdm.slave = virXMLPropString(sources[0], "slave");
break;
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
if (virDomainChrSourceDefParseQemuVdagent(def, sources[0], ctxt) < 0)
goto error;
break;
case VIR_DOMAIN_CHR_TYPE_LAST:
case VIR_DOMAIN_CHR_TYPE_NULL:
case VIR_DOMAIN_CHR_TYPE_VC:
@ -24996,6 +25040,22 @@ virDomainChrSourceDefFormat(virBuffer *buf,
/* nada */
break;
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
if (def->data.qemuVdagent.mouse != VIR_DOMAIN_MOUSE_MODE_DEFAULT ||
def->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT) {
virBufferAddLit(buf, "<source>\n");
virBufferAdjustIndent(buf, 2);
if (def->data.qemuVdagent.clipboard != VIR_TRISTATE_BOOL_ABSENT)
virBufferEscapeString(buf, "<clipboard copypaste='%s'/>\n",
virTristateBoolTypeToString(def->data.qemuVdagent.clipboard));
if (def->data.qemuVdagent.mouse != VIR_DOMAIN_MOUSE_MODE_DEFAULT)
virBufferEscapeString(buf, "<mouse mode='%s'/>\n",
virDomainMouseModeTypeToString(def->data.qemuVdagent.mouse));
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</source>\n");
}
break;
case VIR_DOMAIN_CHR_TYPE_PTY:
case VIR_DOMAIN_CHR_TYPE_DEV:
case VIR_DOMAIN_CHR_TYPE_FILE:
@ -25081,7 +25141,6 @@ virDomainChrSourceDefFormat(virBuffer *buf,
virBufferEscapeString(buf, "<source channel='%s'/>\n",
def->data.spiceport.channel);
break;
}
if (def->logfile) {
@ -25211,7 +25270,6 @@ virDomainChrTargetDefFormat(virBuffer *buf,
return 0;
}
static int
virDomainChrDefFormat(virBuffer *buf,
virDomainChrDef *def,

View File

@ -1241,6 +1241,7 @@ typedef enum {
VIR_DOMAIN_CHR_TYPE_SPICEVMC,
VIR_DOMAIN_CHR_TYPE_SPICEPORT,
VIR_DOMAIN_CHR_TYPE_NMDM,
VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT,
VIR_DOMAIN_CHR_TYPE_LAST
} virDomainChrType;
@ -1268,6 +1269,13 @@ struct _virDomainChrSourceReconnectDef {
unsigned int timeout;
};
typedef enum {
VIR_DOMAIN_MOUSE_MODE_DEFAULT = 0,
VIR_DOMAIN_MOUSE_MODE_SERVER,
VIR_DOMAIN_MOUSE_MODE_CLIENT,
VIR_DOMAIN_MOUSE_MODE_LAST
} virDomainMouseMode;
/* The host side information for a character device. */
struct _virDomainChrSourceDef {
@ -1309,6 +1317,10 @@ struct _virDomainChrSourceDef {
struct {
char *channel;
} spiceport;
struct {
virDomainMouseMode mouse;
virTristateBool clipboard;
} qemuVdagent;
} data;
char *logfile;
virTristateSwitch logappend;
@ -1845,14 +1857,6 @@ typedef enum {
VIR_DOMAIN_GRAPHICS_SPICE_ZLIB_COMPRESSION_LAST
} virDomainGraphicsSpiceZlibCompression;
typedef enum {
VIR_DOMAIN_MOUSE_MODE_DEFAULT = 0,
VIR_DOMAIN_MOUSE_MODE_SERVER,
VIR_DOMAIN_MOUSE_MODE_CLIENT,
VIR_DOMAIN_MOUSE_MODE_LAST
} virDomainMouseMode;
typedef enum {
VIR_DOMAIN_GRAPHICS_SPICE_STREAMING_MODE_DEFAULT = 0,
VIR_DOMAIN_GRAPHICS_SPICE_STREAMING_MODE_FILTER,

View File

@ -920,6 +920,7 @@ virDomainChrSourceDefValidate(const virDomainChrSourceDef *src_def,
case VIR_DOMAIN_CHR_TYPE_VC:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;

View File

@ -4009,23 +4009,10 @@
</element>
</optional>
<optional>
<element name="clipboard">
<attribute name="copypaste">
<ref name="virYesNo"/>
</attribute>
<empty/>
</element>
<ref name="clipboard"/>
</optional>
<optional>
<element name="mouse">
<attribute name="mode">
<choice>
<value>server</value>
<value>client</value>
</choice>
</attribute>
<empty/>
</element>
<ref name="mousemode"/>
</optional>
<optional>
<element name="filetransfer">
@ -4114,6 +4101,25 @@
</element>
</define>
<define name="clipboard">
<element name="clipboard">
<attribute name="copypaste">
<ref name="virYesNo"/>
</attribute>
<empty/>
</element>
</define>
<define name="mousemode">
<element name="mouse">
<attribute name="mode">
<choice>
<value>server</value>
<value>client</value>
</choice>
</attribute>
<empty/>
</element>
</define>
<define name="listenElements">
<zeroOrMore>
<element name="listen">
@ -4514,6 +4520,7 @@
<value>spicevmc</value>
<value>spiceport</value>
<value>nmdm</value>
<value>qemu-vdagent</value>
</choice>
</define>
@ -4599,9 +4606,17 @@
<optional>
<ref name="reconnect"/>
</optional>
<zeroOrMore>
<ref name="devSeclabel"/>
</zeroOrMore>
<interleave>
<zeroOrMore>
<ref name="devSeclabel"/>
</zeroOrMore>
<optional>
<ref name="clipboard"/>
</optional>
<optional>
<ref name="mousemode"/>
</optional>
</interleave>
</element>
</zeroOrMore>
<optional>

View File

@ -1423,6 +1423,7 @@ qemuBuildChardevStr(const virDomainChrSourceDef *dev,
break;
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
default:
break;
@ -1506,6 +1507,7 @@ qemuBuildChardevCommand(virCommand *cmd,
case VIR_DOMAIN_CHR_TYPE_UDP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
break;
case VIR_DOMAIN_CHR_TYPE_NMDM:
@ -8641,6 +8643,7 @@ qemuInterfaceVhostuserConnect(virCommand *cmd,
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("vhost-user type '%s' not supported"),

View File

@ -6824,6 +6824,7 @@ qemuMonitorJSONAttachCharDevGetProps(const char *chrID,
case VIR_DOMAIN_CHR_TYPE_PIPE:
case VIR_DOMAIN_CHR_TYPE_STDIO:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
virReportError(VIR_ERR_OPERATION_FAILED,
_("Hotplug unsupported for char device type '%s'"),
virDomainChrTypeToString(chr->type));

View File

@ -6826,6 +6826,7 @@ qemuProcessPrepareHostBackendChardevOne(virDomainDeviceDef *dev,
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
break;
case VIR_DOMAIN_CHR_TYPE_FILE: {

View File

@ -1997,6 +1997,7 @@ qemuValidateDomainChrSourceDef(const virDomainChrSourceDef *def,
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;
}

View File

@ -1021,6 +1021,7 @@ AppArmorSetChardevLabel(virSecurityManager *mgr,
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
ret = 0;
break;
@ -1083,6 +1084,7 @@ AppArmorSetNetdevLabel(virSecurityManager *mgr,
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
ret = 0;
break;

View File

@ -1555,6 +1555,7 @@ virSecurityDACSetChardevLabelHelper(virSecurityManager *mgr,
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;
}
@ -1639,6 +1640,7 @@ virSecurityDACRestoreChardevLabelHelper(virSecurityManager *mgr,
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_NMDM:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
case VIR_DOMAIN_CHR_TYPE_LAST:
break;
}

View File

@ -1056,6 +1056,7 @@ testQemuPrepareHostBackendChardevOne(virDomainDeviceDef *dev,
case VIR_DOMAIN_CHR_TYPE_TCP:
case VIR_DOMAIN_CHR_TYPE_SPICEVMC:
case VIR_DOMAIN_CHR_TYPE_SPICEPORT:
case VIR_DOMAIN_CHR_TYPE_QEMU_VDAGENT:
break;
case VIR_DOMAIN_CHR_TYPE_FILE: