[libvirt] [PATCH v2 0/4] qemu SPICE migration

With the latest qemu, we are a step closer to seamless migration. However, libvirt needs to lend a helping hand. Michal Privoznik (4): config: Introduce <migration> for SPICE graphics qemu: Implement new seamless attribute qemu: Create SPICE migration test qemu: wait for SPICE to migrate docs/formatdomain.html.in | 10 ++++ docs/schemas/domaincommon.rng | 11 ++++ src/conf/domain_conf.c | 31 +++++++++++- src/conf/domain_conf.h | 10 ++++ src/libvirt_private.syms | 2 + src/qemu/qemu_capabilities.c | 3 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 14 +++++ src/qemu/qemu_migration.c | 38 +++++++++++++-- src/qemu/qemu_monitor.c | 22 ++++++++ src/qemu/qemu_monitor.h | 3 + src/qemu/qemu_monitor_json.c | 52 ++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 + .../qemuxml2argv-graphics-spice-migration.args | 7 +++ .../qemuxml2argv-graphics-spice-migration.xml | 36 ++++++++++++++ tests/qemuxml2argvtest.c | 4 ++ 16 files changed, 242 insertions(+), 5 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.xml -- 1.7.8.6

With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default. --- docs/formatdomain.html.in | 10 ++++++++++ docs/schemas/domaincommon.rng | 11 +++++++++++ src/conf/domain_conf.c | 31 ++++++++++++++++++++++++++++++- src/conf/domain_conf.h | 10 ++++++++++ src/libvirt_private.syms | 2 ++ 5 files changed, 63 insertions(+), 1 deletions(-) diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index c05f3df..8b87055 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -3216,6 +3216,7 @@ qemu-kvm -net nic,model=? /dev/null <streaming mode='filter'/> <clipboard copypaste='no'/> <mouse mode='client'/> + <migration seamless='on'/> </graphics></pre> <p> Spice supports variable compression settings for audio, @@ -3255,6 +3256,15 @@ qemu-kvm -net nic,model=? /dev/null <span class="since">since 0.9.11</span>. If no mode is specified, the qemu default will be used (client mode). </p> + <p> + SPICE migration behaviour can be controlled via + <code>migration</code> element (<span class="since">since + 0.9.11</span>). Notably, <code>seamless</code> + attribute turns on or off seamless migration. Accepted + values are <code>on</code> or <code>off</code>. If you + don't specify anything libvirt turns on this feature for + old enough qemu. + </p> </dd> <dt><code>"rdp"</code></dt> <dd> diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index d70f0c2..09e31eb 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -2061,6 +2061,17 @@ <empty/> </element> </optional> + <optional> + <element name="migration"> + <attribute name="seamless"> + <choice> + <value>on</value> + <value>off</value> + </choice> + </attribute> + <empty/> + </element> + </optional> </interleave> </group> <group> diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 75756c4..35a3ee8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -500,6 +500,11 @@ VIR_ENUM_IMPL(virDomainGraphicsSpiceClipboardCopypaste, "default", "yes", "no"); +VIR_ENUM_IMPL(virDomainGraphicsSpiceMigrationSeamless, + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_LAST, + "default", + "on", + "off"); VIR_ENUM_IMPL(virDomainHostdevMode, VIR_DOMAIN_HOSTDEV_MODE_LAST, "subsystem", @@ -6652,6 +6657,27 @@ virDomainGraphicsDefParseXML(xmlNodePtr node, VIR_FREE(mode); def->data.spice.mousemode = modeVal; + } else if (xmlStrEqual(cur->name, BAD_CAST "migration")) { + char *seamless = virXMLPropString(cur, "seamless"); + int seamlessVal; + + if (!seamless) { + virReportError(VIR_ERR_XML_ERROR, "%s", + _("spice migration missing seamless")); + goto error; + } + + seamlessVal = virDomainGraphicsSpiceMigrationSeamlessTypeFromString(seamless); + if (seamlessVal <= 0) { + virReportError(VIR_ERR_XML_ERROR, + _("unknown seamless migration value '%s'"), + seamless); + VIR_FREE(seamless); + goto error; + } + VIR_FREE(seamless); + + def->data.spice.seamless = seamlessVal; } } cur = cur->next; @@ -13249,7 +13275,7 @@ virDomainGraphicsDefFormat(virBufferPtr buf, if (!children && (def->data.spice.image || def->data.spice.jpeg || def->data.spice.zlib || def->data.spice.playback || def->data.spice.streaming || def->data.spice.copypaste || - def->data.spice.mousemode)) { + def->data.spice.mousemode || def->data.spice.seamless)) { virBufferAddLit(buf, ">\n"); children = 1; } @@ -13274,6 +13300,9 @@ virDomainGraphicsDefFormat(virBufferPtr buf, if (def->data.spice.copypaste) virBufferAsprintf(buf, " <clipboard copypaste='%s'/>\n", virDomainGraphicsSpiceClipboardCopypasteTypeToString(def->data.spice.copypaste)); + if (def->data.spice.seamless) + virBufferAsprintf(buf, " <migration seamless='%s'/>\n", + virDomainGraphicsSpiceMigrationSeamlessTypeToString(def->data.spice.seamless)); } if (children) { diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index b6fb0db..8e61378 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1213,6 +1213,14 @@ enum virDomainGraphicsSpiceClipboardCopypaste { VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_LAST }; +enum virDomainGraphicsSpiceMigrationSeamless { + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_DEFAULT = 0, + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_ON, + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_OFF, + + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_LAST +}; + enum virDomainGraphicsListenType { VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE = 0, VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS, @@ -1280,6 +1288,7 @@ struct _virDomainGraphicsDef { int playback; int streaming; int copypaste; + int seamless; } spice; } data; /* nListens, listens, and *port are only useful if type is vnc, @@ -2198,6 +2207,7 @@ VIR_ENUM_DECL(virDomainGraphicsSpicePlaybackCompression) VIR_ENUM_DECL(virDomainGraphicsSpiceStreamingMode) VIR_ENUM_DECL(virDomainGraphicsSpiceClipboardCopypaste) VIR_ENUM_DECL(virDomainGraphicsSpiceMouseMode) +VIR_ENUM_DECL(virDomainGraphicsSpiceMigrationSeamless) VIR_ENUM_DECL(virDomainNumatuneMemMode) VIR_ENUM_DECL(virDomainNumatuneMemPlacementMode) /* from libvirt.h */ diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 064e2a6..b2ff153 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -377,6 +377,8 @@ virDomainGraphicsSpiceImageCompressionTypeFromString; virDomainGraphicsSpiceImageCompressionTypeToString; virDomainGraphicsSpiceJpegCompressionTypeFromString; virDomainGraphicsSpiceJpegCompressionTypeToString; +virDomainGraphicsSpiceMigrationSeamlessTypeFromString; +virDomainGraphicsSpiceMigrationSeamlessTypeToString; virDomainGraphicsSpiceMouseModeTypeFromString; virDomainGraphicsSpiceMouseModeTypeToString; virDomainGraphicsSpicePlaybackCompressionTypeFromString; -- 1.7.8.6

