1 /*
2  * qemu_migration_cookie.c: QEMU migration cookie handling
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library.  If not, see
16  * <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <config.h>
20 
21 #include <gnutls/gnutls.h>
22 #include <gnutls/x509.h>
23 
24 #include "locking/domain_lock.h"
25 #include "viralloc.h"
26 #include "virerror.h"
27 #include "virlog.h"
28 #include "virnetdevopenvswitch.h"
29 #include "virstring.h"
30 #include "virutil.h"
31 
32 #include "qemu_domain.h"
33 #include "qemu_migration_cookie.h"
34 #include "qemu_migration_params.h"
35 
36 
37 #define VIR_FROM_THIS VIR_FROM_QEMU
38 
39 VIR_LOG_INIT("qemu.qemu_migration_cookie");
40 
41 VIR_ENUM_IMPL(qemuMigrationCookieFlag,
42               QEMU_MIGRATION_COOKIE_FLAG_LAST,
43               "graphics",
44               "lockstate",
45               "persistent",
46               "network",
47               "nbd",
48               "statistics",
49               "memory-hotplug",
50               "cpu-hotplug",
51               "cpu",
52               "allowReboot",
53               "capabilities",
54               "block-dirty-bitmaps",
55 );
56 
57 
58 static void
qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphics * grap)59 qemuMigrationCookieGraphicsFree(qemuMigrationCookieGraphics *grap)
60 {
61     if (!grap)
62         return;
63     g_free(grap->listen);
64     g_free(grap->tlsSubject);
65     g_free(grap);
66 }
67 
68 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieGraphics,
69                               qemuMigrationCookieGraphicsFree);
70 
71 
72 static void
qemuMigrationCookieNetworkFree(qemuMigrationCookieNetwork * network)73 qemuMigrationCookieNetworkFree(qemuMigrationCookieNetwork *network)
74 {
75     size_t i;
76 
77     if (!network)
78         return;
79 
80     if (network->net) {
81         for (i = 0; i < network->nnets; i++)
82             g_free(network->net[i].portdata);
83     }
84     g_free(network->net);
85     g_free(network);
86 }
87 
88 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieNetwork,
89                               qemuMigrationCookieNetworkFree);
90 
91 static void
qemuMigrationCookieNBDFree(qemuMigrationCookieNBD * nbd)92 qemuMigrationCookieNBDFree(qemuMigrationCookieNBD *nbd)
93 {
94     if (!nbd)
95         return;
96 
97     while (nbd->ndisks)
98         g_free(nbd->disks[--nbd->ndisks].target);
99     g_free(nbd->disks);
100     g_free(nbd);
101 }
102 
103 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieNBD,
104                               qemuMigrationCookieNBDFree);
105 
106 static void
qemuMigrationCookieCapsFree(qemuMigrationCookieCaps * caps)107 qemuMigrationCookieCapsFree(qemuMigrationCookieCaps *caps)
108 {
109     if (!caps)
110         return;
111 
112     virBitmapFree(caps->supported);
113     virBitmapFree(caps->automatic);
114     g_free(caps);
115 }
116 
117 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationCookieCaps,
118                               qemuMigrationCookieCapsFree);
119 
120 static void
qemuMigrationBlockDirtyBitmapsDiskBitmapFree(qemuMigrationBlockDirtyBitmapsDiskBitmap * bmp)121 qemuMigrationBlockDirtyBitmapsDiskBitmapFree(qemuMigrationBlockDirtyBitmapsDiskBitmap *bmp)
122 {
123     if (!bmp)
124         return;
125 
126     g_free(bmp->bitmapname);
127     g_free(bmp->alias);
128     g_free(bmp->sourcebitmap);
129     g_free(bmp);
130 }
131 
132 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDiskBitmap,
133                               qemuMigrationBlockDirtyBitmapsDiskBitmapFree);
134 
135 
136 static void
qemuMigrationBlockDirtyBitmapsDiskFree(qemuMigrationBlockDirtyBitmapsDisk * dsk)137 qemuMigrationBlockDirtyBitmapsDiskFree(qemuMigrationBlockDirtyBitmapsDisk *dsk)
138 {
139     if (!dsk)
140         return;
141 
142     g_free(dsk->target);
143     if (dsk->bitmaps)
144         g_slist_free_full(dsk->bitmaps,
145                           (GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskBitmapFree);
146     g_free(dsk);
147 }
148 
149 G_DEFINE_AUTOPTR_CLEANUP_FUNC(qemuMigrationBlockDirtyBitmapsDisk,
150                               qemuMigrationBlockDirtyBitmapsDiskFree);
151 
152 
153 void
qemuMigrationCookieFree(qemuMigrationCookie * mig)154 qemuMigrationCookieFree(qemuMigrationCookie *mig)
155 {
156     if (!mig)
157         return;
158 
159     qemuMigrationCookieGraphicsFree(mig->graphics);
160     virDomainDefFree(mig->persistent);
161     qemuMigrationCookieNetworkFree(mig->network);
162     qemuMigrationCookieNBDFree(mig->nbd);
163 
164     g_free(mig->localHostname);
165     g_free(mig->remoteHostname);
166     g_free(mig->name);
167     g_free(mig->lockState);
168     g_free(mig->lockDriver);
169     g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree);
170     virCPUDefFree(mig->cpu);
171     qemuMigrationCookieCapsFree(mig->caps);
172     if (mig->blockDirtyBitmaps)
173         g_slist_free_full(mig->blockDirtyBitmaps,
174                           (GDestroyNotify) qemuMigrationBlockDirtyBitmapsDiskFree);
175     g_free(mig);
176 }
177 
178 
179 static char *
qemuDomainExtractTLSSubject(const char * certdir)180 qemuDomainExtractTLSSubject(const char *certdir)
181 {
182     g_autofree char *certfile = NULL;
183     char *subject = NULL;
184     g_autofree char *pemdata = NULL;
185     gnutls_datum_t pemdatum;
186     gnutls_x509_crt_t cert;
187     int ret;
188     size_t subjectlen;
189 
190     certfile = g_strdup_printf("%s/server-cert.pem", certdir);
191 
192     if (virFileReadAll(certfile, 8192, &pemdata) < 0) {
193         virReportError(VIR_ERR_INTERNAL_ERROR,
194                        _("unable to read server cert %s"), certfile);
195         return NULL;
196     }
197 
198     ret = gnutls_x509_crt_init(&cert);
199     if (ret < 0) {
200         virReportError(VIR_ERR_INTERNAL_ERROR,
201                        _("cannot initialize cert object: %s"),
202                        gnutls_strerror(ret));
203         return NULL;
204     }
205 
206     pemdatum.data = (unsigned char *)pemdata;
207     pemdatum.size = strlen(pemdata);
208 
209     ret = gnutls_x509_crt_import(cert, &pemdatum, GNUTLS_X509_FMT_PEM);
210     if (ret < 0) {
211         virReportError(VIR_ERR_INTERNAL_ERROR,
212                        _("cannot load cert data from %s: %s"),
213                        certfile, gnutls_strerror(ret));
214         return NULL;
215     }
216 
217     subjectlen = 1024;
218     subject = g_new0(char, subjectlen + 1);
219 
220     gnutls_x509_crt_get_dn(cert, subject, &subjectlen);
221     subject[subjectlen] = '\0';
222 
223     return subject;
224 }
225 
226 
227 static qemuMigrationCookieGraphics *
qemuMigrationCookieGraphicsSpiceAlloc(virQEMUDriver * driver,virDomainGraphicsDef * def,virDomainGraphicsListenDef * glisten)228 qemuMigrationCookieGraphicsSpiceAlloc(virQEMUDriver *driver,
229                                       virDomainGraphicsDef *def,
230                                       virDomainGraphicsListenDef *glisten)
231 {
232     g_autoptr(qemuMigrationCookieGraphics) mig = g_new0(qemuMigrationCookieGraphics, 1);
233     const char *listenAddr;
234     g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(driver);
235 
236     mig->type = VIR_DOMAIN_GRAPHICS_TYPE_SPICE;
237     mig->port = def->data.spice.port;
238     if (cfg->spiceTLS)
239         mig->tlsPort = def->data.spice.tlsPort;
240     else
241         mig->tlsPort = -1;
242 
243     if (!glisten || !(listenAddr = glisten->address))
244         listenAddr = cfg->spiceListen;
245 
246     if (cfg->spiceTLS &&
247         !(mig->tlsSubject = qemuDomainExtractTLSSubject(cfg->spiceTLSx509certdir)))
248         return NULL;
249 
250     mig->listen = g_strdup(listenAddr);
251 
252     return g_steal_pointer(&mig);
253 }
254 
255 
256 static qemuMigrationCookieNetwork *
qemuMigrationCookieNetworkAlloc(virQEMUDriver * driver G_GNUC_UNUSED,virDomainDef * def)257 qemuMigrationCookieNetworkAlloc(virQEMUDriver *driver G_GNUC_UNUSED,
258                                 virDomainDef *def)
259 {
260     g_autoptr(qemuMigrationCookieNetwork) mig = g_new0(qemuMigrationCookieNetwork, 1);
261     size_t i;
262 
263     mig->nnets = def->nnets;
264     mig->net = g_new0(qemuMigrationCookieNetData, def->nnets);
265 
266     for (i = 0; i < def->nnets; i++) {
267         virDomainNetDef *netptr;
268         const virNetDevVPortProfile *vport;
269 
270         netptr = def->nets[i];
271         vport = virDomainNetGetActualVirtPortProfile(netptr);
272 
273         if (vport) {
274             mig->net[i].vporttype = vport->virtPortType;
275 
276             switch (vport->virtPortType) {
277             case VIR_NETDEV_VPORT_PROFILE_NONE:
278             case VIR_NETDEV_VPORT_PROFILE_8021QBG:
279             case VIR_NETDEV_VPORT_PROFILE_8021QBH:
280                break;
281             case VIR_NETDEV_VPORT_PROFILE_OPENVSWITCH:
282                 if (virNetDevOpenvswitchGetMigrateData(&mig->net[i].portdata,
283                                                        netptr->ifname) != 0) {
284                         virReportError(VIR_ERR_INTERNAL_ERROR,
285                                        _("Unable to run command to get OVS port data for "
286                                          "interface %s"), netptr->ifname);
287                         return NULL;
288                 }
289                 break;
290             default:
291                 break;
292             }
293         }
294     }
295     return g_steal_pointer(&mig);
296 }
297 
298 
299 qemuMigrationCookie *
qemuMigrationCookieNew(const virDomainDef * def,const char * origname)300 qemuMigrationCookieNew(const virDomainDef *def,
301                        const char *origname)
302 {
303     qemuMigrationCookie *mig = NULL;
304     unsigned char localHostUUID[VIR_UUID_BUFLEN];
305     g_autofree char *localHostname = NULL;
306 
307     if (!(localHostname = virGetHostname()))
308         return NULL;
309 
310     if (virGetHostUUID(localHostUUID) < 0) {
311         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
312                        _("Unable to obtain host UUID"));
313         return NULL;
314     }
315 
316     mig = g_new0(qemuMigrationCookie, 1);
317 
318     if (origname)
319         mig->name = g_strdup(origname);
320     else
321         mig->name = g_strdup(def->name);
322 
323     memcpy(mig->uuid, def->uuid, VIR_UUID_BUFLEN);
324     memcpy(mig->localHostuuid, localHostUUID, VIR_UUID_BUFLEN);
325     mig->localHostname = g_steal_pointer(&localHostname);
326 
327     return mig;
328 }
329 
330 
331 static int
qemuMigrationCookieAddGraphics(qemuMigrationCookie * mig,virQEMUDriver * driver,virDomainObj * dom)332 qemuMigrationCookieAddGraphics(qemuMigrationCookie *mig,
333                                virQEMUDriver *driver,
334                                virDomainObj *dom)
335 {
336     size_t i = 0;
337 
338     if (mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) {
339         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
340                        _("Migration graphics data already present"));
341         return -1;
342     }
343 
344     for (i = 0; i < dom->def->ngraphics; i++) {
345         if (dom->def->graphics[i]->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
346             virDomainGraphicsListenDef *glisten =
347                 virDomainGraphicsGetListen(dom->def->graphics[i], 0);
348 
349             if (!glisten) {
350                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
351                                _("missing listen element"));
352                 return -1;
353             }
354 
355             switch (glisten->type) {
356             case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_ADDRESS:
357             case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NETWORK:
358                 /* Seamless migration is supported only for listen types
359                  * 'address and 'network'. */
360                 if (!(mig->graphics =
361                       qemuMigrationCookieGraphicsSpiceAlloc(driver,
362                                                             dom->def->graphics[i],
363                                                             glisten)))
364                     return -1;
365                 mig->flags |= QEMU_MIGRATION_COOKIE_GRAPHICS;
366                 break;
367 
368             case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_SOCKET:
369             case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_NONE:
370             case VIR_DOMAIN_GRAPHICS_LISTEN_TYPE_LAST:
371                 break;
372             }
373 
374             /* Seamless migration is supported only for one graphics. */
375             if (mig->graphics)
376                 break;
377         }
378     }
379 
380     return 0;
381 }
382 
383 
384 static int
qemuMigrationCookieAddLockstate(qemuMigrationCookie * mig,virQEMUDriver * driver,virDomainObj * dom)385 qemuMigrationCookieAddLockstate(qemuMigrationCookie *mig,
386                                 virQEMUDriver *driver,
387                                 virDomainObj *dom)
388 {
389     qemuDomainObjPrivate *priv = dom->privateData;
390 
391     if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
392         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
393                        _("Migration lockstate data already present"));
394         return -1;
395     }
396 
397     if (virDomainObjGetState(dom, NULL) == VIR_DOMAIN_PAUSED) {
398         mig->lockState = g_strdup(priv->lockState);
399     } else {
400         if (virDomainLockProcessInquire(driver->lockManager, dom, &mig->lockState) < 0)
401             return -1;
402     }
403 
404     mig->lockDriver = g_strdup(virLockManagerPluginGetName(driver->lockManager));
405 
406     mig->flags |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
407     mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_LOCKSTATE;
408 
409     return 0;
410 }
411 
412 
413 int
qemuMigrationCookieAddPersistent(qemuMigrationCookie * mig,virDomainDef ** def)414 qemuMigrationCookieAddPersistent(qemuMigrationCookie *mig,
415                                  virDomainDef **def)
416 {
417     if (mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) {
418         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
419                        _("Migration persistent data already present"));
420         return -1;
421     }
422 
423     if (!def || !*def)
424         return 0;
425 
426     mig->persistent = g_steal_pointer(&*def);
427     mig->flags |= QEMU_MIGRATION_COOKIE_PERSISTENT;
428     mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_PERSISTENT;
429     return 0;
430 }
431 
432 
433 virDomainDef *
qemuMigrationCookieGetPersistent(qemuMigrationCookie * mig)434 qemuMigrationCookieGetPersistent(qemuMigrationCookie *mig)
435 {
436     virDomainDef *def = mig->persistent;
437 
438     mig->persistent = NULL;
439     mig->flags &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
440     mig->flagsMandatory &= ~QEMU_MIGRATION_COOKIE_PERSISTENT;
441 
442     return def;
443 }
444 
445 
446 static int
qemuMigrationCookieAddNetwork(qemuMigrationCookie * mig,virQEMUDriver * driver,virDomainObj * dom)447 qemuMigrationCookieAddNetwork(qemuMigrationCookie *mig,
448                               virQEMUDriver *driver,
449                               virDomainObj *dom)
450 {
451     if (mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) {
452         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
453                        _("Network migration data already present"));
454         return -1;
455     }
456 
457     if (dom->def->nnets > 0) {
458         mig->network = qemuMigrationCookieNetworkAlloc(driver, dom->def);
459         if (!mig->network)
460             return -1;
461         mig->flags |= QEMU_MIGRATION_COOKIE_NETWORK;
462     }
463 
464     return 0;
465 }
466 
467 
468 static int
qemuMigrationCookieAddNBD(qemuMigrationCookie * mig,virQEMUDriver * driver,virDomainObj * vm)469 qemuMigrationCookieAddNBD(qemuMigrationCookie *mig,
470                           virQEMUDriver *driver,
471                           virDomainObj *vm)
472 {
473     qemuDomainObjPrivate *priv = vm->privateData;
474     g_autoptr(GHashTable) stats = virHashNew(g_free);
475     bool blockdev = virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV);
476     size_t i;
477     int rc;
478 
479     /* It is not a bug if there already is a NBD data */
480     qemuMigrationCookieNBDFree(mig->nbd);
481 
482     mig->nbd = g_new0(qemuMigrationCookieNBD, 1);
483 
484     mig->nbd->port = priv->nbdPort;
485     mig->flags |= QEMU_MIGRATION_COOKIE_NBD;
486 
487     if (vm->def->ndisks == 0)
488         return 0;
489 
490     mig->nbd->disks = g_new0(struct qemuMigrationCookieNBDDisk, vm->def->ndisks);
491     mig->nbd->ndisks = 0;
492 
493     if (qemuDomainObjEnterMonitorAsync(driver, vm, priv->job.asyncJob) < 0)
494         return -1;
495     if (blockdev)
496         rc = qemuMonitorBlockStatsUpdateCapacityBlockdev(priv->mon, stats);
497     else
498         rc = qemuMonitorBlockStatsUpdateCapacity(priv->mon, stats);
499     if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
500         return -1;
501 
502     for (i = 0; i < vm->def->ndisks; i++) {
503         virDomainDiskDef *disk = vm->def->disks[i];
504         qemuBlockStats *entry;
505 
506         if (blockdev) {
507             if (!(entry = virHashLookup(stats, disk->src->nodeformat)))
508                 continue;
509         } else {
510             if (!disk->info.alias ||
511                 !(entry = virHashLookup(stats, disk->info.alias)))
512                 continue;
513         }
514 
515         mig->nbd->disks[mig->nbd->ndisks].target = g_strdup(disk->dst);
516         mig->nbd->disks[mig->nbd->ndisks].capacity = entry->capacity;
517         mig->nbd->ndisks++;
518     }
519 
520     return 0;
521 }
522 
523 
524 static int
qemuMigrationCookieAddStatistics(qemuMigrationCookie * mig,virDomainObj * vm)525 qemuMigrationCookieAddStatistics(qemuMigrationCookie *mig,
526                                  virDomainObj *vm)
527 {
528     qemuDomainObjPrivate *priv = vm->privateData;
529 
530     if (!priv->job.completed)
531         return 0;
532 
533     g_clear_pointer(&mig->jobInfo, qemuDomainJobInfoFree);
534     mig->jobInfo = qemuDomainJobInfoCopy(priv->job.completed);
535 
536     mig->flags |= QEMU_MIGRATION_COOKIE_STATS;
537 
538     return 0;
539 }
540 
541 
542 static int
qemuMigrationCookieAddCPU(qemuMigrationCookie * mig,virDomainObj * vm)543 qemuMigrationCookieAddCPU(qemuMigrationCookie *mig,
544                           virDomainObj *vm)
545 {
546     if (mig->cpu)
547         return 0;
548 
549     if (!(mig->cpu = virCPUDefCopy(vm->def->cpu)))
550         return -1;
551 
552     if (qemuDomainMakeCPUMigratable(mig->cpu) < 0)
553         return -1;
554 
555     mig->flags |= QEMU_MIGRATION_COOKIE_CPU;
556 
557     return 0;
558 }
559 
560 
561 static int
qemuMigrationCookieAddCaps(qemuMigrationCookie * mig,virDomainObj * vm,qemuMigrationParty party)562 qemuMigrationCookieAddCaps(qemuMigrationCookie *mig,
563                            virDomainObj *vm,
564                            qemuMigrationParty party)
565 {
566     qemuDomainObjPrivate *priv = vm->privateData;
567 
568     qemuMigrationCookieCapsFree(mig->caps);
569     mig->caps = g_new0(qemuMigrationCookieCaps, 1);
570 
571     if (priv->migrationCaps)
572         mig->caps->supported = virBitmapNewCopy(priv->migrationCaps);
573     else
574         mig->caps->supported = virBitmapNew(0);
575 
576     mig->caps->automatic = qemuMigrationParamsGetAlwaysOnCaps(party);
577 
578     mig->flags |= QEMU_MIGRATION_COOKIE_CAPS;
579 
580     return 0;
581 }
582 
583 
584 static void
qemuMigrationCookieGraphicsXMLFormat(virBuffer * buf,qemuMigrationCookieGraphics * grap)585 qemuMigrationCookieGraphicsXMLFormat(virBuffer *buf,
586                                      qemuMigrationCookieGraphics *grap)
587 {
588     g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
589     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
590 
591     virBufferAsprintf(&attrBuf, " type='%s' port='%d' listen='%s'",
592                       virDomainGraphicsTypeToString(grap->type),
593                       grap->port, grap->listen);
594 
595     if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE)
596         virBufferAsprintf(&attrBuf, " tlsPort='%d'", grap->tlsPort);
597 
598     virBufferEscapeString(&childBuf, "<cert info='subject' value='%s'/>\n", grap->tlsSubject);
599 
600     virXMLFormatElement(buf, "graphics", &attrBuf, &childBuf);
601 }
602 
603 
604 static void
qemuMigrationCookieNetworkXMLFormat(virBuffer * buf,qemuMigrationCookieNetwork * optr)605 qemuMigrationCookieNetworkXMLFormat(virBuffer *buf,
606                                     qemuMigrationCookieNetwork *optr)
607 {
608     g_auto(virBuffer) interfaceBuf = VIR_BUFFER_INIT_CHILD(buf);
609     size_t i;
610 
611     for (i = 0; i < optr->nnets; i++) {
612         g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
613         g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(&interfaceBuf);
614 
615         /* If optr->net[i].vporttype is not set, there is nothing to transfer */
616         if (optr->net[i].vporttype == VIR_NETDEV_VPORT_PROFILE_NONE)
617             continue;
618 
619         virBufferAsprintf(&attrBuf, " index='%zu' vporttype='%s'",
620                           i, virNetDevVPortTypeToString(optr->net[i].vporttype));
621 
622         virBufferEscapeString(&childBuf, "<portdata>%s</portdata>\n",
623                               optr->net[i].portdata);
624 
625         virXMLFormatElement(&interfaceBuf, "interface", &attrBuf, &childBuf);
626     }
627 
628     virXMLFormatElement(buf, "network", NULL, &interfaceBuf);
629 }
630 
631 
632 static void
qemuMigrationCookieStatisticsXMLFormat(virBuffer * buf,qemuDomainJobInfo * jobInfo)633 qemuMigrationCookieStatisticsXMLFormat(virBuffer *buf,
634                                        qemuDomainJobInfo *jobInfo)
635 {
636     qemuMonitorMigrationStats *stats = &jobInfo->stats.mig;
637 
638     virBufferAddLit(buf, "<statistics>\n");
639     virBufferAdjustIndent(buf, 2);
640 
641     virBufferAsprintf(buf, "<started>%llu</started>\n", jobInfo->started);
642     virBufferAsprintf(buf, "<stopped>%llu</stopped>\n", jobInfo->stopped);
643     virBufferAsprintf(buf, "<sent>%llu</sent>\n", jobInfo->sent);
644     if (jobInfo->timeDeltaSet)
645         virBufferAsprintf(buf, "<delta>%lld</delta>\n", jobInfo->timeDelta);
646 
647     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
648                       VIR_DOMAIN_JOB_TIME_ELAPSED,
649                       jobInfo->timeElapsed);
650     if (stats->downtime_set)
651         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
652                           VIR_DOMAIN_JOB_DOWNTIME,
653                           stats->downtime);
654     if (stats->setup_time_set)
655         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
656                           VIR_DOMAIN_JOB_SETUP_TIME,
657                           stats->setup_time);
658 
659     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
660                       VIR_DOMAIN_JOB_MEMORY_TOTAL,
661                       stats->ram_total);
662     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
663                       VIR_DOMAIN_JOB_MEMORY_PROCESSED,
664                       stats->ram_transferred);
665     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
666                       VIR_DOMAIN_JOB_MEMORY_REMAINING,
667                       stats->ram_remaining);
668     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
669                       VIR_DOMAIN_JOB_MEMORY_BPS,
670                       stats->ram_bps);
671 
672     if (stats->ram_duplicate_set) {
673         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
674                           VIR_DOMAIN_JOB_MEMORY_CONSTANT,
675                           stats->ram_duplicate);
676         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
677                           VIR_DOMAIN_JOB_MEMORY_NORMAL,
678                           stats->ram_normal);
679         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
680                           VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES,
681                           stats->ram_normal_bytes);
682     }
683 
684     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
685                       VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE,
686                       stats->ram_dirty_rate);
687     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
688                       VIR_DOMAIN_JOB_MEMORY_ITERATION,
689                       stats->ram_iteration);
690     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
691                       VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS,
692                       stats->ram_postcopy_reqs);
693 
694     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
695                       VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE,
696                       stats->ram_page_size);
697 
698     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
699                       VIR_DOMAIN_JOB_DISK_TOTAL,
700                       stats->disk_total);
701     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
702                       VIR_DOMAIN_JOB_DISK_PROCESSED,
703                       stats->disk_transferred);
704     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
705                       VIR_DOMAIN_JOB_DISK_REMAINING,
706                       stats->disk_remaining);
707     virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
708                       VIR_DOMAIN_JOB_DISK_BPS,
709                       stats->disk_bps);
710 
711     if (stats->xbzrle_set) {
712         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
713                           VIR_DOMAIN_JOB_COMPRESSION_CACHE,
714                           stats->xbzrle_cache_size);
715         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
716                           VIR_DOMAIN_JOB_COMPRESSION_BYTES,
717                           stats->xbzrle_bytes);
718         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
719                           VIR_DOMAIN_JOB_COMPRESSION_PAGES,
720                           stats->xbzrle_pages);
721         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
722                           VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES,
723                           stats->xbzrle_cache_miss);
724         virBufferAsprintf(buf, "<%1$s>%2$llu</%1$s>\n",
725                           VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW,
726                           stats->xbzrle_overflow);
727     }
728 
729     virBufferAsprintf(buf, "<%1$s>%2$d</%1$s>\n",
730                       VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE,
731                       stats->cpu_throttle_percentage);
732 
733     virBufferAdjustIndent(buf, -2);
734     virBufferAddLit(buf, "</statistics>\n");
735 }
736 
737 
738 static void
qemuMigrationCookieCapsXMLFormat(virBuffer * buf,qemuMigrationCookieCaps * caps)739 qemuMigrationCookieCapsXMLFormat(virBuffer *buf,
740                                  qemuMigrationCookieCaps *caps)
741 {
742     qemuMigrationCapability cap;
743 
744     virBufferAddLit(buf, "<capabilities>\n");
745     virBufferAdjustIndent(buf, 2);
746 
747     for (cap = 0; cap < QEMU_MIGRATION_CAP_LAST; cap++) {
748         bool supported = false;
749         bool automatic = false;
750 
751         ignore_value(virBitmapGetBit(caps->supported, cap, &supported));
752         ignore_value(virBitmapGetBit(caps->automatic, cap, &automatic));
753         if (supported) {
754             virBufferAsprintf(buf, "<cap name='%s' auto='%s'/>\n",
755                               qemuMigrationCapabilityTypeToString(cap),
756                               automatic ? "yes" : "no");
757         }
758     }
759 
760     virBufferAdjustIndent(buf, -2);
761     virBufferAddLit(buf, "</capabilities>\n");
762 }
763 
764 
765 static void
qemuMigrationCookieNBDXMLFormat(qemuMigrationCookieNBD * nbd,virBuffer * buf)766 qemuMigrationCookieNBDXMLFormat(qemuMigrationCookieNBD *nbd,
767                                 virBuffer *buf)
768 {
769     g_auto(virBuffer) attrBuf = VIR_BUFFER_INITIALIZER;
770     g_auto(virBuffer) childBuf = VIR_BUFFER_INIT_CHILD(buf);
771     size_t i;
772 
773     if (nbd->port)
774         virBufferAsprintf(&attrBuf, " port='%d'", nbd->port);
775 
776     for (i = 0; i < nbd->ndisks; i++) {
777         virBufferEscapeString(&childBuf, "<disk target='%s'", nbd->disks[i].target);
778         virBufferAsprintf(&childBuf, " capacity='%llu'/>\n", nbd->disks[i].capacity);
779     }
780 
781     virXMLFormatElementEmpty(buf, "nbd", &attrBuf, &childBuf);
782 }
783 
784 
785 static void
qemuMigrationCookieBlockDirtyBitmapsFormat(virBuffer * buf,GSList * bitmaps)786 qemuMigrationCookieBlockDirtyBitmapsFormat(virBuffer *buf,
787                                            GSList *bitmaps)
788 {
789     g_auto(virBuffer) disksBuf = VIR_BUFFER_INIT_CHILD(buf);
790     GSList *nextdisk;
791 
792     for (nextdisk = bitmaps; nextdisk; nextdisk = nextdisk->next) {
793         qemuMigrationBlockDirtyBitmapsDisk *disk = nextdisk->data;
794         g_auto(virBuffer) diskAttrBuf = VIR_BUFFER_INITIALIZER;
795         g_auto(virBuffer) diskChildBuf = VIR_BUFFER_INIT_CHILD(&disksBuf);
796         bool hasBitmaps = false;
797         GSList *nextbitmap;
798 
799         if (disk->skip || !disk->bitmaps)
800             continue;
801 
802         for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) {
803             qemuMigrationBlockDirtyBitmapsDiskBitmap *bitmap = nextbitmap->data;
804 
805             if (bitmap->skip)
806                 continue;
807 
808             virBufferAsprintf(&diskChildBuf,
809                               "<bitmap name='%s' alias='%s'/>\n",
810                               bitmap->bitmapname, bitmap->alias);
811 
812             hasBitmaps = true;
813         }
814 
815         if (!hasBitmaps)
816             continue;
817 
818         virBufferAsprintf(&diskAttrBuf, " target='%s'", disk->target);
819         virXMLFormatElement(&disksBuf, "disk", &diskAttrBuf, &diskChildBuf);
820     }
821 
822 
823     virXMLFormatElement(buf, "blockDirtyBitmaps", NULL, &disksBuf);
824 }
825 
826 
827 int
qemuMigrationCookieXMLFormat(virQEMUDriver * driver,virQEMUCaps * qemuCaps,virBuffer * buf,qemuMigrationCookie * mig)828 qemuMigrationCookieXMLFormat(virQEMUDriver *driver,
829                              virQEMUCaps *qemuCaps,
830                              virBuffer *buf,
831                              qemuMigrationCookie *mig)
832 {
833     char uuidstr[VIR_UUID_STRING_BUFLEN];
834     char hostuuidstr[VIR_UUID_STRING_BUFLEN];
835     size_t i;
836 
837     virUUIDFormat(mig->uuid, uuidstr);
838     virUUIDFormat(mig->localHostuuid, hostuuidstr);
839 
840     virBufferAddLit(buf, "<qemu-migration>\n");
841     virBufferAdjustIndent(buf, 2);
842     virBufferEscapeString(buf, "<name>%s</name>\n", mig->name);
843     virBufferAsprintf(buf, "<uuid>%s</uuid>\n", uuidstr);
844     virBufferEscapeString(buf, "<hostname>%s</hostname>\n", mig->localHostname);
845     virBufferAsprintf(buf, "<hostuuid>%s</hostuuid>\n", hostuuidstr);
846 
847     for (i = 0; i < QEMU_MIGRATION_COOKIE_FLAG_LAST; i++) {
848         if (mig->flagsMandatory & (1 << i))
849             virBufferAsprintf(buf, "<feature name='%s'/>\n",
850                               qemuMigrationCookieFlagTypeToString(i));
851     }
852 
853     if ((mig->flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
854         mig->graphics)
855         qemuMigrationCookieGraphicsXMLFormat(buf, mig->graphics);
856 
857     if ((mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
858         mig->lockState) {
859         virBufferAsprintf(buf, "<lockstate driver='%s'>\n",
860                           mig->lockDriver);
861         virBufferAdjustIndent(buf, 2);
862         virBufferAsprintf(buf, "<leases>%s</leases>\n",
863                           mig->lockState);
864         virBufferAdjustIndent(buf, -2);
865         virBufferAddLit(buf, "</lockstate>\n");
866     }
867 
868     if ((mig->flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
869         mig->persistent) {
870         if (qemuDomainDefFormatBuf(driver,
871                                    qemuCaps,
872                                    mig->persistent,
873                                    VIR_DOMAIN_XML_INACTIVE |
874                                    VIR_DOMAIN_XML_SECURE |
875                                    VIR_DOMAIN_XML_MIGRATABLE,
876                                    buf) < 0)
877             return -1;
878     }
879 
880     if ((mig->flags & QEMU_MIGRATION_COOKIE_NETWORK) && mig->network)
881         qemuMigrationCookieNetworkXMLFormat(buf, mig->network);
882 
883     if ((mig->flags & QEMU_MIGRATION_COOKIE_NBD) && mig->nbd)
884         qemuMigrationCookieNBDXMLFormat(mig->nbd, buf);
885 
886     if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo)
887         qemuMigrationCookieStatisticsXMLFormat(buf, mig->jobInfo);
888 
889     if (mig->flags & QEMU_MIGRATION_COOKIE_CPU && mig->cpu)
890         virCPUDefFormatBufFull(buf, mig->cpu, NULL);
891 
892     if (mig->flags & QEMU_MIGRATION_COOKIE_CAPS)
893         qemuMigrationCookieCapsXMLFormat(buf, mig->caps);
894 
895     if (mig->flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS)
896         qemuMigrationCookieBlockDirtyBitmapsFormat(buf, mig->blockDirtyBitmaps);
897 
898     virBufferAdjustIndent(buf, -2);
899     virBufferAddLit(buf, "</qemu-migration>\n");
900     return 0;
901 }
902 
903 
904 static qemuMigrationCookieGraphics *
qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)905 qemuMigrationCookieGraphicsXMLParse(xmlXPathContextPtr ctxt)
906 {
907     g_autoptr(qemuMigrationCookieGraphics) grap = g_new0(qemuMigrationCookieGraphics, 1);
908     g_autofree char *graphicstype = NULL;
909 
910     if (!(graphicstype = virXPathString("string(./graphics/@type)", ctxt))) {
911         virReportError(VIR_ERR_INTERNAL_ERROR,
912                        "%s", _("missing type attribute in migration data"));
913         return NULL;
914     }
915     if ((grap->type = virDomainGraphicsTypeFromString(graphicstype)) < 0) {
916         virReportError(VIR_ERR_INTERNAL_ERROR,
917                        _("unknown graphics type %s"), graphicstype);
918         return NULL;
919     }
920     if (virXPathInt("string(./graphics/@port)", ctxt, &grap->port) < 0) {
921         virReportError(VIR_ERR_INTERNAL_ERROR,
922                        "%s", _("missing port attribute in migration data"));
923         return NULL;
924     }
925     if (grap->type == VIR_DOMAIN_GRAPHICS_TYPE_SPICE) {
926         if (virXPathInt("string(./graphics/@tlsPort)", ctxt, &grap->tlsPort) < 0) {
927             virReportError(VIR_ERR_INTERNAL_ERROR,
928                            "%s", _("missing tlsPort attribute in migration data"));
929             return NULL;
930         }
931     }
932     if (!(grap->listen = virXPathString("string(./graphics/@listen)", ctxt))) {
933         virReportError(VIR_ERR_INTERNAL_ERROR,
934                        "%s", _("missing listen attribute in migration data"));
935         return NULL;
936     }
937     /* Optional */
938     grap->tlsSubject = virXPathString("string(./graphics/cert[@info='subject']/@value)", ctxt);
939 
940     return g_steal_pointer(&grap);
941 }
942 
943 
944 static qemuMigrationCookieNetwork *
qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)945 qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt)
946 {
947     g_autoptr(qemuMigrationCookieNetwork) optr = g_new0(qemuMigrationCookieNetwork, 1);
948     size_t i;
949     int n;
950     g_autofree xmlNodePtr *interfaces = NULL;
951     VIR_XPATH_NODE_AUTORESTORE(ctxt)
952 
953     if ((n = virXPathNodeSet("./network/interface", ctxt, &interfaces)) < 0) {
954         virReportError(VIR_ERR_INTERNAL_ERROR,
955                        "%s", _("missing interface information"));
956         return NULL;
957     }
958 
959     optr->nnets = n;
960     optr->net = g_new0(qemuMigrationCookieNetData, optr->nnets);
961 
962     for (i = 0; i < n; i++) {
963         g_autofree char *vporttype = NULL;
964 
965         /* portdata is optional, and may not exist */
966         ctxt->node = interfaces[i];
967         optr->net[i].portdata = virXPathString("string(./portdata[1])", ctxt);
968 
969         if (!(vporttype = virXMLPropString(interfaces[i], "vporttype"))) {
970             virReportError(VIR_ERR_INTERNAL_ERROR,
971                            "%s", _("missing vporttype attribute in migration data"));
972             return NULL;
973         }
974         optr->net[i].vporttype = virNetDevVPortTypeFromString(vporttype);
975     }
976 
977     return g_steal_pointer(&optr);
978 }
979 
980 
981 static qemuMigrationCookieNBD *
qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt)982 qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt)
983 {
984     g_autoptr(qemuMigrationCookieNBD) ret = g_new0(qemuMigrationCookieNBD, 1);
985     g_autofree char *port = NULL;
986     size_t i;
987     int n;
988     g_autofree xmlNodePtr *disks = NULL;
989     VIR_XPATH_NODE_AUTORESTORE(ctxt)
990 
991     port = virXPathString("string(./nbd/@port)", ctxt);
992     if (port && virStrToLong_i(port, NULL, 10, &ret->port) < 0) {
993         virReportError(VIR_ERR_INTERNAL_ERROR,
994                        _("Malformed nbd port '%s'"),
995                        port);
996         return NULL;
997     }
998 
999     /* Now check if source sent a list of disks to prealloc. We might be
1000      * talking to an older server, so it's not an error if the list is
1001      * missing. */
1002     if ((n = virXPathNodeSet("./nbd/disk", ctxt, &disks)) > 0) {
1003         ret->disks = g_new0(struct qemuMigrationCookieNBDDisk, n);
1004         ret->ndisks = n;
1005 
1006         for (i = 0; i < n; i++) {
1007             g_autofree char *capacity = NULL;
1008 
1009             ctxt->node = disks[i];
1010 
1011             if (!(ret->disks[i].target = virXPathString("string(./@target)", ctxt))) {
1012                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1013                                _("Malformed disk target"));
1014                 return NULL;
1015             }
1016 
1017             capacity = virXPathString("string(./@capacity)", ctxt);
1018             if (!capacity ||
1019                 virStrToLong_ull(capacity, NULL, 10,
1020                                  &ret->disks[i].capacity) < 0) {
1021                 virReportError(VIR_ERR_INTERNAL_ERROR,
1022                                _("Malformed disk capacity: '%s'"),
1023                                NULLSTR(capacity));
1024                 return NULL;
1025             }
1026         }
1027     }
1028 
1029     return g_steal_pointer(&ret);
1030 }
1031 
1032 
1033 static qemuDomainJobInfo *
qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt)1034 qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt)
1035 {
1036     qemuDomainJobInfo *jobInfo = NULL;
1037     qemuMonitorMigrationStats *stats;
1038     VIR_XPATH_NODE_AUTORESTORE(ctxt)
1039 
1040     if (!(ctxt->node = virXPathNode("./statistics", ctxt)))
1041         return NULL;
1042 
1043     jobInfo = g_new0(qemuDomainJobInfo, 1);
1044 
1045     stats = &jobInfo->stats.mig;
1046     jobInfo->status = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
1047 
1048     virXPathULongLong("string(./started[1])", ctxt, &jobInfo->started);
1049     virXPathULongLong("string(./stopped[1])", ctxt, &jobInfo->stopped);
1050     virXPathULongLong("string(./sent[1])", ctxt, &jobInfo->sent);
1051     if (virXPathLongLong("string(./delta[1])", ctxt, &jobInfo->timeDelta) == 0)
1052         jobInfo->timeDeltaSet = true;
1053 
1054     virXPathULongLong("string(./" VIR_DOMAIN_JOB_TIME_ELAPSED "[1])",
1055                       ctxt, &jobInfo->timeElapsed);
1056 
1057     if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_DOWNTIME "[1])",
1058                           ctxt, &stats->downtime) == 0)
1059         stats->downtime_set = true;
1060     if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_SETUP_TIME "[1])",
1061                           ctxt, &stats->setup_time) == 0)
1062         stats->setup_time_set = true;
1063 
1064     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_TOTAL "[1])",
1065                       ctxt, &stats->ram_total);
1066     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_PROCESSED "[1])",
1067                       ctxt, &stats->ram_transferred);
1068     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_REMAINING "[1])",
1069                       ctxt, &stats->ram_remaining);
1070     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_BPS "[1])",
1071                       ctxt, &stats->ram_bps);
1072 
1073     if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_CONSTANT "[1])",
1074                           ctxt, &stats->ram_duplicate) == 0)
1075         stats->ram_duplicate_set = true;
1076     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL "[1])",
1077                       ctxt, &stats->ram_normal);
1078     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_NORMAL_BYTES "[1])",
1079                       ctxt, &stats->ram_normal_bytes);
1080 
1081     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_DIRTY_RATE "[1])",
1082                       ctxt, &stats->ram_dirty_rate);
1083     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_ITERATION "[1])",
1084                       ctxt, &stats->ram_iteration);
1085     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_POSTCOPY_REQS "[1])",
1086                       ctxt, &stats->ram_postcopy_reqs);
1087 
1088     virXPathULongLong("string(./" VIR_DOMAIN_JOB_MEMORY_PAGE_SIZE "[1])",
1089                       ctxt, &stats->ram_page_size);
1090 
1091     virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_TOTAL "[1])",
1092                       ctxt, &stats->disk_total);
1093     virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_PROCESSED "[1])",
1094                       ctxt, &stats->disk_transferred);
1095     virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_REMAINING "[1])",
1096                       ctxt, &stats->disk_remaining);
1097     virXPathULongLong("string(./" VIR_DOMAIN_JOB_DISK_BPS "[1])",
1098                       ctxt, &stats->disk_bps);
1099 
1100     if (virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE "[1])",
1101                           ctxt, &stats->xbzrle_cache_size) == 0)
1102         stats->xbzrle_set = true;
1103     virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_BYTES "[1])",
1104                       ctxt, &stats->xbzrle_bytes);
1105     virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_PAGES "[1])",
1106                       ctxt, &stats->xbzrle_pages);
1107     virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_CACHE_MISSES "[1])",
1108                       ctxt, &stats->xbzrle_cache_miss);
1109     virXPathULongLong("string(./" VIR_DOMAIN_JOB_COMPRESSION_OVERFLOW "[1])",
1110                       ctxt, &stats->xbzrle_overflow);
1111 
1112     virXPathInt("string(./" VIR_DOMAIN_JOB_AUTO_CONVERGE_THROTTLE "[1])",
1113                 ctxt, &stats->cpu_throttle_percentage);
1114 
1115     return jobInfo;
1116 }
1117 
1118 
1119 static qemuMigrationCookieCaps *
qemuMigrationCookieCapsXMLParse(xmlXPathContextPtr ctxt)1120 qemuMigrationCookieCapsXMLParse(xmlXPathContextPtr ctxt)
1121 {
1122     g_autoptr(qemuMigrationCookieCaps) caps = g_new0(qemuMigrationCookieCaps, 1);
1123     g_autofree xmlNodePtr *nodes = NULL;
1124     size_t i;
1125     int n;
1126 
1127     caps->supported = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
1128     caps->automatic = virBitmapNew(QEMU_MIGRATION_CAP_LAST);
1129 
1130     if ((n = virXPathNodeSet("./capabilities[1]/cap", ctxt, &nodes)) < 0)
1131         return NULL;
1132 
1133     for (i = 0; i < n; i++) {
1134         g_autofree char *name = NULL;
1135         g_autofree char *automatic = NULL;
1136         int cap;
1137 
1138         if (!(name = virXMLPropString(nodes[i], "name"))) {
1139             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1140                            _("missing migration capability name"));
1141             return NULL;
1142         }
1143 
1144         if ((cap = qemuMigrationCapabilityTypeFromString(name)) < 0)
1145             VIR_DEBUG("unknown migration capability '%s'", name);
1146         else
1147             ignore_value(virBitmapSetBit(caps->supported, cap));
1148 
1149         if ((automatic = virXMLPropString(nodes[i], "auto")) &&
1150             STREQ(automatic, "yes"))
1151             ignore_value(virBitmapSetBit(caps->automatic, cap));
1152     }
1153 
1154     return g_steal_pointer(&caps);
1155 }
1156 
1157 
1158 /**
1159  * qemuMigrationCookieXMLParseMandatoryFeatures:
1160  *
1161  * Check to ensure all mandatory features from XML are also present in 'flags'.
1162  */
1163 static int
qemuMigrationCookieXMLParseMandatoryFeatures(xmlXPathContextPtr ctxt,unsigned int flags)1164 qemuMigrationCookieXMLParseMandatoryFeatures(xmlXPathContextPtr ctxt,
1165                                              unsigned int flags)
1166 {
1167     g_autofree xmlNodePtr *nodes = NULL;
1168     size_t i;
1169     ssize_t n;
1170 
1171     if ((n = virXPathNodeSet("./feature", ctxt, &nodes)) < 0)
1172         return -1;
1173 
1174     for (i = 0; i < n; i++) {
1175         int val;
1176         g_autofree char *str = virXMLPropString(nodes[i], "name");
1177 
1178         if (!str) {
1179             virReportError(VIR_ERR_INTERNAL_ERROR,
1180                            "%s", _("missing feature name"));
1181             return -1;
1182         }
1183 
1184         if ((val = qemuMigrationCookieFlagTypeFromString(str)) < 0) {
1185             virReportError(VIR_ERR_INTERNAL_ERROR,
1186                            _("Unknown migration cookie feature %s"), str);
1187             return -1;
1188         }
1189 
1190         if ((flags & (1 << val)) == 0) {
1191             virReportError(VIR_ERR_INTERNAL_ERROR,
1192                            _("Unsupported migration cookie feature %s"), str);
1193             return -1;
1194         }
1195     }
1196 
1197     return 0;
1198 }
1199 
1200 
1201 static int
qemuMigrationCookieBlockDirtyBitmapsParse(xmlXPathContextPtr ctxt,qemuMigrationCookie * mig)1202 qemuMigrationCookieBlockDirtyBitmapsParse(xmlXPathContextPtr ctxt,
1203                                           qemuMigrationCookie *mig)
1204 {
1205     g_autoslist(qemuMigrationBlockDirtyBitmapsDisk) disks = NULL;
1206     g_autofree xmlNodePtr *disknodes = NULL;
1207     int ndisknodes;
1208     size_t i;
1209     VIR_XPATH_NODE_AUTORESTORE(ctxt)
1210 
1211     if ((ndisknodes = virXPathNodeSet("./blockDirtyBitmaps/disk", ctxt, &disknodes)) < 0)
1212         return -1;
1213 
1214     for (i = 0; i < ndisknodes; i++) {
1215         g_autoslist(qemuMigrationBlockDirtyBitmapsDiskBitmap) bitmaps = NULL;
1216         qemuMigrationBlockDirtyBitmapsDisk *disk;
1217         g_autofree xmlNodePtr *bitmapnodes = NULL;
1218         int nbitmapnodes;
1219         size_t j;
1220 
1221         ctxt->node = disknodes[i];
1222 
1223         if ((nbitmapnodes = virXPathNodeSet("./bitmap", ctxt, &bitmapnodes)) < 0)
1224             return -1;
1225 
1226         for (j = 0; j < nbitmapnodes; j++) {
1227             qemuMigrationBlockDirtyBitmapsDiskBitmap *bitmap;
1228 
1229             bitmap = g_new0(qemuMigrationBlockDirtyBitmapsDiskBitmap, 1);
1230             bitmap->bitmapname = virXMLPropString(bitmapnodes[j], "name");
1231             bitmap->alias = virXMLPropString(bitmapnodes[j], "alias");
1232             bitmaps = g_slist_prepend(bitmaps, bitmap);
1233 
1234             if (!bitmap->bitmapname || !bitmap->alias) {
1235                 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1236                                _("malformed <blockDirtyBitmaps> in migration cookie"));
1237                 return -1;
1238             }
1239         }
1240 
1241         disk = g_new0(qemuMigrationBlockDirtyBitmapsDisk, 1);
1242         disk->target = virXMLPropString(disknodes[i], "target");
1243         disk->bitmaps = g_slist_reverse(g_steal_pointer(&bitmaps));
1244 
1245         disks = g_slist_prepend(disks, disk);
1246 
1247         if (!disk->target) {
1248             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1249                            _("malformed <blockDirtyBitmaps> in migration cookie"));
1250             return -1;
1251         }
1252     }
1253 
1254     mig->blockDirtyBitmaps = g_slist_reverse(g_steal_pointer(&disks));
1255 
1256     return 0;
1257 }
1258 
1259 
1260 static int
qemuMigrationCookieXMLParse(qemuMigrationCookie * mig,virQEMUDriver * driver,virQEMUCaps * qemuCaps,xmlDocPtr doc,xmlXPathContextPtr ctxt,unsigned int flags)1261 qemuMigrationCookieXMLParse(qemuMigrationCookie *mig,
1262                             virQEMUDriver *driver,
1263                             virQEMUCaps *qemuCaps,
1264                             xmlDocPtr doc,
1265                             xmlXPathContextPtr ctxt,
1266                             unsigned int flags)
1267 {
1268     g_autofree char *name = NULL;
1269     g_autofree char *uuid = NULL;
1270     g_autofree char *hostuuid = NULL;
1271     char localdomuuid[VIR_UUID_STRING_BUFLEN];
1272 
1273     /* We don't store the uuid, name, hostname, or hostuuid
1274      * values. We just compare them to local data to do some
1275      * sanity checking on migration operation
1276      */
1277 
1278     /* Extract domain name */
1279     if (!(name = virXPathString("string(./name[1])", ctxt))) {
1280         virReportError(VIR_ERR_INTERNAL_ERROR,
1281                        "%s", _("missing name element in migration data"));
1282         return -1;
1283     }
1284     if (STRNEQ(name, mig->name)) {
1285         virReportError(VIR_ERR_INTERNAL_ERROR,
1286                        _("Incoming cookie data had unexpected name %s vs %s"),
1287                        name, mig->name);
1288         return -1;
1289     }
1290 
1291     /* Extract domain uuid */
1292     if (!(uuid = virXPathString("string(./uuid[1])", ctxt))) {
1293         virReportError(VIR_ERR_INTERNAL_ERROR,
1294                        "%s", _("missing uuid element in migration data"));
1295         return -1;
1296     }
1297     virUUIDFormat(mig->uuid, localdomuuid);
1298     if (STRNEQ(uuid, localdomuuid)) {
1299         virReportError(VIR_ERR_INTERNAL_ERROR,
1300                        _("Incoming cookie data had unexpected UUID %s vs %s"),
1301                        uuid, localdomuuid);
1302         return -1;
1303     }
1304 
1305     if (!(mig->remoteHostname = virXPathString("string(./hostname[1])", ctxt))) {
1306         virReportError(VIR_ERR_INTERNAL_ERROR,
1307                        "%s", _("missing hostname element in migration data"));
1308         return -1;
1309     }
1310     /* Historically, this is the place where we checked whether remoteHostname
1311      * and localHostname are the same. But even if they were, it doesn't mean
1312      * the domain is migrating onto the same host. Rely on UUID which can tell
1313      * for sure. */
1314 
1315     /* Check & forbid localhost migration */
1316     if (!(hostuuid = virXPathString("string(./hostuuid[1])", ctxt))) {
1317         virReportError(VIR_ERR_INTERNAL_ERROR,
1318                        "%s", _("missing hostuuid element in migration data"));
1319         return -1;
1320     }
1321     if (virUUIDParse(hostuuid, mig->remoteHostuuid) < 0) {
1322         virReportError(VIR_ERR_INTERNAL_ERROR,
1323                        "%s", _("malformed hostuuid element in migration data"));
1324         return -1;
1325     }
1326     if (memcmp(mig->remoteHostuuid, mig->localHostuuid, VIR_UUID_BUFLEN) == 0) {
1327         virReportError(VIR_ERR_INTERNAL_ERROR,
1328                        _("Attempt to migrate guest to the same host %s"),
1329                        hostuuid);
1330         return -1;
1331     }
1332 
1333     if (qemuMigrationCookieXMLParseMandatoryFeatures(ctxt, flags) < 0)
1334         return -1;
1335 
1336     if ((flags & QEMU_MIGRATION_COOKIE_GRAPHICS) &&
1337         virXPathBoolean("count(./graphics) > 0", ctxt) &&
1338         (!(mig->graphics = qemuMigrationCookieGraphicsXMLParse(ctxt))))
1339         return -1;
1340 
1341     if ((flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) &&
1342         virXPathBoolean("count(./lockstate) > 0", ctxt)) {
1343         g_autofree char *lockState = NULL;
1344 
1345         mig->lockDriver = virXPathString("string(./lockstate[1]/@driver)", ctxt);
1346         if (!mig->lockDriver) {
1347             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1348                            _("Missing lock driver name in migration cookie"));
1349             return -1;
1350         }
1351 
1352         lockState = virXPathString("string(./lockstate[1]/leases[1])", ctxt);
1353         if (STRNEQ_NULLABLE(lockState, ""))
1354             mig->lockState = g_steal_pointer(&lockState);
1355     }
1356 
1357     if ((flags & QEMU_MIGRATION_COOKIE_PERSISTENT) &&
1358         virXPathBoolean("count(./domain) > 0", ctxt)) {
1359         g_autofree xmlNodePtr *nodes = NULL;
1360 
1361         if ((virXPathNodeSet("./domain", ctxt, &nodes)) != 1) {
1362             virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1363                            _("Too many domain elements in migration cookie"));
1364             return -1;
1365         }
1366         mig->persistent = virDomainDefParseNode(doc, nodes[0],
1367                                                 driver->xmlopt, qemuCaps,
1368                                                 VIR_DOMAIN_DEF_PARSE_INACTIVE |
1369                                                 VIR_DOMAIN_DEF_PARSE_ABI_UPDATE_MIGRATION |
1370                                                 VIR_DOMAIN_DEF_PARSE_SKIP_VALIDATE);
1371         if (!mig->persistent)
1372             return -1;
1373     }
1374 
1375     if ((flags & QEMU_MIGRATION_COOKIE_NETWORK) &&
1376         virXPathBoolean("count(./network) > 0", ctxt) &&
1377         (!(mig->network = qemuMigrationCookieNetworkXMLParse(ctxt))))
1378         return -1;
1379 
1380     if (flags & QEMU_MIGRATION_COOKIE_NBD &&
1381         virXPathBoolean("boolean(./nbd)", ctxt) &&
1382         (!(mig->nbd = qemuMigrationCookieNBDXMLParse(ctxt))))
1383         return -1;
1384 
1385     if (flags & QEMU_MIGRATION_COOKIE_STATS &&
1386         virXPathBoolean("boolean(./statistics)", ctxt) &&
1387         (!(mig->jobInfo = qemuMigrationCookieStatisticsXMLParse(ctxt))))
1388         return -1;
1389 
1390     if (flags & QEMU_MIGRATION_COOKIE_CPU &&
1391         virCPUDefParseXML(ctxt, "./cpu[1]", VIR_CPU_TYPE_GUEST, &mig->cpu,
1392                           false) < 0)
1393         return -1;
1394 
1395     if (flags & QEMU_MIGRATION_COOKIE_CAPS &&
1396         !(mig->caps = qemuMigrationCookieCapsXMLParse(ctxt)))
1397         return -1;
1398 
1399     if (flags & QEMU_MIGRATION_COOKIE_BLOCK_DIRTY_BITMAPS &&
1400         virXPathBoolean("boolean(./blockDirtyBitmaps)", ctxt) &&
1401         qemuMigrationCookieBlockDirtyBitmapsParse(ctxt, mig) < 0)
1402         return -1;
1403 
1404     return 0;
1405 }
1406 
1407 
1408 static int
qemuMigrationCookieXMLParseStr(qemuMigrationCookie * mig,virQEMUDriver * driver,virQEMUCaps * qemuCaps,const char * xml,unsigned int flags)1409 qemuMigrationCookieXMLParseStr(qemuMigrationCookie *mig,
1410                                virQEMUDriver *driver,
1411                                virQEMUCaps *qemuCaps,
1412                                const char *xml,
1413                                unsigned int flags)
1414 {
1415     g_autoptr(xmlDoc) doc = NULL;
1416     g_autoptr(xmlXPathContext) ctxt = NULL;
1417 
1418     VIR_DEBUG("xml=%s", NULLSTR(xml));
1419 
1420     if (!(doc = virXMLParseStringCtxt(xml, _("(qemu_migration_cookie)"), &ctxt)))
1421         return -1;
1422 
1423     return qemuMigrationCookieXMLParse(mig, driver, qemuCaps, doc, ctxt, flags);
1424 }
1425 
1426 
1427 int
qemuMigrationCookieFormat(qemuMigrationCookie * mig,virQEMUDriver * driver,virDomainObj * dom,qemuMigrationParty party,char ** cookieout,int * cookieoutlen,unsigned int flags)1428 qemuMigrationCookieFormat(qemuMigrationCookie *mig,
1429                           virQEMUDriver *driver,
1430                           virDomainObj *dom,
1431                           qemuMigrationParty party,
1432                           char **cookieout,
1433                           int *cookieoutlen,
1434                           unsigned int flags)
1435 {
1436     qemuDomainObjPrivate *priv = dom->privateData;
1437     g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
1438 
1439     if (!cookieout || !cookieoutlen)
1440         return 0;
1441 
1442     *cookieoutlen = 0;
1443 
1444     if (flags & QEMU_MIGRATION_COOKIE_GRAPHICS &&
1445         qemuMigrationCookieAddGraphics(mig, driver, dom) < 0)
1446         return -1;
1447 
1448     if (flags & QEMU_MIGRATION_COOKIE_LOCKSTATE &&
1449         qemuMigrationCookieAddLockstate(mig, driver, dom) < 0)
1450         return -1;
1451 
1452     if (flags & QEMU_MIGRATION_COOKIE_NETWORK &&
1453         qemuMigrationCookieAddNetwork(mig, driver, dom) < 0) {
1454         return -1;
1455     }
1456 
1457     if ((flags & QEMU_MIGRATION_COOKIE_NBD) &&
1458         qemuMigrationCookieAddNBD(mig, driver, dom) < 0)
1459         return -1;
1460 
1461     if (flags & QEMU_MIGRATION_COOKIE_STATS &&
1462         qemuMigrationCookieAddStatistics(mig, dom) < 0)
1463         return -1;
1464 
1465     if (flags & QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG)
1466         mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_MEMORY_HOTPLUG;
1467 
1468     if (flags & QEMU_MIGRATION_COOKIE_CPU_HOTPLUG)
1469         mig->flagsMandatory |= QEMU_MIGRATION_COOKIE_CPU_HOTPLUG;
1470 
1471     if (flags & QEMU_MIGRATION_COOKIE_CPU &&
1472         qemuMigrationCookieAddCPU(mig, dom) < 0)
1473         return -1;
1474 
1475     if (flags & QEMU_MIGRATION_COOKIE_CAPS &&
1476         qemuMigrationCookieAddCaps(mig, dom, party) < 0)
1477         return -1;
1478 
1479     if (qemuMigrationCookieXMLFormat(driver, priv->qemuCaps, &buf, mig) < 0)
1480         return -1;
1481 
1482     *cookieoutlen = virBufferUse(&buf) + 1;
1483     *cookieout = virBufferContentAndReset(&buf);
1484 
1485     VIR_DEBUG("cookielen=%d cookie=%s", *cookieoutlen, *cookieout);
1486 
1487     return 0;
1488 }
1489 
1490 
1491 qemuMigrationCookie *
qemuMigrationCookieParse(virQEMUDriver * driver,const virDomainDef * def,const char * origname,qemuDomainObjPrivate * priv,const char * cookiein,int cookieinlen,unsigned int flags)1492 qemuMigrationCookieParse(virQEMUDriver *driver,
1493                          const virDomainDef *def,
1494                          const char *origname,
1495                          qemuDomainObjPrivate *priv,
1496                          const char *cookiein,
1497                          int cookieinlen,
1498                          unsigned int flags)
1499 {
1500     g_autoptr(qemuMigrationCookie) mig = NULL;
1501 
1502     /* Parse & validate incoming cookie (if any) */
1503     if (cookiein && cookieinlen &&
1504         cookiein[cookieinlen-1] != '\0') {
1505         virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1506                        _("Migration cookie was not NULL terminated"));
1507         return NULL;
1508     }
1509 
1510     VIR_DEBUG("cookielen=%d cookie='%s'", cookieinlen, NULLSTR(cookiein));
1511 
1512     if (!(mig = qemuMigrationCookieNew(def, origname)))
1513         return NULL;
1514 
1515     if (cookiein && cookieinlen &&
1516         qemuMigrationCookieXMLParseStr(mig,
1517                                        driver,
1518                                        priv ? priv->qemuCaps : NULL,
1519                                        cookiein,
1520                                        flags) < 0)
1521         return NULL;
1522 
1523     if (flags & QEMU_MIGRATION_COOKIE_PERSISTENT &&
1524         mig->persistent &&
1525         STRNEQ(def->name, mig->persistent->name)) {
1526         g_free(mig->persistent->name);
1527         mig->persistent->name = g_strdup(def->name);
1528     }
1529 
1530     if (mig->flags & QEMU_MIGRATION_COOKIE_LOCKSTATE) {
1531         if (!mig->lockDriver) {
1532             if (virLockManagerPluginUsesState(driver->lockManager)) {
1533                 virReportError(VIR_ERR_INTERNAL_ERROR,
1534                                _("Missing %s lock state for migration cookie"),
1535                                virLockManagerPluginGetName(driver->lockManager));
1536                 return NULL;
1537             }
1538         } else if (STRNEQ(mig->lockDriver,
1539                           virLockManagerPluginGetName(driver->lockManager))) {
1540             virReportError(VIR_ERR_INTERNAL_ERROR,
1541                            _("Source host lock driver %s different from target %s"),
1542                            mig->lockDriver,
1543                            virLockManagerPluginGetName(driver->lockManager));
1544             return NULL;
1545         }
1546     }
1547 
1548     if (flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo && priv->job.current)
1549         mig->jobInfo->operation = priv->job.current->operation;
1550 
1551     return g_steal_pointer(&mig);
1552 }
1553 
1554 
1555 /**
1556  * qemuMigrationCookieBlockDirtyBitmapsMatchDisks:
1557  * @def: domain definition
1558  * @disks: list of qemuMigrationBlockDirtyBitmapsDisk *
1559  *
1560  * Matches all of the @disks to the actual domain disk definition objects
1561  * by looking up the target.
1562  */
1563 int
qemuMigrationCookieBlockDirtyBitmapsMatchDisks(virDomainDef * def,GSList * disks)1564 qemuMigrationCookieBlockDirtyBitmapsMatchDisks(virDomainDef *def,
1565                                                GSList *disks)
1566 {
1567     GSList *next;
1568 
1569     for (next = disks; next; next = next->next) {
1570         qemuMigrationBlockDirtyBitmapsDisk *disk = next->data;
1571 
1572         if (!(disk->disk = virDomainDiskByTarget(def, disk->target))) {
1573             virReportError(VIR_ERR_INTERNAL_ERROR,
1574                            _("Can't find disk '%s' in domain definition"),
1575                            disk->target);
1576             return -1;
1577         }
1578 
1579         disk->nodename = disk->disk->src->nodeformat;
1580     }
1581 
1582     return 0;
1583 }
1584 
1585 
1586 /**
1587  * qemuMigrationCookieBlockDirtyBitmapsToParams:
1588  * @disks: list of qemuMigrationBlockDirtyBitmapsDisk
1589  * @mapping: filled with resulting mapping
1590  *
1591  * Converts @disks into the arguments for 'block-bitmap-mapping' migration
1592  * parameter.
1593  */
1594 int
qemuMigrationCookieBlockDirtyBitmapsToParams(GSList * disks,virJSONValue ** mapping)1595 qemuMigrationCookieBlockDirtyBitmapsToParams(GSList *disks,
1596                                              virJSONValue **mapping)
1597 {
1598     g_autoptr(virJSONValue) map = virJSONValueNewArray();
1599     bool hasDisks = false;
1600     GSList *nextdisk;
1601 
1602     for (nextdisk = disks; nextdisk; nextdisk = nextdisk->next) {
1603         qemuMigrationBlockDirtyBitmapsDisk *disk = nextdisk->data;
1604         g_autoptr(virJSONValue) jsondisk = NULL;
1605         g_autoptr(virJSONValue) jsonbitmaps = virJSONValueNewArray();
1606         bool hasBitmaps = false;
1607         GSList *nextbitmap;
1608 
1609         if (disk->skip || !disk->bitmaps)
1610             continue;
1611 
1612         for (nextbitmap = disk->bitmaps; nextbitmap; nextbitmap = nextbitmap->next) {
1613             qemuMigrationBlockDirtyBitmapsDiskBitmap *bitmap = nextbitmap->data;
1614             g_autoptr(virJSONValue) jsonbitmap = NULL;
1615             g_autoptr(virJSONValue) transform = NULL;
1616             const char *bitmapname = bitmap->sourcebitmap;
1617 
1618             if (bitmap->skip)
1619                 continue;
1620 
1621             /* if there isn't an override, use the real name */
1622             if (!bitmapname)
1623                 bitmapname = bitmap->bitmapname;
1624 
1625             if (bitmap->persistent == VIR_TRISTATE_BOOL_YES) {
1626                 if (virJSONValueObjectAdd(&transform,
1627                                           "b:persistent", true,
1628                                           NULL) < 0)
1629                     return -1;
1630             }
1631 
1632             if (virJSONValueObjectAdd(&jsonbitmap,
1633                                       "s:name", bitmapname,
1634                                       "s:alias", bitmap->alias,
1635                                       "A:transform", &transform,
1636                                       NULL) < 0)
1637                 return -1;
1638 
1639             if (virJSONValueArrayAppend(jsonbitmaps, &jsonbitmap) < 0)
1640                 return -1;
1641 
1642             hasBitmaps = true;
1643         }
1644 
1645         if (!hasBitmaps)
1646             continue;
1647 
1648         if (virJSONValueObjectAdd(&jsondisk,
1649                                   "s:node-name", disk->nodename,
1650                                   "s:alias", disk->target,
1651                                   "a:bitmaps", &jsonbitmaps,
1652                                   NULL) < 0)
1653             return -1;
1654 
1655         if (virJSONValueArrayAppend(map, &jsondisk) < 0)
1656             return -1;
1657 
1658         hasDisks = true;
1659     }
1660 
1661     if (!hasDisks)
1662         return 0;
1663 
1664     *mapping = g_steal_pointer(&map);
1665     return 0;
1666 }
1667