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