On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

[adding qemu] On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration. This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side. -- Eric Blake eblake@redhat.com +1-919-301-3266 Libvirt virtualization library http://libvirt.org

On Fri, Sep 14, 2012 at 05:23:16PM -0600, Eric Blake wrote:
[adding qemu]
On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration.
This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side.
Hmm, my understanding of the QEMU flag was different. Based on the commit message: spice: adding seamless-migration option to the command line The seamless-migration flag is required in order to identify whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not (by default the flag is off). New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. When this flag is off, spice fallbacks to its old migration method, which can result in data loss. This says to me that any libvirt which knows about the new SPICE_MIGRATE_COMPLETED event, should set the seamless-migration flag unconditionally, to indicate that it can handle the event and thus the new migration method. It says nothing about only setting this flag if the destination QEMU also supports it. As such, IMHO, we can & should set this flag unconditonally on all QEMUs we run which support it. If it turns out that this flag does indeed require that the destination QEMU also has the same setting, then IMHO this flag is a fatally flawed design. At time of starting any QEMU instance, we can't know whether the destination QEMU we want to migrate to will have the support or not. Compatibility checks of this kind can only be decided at time the migrate command is actually issued. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 15.09.2012 17:10, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 05:23:16PM -0600, Eric Blake wrote:
[adding qemu]
On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration.
This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side.
Hmm, my understanding of the QEMU flag was different. Based on the commit message:
spice: adding seamless-migration option to the command line
The seamless-migration flag is required in order to identify whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not (by default the flag is off). New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. When this flag is off, spice fallbacks to its old migration method, which can result in data loss.
This says to me that any libvirt which knows about the new SPICE_MIGRATE_COMPLETED event, should set the seamless-migration flag unconditionally, to indicate that it can handle the event and thus the new migration method. It says nothing about only setting this flag if the destination QEMU also supports it. As such, IMHO, we can & should set this flag unconditonally on all QEMUs we run which support it.
If it turns out that this flag does indeed require that the destination QEMU also has the same setting, then IMHO this flag is a fatally flawed design. At time of starting any QEMU instance, we can't know whether the destination QEMU we want to migrate to will have the support or not. Compatibility checks of this kind can only be decided at time the migrate command is actually issued.
Daniel
From my investigation I was able to migrate between qemu where one had seamless_migration=on and the other one didn't support such argument at all. Literally, on source I've checked out 27af778828db9 (the last commit in Yonit's patchset) on destination f5bb039c6d97e (the last before the patchset). Then I've migrated and receive both event and flag set in query-spice response. So I guess the design is okay.
Having said that - what's desired solution here? Unconditionally set seamless_migration=on on enough new qemu and don't expose it in XML at all? Michal

On Wed, Sep 19, 2012 at 03:26:49PM +0200, Michal Privoznik wrote:
On 15.09.2012 17:10, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 05:23:16PM -0600, Eric Blake wrote:
[adding qemu]
On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration.
This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side.
Hmm, my understanding of the QEMU flag was different. Based on the commit message:
spice: adding seamless-migration option to the command line
The seamless-migration flag is required in order to identify whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not (by default the flag is off). New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. When this flag is off, spice fallbacks to its old migration method, which can result in data loss.
This says to me that any libvirt which knows about the new SPICE_MIGRATE_COMPLETED event, should set the seamless-migration flag unconditionally, to indicate that it can handle the event and thus the new migration method. It says nothing about only setting this flag if the destination QEMU also supports it. As such, IMHO, we can & should set this flag unconditonally on all QEMUs we run which support it.
If it turns out that this flag does indeed require that the destination QEMU also has the same setting, then IMHO this flag is a fatally flawed design. At time of starting any QEMU instance, we can't know whether the destination QEMU we want to migrate to will have the support or not. Compatibility checks of this kind can only be decided at time the migrate command is actually issued.
Daniel
From my investigation I was able to migrate between qemu where one had seamless_migration=on and the other one didn't support such argument at all. Literally, on source I've checked out 27af778828db9 (the last commit in Yonit's patchset) on destination f5bb039c6d97e (the last before the patchset). Then I've migrated and receive both event and flag set in query-spice response. So I guess the design is okay.
Having said that - what's desired solution here? Unconditionally set seamless_migration=on on enough new qemu and don't expose it in XML at all?
Yes, that's my desired impl. Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On Wed, Sep 19, 2012 at 03:26:49PM +0200, Michal Privoznik wrote:
On 15.09.2012 17:10, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 05:23:16PM -0600, Eric Blake wrote:
[adding qemu]
On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration.
This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side.
Hmm, my understanding of the QEMU flag was different. Based on the commit message:
spice: adding seamless-migration option to the command line
The seamless-migration flag is required in order to identify whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not (by default the flag is off). New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. When this flag is off, spice fallbacks to its old migration method, which can result in data loss.
This says to me that any libvirt which knows about the new SPICE_MIGRATE_COMPLETED event, should set the seamless-migration flag unconditionally, to indicate that it can handle the event and thus the new migration method. It says nothing about only setting this flag if the destination QEMU also supports it. As such, IMHO, we can & should set this flag unconditonally on all QEMUs we run which support it.
If it turns out that this flag does indeed require that the destination QEMU also has the same setting, then IMHO this flag is a fatally flawed design. At time of starting any QEMU instance, we can't know whether the destination QEMU we want to migrate to will have the support or not. Compatibility checks of this kind can only be decided at time the migrate command is actually issued.
Daniel
From my investigation I was able to migrate between qemu where one had seamless_migration=on and the other one didn't support such argument at all. Literally, on source I've checked out 27af778828db9 (the last commit in Yonit's patchset) on destination f5bb039c6d97e (the last before the patchset). Then I've migrated and receive both event and flag set in query-spice response. So I guess the design is okay.
Oh, BTW, when you tested did you have an actual SPICE client connected and did it migrate ok ? Daniel -- |: http://berrange.com -o- http://www.flickr.com/photos/dberrange/ :| |: http://libvirt.org -o- http://virt-manager.org :| |: http://autobuild.org -o- http://search.cpan.org/~danberr/ :| |: http://entangle-photo.org -o- http://live.gnome.org/gtk-vnc :|

On 19.09.2012 16:25, Daniel P. Berrange wrote:
On Wed, Sep 19, 2012 at 03:26:49PM +0200, Michal Privoznik wrote:
On 15.09.2012 17:10, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 05:23:16PM -0600, Eric Blake wrote:
[adding qemu]
On 09/14/2012 11:47 AM, Daniel P. Berrange wrote:
On Fri, Sep 14, 2012 at 07:34:50PM +0200, Michal Privoznik wrote:
With this element users will control how SPICE server behaves upon migration. For now, there's just one attribute 'seamless' turning seamless migration on/off/default.
Ewww, no. This information is a related to a API operation, not the VM configuration. It should be either auto-detected by libvirt to the best compatible setting, or passed as a flag to the virDomainMigrate API call if auto-detection is not possible.
But with the current qemu implementation, there's no way to know if the destination supports this until after you've started the source, and the current implementation in qemu is that you must declare the semantics at the time you start qemu, not at the time you send the 'migrate' monitor command. For libvirt autodetection to work without polluting the domain XML, we'd need to be able to auto-detect at the time we start migration.
This sounds like we need to enhance the 'migrate-set-capabilities' command to enable or disable this feature on the fly, according to what libvirt detects from the remote end, rather than hard-coding it to the startup state of qemu on the source side.
Hmm, my understanding of the QEMU flag was different. Based on the commit message:
spice: adding seamless-migration option to the command line
The seamless-migration flag is required in order to identify whether libvirt supports the new QEVENT_SPICE_MIGRATE_COMPLETED or not (by default the flag is off). New libvirt versions that wait for QEVENT_SPICE_MIGRATE_COMPLETED should turn on this flag. When this flag is off, spice fallbacks to its old migration method, which can result in data loss.
This says to me that any libvirt which knows about the new SPICE_MIGRATE_COMPLETED event, should set the seamless-migration flag unconditionally, to indicate that it can handle the event and thus the new migration method. It says nothing about only setting this flag if the destination QEMU also supports it. As such, IMHO, we can & should set this flag unconditonally on all QEMUs we run which support it.
If it turns out that this flag does indeed require that the destination QEMU also has the same setting, then IMHO this flag is a fatally flawed design. At time of starting any QEMU instance, we can't know whether the destination QEMU we want to migrate to will have the support or not. Compatibility checks of this kind can only be decided at time the migrate command is actually issued.
Daniel
From my investigation I was able to migrate between qemu where one had seamless_migration=on and the other one didn't support such argument at all. Literally, on source I've checked out 27af778828db9 (the last commit in Yonit's patchset) on destination f5bb039c6d97e (the last before the patchset). Then I've migrated and receive both event and flag set in query-spice response. So I guess the design is okay.
Oh, BTW, when you tested did you have an actual SPICE client connected and did it migrate ok ?
That's correct. Michal
Daniel

This requires adding "seamless-migration='on|off'" to the qemu command line. It is turned on by default (if not running on ancient qemu that doesn't support this). --- src/qemu/qemu_capabilities.c | 3 +++ src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_command.c | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 0 deletions(-) diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index af3b0b2..7514f04 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -177,6 +177,7 @@ VIR_ENUM_IMPL(qemuCaps, QEMU_CAPS_LAST, "disable-s4", /* 105 */ "usb-redir.filter", + "seamless-migration", ); struct _qemuCaps { @@ -1140,6 +1141,8 @@ qemuCapsComputeCmdFlags(const char *help, } if (strstr(help, "-spice")) qemuCapsSet(caps, QEMU_CAPS_SPICE); + if (strstr(help, "seamless-migration=")) + qemuCapsSet(caps, QEMU_CAPS_SEAMLESS_MIGRATION); if (strstr(help, "boot=on")) qemuCapsSet(caps, QEMU_CAPS_DRIVE_BOOT); if (strstr(help, "serial=s")) diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index bbb07c9..d33cdd1 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -142,6 +142,7 @@ enum qemuCapsFlags { QEMU_CAPS_DISABLE_S3 = 104, /* S3 BIOS Advertisement on/off */ QEMU_CAPS_DISABLE_S4 = 105, /* S4 BIOS Advertisement on/off */ QEMU_CAPS_USB_REDIR_FILTER = 106, /* usb-redir.filter */ + QEMU_CAPS_SEAMLESS_MIGRATION = 107, /* seamless-migration for SPICE */ QEMU_CAPS_LAST, /* this must always be the last item */ }; diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index ec825bc..a9e54fb 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -6069,6 +6069,20 @@ qemuBuildCommandLine(virConnectPtr conn, if (def->graphics[0]->data.spice.copypaste == VIR_DOMAIN_GRAPHICS_SPICE_CLIPBOARD_COPYPASTE_NO) virBufferAddLit(&opt, ",disable-copy-paste"); + if (qemuCapsGet(caps, QEMU_CAPS_SEAMLESS_MIGRATION)) { + if (def->graphics[0]->data.spice.seamless == + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_OFF) + virBufferAddLit(&opt, ",seamless-migration=off"); + else + virBufferAddLit(&opt, ",seamless-migration=on"); + } else if (def->graphics[0]->data.spice.seamless == + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_ON) { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("SPICE seamless migration is not " + "supported by this version of qemu")); + goto error; + } + virCommandAddArg(cmd, "-spice"); virCommandAddArgBuffer(cmd, &opt); if (def->graphics[0]->data.spice.keymap) -- 1.7.8.6

to reflect new <migration seamless='on|off'/> extension. --- .../qemuxml2argv-graphics-spice-migration.args | 7 ++++ .../qemuxml2argv-graphics-spice-migration.xml | 36 ++++++++++++++++++++ tests/qemuxml2argvtest.c | 4 ++ 3 files changed, 47 insertions(+), 0 deletions(-) create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.args create mode 100644 tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.xml diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.args b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.args new file mode 100644 index 0000000..d2cdc09 --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.args @@ -0,0 +1,7 @@ +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,seamless-migration=off -vga \ +qxl -global qxl.vram_size=33554432 -device qxl,id=video1,vram_size=67108864,bus=pci.0,addr=0x4 \ +-device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x3 diff --git a/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.xml b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.xml new file mode 100644 index 0000000..58b41bb --- /dev/null +++ b/tests/qemuxml2argvdata/qemuxml2argv-graphics-spice-migration.xml @@ -0,0 +1,36 @@ +<domain type='qemu'> + <name>QEMUGuest1</name> + <uuid>3ba2ce84-cb2f-4664-8954-c92123ace8af</uuid> + <memory unit='KiB'>219136</memory> + <currentMemory unit='KiB'>219136</currentMemory> + <vcpu placement='static'>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' target='0' unit='0'/> + </disk> + <controller type='usb' index='0'/> + <controller type='ide' index='0'/> + <input type='mouse' bus='ps2'/> + <graphics type='spice' port='5903' tlsPort='5904' autoport='no' listen='127.0.0.1'> + <migration seamless='off'/> + </graphics> + <video> + <model type='qxl' vram='32768' heads='1'/> + </video> + <video> + <model type='qxl' vram='65536' heads='1'/> + </video> + <memballoon model='virtio'/> + </devices> +</domain> diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 468014c..9ae23f7 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -554,6 +554,10 @@ mymain(void) QEMU_CAPS_PCI_MULTIFUNCTION, QEMU_CAPS_USB_HUB, QEMU_CAPS_ICH9_USB_EHCI1, QEMU_CAPS_USB_REDIR, QEMU_CAPS_CHARDEV_SPICEVMC); + DO_TEST("graphics-spice-migration", + QEMU_CAPS_VGA, QEMU_CAPS_VGA_QXL, + QEMU_CAPS_DEVICE, QEMU_CAPS_SPICE, + QEMU_CAPS_SEAMLESS_MIGRATION); DO_TEST("input-usbmouse", NONE); DO_TEST("input-usbtablet", NONE); -- 1.7.8.6

Recently, there have been some improvements made to qemu so it supports seamless migration or something very close to it. However, it requires libvirt interaction. Once qemu is migrated, the SPICE server needs to send its internal state to the destination. Once it's done, it fires SPICE_MIGRATE_COMPLETED event and this fact is advertised in 'query-spice' output as well. We must not kill qemu until SPICE server finishes the transfer. --- src/qemu/qemu_migration.c | 38 +++++++++++++++++++++++++++--- src/qemu/qemu_monitor.c | 22 +++++++++++++++++ src/qemu/qemu_monitor.h | 3 ++ src/qemu/qemu_monitor_json.c | 52 ++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_monitor_json.h | 3 ++ 5 files changed, 114 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index 99fc8ce..19666e2 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -891,11 +891,13 @@ static int qemuMigrationUpdateJobStatus(struct qemud_driver *driver, virDomainObjPtr vm, const char *job, - enum qemuDomainAsyncJob asyncJob) + enum qemuDomainAsyncJob asyncJob, + bool wait_for_spice) { qemuDomainObjPrivatePtr priv = vm->privateData; int ret; int status; + bool spice_migrated = true; unsigned long long memProcessed; unsigned long long memRemaining; unsigned long long memTotal; @@ -910,6 +912,13 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver, &memProcessed, &memRemaining, &memTotal); + + /* If qemu says migrated, check spice */ + if (wait_for_spice && (ret == 0) && + (status == QEMU_MONITOR_MIGRATION_STATUS_COMPLETED)) + ret = qemuMonitorGetSpiceMigrationStatus(priv->mon, + &spice_migrated); + qemuDomainObjExitMonitorWithDriver(driver, vm); if (ret < 0 || virTimeMillisNow(&priv->job.info.timeElapsed) < 0) { @@ -939,7 +948,8 @@ qemuMigrationUpdateJobStatus(struct qemud_driver *driver, break; case QEMU_MONITOR_MIGRATION_STATUS_COMPLETED: - priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED; + if (spice_migrated) + priv->job.info.type = VIR_DOMAIN_JOB_COMPLETED; ret = 0; break; @@ -967,6 +977,15 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm, { qemuDomainObjPrivatePtr priv = vm->privateData; const char *job; + bool wait_for_spice; + + /* If guest uses SPICE and supports seamles_migration we have to hold up + * migration finish until SPICE server transfers its data */ + wait_for_spice = (vm->def->ngraphics == 1) && + (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) && + qemuCapsGet(priv->caps, QEMU_CAPS_SEAMLESS_MIGRATION) && + (vm->def->graphics[0]->data.spice.seamless != + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_OFF); switch (priv->job.asyncJob) { case QEMU_ASYNC_JOB_MIGRATION_OUT: @@ -988,7 +1007,8 @@ qemuMigrationWaitForCompletion(struct qemud_driver *driver, virDomainObjPtr vm, /* Poll every 50ms for progress & to allow cancellation */ struct timespec ts = { .tv_sec = 0, .tv_nsec = 50 * 1000 * 1000ull }; - if (qemuMigrationUpdateJobStatus(driver, vm, job, asyncJob) < 0) + if (qemuMigrationUpdateJobStatus(driver, vm, job, + asyncJob, wait_for_spice) < 0) goto cleanup; if (dconn && virConnectIsAlive(dconn) <= 0) { @@ -1840,6 +1860,7 @@ qemuMigrationRun(struct qemud_driver *driver, int fd = -1; unsigned long migrate_speed = resource ? resource : priv->migMaxBandwidth; virErrorPtr orig_err = NULL; + bool wait_for_spice; VIR_DEBUG("driver=%p, vm=%p, cookiein=%s, cookieinlen=%d, " "cookieout=%p, cookieoutlen=%p, flags=%lx, resource=%lu, " @@ -1848,6 +1869,14 @@ qemuMigrationRun(struct qemud_driver *driver, cookieout, cookieoutlen, flags, resource, spec, spec->destType, spec->fwdType); + /* If guest uses SPICE and supports seamless migration we have to hold up + * migration finish until SPICE server transfers its data */ + wait_for_spice = (vm->def->ngraphics == 1) && + (vm->def->graphics[0]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) && + qemuCapsGet(priv->caps, QEMU_CAPS_SEAMLESS_MIGRATION) && + (vm->def->graphics[0]->data.spice.seamless != + VIR_DOMAIN_GRAPHICS_SPICE_MIGRATION_SEAMLESS_OFF); + if (virLockManagerPluginUsesState(driver->lockManager) && !cookieout) { virReportError(VIR_ERR_INTERNAL_ERROR, @@ -1946,7 +1975,8 @@ qemuMigrationRun(struct qemud_driver *driver, * connection from qemu which may never be initiated. */ if (qemuMigrationUpdateJobStatus(driver, vm, _("migration job"), - QEMU_ASYNC_JOB_MIGRATION_OUT) < 0) + QEMU_ASYNC_JOB_MIGRATION_OUT, + wait_for_spice) < 0) goto cancel; while ((fd = accept(spec->dest.unix_socket.sock, NULL, NULL)) < 0) { diff --git a/src/qemu/qemu_monitor.c b/src/qemu/qemu_monitor.c index b43b15e..20bb333 100644 --- a/src/qemu/qemu_monitor.c +++ b/src/qemu/qemu_monitor.c @@ -1844,6 +1844,28 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, return ret; } +int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon, + bool *spice_migrated) +{ + int ret; + VIR_DEBUG("mon=%p", mon); + + if (!mon) { + virReportError(VIR_ERR_INVALID_ARG, "%s", + _("monitor must not be NULL")); + return -1; + } + + if (mon->json) { + ret = qemuMonitorJSONGetSpiceMigrationStatus(mon, spice_migrated); + } else { + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s", + _("JSON monitor is required")); + return -1; + } + + return ret; +} int qemuMonitorMigrateToFd(qemuMonitorPtr mon, unsigned int flags, diff --git a/src/qemu/qemu_monitor.h b/src/qemu/qemu_monitor.h index f206d49..d3a0877 100644 --- a/src/qemu/qemu_monitor.h +++ b/src/qemu/qemu_monitor.h @@ -346,6 +346,9 @@ int qemuMonitorGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total); +int qemuMonitorGetSpiceMigrationStatus(qemuMonitorPtr mon, + bool *spice_migrated); + typedef enum { QEMU_MONITOR_MIGRATE_BACKGROUND = 1 << 0, QEMU_MONITOR_MIGRATE_NON_SHARED_DISK = 1 << 1, /* migration with non-shared storage with full disk copy */ diff --git a/src/qemu/qemu_monitor_json.c b/src/qemu/qemu_monitor_json.c index 8842817..63dbd25 100644 --- a/src/qemu/qemu_monitor_json.c +++ b/src/qemu/qemu_monitor_json.c @@ -2487,6 +2487,58 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, } +static int +qemuMonitorJSONSpiceGetMigrationStatusReply(virJSONValuePtr reply, + bool *spice_migrated) +{ + virJSONValuePtr ret; + const char *migrated_str; + + if (!(ret = virJSONValueObjectGet(reply, "return"))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("query-spice reply was missing return data")); + return -1; + } + + if (!(migrated_str = virJSONValueObjectGetString(ret, "migrated"))) { + /* Deliberately don't report error here as we are + * probably dealing with older qemu which doesn't + * report this yet. Pretend spice is migrated. */ + *spice_migrated = true; + } else { + *spice_migrated = STREQ(migrated_str, "true"); + } + + return 0; +} + + +int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon, + bool *spice_migrated) +{ + int ret; + virJSONValuePtr cmd = qemuMonitorJSONMakeCommand("query-spice", + NULL); + virJSONValuePtr reply = NULL; + + if (!cmd) + return -1; + + ret = qemuMonitorJSONCommand(mon, cmd, &reply); + + if (ret == 0) + ret = qemuMonitorJSONCheckError(cmd, reply); + + if (ret == 0) + ret = qemuMonitorJSONSpiceGetMigrationStatusReply(reply, + spice_migrated); + + virJSONValueFree(cmd); + virJSONValueFree(reply); + return ret; +} + + int qemuMonitorJSONMigrate(qemuMonitorPtr mon, unsigned int flags, const char *uri) diff --git a/src/qemu/qemu_monitor_json.h b/src/qemu/qemu_monitor_json.h index f5d515f..88edb73 100644 --- a/src/qemu/qemu_monitor_json.h +++ b/src/qemu/qemu_monitor_json.h @@ -132,6 +132,9 @@ int qemuMonitorJSONGetMigrationStatus(qemuMonitorPtr mon, unsigned long long *remaining, unsigned long long *total); +int qemuMonitorJSONGetSpiceMigrationStatus(qemuMonitorPtr mon, + bool *spice_migrated); + int qemuMonitorJSONMigrate(qemuMonitorPtr mon, unsigned int flags, const char *uri); -- 1.7.8.6

On Fri, Sep 14, 2012 at 12:34 PM, Michal Privoznik <mprivozn@redhat.com> wrote:
With the latest qemu, we are a step closer to seamless migration. However, libvirt needs to lend a helping hand.
OT: But this made me think. Do we need to expose SPICE support via capabilities? And potentially VNC? -- Doug Goldstein
participants (4)
-
Daniel P. Berrange
-
Doug Goldstein
-
Eric Blake
-
Michal Privoznik