1 /*
2 * qemu_backup.c: Implementation and handling of the backup jobs
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 "qemu_alias.h"
22 #include "qemu_block.h"
23 #include "qemu_conf.h"
24 #include "qemu_capabilities.h"
25 #include "qemu_monitor.h"
26 #include "qemu_process.h"
27 #include "qemu_backup.h"
28 #include "qemu_monitor_json.h"
29 #include "qemu_checkpoint.h"
30 #include "qemu_command.h"
31 #include "qemu_security.h"
32
33 #include "storage_source.h"
34 #include "storage_source_conf.h"
35 #include "virerror.h"
36 #include "virlog.h"
37 #include "virbuffer.h"
38 #include "viralloc.h"
39 #include "virxml.h"
40 #include "virstring.h"
41 #include "backup_conf.h"
42 #include "virdomaincheckpointobjlist.h"
43
44 #define VIR_FROM_THIS VIR_FROM_QEMU
45
46 VIR_LOG_INIT("qemu.qemu_backup");
47
48
49 static virDomainBackupDef *
qemuDomainGetBackup(virDomainObj * vm)50 qemuDomainGetBackup(virDomainObj *vm)
51 {
52 qemuDomainObjPrivate *priv = vm->privateData;
53
54 if (!priv->backup) {
55 virReportError(VIR_ERR_NO_DOMAIN_BACKUP, "%s",
56 _("no domain backup job present"));
57 return NULL;
58 }
59
60 return priv->backup;
61 }
62
63
64 static int
qemuBackupPrepare(virDomainBackupDef * def)65 qemuBackupPrepare(virDomainBackupDef *def)
66 {
67
68 if (def->type == VIR_DOMAIN_BACKUP_TYPE_PULL) {
69 if (!def->server) {
70 def->server = g_new0(virStorageNetHostDef, 1);
71
72 def->server->transport = VIR_STORAGE_NET_HOST_TRANS_TCP;
73 def->server->name = g_strdup("localhost");
74 }
75
76 switch ((virStorageNetHostTransport) def->server->transport) {
77 case VIR_STORAGE_NET_HOST_TRANS_TCP:
78 /* TODO: Update qemu.conf to provide a port range,
79 * probably starting at 10809, for obtaining automatic
80 * port via virPortAllocatorAcquire, as well as store
81 * somewhere if we need to call virPortAllocatorRelease
82 * during BackupEnd. Until then, user must provide port */
83 if (!def->server->port) {
84 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
85 _("<domainbackup> must specify TCP port for now"));
86 return -1;
87 }
88 break;
89
90 case VIR_STORAGE_NET_HOST_TRANS_UNIX:
91 /* TODO: Do we need to mess with selinux? */
92 break;
93
94 case VIR_STORAGE_NET_HOST_TRANS_RDMA:
95 case VIR_STORAGE_NET_HOST_TRANS_LAST:
96 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
97 _("unexpected transport in <domainbackup>"));
98 return -1;
99 }
100 }
101
102 return 0;
103 }
104
105
106 struct qemuBackupDiskData {
107 virDomainBackupDiskDef *backupdisk;
108 virDomainDiskDef *domdisk;
109 qemuBlockJobData *blockjob;
110 virStorageSource *store;
111 virStorageSource *terminator;
112 virStorageSource *backingStore;
113 char *incrementalBitmap;
114 const char *domdiskIncrementalBitmap; /* name of temporary bitmap installed on disk source */
115 qemuBlockStorageSourceChainData *crdata;
116 bool labelled;
117 bool initialized;
118 bool created;
119 bool added;
120 bool started;
121 bool done;
122 };
123
124
125 static void
qemuBackupDiskDataCleanupOne(virDomainObj * vm,struct qemuBackupDiskData * dd)126 qemuBackupDiskDataCleanupOne(virDomainObj *vm,
127 struct qemuBackupDiskData *dd)
128 {
129 qemuDomainObjPrivate *priv = vm->privateData;
130
131 if (!dd->started) {
132 if (dd->added) {
133 qemuDomainObjEnterMonitor(priv->driver, vm);
134 qemuBlockStorageSourceAttachRollback(priv->mon, dd->crdata->srcdata[0]);
135 ignore_value(qemuDomainObjExitMonitor(priv->driver, vm));
136 }
137
138 if (dd->created) {
139 if (virStorageSourceUnlink(dd->store) < 0)
140 VIR_WARN("Unable to remove just-created %s", NULLSTR(dd->store->path));
141 }
142
143 if (dd->labelled)
144 qemuDomainStorageSourceAccessRevoke(priv->driver, vm, dd->store);
145 }
146
147 if (dd->initialized)
148 virStorageSourceDeinit(dd->store);
149
150 if (dd->blockjob)
151 qemuBlockJobStartupFinalize(vm, dd->blockjob);
152
153 qemuBlockStorageSourceChainDataFree(dd->crdata);
154 virObjectUnref(dd->terminator);
155 g_free(dd->incrementalBitmap);
156 }
157
158
159 static void
qemuBackupDiskDataCleanup(virDomainObj * vm,struct qemuBackupDiskData * dd,size_t ndd)160 qemuBackupDiskDataCleanup(virDomainObj *vm,
161 struct qemuBackupDiskData *dd,
162 size_t ndd)
163 {
164 virErrorPtr orig_err;
165 size_t i;
166
167 if (!dd)
168 return;
169
170 virErrorPreserveLast(&orig_err);
171
172 for (i = 0; i < ndd; i++)
173 qemuBackupDiskDataCleanupOne(vm, dd + i);
174
175 g_free(dd);
176 virErrorRestore(&orig_err);
177 }
178
179
180 int
qemuBackupDiskPrepareOneBitmapsChain(virStorageSource * backingChain,virStorageSource * targetsrc,const char * targetbitmap,const char * incremental,virJSONValue * actions,GHashTable * blockNamedNodeData)181 qemuBackupDiskPrepareOneBitmapsChain(virStorageSource *backingChain,
182 virStorageSource *targetsrc,
183 const char *targetbitmap,
184 const char *incremental,
185 virJSONValue *actions,
186 GHashTable *blockNamedNodeData)
187 {
188 g_autoptr(virJSONValue) tmpactions = NULL;
189
190 if (qemuBlockGetBitmapMergeActions(backingChain, NULL, targetsrc,
191 incremental, targetbitmap, NULL,
192 &tmpactions,
193 blockNamedNodeData) < 0)
194 return -1;
195
196 if (tmpactions &&
197 virJSONValueArrayConcat(actions, tmpactions) < 0)
198 return -1;
199
200 return 0;
201 }
202
203
204 static int
qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData * dd,virJSONValue * actions,bool pull,GHashTable * blockNamedNodeData)205 qemuBackupDiskPrepareOneBitmaps(struct qemuBackupDiskData *dd,
206 virJSONValue *actions,
207 bool pull,
208 GHashTable *blockNamedNodeData)
209 {
210 if (!qemuBlockBitmapChainIsValid(dd->domdisk->src,
211 dd->backupdisk->incremental,
212 blockNamedNodeData)) {
213 virReportError(VIR_ERR_CHECKPOINT_INCONSISTENT,
214 _("missing or broken bitmap '%s' for disk '%s'"),
215 dd->backupdisk->incremental, dd->domdisk->dst);
216 return -1;
217 }
218
219 /* For pull-mode backup, we need the bitmap to be present in the scratch
220 * file as that will be exported. For push-mode backup the bitmap is
221 * actually required on top of the image backing the disk */
222
223 if (pull) {
224 if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
225 dd->store,
226 dd->incrementalBitmap,
227 dd->backupdisk->incremental,
228 actions,
229 blockNamedNodeData) < 0)
230 return -1;
231 } else {
232 if (qemuBackupDiskPrepareOneBitmapsChain(dd->domdisk->src,
233 dd->domdisk->src,
234 dd->incrementalBitmap,
235 dd->backupdisk->incremental,
236 actions,
237 blockNamedNodeData) < 0)
238 return -1;
239
240 dd->domdiskIncrementalBitmap = dd->incrementalBitmap;
241 }
242
243 return 0;
244 }
245
246
247 static int
qemuBackupDiskPrepareDataOne(virDomainObj * vm,virDomainBackupDiskDef * backupdisk,struct qemuBackupDiskData * dd,virJSONValue * actions,bool pull,GHashTable * blockNamedNodeData,virQEMUDriverConfig * cfg)248 qemuBackupDiskPrepareDataOne(virDomainObj *vm,
249 virDomainBackupDiskDef *backupdisk,
250 struct qemuBackupDiskData *dd,
251 virJSONValue *actions,
252 bool pull,
253 GHashTable *blockNamedNodeData,
254 virQEMUDriverConfig *cfg)
255 {
256 qemuDomainObjPrivate *priv = vm->privateData;
257
258 /* set data structure */
259 dd->backupdisk = backupdisk;
260 dd->store = dd->backupdisk->store;
261
262 if (!(dd->domdisk = virDomainDiskByTarget(vm->def, dd->backupdisk->name))) {
263 virReportError(VIR_ERR_INVALID_ARG,
264 _("no disk named '%s'"), dd->backupdisk->name);
265 return -1;
266 }
267
268 if (!qemuDomainDiskBlockJobIsSupported(vm, dd->domdisk))
269 return -1;
270
271 if (dd->store->format == VIR_STORAGE_FILE_NONE) {
272 dd->store->format = VIR_STORAGE_FILE_QCOW2;
273 } else if (dd->store->format != VIR_STORAGE_FILE_QCOW2) {
274 if (pull) {
275 virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
276 _("pull mode backup for disk '%s' requires qcow2 driver"),
277 dd->backupdisk->name);
278 return -1;
279 }
280 }
281
282 /* calculate backing store to use:
283 * push mode:
284 * full backups: no backing store
285 * incremental: original disk if format supports backing store
286 * pull mode:
287 * both: original disk
288 */
289 if (pull || (dd->backupdisk->incremental &&
290 dd->store->format >= VIR_STORAGE_FILE_BACKING)) {
291 dd->backingStore = dd->domdisk->src;
292 } else {
293 dd->backingStore = dd->terminator = virStorageSourceNew();
294 }
295
296 if (qemuDomainPrepareStorageSourceBlockdev(NULL, dd->store, priv, cfg) < 0)
297 return -1;
298
299 if (dd->backupdisk->incremental) {
300 /* We deliberately don't check the config of the disk in the checkpoint
301 * definition as it's not guaranteed that the disks still correspond.
302 * We just verify that a checkpoint exists and later on that the disk
303 * has corresponding bitmap. */
304 if (!virDomainCheckpointFindByName(vm->checkpoints, dd->backupdisk->incremental)) {
305 virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT,
306 _("Checkpoint '%s' for incremental backup of disk '%s' not found"),
307 dd->backupdisk->incremental, dd->backupdisk->name);
308 return -1;
309 }
310
311 if (dd->backupdisk->exportbitmap)
312 dd->incrementalBitmap = g_strdup(dd->backupdisk->exportbitmap);
313 else
314 dd->incrementalBitmap = g_strdup_printf("backup-%s", dd->domdisk->dst);
315
316 if (qemuBackupDiskPrepareOneBitmaps(dd, actions, pull, blockNamedNodeData) < 0)
317 return -1;
318 }
319
320 if (!(dd->blockjob = qemuBlockJobDiskNewBackup(vm, dd->domdisk, dd->store,
321 dd->domdiskIncrementalBitmap)))
322 return -1;
323
324 /* use original disk as backing to prevent opening the backing chain */
325 if (!(dd->crdata = qemuBuildStorageSourceChainAttachPrepareBlockdevTop(dd->store,
326 dd->backingStore)))
327 return -1;
328
329 return 0;
330 }
331
332
333 static int
qemuBackupDiskPrepareDataOnePush(virJSONValue * actions,struct qemuBackupDiskData * dd)334 qemuBackupDiskPrepareDataOnePush(virJSONValue *actions,
335 struct qemuBackupDiskData *dd)
336 {
337 qemuMonitorTransactionBackupSyncMode syncmode = QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_FULL;
338
339 if (dd->incrementalBitmap)
340 syncmode = QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_INCREMENTAL;
341
342 if (qemuMonitorTransactionBackup(actions,
343 dd->domdisk->src->nodeformat,
344 dd->blockjob->name,
345 dd->store->nodeformat,
346 dd->incrementalBitmap,
347 syncmode) < 0)
348 return -1;
349
350 return 0;
351 }
352
353
354 static int
qemuBackupDiskPrepareDataOnePull(virJSONValue * actions,struct qemuBackupDiskData * dd)355 qemuBackupDiskPrepareDataOnePull(virJSONValue *actions,
356 struct qemuBackupDiskData *dd)
357 {
358 if (!dd->backupdisk->exportbitmap &&
359 dd->incrementalBitmap)
360 dd->backupdisk->exportbitmap = g_strdup(dd->incrementalBitmap);
361
362 if (qemuMonitorTransactionBackup(actions,
363 dd->domdisk->src->nodeformat,
364 dd->blockjob->name,
365 dd->store->nodeformat,
366 NULL,
367 QEMU_MONITOR_TRANSACTION_BACKUP_SYNC_MODE_NONE) < 0)
368 return -1;
369
370 return 0;
371 }
372
373
374 static ssize_t
qemuBackupDiskPrepareData(virDomainObj * vm,virDomainBackupDef * def,GHashTable * blockNamedNodeData,virJSONValue * actions,virQEMUDriverConfig * cfg,struct qemuBackupDiskData ** rdd)375 qemuBackupDiskPrepareData(virDomainObj *vm,
376 virDomainBackupDef *def,
377 GHashTable *blockNamedNodeData,
378 virJSONValue *actions,
379 virQEMUDriverConfig *cfg,
380 struct qemuBackupDiskData **rdd)
381 {
382 struct qemuBackupDiskData *disks = NULL;
383 ssize_t ndisks = 0;
384 size_t i;
385 bool pull = def->type == VIR_DOMAIN_BACKUP_TYPE_PULL;
386
387 disks = g_new0(struct qemuBackupDiskData, def->ndisks);
388
389 for (i = 0; i < def->ndisks; i++) {
390 virDomainBackupDiskDef *backupdisk = &def->disks[i];
391 struct qemuBackupDiskData *dd = disks + ndisks;
392
393 if (!backupdisk->store)
394 continue;
395
396 ndisks++;
397
398 if (qemuBackupDiskPrepareDataOne(vm, backupdisk, dd, actions, pull,
399 blockNamedNodeData, cfg) < 0)
400 goto error;
401
402 if (pull) {
403 if (qemuBackupDiskPrepareDataOnePull(actions, dd) < 0)
404 goto error;
405 } else {
406 if (qemuBackupDiskPrepareDataOnePush(actions, dd) < 0)
407 goto error;
408 }
409 }
410
411 *rdd = g_steal_pointer(&disks);
412
413 return ndisks;
414
415 error:
416 qemuBackupDiskDataCleanup(vm, disks, ndisks);
417 return -1;
418 }
419
420
421 static int
qemuBackupDiskPrepareOneStorage(virDomainObj * vm,GHashTable * blockNamedNodeData,struct qemuBackupDiskData * dd,bool reuse_external)422 qemuBackupDiskPrepareOneStorage(virDomainObj *vm,
423 GHashTable *blockNamedNodeData,
424 struct qemuBackupDiskData *dd,
425 bool reuse_external)
426 {
427 qemuDomainObjPrivate *priv = vm->privateData;
428 int rc;
429
430 if (!reuse_external &&
431 dd->store->type == VIR_STORAGE_TYPE_FILE &&
432 virStorageSourceSupportsCreate(dd->store)) {
433
434 if (virFileExists(dd->store->path)) {
435 virReportError(VIR_ERR_INVALID_ARG,
436 _("store '%s' for backup of '%s' exists"),
437 dd->store->path, dd->domdisk->dst);
438 return -1;
439 }
440
441 if (qemuDomainStorageFileInit(priv->driver, vm, dd->store, dd->domdisk->src) < 0)
442 return -1;
443
444 dd->initialized = true;
445
446 if (virStorageSourceCreate(dd->store) < 0) {
447 virReportSystemError(errno,
448 _("failed to create image file '%s'"),
449 NULLSTR(dd->store->path));
450 return -1;
451 }
452
453 dd->created = true;
454 }
455
456 if (qemuDomainStorageSourceAccessAllow(priv->driver, vm, dd->store,
457 false, true, true) < 0)
458 return -1;
459
460 dd->labelled = true;
461
462 if (!reuse_external) {
463 if (qemuBlockStorageSourceCreateDetectSize(blockNamedNodeData,
464 dd->store, dd->domdisk->src) < 0)
465 return -1;
466
467 if (qemuBlockStorageSourceCreate(vm, dd->store, dd->backingStore, NULL,
468 dd->crdata->srcdata[0],
469 QEMU_ASYNC_JOB_BACKUP) < 0)
470 return -1;
471 } else {
472 if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, QEMU_ASYNC_JOB_BACKUP) < 0)
473 return -1;
474
475 rc = qemuBlockStorageSourceAttachApply(priv->mon, dd->crdata->srcdata[0]);
476
477 if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
478 return -1;
479 }
480
481 dd->added = true;
482
483 return 0;
484 }
485
486
487 static int
qemuBackupDiskPrepareStorage(virDomainObj * vm,struct qemuBackupDiskData * disks,size_t ndisks,GHashTable * blockNamedNodeData,bool reuse_external)488 qemuBackupDiskPrepareStorage(virDomainObj *vm,
489 struct qemuBackupDiskData *disks,
490 size_t ndisks,
491 GHashTable *blockNamedNodeData,
492 bool reuse_external)
493 {
494 size_t i;
495
496 for (i = 0; i < ndisks; i++) {
497 if (qemuBackupDiskPrepareOneStorage(vm, blockNamedNodeData, disks + i,
498 reuse_external) < 0)
499 return -1;
500 }
501
502 return 0;
503 }
504
505
506 static void
qemuBackupDiskStarted(virDomainObj * vm,struct qemuBackupDiskData * dd,size_t ndd)507 qemuBackupDiskStarted(virDomainObj *vm,
508 struct qemuBackupDiskData *dd,
509 size_t ndd)
510 {
511 size_t i;
512
513 for (i = 0; i < ndd; i++) {
514 dd[i].started = true;
515 dd[i].backupdisk->state = VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING;
516 qemuBlockJobStarted(dd[i].blockjob, vm);
517 }
518 }
519
520
521 /**
522 * qemuBackupBeginPullExportDisks:
523 * @vm: domain object
524 * @disks: backup disk data list
525 * @ndisks: number of valid disks in @disks
526 *
527 * Exports all disks from @dd when doing a pull backup in the NBD server. This
528 * function must be called while in the monitor context.
529 */
530 static int
qemuBackupBeginPullExportDisks(virDomainObj * vm,struct qemuBackupDiskData * disks,size_t ndisks)531 qemuBackupBeginPullExportDisks(virDomainObj *vm,
532 struct qemuBackupDiskData *disks,
533 size_t ndisks)
534 {
535 size_t i;
536
537 for (i = 0; i < ndisks; i++) {
538 struct qemuBackupDiskData *dd = disks + i;
539
540 if (!dd->backupdisk->exportname)
541 dd->backupdisk->exportname = g_strdup(dd->domdisk->dst);
542
543 if (qemuBlockExportAddNBD(vm, NULL,
544 dd->store,
545 dd->backupdisk->exportname,
546 false,
547 dd->incrementalBitmap) < 0)
548 return -1;
549 }
550
551 return 0;
552 }
553
554
555 void
qemuBackupJobTerminate(virDomainObj * vm,qemuDomainJobStatus jobstatus)556 qemuBackupJobTerminate(virDomainObj *vm,
557 qemuDomainJobStatus jobstatus)
558
559 {
560 qemuDomainObjPrivate *priv = vm->privateData;
561 g_autoptr(virQEMUDriverConfig) cfg = NULL;
562 size_t i;
563
564 for (i = 0; i < priv->backup->ndisks; i++) {
565 virDomainBackupDiskDef *backupdisk = priv->backup->disks + i;
566
567 if (!backupdisk->store)
568 continue;
569
570 /* restore security label on the images in case the blockjob finishing
571 * handler didn't do so, such as when the VM was destroyed */
572 if (backupdisk->state == VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING ||
573 backupdisk->state == VIR_DOMAIN_BACKUP_DISK_STATE_NONE) {
574 if (qemuSecurityRestoreImageLabel(priv->driver, vm, backupdisk->store,
575 false) < 0)
576 VIR_WARN("Unable to restore security label on %s",
577 NULLSTR(backupdisk->store->path));
578 }
579
580 /* delete unneeded images created by libvirt */
581 if (backupdisk->store->type == VIR_STORAGE_TYPE_FILE &&
582 !(priv->backup->apiFlags & VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL) &&
583 (priv->backup->type == VIR_DOMAIN_BACKUP_TYPE_PULL ||
584 (priv->backup->type == VIR_DOMAIN_BACKUP_TYPE_PUSH &&
585 jobstatus != QEMU_DOMAIN_JOB_STATUS_COMPLETED))) {
586
587 uid_t uid;
588 gid_t gid;
589
590 if (!cfg)
591 cfg = virQEMUDriverGetConfig(priv->driver);
592
593 qemuDomainGetImageIds(cfg, vm, backupdisk->store, NULL, &uid, &gid);
594
595 if (virFileRemove(backupdisk->store->path, uid, gid) < 0)
596 VIR_WARN("failed to remove scratch file '%s'",
597 backupdisk->store->path);
598 }
599 }
600
601 if (priv->job.current) {
602 qemuDomainJobInfoUpdateTime(priv->job.current);
603
604 g_clear_pointer(&priv->job.completed, qemuDomainJobInfoFree);
605 priv->job.completed = qemuDomainJobInfoCopy(priv->job.current);
606
607 priv->job.completed->stats.backup.total = priv->backup->push_total;
608 priv->job.completed->stats.backup.transferred = priv->backup->push_transferred;
609 priv->job.completed->stats.backup.tmp_used = priv->backup->pull_tmp_used;
610 priv->job.completed->stats.backup.tmp_total = priv->backup->pull_tmp_total;
611
612 priv->job.completed->status = jobstatus;
613 priv->job.completed->errmsg = g_strdup(priv->backup->errmsg);
614
615 qemuDomainEventEmitJobCompleted(priv->driver, vm);
616 }
617
618 virDomainBackupDefFree(priv->backup);
619 priv->backup = NULL;
620
621 if (priv->job.asyncJob == QEMU_ASYNC_JOB_BACKUP)
622 qemuDomainObjEndAsyncJob(priv->driver, vm);
623 }
624
625
626 /**
627 * qemuBackupJobCancelBlockjobs:
628 * @vm: domain object
629 * @backup: backup definition
630 * @terminatebackup: flag whether to terminate and unregister the backup
631 * @asyncJob: currently used qemu asynchronous job type
632 *
633 * Sends all active blockjobs which are part of @backup of @vm a signal to
634 * cancel. If @terminatebackup is true qemuBackupJobTerminate is also called
635 * if there are no outstanding active blockjobs.
636 */
637 void
qemuBackupJobCancelBlockjobs(virDomainObj * vm,virDomainBackupDef * backup,bool terminatebackup,int asyncJob)638 qemuBackupJobCancelBlockjobs(virDomainObj *vm,
639 virDomainBackupDef *backup,
640 bool terminatebackup,
641 int asyncJob)
642 {
643 qemuDomainObjPrivate *priv = vm->privateData;
644 size_t i;
645 int rc = 0;
646 bool has_active = false;
647
648 if (!backup)
649 return;
650
651 for (i = 0; i < backup->ndisks; i++) {
652 virDomainBackupDiskDef *backupdisk = backup->disks + i;
653 virDomainDiskDef *disk;
654 g_autoptr(qemuBlockJobData) job = NULL;
655
656 if (!backupdisk->store)
657 continue;
658
659 /* Look up corresponding disk as backupdisk->idx is no longer reliable */
660 if (!(disk = virDomainDiskByTarget(vm->def, backupdisk->name)))
661 continue;
662
663 if (!(job = qemuBlockJobDiskGetJob(disk)))
664 continue;
665
666 if (backupdisk->state != VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING &&
667 backupdisk->state != VIR_DOMAIN_BACKUP_DISK_STATE_CANCELLING)
668 continue;
669
670 has_active = true;
671
672 if (backupdisk->state != VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING)
673 continue;
674
675 if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
676 return;
677
678 rc = qemuMonitorBlockJobCancel(priv->mon, job->name, true);
679
680 if (qemuDomainObjExitMonitor(priv->driver, vm) < 0)
681 return;
682
683 if (rc == 0) {
684 backupdisk->state = VIR_DOMAIN_BACKUP_DISK_STATE_CANCELLING;
685 job->state = QEMU_BLOCKJOB_STATE_ABORTING;
686 }
687 }
688
689 if (terminatebackup && !has_active)
690 qemuBackupJobTerminate(vm, QEMU_DOMAIN_JOB_STATUS_CANCELED);
691 }
692
693
694 #define QEMU_BACKUP_TLS_ALIAS_BASE "libvirt_backup"
695
696 static int
qemuBackupBeginPrepareTLS(virDomainObj * vm,virQEMUDriverConfig * cfg,virDomainBackupDef * def,virJSONValue ** tlsProps,virJSONValue ** tlsSecretProps)697 qemuBackupBeginPrepareTLS(virDomainObj *vm,
698 virQEMUDriverConfig *cfg,
699 virDomainBackupDef *def,
700 virJSONValue **tlsProps,
701 virJSONValue **tlsSecretProps)
702 {
703 qemuDomainObjPrivate *priv = vm->privateData;
704 g_autofree char *tlsObjAlias = qemuAliasTLSObjFromSrcAlias(QEMU_BACKUP_TLS_ALIAS_BASE);
705 g_autoptr(qemuDomainSecretInfo) secinfo = NULL;
706 const char *tlsKeySecretAlias = NULL;
707
708 if (def->tls != VIR_TRISTATE_BOOL_YES)
709 return 0;
710
711 if (!cfg->backupTLSx509certdir) {
712 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
713 _("backup TLS directory not configured"));
714 return -1;
715 }
716
717 if (cfg->backupTLSx509secretUUID) {
718 if (!(secinfo = qemuDomainSecretInfoTLSNew(priv, tlsObjAlias,
719 cfg->backupTLSx509secretUUID)))
720 return -1;
721
722 if (qemuBuildSecretInfoProps(secinfo, tlsSecretProps) < 0)
723 return -1;
724
725 tlsKeySecretAlias = secinfo->alias;
726 }
727
728 if (qemuBuildTLSx509BackendProps(cfg->backupTLSx509certdir, true,
729 cfg->backupTLSx509verify, tlsObjAlias,
730 tlsKeySecretAlias,
731 tlsProps) < 0)
732 return -1;
733
734 return 0;
735 }
736
737
738 int
qemuBackupBegin(virDomainObj * vm,const char * backupXML,const char * checkpointXML,unsigned int flags)739 qemuBackupBegin(virDomainObj *vm,
740 const char *backupXML,
741 const char *checkpointXML,
742 unsigned int flags)
743 {
744 qemuDomainObjPrivate *priv = vm->privateData;
745 g_autoptr(virQEMUDriverConfig) cfg = virQEMUDriverGetConfig(priv->driver);
746 g_autoptr(virDomainBackupDef) def = NULL;
747 g_autofree char *suffix = NULL;
748 bool pull = false;
749 virDomainMomentObj *chk = NULL;
750 g_autoptr(virDomainCheckpointDef) chkdef = NULL;
751 g_autoptr(virJSONValue) actions = NULL;
752 g_autoptr(virJSONValue) tlsProps = NULL;
753 g_autofree char *tlsAlias = NULL;
754 g_autoptr(virJSONValue) tlsSecretProps = NULL;
755 g_autofree char *tlsSecretAlias = NULL;
756 struct qemuBackupDiskData *dd = NULL;
757 ssize_t ndd = 0;
758 g_autoptr(GHashTable) blockNamedNodeData = NULL;
759 bool job_started = false;
760 bool nbd_running = false;
761 bool reuse = (flags & VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL);
762 int rc = 0;
763 int ret = -1;
764
765 virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_REUSE_EXTERNAL, -1);
766
767 if (!(def = virDomainBackupDefParseString(backupXML, priv->driver->xmlopt, 0)))
768 return -1;
769
770 if (checkpointXML) {
771 if (!(chkdef = virDomainCheckpointDefParseString(checkpointXML,
772 priv->driver->xmlopt,
773 priv->qemuCaps, 0)))
774 return -1;
775
776 suffix = g_strdup(chkdef->parent.name);
777 } else {
778 gint64 now_us = g_get_real_time();
779 suffix = g_strdup_printf("%lld", (long long)now_us/(1000*1000));
780 }
781
782 if (def->type == VIR_DOMAIN_BACKUP_TYPE_PULL)
783 pull = true;
784
785 def->apiFlags = flags;
786
787 /* we'll treat this kind of backup job as an asyncjob as it uses some of the
788 * infrastructure for async jobs. We'll allow standard modify-type jobs
789 * as the interlocking of conflicting operations is handled on the block
790 * job level */
791 if (qemuDomainObjBeginAsyncJob(priv->driver, vm, QEMU_ASYNC_JOB_BACKUP,
792 VIR_DOMAIN_JOB_OPERATION_BACKUP, flags) < 0)
793 return -1;
794
795 qemuDomainObjSetAsyncJobMask(vm, (QEMU_JOB_DEFAULT_MASK |
796 JOB_MASK(QEMU_JOB_SUSPEND) |
797 JOB_MASK(QEMU_JOB_MODIFY)));
798 priv->job.current->statsType = QEMU_DOMAIN_JOB_STATS_TYPE_BACKUP;
799
800 if (!virDomainObjIsActive(vm)) {
801 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
802 _("cannot perform disk backup for inactive domain"));
803 goto endjob;
804 }
805
806 if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_BLOCKDEV_BACKUP)) {
807 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
808 _("backup is not supported with this QEMU"));
809 goto endjob;
810 }
811
812 if (virDomainBackupAlignDisks(def, vm->def, suffix) < 0)
813 goto endjob;
814
815 if (!virQEMUCapsGet(priv->qemuCaps, QEMU_CAPS_INCREMENTAL_BACKUP)) {
816 size_t i;
817
818 if (chkdef) {
819 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
820 _("creating checkpoint for incremental backup is not supported yet"));
821 goto endjob;
822 }
823
824 for (i = 0; i < def->ndisks; i++) {
825 if (def->disks[i].backupmode == VIR_DOMAIN_BACKUP_DISK_BACKUP_MODE_INCREMENTAL) {
826 virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s",
827 _("incremental backup is not supported yet"));
828 goto endjob;
829 }
830 }
831 }
832
833 if (priv->backup) {
834 virReportError(VIR_ERR_OPERATION_INVALID, "%s",
835 _("another backup job is already running"));
836 goto endjob;
837 }
838
839 if (qemuBackupPrepare(def) < 0)
840 goto endjob;
841
842 if (qemuBackupBeginPrepareTLS(vm, cfg, def, &tlsProps, &tlsSecretProps) < 0)
843 goto endjob;
844
845 actions = virJSONValueNewArray();
846
847 /* The 'chk' checkpoint must be rolled back if the transaction command
848 * which creates it on disk is not executed or fails */
849 if (chkdef) {
850 if (qemuCheckpointCreateCommon(priv->driver, vm, &chkdef,
851 &actions, &chk) < 0)
852 goto endjob;
853 }
854
855 if (!(blockNamedNodeData = qemuBlockGetNamedNodeData(vm, QEMU_ASYNC_JOB_BACKUP)))
856 goto endjob;
857
858 if ((ndd = qemuBackupDiskPrepareData(vm, def, blockNamedNodeData, actions,
859 cfg, &dd)) <= 0) {
860 if (ndd == 0) {
861 virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
862 _("no disks selected for backup"));
863 }
864
865 goto endjob;
866 }
867
868 if (qemuBackupDiskPrepareStorage(vm, dd, ndd, blockNamedNodeData, reuse) < 0)
869 goto endjob;
870
871 priv->backup = g_steal_pointer(&def);
872
873 if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, QEMU_ASYNC_JOB_BACKUP) < 0)
874 goto endjob;
875
876 if (pull) {
877 if (tlsSecretProps)
878 rc = qemuMonitorAddObject(priv->mon, &tlsSecretProps, &tlsSecretAlias);
879
880 if (rc == 0 && tlsProps)
881 rc = qemuMonitorAddObject(priv->mon, &tlsProps, &tlsAlias);
882
883 if (rc == 0) {
884 if ((rc = qemuMonitorNBDServerStart(priv->mon, priv->backup->server, tlsAlias)) == 0)
885 nbd_running = true;
886 }
887 }
888
889 if (rc == 0)
890 rc = qemuMonitorTransaction(priv->mon, &actions);
891
892 if (qemuDomainObjExitMonitor(priv->driver, vm) < 0 || rc < 0)
893 goto endjob;
894
895 job_started = true;
896 priv->backup->tlsAlias = g_steal_pointer(&tlsAlias);
897 priv->backup->tlsSecretAlias = g_steal_pointer(&tlsSecretAlias);
898 /* qemuBackupDiskStarted saves the status XML */
899 qemuBackupDiskStarted(vm, dd, ndd);
900
901 if (chk) {
902 virDomainMomentObj *tmpchk = g_steal_pointer(&chk);
903 if (qemuCheckpointCreateFinalize(priv->driver, vm, cfg, tmpchk, true) < 0)
904 goto endjob;
905 }
906
907 if (pull) {
908 if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, QEMU_ASYNC_JOB_BACKUP) < 0)
909 goto endjob;
910 /* note that if the export fails we've already created the checkpoint
911 * and we will not delete it */
912 rc = qemuBackupBeginPullExportDisks(vm, dd, ndd);
913 if (qemuDomainObjExitMonitor(priv->driver, vm) < 0)
914 goto endjob;
915
916 if (rc < 0) {
917 qemuBackupJobCancelBlockjobs(vm, priv->backup, false, QEMU_ASYNC_JOB_BACKUP);
918 goto endjob;
919 }
920 }
921
922 ret = 0;
923
924 endjob:
925 qemuBackupDiskDataCleanup(vm, dd, ndd);
926
927 /* if 'chk' is non-NULL here it's a failure and it must be rolled back */
928 qemuCheckpointRollbackMetadata(vm, chk);
929
930 if (!job_started && (nbd_running || tlsAlias || tlsSecretAlias) &&
931 qemuDomainObjEnterMonitorAsync(priv->driver, vm, QEMU_ASYNC_JOB_BACKUP) == 0) {
932 if (nbd_running)
933 ignore_value(qemuMonitorNBDServerStop(priv->mon));
934 if (tlsAlias)
935 ignore_value(qemuMonitorDelObject(priv->mon, tlsAlias, false));
936 if (tlsSecretAlias)
937 ignore_value(qemuMonitorDelObject(priv->mon, tlsSecretAlias, false));
938 ignore_value(qemuDomainObjExitMonitor(priv->driver, vm));
939 }
940
941 if (ret < 0 && !job_started && priv->backup)
942 def = g_steal_pointer(&priv->backup);
943
944 if (ret == 0)
945 qemuDomainObjReleaseAsyncJob(vm);
946 else
947 qemuDomainObjEndAsyncJob(priv->driver, vm);
948
949 return ret;
950 }
951
952
953 char *
qemuBackupGetXMLDesc(virDomainObj * vm,unsigned int flags)954 qemuBackupGetXMLDesc(virDomainObj *vm,
955 unsigned int flags)
956 {
957 qemuDomainObjPrivate *priv = vm->privateData;
958
959 g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
960 virDomainBackupDef *backup;
961
962 virCheckFlags(0, NULL);
963
964 if (!(backup = qemuDomainGetBackup(vm)))
965 return NULL;
966
967 if (virDomainBackupDefFormat(&buf, backup, false, priv->driver->xmlopt) < 0)
968 return NULL;
969
970 return virBufferContentAndReset(&buf);
971 }
972
973
974 void
qemuBackupNotifyBlockjobEnd(virDomainObj * vm,virDomainDiskDef * disk,qemuBlockjobState state,const char * errmsg,unsigned long long cur,unsigned long long end,int asyncJob)975 qemuBackupNotifyBlockjobEnd(virDomainObj *vm,
976 virDomainDiskDef *disk,
977 qemuBlockjobState state,
978 const char *errmsg,
979 unsigned long long cur,
980 unsigned long long end,
981 int asyncJob)
982 {
983 qemuDomainObjPrivate *priv = vm->privateData;
984 bool has_running = false;
985 bool has_cancelling = false;
986 bool has_cancelled = false;
987 bool has_failed = false;
988 qemuDomainJobStatus jobstatus = QEMU_DOMAIN_JOB_STATUS_COMPLETED;
989 virDomainBackupDef *backup = priv->backup;
990 size_t i;
991
992 VIR_DEBUG("vm: '%s', disk:'%s', state:'%d' errmsg:'%s'",
993 vm->def->name, disk->dst, state, NULLSTR(errmsg));
994
995 if (!backup)
996 return;
997
998 if (backup->type == VIR_DOMAIN_BACKUP_TYPE_PULL) {
999 if (qemuDomainObjEnterMonitorAsync(priv->driver, vm, asyncJob) < 0)
1000 return;
1001 ignore_value(qemuMonitorNBDServerStop(priv->mon));
1002 if (backup->tlsAlias)
1003 ignore_value(qemuMonitorDelObject(priv->mon, backup->tlsAlias, false));
1004 if (backup->tlsSecretAlias)
1005 ignore_value(qemuMonitorDelObject(priv->mon, backup->tlsSecretAlias, false));
1006 if (qemuDomainObjExitMonitor(priv->driver, vm) < 0)
1007 return;
1008
1009 /* update the final statistics with the current job's data */
1010 backup->pull_tmp_used += cur;
1011 backup->pull_tmp_total += end;
1012 } else {
1013 backup->push_transferred += cur;
1014 backup->push_total += end;
1015 }
1016
1017 /* record first error message */
1018 if (!backup->errmsg)
1019 backup->errmsg = g_strdup(errmsg);
1020
1021 for (i = 0; i < backup->ndisks; i++) {
1022 virDomainBackupDiskDef *backupdisk = backup->disks + i;
1023
1024 if (!backupdisk->store)
1025 continue;
1026
1027 if (STREQ(disk->dst, backupdisk->name)) {
1028 switch (state) {
1029 case QEMU_BLOCKJOB_STATE_COMPLETED:
1030 backupdisk->state = VIR_DOMAIN_BACKUP_DISK_STATE_COMPLETE;
1031 break;
1032
1033 case QEMU_BLOCKJOB_STATE_CONCLUDED:
1034 case QEMU_BLOCKJOB_STATE_FAILED:
1035 backupdisk->state = VIR_DOMAIN_BACKUP_DISK_STATE_FAILED;
1036 break;
1037
1038 case QEMU_BLOCKJOB_STATE_CANCELLED:
1039 backupdisk->state = VIR_DOMAIN_BACKUP_DISK_STATE_CANCELLED;
1040 break;
1041
1042 case QEMU_BLOCKJOB_STATE_READY:
1043 case QEMU_BLOCKJOB_STATE_NEW:
1044 case QEMU_BLOCKJOB_STATE_RUNNING:
1045 case QEMU_BLOCKJOB_STATE_ABORTING:
1046 case QEMU_BLOCKJOB_STATE_PIVOTING:
1047 case QEMU_BLOCKJOB_STATE_LAST:
1048 default:
1049 break;
1050 }
1051 }
1052
1053 switch (backupdisk->state) {
1054 case VIR_DOMAIN_BACKUP_DISK_STATE_COMPLETE:
1055 break;
1056
1057 case VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING:
1058 has_running = true;
1059 break;
1060
1061 case VIR_DOMAIN_BACKUP_DISK_STATE_CANCELLING:
1062 has_cancelling = true;
1063 break;
1064
1065 case VIR_DOMAIN_BACKUP_DISK_STATE_FAILED:
1066 has_failed = true;
1067 break;
1068
1069 case VIR_DOMAIN_BACKUP_DISK_STATE_CANCELLED:
1070 has_cancelled = true;
1071 break;
1072
1073 case VIR_DOMAIN_BACKUP_DISK_STATE_NONE:
1074 case VIR_DOMAIN_BACKUP_DISK_STATE_LAST:
1075 break;
1076 }
1077 }
1078
1079 if (has_running && (has_failed || has_cancelled)) {
1080 /* cancel the rest of the jobs */
1081 qemuBackupJobCancelBlockjobs(vm, backup, false, asyncJob);
1082 } else if (!has_running && !has_cancelling) {
1083 /* all sub-jobs have stopped */
1084
1085 if (has_failed)
1086 jobstatus = QEMU_DOMAIN_JOB_STATUS_FAILED;
1087 else if (has_cancelled && backup->type == VIR_DOMAIN_BACKUP_TYPE_PUSH)
1088 jobstatus = QEMU_DOMAIN_JOB_STATUS_CANCELED;
1089
1090 qemuBackupJobTerminate(vm, jobstatus);
1091 }
1092
1093 /* otherwise we must wait for the jobs to end */
1094 }
1095
1096
1097 static void
qemuBackupGetJobInfoStatsUpdateOne(virDomainObj * vm,bool push,const char * diskdst,qemuDomainBackupStats * stats,qemuMonitorJobInfo ** blockjobs,size_t nblockjobs)1098 qemuBackupGetJobInfoStatsUpdateOne(virDomainObj *vm,
1099 bool push,
1100 const char *diskdst,
1101 qemuDomainBackupStats *stats,
1102 qemuMonitorJobInfo **blockjobs,
1103 size_t nblockjobs)
1104 {
1105 virDomainDiskDef *domdisk;
1106 qemuMonitorJobInfo *monblockjob = NULL;
1107 g_autoptr(qemuBlockJobData) diskblockjob = NULL;
1108 size_t i;
1109
1110 /* it's just statistics so let's not worry so much about errors */
1111 if (!(domdisk = virDomainDiskByTarget(vm->def, diskdst)))
1112 return;
1113
1114 if (!(diskblockjob = qemuBlockJobDiskGetJob(domdisk)))
1115 return;
1116
1117 for (i = 0; i < nblockjobs; i++) {
1118 if (STREQ_NULLABLE(blockjobs[i]->id, diskblockjob->name)) {
1119 monblockjob = blockjobs[i];
1120 break;
1121 }
1122 }
1123 if (!monblockjob)
1124 return;
1125
1126 if (push) {
1127 stats->total += monblockjob->progressTotal;
1128 stats->transferred += monblockjob->progressCurrent;
1129 } else {
1130 stats->tmp_used += monblockjob->progressCurrent;
1131 stats->tmp_total += monblockjob->progressTotal;
1132 }
1133 }
1134
1135
1136 int
qemuBackupGetJobInfoStats(virQEMUDriver * driver,virDomainObj * vm,qemuDomainJobInfo * jobInfo)1137 qemuBackupGetJobInfoStats(virQEMUDriver *driver,
1138 virDomainObj *vm,
1139 qemuDomainJobInfo *jobInfo)
1140 {
1141 qemuDomainBackupStats *stats = &jobInfo->stats.backup;
1142 qemuDomainObjPrivate *priv = vm->privateData;
1143 qemuMonitorJobInfo **blockjobs = NULL;
1144 size_t nblockjobs = 0;
1145 size_t i;
1146 int rc;
1147 int ret = -1;
1148
1149 if (!priv->backup) {
1150 virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
1151 _("backup job data missing"));
1152 return -1;
1153 }
1154
1155 if (qemuDomainJobInfoUpdateTime(jobInfo) < 0)
1156 return -1;
1157
1158 jobInfo->status = QEMU_DOMAIN_JOB_STATUS_ACTIVE;
1159
1160 qemuDomainObjEnterMonitor(driver, vm);
1161
1162 rc = qemuMonitorGetJobInfo(priv->mon, &blockjobs, &nblockjobs);
1163
1164 if (qemuDomainObjExitMonitor(driver, vm) < 0 || rc < 0)
1165 goto cleanup;
1166
1167 /* count in completed jobs */
1168 stats->total = priv->backup->push_total;
1169 stats->transferred = priv->backup->push_transferred;
1170 stats->tmp_used = priv->backup->pull_tmp_used;
1171 stats->tmp_total = priv->backup->pull_tmp_total;
1172
1173 for (i = 0; i < priv->backup->ndisks; i++) {
1174 if (priv->backup->disks[i].state != VIR_DOMAIN_BACKUP_DISK_STATE_RUNNING)
1175 continue;
1176
1177 qemuBackupGetJobInfoStatsUpdateOne(vm,
1178 priv->backup->type == VIR_DOMAIN_BACKUP_TYPE_PUSH,
1179 priv->backup->disks[i].name,
1180 stats,
1181 blockjobs,
1182 nblockjobs);
1183 }
1184
1185 ret = 0;
1186
1187 cleanup:
1188 for (i = 0; i < nblockjobs; i++)
1189 qemuMonitorJobInfoFree(blockjobs[i]);
1190 g_free(blockjobs);
1191 return ret;
1192 }
1193