From nobody Sun Feb 8 22:49:58 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com; dmarc=pass(p=none dis=none) header.from=redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1553681562244206.1851775825029; Wed, 27 Mar 2019 03:12:42 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.phx2.redhat.com [10.5.11.22]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 0266188ACC; Wed, 27 Mar 2019 10:12:41 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.21]) by smtp.corp.redhat.com (Postfix) with ESMTPS id C96191001E87; Wed, 27 Mar 2019 10:12:40 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 863E13FA46; Wed, 27 Mar 2019 10:12:40 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id x2RABUi6022860 for ; Wed, 27 Mar 2019 06:11:30 -0400 Received: by smtp.corp.redhat.com (Postfix) id 541761915B; Wed, 27 Mar 2019 10:11:30 +0000 (UTC) Received: from blue.redhat.com (ovpn-116-59.phx2.redhat.com [10.3.116.59]) by smtp.corp.redhat.com (Postfix) with ESMTP id D6FA66085B; Wed, 27 Mar 2019 10:11:29 +0000 (UTC) From: Eric Blake To: libvir-list@redhat.com Date: Wed, 27 Mar 2019 05:10:51 -0500 Message-Id: <20190327101054.10648-20-eblake@redhat.com> In-Reply-To: <20190327100734.10225-1-eblake@redhat.com> References: <20190327100734.10225-1-eblake@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-loop: libvir-list@redhat.com Cc: nsoffer@redhat.com Subject: [libvirt] [PATCH v7 20/23] backup: qemu: Implement framework for backup job APIs X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.84 on 10.5.11.22 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.26]); Wed, 27 Mar 2019 10:12:41 +0000 (UTC) Content-Type: text/plain; charset="utf-8" Still needs to actually kick off the right QMP commands, but at least allows validation of backup XML, including the fact that a backup job can survive a libvirtd restart. Atomically creating a checkpoint alongside the backup still needs implementing. Signed-off-by: Eric Blake --- src/qemu/qemu_domain.h | 4 + src/qemu/qemu_domain.c | 32 ++++++- src/qemu/qemu_driver.c | 185 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+), 4 deletions(-) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 9b29628107..7e3641c6d0 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -376,6 +376,10 @@ struct _qemuDomainObjPrivate { /* true if global -mem-prealloc appears on cmd line */ bool memPrealloc; + + /* Any currently running backup job. + * FIXME: allow jobs in parallel. For now, at most one job, always id = 1. */ + virDomainBackupDefPtr backup; }; # define QEMU_DOMAIN_PRIVATE(vm) \ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 8a2b951dde..6648240dc4 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -57,6 +57,7 @@ #include "locking/domain_lock.h" #include "virdomainsnapshotobjlist.h" #include "virdomaincheckpointobjlist.h" +#include "backup_conf.h" #ifdef MAJOR_IN_MKDEV # include @@ -2313,13 +2314,25 @@ static int qemuDomainObjPrivateXMLFormatBlockjobs(virBufferPtr buf, virDomainObjPtr vm) { + qemuDomainObjPrivatePtr priv =3D vm->privateData; virBuffer attrBuf =3D VIR_BUFFER_INITIALIZER; bool bj =3D qemuDomainHasBlockjob(vm, false); + int ret =3D -1; virBufferAsprintf(&attrBuf, " active=3D'%s'", virTristateBoolTypeToString(virTristateBoolFromBool(= bj))); - return virXMLFormatElement(buf, "blockjobs", &attrBuf, NULL); + if (virXMLFormatElement(buf, "blockjobs", &attrBuf, NULL) < 0) + goto cleanup; + + /* TODO: merge other blockjobs and backups into uniform space? */ + if (priv->backup && virDomainBackupDefFormat(buf, priv->backup, true) = < 0) + goto cleanup; + + ret =3D 0; + cleanup: + virBufferFreeAndReset(&attrBuf); + return ret; } @@ -2666,18 +2679,29 @@ qemuDomainObjPrivateXMLParseAutomaticPlacement(xmlX= PathContextPtr ctxt, static int -qemuDomainObjPrivateXMLParseBlockjobs(qemuDomainObjPrivatePtr priv, +qemuDomainObjPrivateXMLParseBlockjobs(virQEMUDriverPtr driver, + qemuDomainObjPrivatePtr priv, xmlXPathContextPtr ctxt) { + xmlNodePtr node; char *active; int tmp; + int ret =3D -1; if ((active =3D virXPathString("string(./blockjobs/@active)", ctxt)) && (tmp =3D virTristateBoolTypeFromString(active)) > 0) priv->reconnectBlockjobs =3D tmp; + if ((node =3D virXPathNode("./domainbackup", ctxt)) && + !(priv->backup =3D virDomainBackupDefParseNode(ctxt->doc, node, + driver->xmlopt, + VIR_DOMAIN_BACKUP_PAR= SE_INTERNAL))) + goto cleanup; + + ret =3D 0; + cleanup: VIR_FREE(active); - return 0; + return ret; } @@ -3053,7 +3077,7 @@ qemuDomainObjPrivateXMLParse(xmlXPathContextPtr ctxt, qemuDomainObjPrivateXMLParsePR(ctxt, &priv->prDaemonRunning); - if (qemuDomainObjPrivateXMLParseBlockjobs(priv, ctxt) < 0) + if (qemuDomainObjPrivateXMLParseBlockjobs(driver, priv, ctxt) < 0) goto error; qemuDomainStorageIdReset(priv); diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7eda8cf2c4..78c9f963ca 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -104,6 +104,7 @@ #include "virqemu.h" #include "virdomainsnapshotobjlist.h" #include "virdomaincheckpointobjlist.h" +#include "backup_conf.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -17721,6 +17722,187 @@ qemuDomainCheckpointDelete(virDomainCheckpointPtr= checkpoint, return ret; } +static int qemuDomainBackupBegin(virDomainPtr domain, const char *diskXml, + const char *checkpointXml, unsigned int f= lags) +{ + virQEMUDriverPtr driver =3D domain->conn->privateData; + virDomainObjPtr vm =3D NULL; + virDomainBackupDefPtr def =3D NULL; + virQEMUDriverConfigPtr cfg =3D NULL; + virCapsPtr caps =3D NULL; + qemuDomainObjPrivatePtr priv; + int ret =3D -1; + struct timeval tv; + char *suffix =3D NULL; + + virCheckFlags(VIR_DOMAIN_BACKUP_BEGIN_NO_METADATA, -1); + /* TODO: VIR_DOMAIN_BACKUP_BEGIN_QUIESCE */ + + // FIXME: Support non-null checkpointXML for incremental - what + // code can be shared with CheckpointCreateXML, then add to transaction + // to create new checkpoint at same time as starting blockdev-backup + if (checkpointXml) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot create incremental backups yet")); + return -1; + } + // if (chk) VIR_STRDUP(suffix, chk->name); + gettimeofday(&tv, NULL); + if (virAsprintf(&suffix, "%lld", (long long)tv.tv_sec) < 0) + goto cleanup; + + if (!(vm =3D qemuDomObjFromDomain(domain))) + goto cleanup; + + cfg =3D virQEMUDriverGetConfig(driver); + + if (virDomainBackupBeginEnsureACL(domain->conn, vm->def, flags) < 0) + goto cleanup; + + if (!(caps =3D virQEMUDriverGetCapabilities(driver, false))) + goto cleanup; + + if (qemuProcessAutoDestroyActive(driver, vm)) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("domain is marked for auto destroy")); + goto cleanup; + } + + if (!virDomainObjIsActive(vm)) { + virReportError(VIR_ERR_OPERATION_UNSUPPORTED, "%s", + _("cannot perform disk backup for inactive domain")= ); + goto cleanup; + } + if (!(def =3D virDomainBackupDefParseString(diskXml, driver->xmlopt, 0= ))) + goto cleanup; + + /* We are going to modify the domain below. */ + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_MODIFY) < 0) + goto cleanup; + + priv =3D vm->privateData; + if (priv->backup) { + virReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("another backup job is already running")); + goto endjob; + } + + if (virDomainBackupAlignDisks(def, vm->def, suffix) < 0) + goto endjob; + + /* actually start the checkpoint. 2x2 array of push/pull, full/incr, + plus additional tweak if checkpoint requested */ + /* TODO: issue QMP commands: + - pull: nbd-server-start with from user (or autogenerate s= erver) + - push/pull: blockdev-add per + - incr: bitmap-add of tmp, bitmap-merge per + - transaction, containing: + - push+full: blockdev-backup sync:full + - push+incr: blockdev-backup sync:incremental bitmap:tmp + - pull+full: blockdev-backup sync:none + - pull+incr: blockdev-backup sync:none, bitmap-disable of tmp + - if checkpoint: bitmap-disable of old, bitmap-add of new + - pull: nbd-server-add per , including bitmap for incr + */ + + VIR_STEAL_PTR(priv->backup, def); + ret =3D priv->backup->id =3D 1; /* Hard-coded job id for now */ + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, + driver->caps) < 0) + VIR_WARN("Unable to save status on vm %s after backup job", + vm->def->name); + + endjob: + qemuDomainObjEndJob(driver, vm); + + cleanup: + VIR_FREE(suffix); + virDomainObjEndAPI(&vm); + virDomainBackupDefFree(def); + virObjectUnref(caps); + virObjectUnref(cfg); + return ret; +} + +static char *qemuDomainBackupGetXMLDesc(virDomainPtr domain, int id, + unsigned int flags) +{ + virDomainObjPtr vm =3D NULL; + char *xml =3D NULL; + qemuDomainObjPrivatePtr priv; + virBuffer buf =3D VIR_BUFFER_INITIALIZER; + + virCheckFlags(0, NULL); + + if (!(vm =3D qemuDomObjFromDomain(domain))) + return NULL; + + if (virDomainBackupGetXMLDescEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + /* TODO: Allow more than one hard-coded job id */ + priv =3D vm->privateData; + if (id !=3D 1 || !priv->backup) { + virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT, + _("no domain backup job with id '%d'"), id); + goto cleanup; + } + + if (virDomainBackupDefFormat(&buf, priv->backup, false) < 0) + goto cleanup; + xml =3D virBufferContentAndReset(&buf); + + cleanup: + virDomainObjEndAPI(&vm); + return xml; +} + +static int qemuDomainBackupEnd(virDomainPtr domain, int id, unsigned int f= lags) +{ + virQEMUDriverPtr driver =3D domain->conn->privateData; + virQEMUDriverConfigPtr cfg =3D NULL; + virDomainObjPtr vm =3D NULL; + int ret =3D -1; + virDomainBackupDefPtr backup =3D NULL; + qemuDomainObjPrivatePtr priv; + bool want_abort =3D flags & VIR_DOMAIN_BACKUP_END_ABORT; + + virCheckFlags(VIR_DOMAIN_BACKUP_END_ABORT, -1); + + if (!(vm =3D qemuDomObjFromDomain(domain))) + return -1; + + cfg =3D virQEMUDriverGetConfig(driver); + if (virDomainBackupEndEnsureACL(domain->conn, vm->def) < 0) + goto cleanup; + + /* TODO: Allow more than one hard-coded job id */ + priv =3D vm->privateData; + if (id !=3D 1 || !priv->backup) { + virReportError(VIR_ERR_NO_DOMAIN_CHECKPOINT, + _("no domain backup job with id '%d'"), id); + goto cleanup; + } + + if (priv->backup->type !=3D VIR_DOMAIN_BACKUP_TYPE_PUSH) + want_abort =3D false; + + /* TODO: QMP commands to actually cancel the pending job, and on + * pull, also tear down the NBD server */ + VIR_STEAL_PTR(backup, priv->backup); + if (virDomainSaveStatus(driver->xmlopt, cfg->stateDir, vm, + driver->caps) < 0) + VIR_WARN("Unable to save status on vm %s after backup job", + vm->def->name); + + ret =3D want_abort ? 0 : 1; + + cleanup: + virDomainBackupDefFree(backup); + virDomainObjEndAPI(&vm); + return ret; +} + static int qemuDomainQemuMonitorCommand(virDomainPtr domain, const char *c= md, char **result, unsigned int flags) { @@ -23486,6 +23668,9 @@ static virHypervisorDriver qemuHypervisorDriver =3D= { .domainCheckpointIsCurrent =3D qemuDomainCheckpointIsCurrent, /* 5.2.0= */ .domainCheckpointHasMetadata =3D qemuDomainCheckpointHasMetadata, /* 5= .2.0 */ .domainCheckpointDelete =3D qemuDomainCheckpointDelete, /* 5.2.0 */ + .domainBackupBegin =3D qemuDomainBackupBegin, /* 5.2.0 */ + .domainBackupGetXMLDesc =3D qemuDomainBackupGetXMLDesc, /* 5.2.0 */ + .domainBackupEnd =3D qemuDomainBackupEnd, /* 5.2.0 */ }; --=20 2.20.1 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list