[libvirt] [PATCH v1 0/5] Precreate storage on migration

Yet another attempt. One thing that I'm not sure about is, whether this functionality should be on by default or tunable via say flag to migrate APIs. Thing is, in my approach I require disks to be in a storage pool (see the last patch for reasoning). On the other hand, if users still precreate storage themselves, they won't see any failure if disks doesn't belong to any pool. So I'm undecided yet, thoughts? Michal Privoznik (5): qemu: Expose qemuOpenFile and qemuOpenFileAs qemu: Split qemuDomainGetBlockInfo storage: Introduce storagePoolLookupByPath qemu_migration: Send disk sizes to the other side qemu_migration: Precreate missing storage src/qemu/qemu_domain.c | 319 ++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 16 ++ src/qemu/qemu_driver.c | 316 +------------------------------------- src/qemu/qemu_migration.c | 354 ++++++++++++++++++++++++++++++++++++++++--- src/storage/storage_driver.c | 36 +++++ src/storage/storage_driver.h | 4 + 6 files changed, 711 insertions(+), 334 deletions(-) -- 2.0.4

This is a pure code movement. Both of the APIs are going to be needed later in several areas of qemu driver code. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 10 ++++ src/qemu/qemu_driver.c | 156 ------------------------------------------------- 3 files changed, 157 insertions(+), 156 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 6513c78..e500fb3 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2810,3 +2810,150 @@ qemuDomainAgentAvailable(qemuDomainObjPrivatePtr priv, } return true; } + +/* Internal function to properly create or open existing files, with + * ownership affected by qemu driver setup and domain DAC label. */ +int +qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver) +{ + int ret = -1; + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + uid_t user = cfg->user; + gid_t group = cfg->group; + bool dynamicOwnership = cfg->dynamicOwnership; + virSecurityLabelDefPtr seclabel; + + virObjectUnref(cfg); + + /* TODO: Take imagelabel into account? */ + if (vm && + (seclabel = virDomainDefGetSecurityLabelDef(vm->def, "dac")) != NULL && + seclabel->label != NULL && + (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) + goto cleanup; + + ret = qemuOpenFileAs(user, group, dynamicOwnership, + path, oflags, needUnlink, bypassSecurityDriver); + + cleanup: + return ret; +} + + +int +qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, + bool dynamicOwnership, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver) +{ + struct stat sb; + bool is_reg = true; + bool need_unlink = false; + bool bypass_security = false; + unsigned int vfoflags = 0; + int fd = -1; + int path_shared = virFileIsSharedFS(path); + uid_t uid = geteuid(); + gid_t gid = getegid(); + + /* path might be a pre-existing block dev, in which case + * we need to skip the create step, and also avoid unlink + * in the failure case */ + if (oflags & O_CREAT) { + need_unlink = true; + + /* Don't force chown on network-shared FS + * as it is likely to fail. */ + if (path_shared <= 0 || dynamicOwnership) + vfoflags |= VIR_FILE_OPEN_FORCE_OWNER; + + if (stat(path, &sb) == 0) { + is_reg = !!S_ISREG(sb.st_mode); + /* If the path is regular file which exists + * already and dynamic_ownership is off, we don't + * want to change it's ownership, just open it as-is */ + if (is_reg && !dynamicOwnership) { + uid = sb.st_uid; + gid = sb.st_gid; + } + } + } + + /* First try creating the file as root */ + if (!is_reg) { + if ((fd = open(path, oflags & ~O_CREAT)) < 0) { + fd = -errno; + goto error; + } + } else { + if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid, + vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { + /* If we failed as root, and the error was permission-denied + (EACCES or EPERM), assume it's on a network-connected share + where root access is restricted (eg, root-squashed NFS). If the + qemu user is non-root, just set a flag to + bypass security driver shenanigans, and retry the operation + after doing setuid to qemu user */ + if ((fd != -EACCES && fd != -EPERM) || fallback_uid == geteuid()) + goto error; + + /* On Linux we can also verify the FS-type of the directory. */ + switch (path_shared) { + case 1: + /* it was on a network share, so we'll continue + * as outlined above + */ + break; + + case -1: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file " + "'%s': couldn't determine fs type") + : _("Failed to open file " + "'%s': couldn't determine fs type"), + path); + goto cleanup; + + case 0: + default: + /* local file - log the error returned by virFileOpenAs */ + goto error; + } + + /* Retry creating the file as qemu user */ + + if ((fd = virFileOpenAs(path, oflags, + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, + fallback_uid, fallback_gid, + vfoflags | VIR_FILE_OPEN_FORK)) < 0) { + virReportSystemError(-fd, oflags & O_CREAT + ? _("Error from child process creating '%s'") + : _("Error from child process opening '%s'"), + path); + goto cleanup; + } + + /* Since we had to setuid to create the file, and the fstype + is NFS, we assume it's a root-squashing NFS share, and that + the security driver stuff would have failed anyway */ + + bypass_security = true; + } + } + cleanup: + if (needUnlink) + *needUnlink = need_unlink; + if (bypassSecurityDriver) + *bypassSecurityDriver = bypass_security; + return fd; + + error: + virReportSystemError(-fd, oflags & O_CREAT + ? _("Failed to create file '%s'") + : _("Failed to open file '%s'"), + path); + goto cleanup; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index e4ea4ce..5f36892 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -414,4 +414,14 @@ int qemuDomainJobInfoToParams(qemuDomainJobInfoPtr jobInfo, ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_NONNULL(3) ATTRIBUTE_NONNULL(4); +int qemuOpenFile(virQEMUDriverPtr driver, + virDomainObjPtr vm, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver); + +int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, + bool dynamicOwnership, + const char *path, int oflags, + bool *needUnlink, bool *bypassSecurityDriver); + #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 07da3e3..561fa6c 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -160,16 +160,6 @@ static int qemuDomainGetMaxVcpus(virDomainPtr dom); static int qemuDomainManagedSaveLoad(virDomainObjPtr vm, void *opaque); -static int qemuOpenFile(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver); - -static int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver); - virQEMUDriverPtr qemu_driver = NULL; @@ -2881,152 +2871,6 @@ qemuCompressGetCommand(virQEMUSaveFormat compression) return ret; } -/* Internal function to properly create or open existing files, with - * ownership affected by qemu driver setup and domain DAC label. */ -static int -qemuOpenFile(virQEMUDriverPtr driver, - virDomainObjPtr vm, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver) -{ - int ret = -1; - virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); - uid_t user = cfg->user; - gid_t group = cfg->group; - bool dynamicOwnership = cfg->dynamicOwnership; - virSecurityLabelDefPtr seclabel; - - virObjectUnref(cfg); - - /* TODO: Take imagelabel into account? */ - if (vm && - (seclabel = virDomainDefGetSecurityLabelDef(vm->def, "dac")) != NULL && - seclabel->label != NULL && - (virParseOwnershipIds(seclabel->label, &user, &group) < 0)) - goto cleanup; - - ret = qemuOpenFileAs(user, group, dynamicOwnership, - path, oflags, needUnlink, bypassSecurityDriver); - - cleanup: - return ret; -} - -static int -qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, - bool dynamicOwnership, - const char *path, int oflags, - bool *needUnlink, bool *bypassSecurityDriver) -{ - struct stat sb; - bool is_reg = true; - bool need_unlink = false; - bool bypass_security = false; - unsigned int vfoflags = 0; - int fd = -1; - int path_shared = virFileIsSharedFS(path); - uid_t uid = geteuid(); - gid_t gid = getegid(); - - /* path might be a pre-existing block dev, in which case - * we need to skip the create step, and also avoid unlink - * in the failure case */ - if (oflags & O_CREAT) { - need_unlink = true; - - /* Don't force chown on network-shared FS - * as it is likely to fail. */ - if (path_shared <= 0 || dynamicOwnership) - vfoflags |= VIR_FILE_OPEN_FORCE_OWNER; - - if (stat(path, &sb) == 0) { - is_reg = !!S_ISREG(sb.st_mode); - /* If the path is regular file which exists - * already and dynamic_ownership is off, we don't - * want to change it's ownership, just open it as-is */ - if (is_reg && !dynamicOwnership) { - uid = sb.st_uid; - gid = sb.st_gid; - } - } - } - - /* First try creating the file as root */ - if (!is_reg) { - if ((fd = open(path, oflags & ~O_CREAT)) < 0) { - fd = -errno; - goto error; - } - } else { - if ((fd = virFileOpenAs(path, oflags, S_IRUSR | S_IWUSR, uid, gid, - vfoflags | VIR_FILE_OPEN_NOFORK)) < 0) { - /* If we failed as root, and the error was permission-denied - (EACCES or EPERM), assume it's on a network-connected share - where root access is restricted (eg, root-squashed NFS). If the - qemu user is non-root, just set a flag to - bypass security driver shenanigans, and retry the operation - after doing setuid to qemu user */ - if ((fd != -EACCES && fd != -EPERM) || fallback_uid == geteuid()) - goto error; - - /* On Linux we can also verify the FS-type of the directory. */ - switch (path_shared) { - case 1: - /* it was on a network share, so we'll continue - * as outlined above - */ - break; - - case -1: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file " - "'%s': couldn't determine fs type") - : _("Failed to open file " - "'%s': couldn't determine fs type"), - path); - goto cleanup; - - case 0: - default: - /* local file - log the error returned by virFileOpenAs */ - goto error; - } - - /* Retry creating the file as qemu user */ - - if ((fd = virFileOpenAs(path, oflags, - S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP, - fallback_uid, fallback_gid, - vfoflags | VIR_FILE_OPEN_FORK)) < 0) { - virReportSystemError(-fd, oflags & O_CREAT - ? _("Error from child process creating '%s'") - : _("Error from child process opening '%s'"), - path); - goto cleanup; - } - - /* Since we had to setuid to create the file, and the fstype - is NFS, we assume it's a root-squashing NFS share, and that - the security driver stuff would have failed anyway */ - - bypass_security = true; - } - } - cleanup: - if (needUnlink) - *needUnlink = need_unlink; - if (bypassSecurityDriver) - *bypassSecurityDriver = bypass_security; - return fd; - - error: - virReportSystemError(-fd, oflags & O_CREAT - ? _("Failed to create file '%s'") - : _("Failed to open file '%s'"), - path); - goto cleanup; -} - /* Helper function to execute a migration to file with a correct save header * the caller needs to make sure that the processors are stopped and do all other * actions besides saving memory */ -- 2.0.4

Again, this is a pure code movement. The function internals are going to be needed later when determining the disk capacity. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_domain.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++ src/qemu/qemu_domain.h | 6 ++ src/qemu/qemu_driver.c | 160 +-------------------------------------------- 3 files changed, 179 insertions(+), 159 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index e500fb3..0484df5 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -2957,3 +2957,175 @@ qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, path); goto cleanup; } + + +int +qemuDomainGetBlockInfoImpl(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + virDomainBlockInfoPtr info, + const char *path) +{ + virQEMUDriverConfigPtr cfg = virQEMUDriverGetConfig(driver); + int ret = -1; + int fd = -1; + struct stat sb; + ssize_t len; + char *buf = NULL; + int format; + virStorageSourcePtr meta = NULL; + off_t end; + char *alias = NULL; + bool activeFail = false; + + if (virStorageSourceIsLocalStorage(disk->src)) { + if (!disk->src->path) { + virReportError(VIR_ERR_INVALID_ARG, + _("disk '%s' does not currently have a source assigned"), + path); + goto cleanup; + } + + if ((fd = qemuOpenFile(driver, vm, disk->src->path, O_RDONLY, + NULL, NULL)) == -1) + goto cleanup; + + if (fstat(fd, &sb) < 0) { + virReportSystemError(errno, + _("cannot stat file '%s'"), disk->src->path); + goto cleanup; + } + + if ((len = virFileReadHeaderFD(fd, VIR_STORAGE_MAX_HEADER, &buf)) < 0) { + virReportSystemError(errno, _("cannot read header '%s'"), + disk->src->path); + goto cleanup; + } + } else { + if (virStorageFileInitAs(disk->src, cfg->user, cfg->group) < 0) + goto cleanup; + + if ((len = virStorageFileReadHeader(disk->src, VIR_STORAGE_MAX_HEADER, + &buf)) < 0) + goto cleanup; + + if (virStorageFileStat(disk->src, &sb) < 0) { + virReportSystemError(errno, _("failed to stat remote file '%s'"), + NULLSTR(disk->src->path)); + goto cleanup; + } + } + + /* Probe for magic formats */ + if (virDomainDiskGetFormat(disk)) { + format = virDomainDiskGetFormat(disk); + } else { + if (!cfg->allowDiskFormatProbing) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("no disk format for %s and probing is disabled"), + path); + goto cleanup; + } + + if ((format = virStorageFileProbeFormatFromBuf(disk->src->path, + buf, len)) < 0) + goto cleanup; + } + + if (!(meta = virStorageFileGetMetadataFromBuf(disk->src->path, buf, len, + format, NULL))) + goto cleanup; + + /* Get info for normal formats */ + if (S_ISREG(sb.st_mode) || fd == -1) { +#ifndef WIN32 + info->physical = (unsigned long long)sb.st_blocks * + (unsigned long long)DEV_BSIZE; +#else + info->physical = sb.st_size; +#endif + /* Regular files may be sparse, so logical size (capacity) is not same + * as actual physical above + */ + info->capacity = sb.st_size; + } else { + /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should + * be 64 bits on all platforms. + */ + end = lseek(fd, 0, SEEK_END); + if (end == (off_t)-1) { + virReportSystemError(errno, + _("failed to seek to end of %s"), path); + goto cleanup; + } + info->physical = end; + info->capacity = end; + } + + /* If the file we probed has a capacity set, then override + * what we calculated from file/block extents */ + if (meta->capacity) + info->capacity = meta->capacity; + + /* Set default value .. */ + info->allocation = info->physical; + + /* ..but if guest is not using raw disk format and on a block device, + * then query highest allocated extent from QEMU + */ + if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_BLOCK && + format != VIR_STORAGE_FILE_RAW && + S_ISBLK(sb.st_mode)) { + qemuDomainObjPrivatePtr priv = vm->privateData; + + /* If the guest is not running, then success/failure return + * depends on whether domain is persistent + */ + if (!virDomainObjIsActive(vm)) { + activeFail = true; + ret = 0; + goto cleanup; + } + + if (VIR_STRDUP(alias, disk->info.alias) < 0) + goto cleanup; + + if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) + goto cleanup; + + if (virDomainObjIsActive(vm)) { + qemuDomainObjEnterMonitor(driver, vm); + ret = qemuMonitorGetBlockExtent(priv->mon, + alias, + &info->allocation); + qemuDomainObjExitMonitor(driver, vm); + } else { + activeFail = true; + ret = 0; + } + + if (!qemuDomainObjEndJob(driver, vm)) + vm = NULL; + } else { + ret = 0; + } + + cleanup: + VIR_FREE(buf); + VIR_FREE(alias); + virStorageSourceFree(meta); + VIR_FORCE_CLOSE(fd); + if (disk) + virStorageFileDeinit(disk->src); + + /* If we failed to get data from a domain because it's inactive and + * it's not a persistent domain, then force failure. + */ + if (activeFail && vm && !vm->persistent) { + virReportError(VIR_ERR_OPERATION_INVALID, "%s", + _("domain is not running")); + ret = -1; + } + virObjectUnref(cfg); + return ret; +} diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 5f36892..d3377c5 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -424,4 +424,10 @@ int qemuOpenFileAs(uid_t fallback_uid, gid_t fallback_gid, const char *path, int oflags, bool *needUnlink, bool *bypassSecurityDriver); +int +qemuDomainGetBlockInfoImpl(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + virDomainBlockInfoPtr info, + const char *path); #endif /* __QEMU_DOMAIN_H__ */ diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 561fa6c..71b7e81 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -10833,26 +10833,14 @@ qemuDomainGetBlockInfo(virDomainPtr dom, virQEMUDriverPtr driver = dom->conn->privateData; virDomainObjPtr vm; int ret = -1; - int fd = -1; - off_t end; - virStorageSourcePtr meta = NULL; virDomainDiskDefPtr disk = NULL; - struct stat sb; int idx; - int format; - bool activeFail = false; - virQEMUDriverConfigPtr cfg = NULL; - char *alias = NULL; - char *buf = NULL; - ssize_t len; virCheckFlags(0, -1); if (!(vm = qemuDomObjFromDomain(dom))) return -1; - cfg = virQEMUDriverGetConfig(driver); - if (virDomainGetBlockInfoEnsureACL(dom->conn, vm->def) < 0) goto cleanup; @@ -10870,156 +10858,10 @@ qemuDomainGetBlockInfo(virDomainPtr dom, disk = vm->def->disks[idx]; - if (virStorageSourceIsLocalStorage(disk->src)) { - if (!disk->src->path) { - virReportError(VIR_ERR_INVALID_ARG, - _("disk '%s' does not currently have a source assigned"), - path); - goto cleanup; - } - - if ((fd = qemuOpenFile(driver, vm, disk->src->path, O_RDONLY, - NULL, NULL)) == -1) - goto cleanup; - - if (fstat(fd, &sb) < 0) { - virReportSystemError(errno, - _("cannot stat file '%s'"), disk->src->path); - goto cleanup; - } - - if ((len = virFileReadHeaderFD(fd, VIR_STORAGE_MAX_HEADER, &buf)) < 0) { - virReportSystemError(errno, _("cannot read header '%s'"), - disk->src->path); - goto cleanup; - } - } else { - if (virStorageFileInitAs(disk->src, cfg->user, cfg->group) < 0) - goto cleanup; - - if ((len = virStorageFileReadHeader(disk->src, VIR_STORAGE_MAX_HEADER, - &buf)) < 0) - goto cleanup; - - if (virStorageFileStat(disk->src, &sb) < 0) { - virReportSystemError(errno, _("failed to stat remote file '%s'"), - NULLSTR(disk->src->path)); - goto cleanup; - } - } - - /* Probe for magic formats */ - if (virDomainDiskGetFormat(disk)) { - format = virDomainDiskGetFormat(disk); - } else { - if (!cfg->allowDiskFormatProbing) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("no disk format for %s and probing is disabled"), - path); - goto cleanup; - } - - if ((format = virStorageFileProbeFormatFromBuf(disk->src->path, - buf, len)) < 0) - goto cleanup; - } - - if (!(meta = virStorageFileGetMetadataFromBuf(disk->src->path, buf, len, - format, NULL))) - goto cleanup; - - /* Get info for normal formats */ - if (S_ISREG(sb.st_mode) || fd == -1) { -#ifndef WIN32 - info->physical = (unsigned long long)sb.st_blocks * - (unsigned long long)DEV_BSIZE; -#else - info->physical = sb.st_size; -#endif - /* Regular files may be sparse, so logical size (capacity) is not same - * as actual physical above - */ - info->capacity = sb.st_size; - } else { - /* NB. Because we configure with AC_SYS_LARGEFILE, off_t should - * be 64 bits on all platforms. - */ - end = lseek(fd, 0, SEEK_END); - if (end == (off_t)-1) { - virReportSystemError(errno, - _("failed to seek to end of %s"), path); - goto cleanup; - } - info->physical = end; - info->capacity = end; - } - - /* If the file we probed has a capacity set, then override - * what we calculated from file/block extents */ - if (meta->capacity) - info->capacity = meta->capacity; - - /* Set default value .. */ - info->allocation = info->physical; - - /* ..but if guest is not using raw disk format and on a block device, - * then query highest allocated extent from QEMU - */ - if (virStorageSourceGetActualType(disk->src) == VIR_STORAGE_TYPE_BLOCK && - format != VIR_STORAGE_FILE_RAW && - S_ISBLK(sb.st_mode)) { - qemuDomainObjPrivatePtr priv = vm->privateData; - - /* If the guest is not running, then success/failure return - * depends on whether domain is persistent - */ - if (!virDomainObjIsActive(vm)) { - activeFail = true; - ret = 0; - goto cleanup; - } - - if (VIR_STRDUP(alias, disk->info.alias) < 0) - goto cleanup; - - if (qemuDomainObjBeginJob(driver, vm, QEMU_JOB_QUERY) < 0) - goto cleanup; - - if (virDomainObjIsActive(vm)) { - qemuDomainObjEnterMonitor(driver, vm); - ret = qemuMonitorGetBlockExtent(priv->mon, - alias, - &info->allocation); - qemuDomainObjExitMonitor(driver, vm); - } else { - activeFail = true; - ret = 0; - } - - if (!qemuDomainObjEndJob(driver, vm)) - vm = NULL; - } else { - ret = 0; - } + ret = qemuDomainGetBlockInfoImpl(driver, vm, disk, info, path); cleanup: - VIR_FREE(buf); - VIR_FREE(alias); - virStorageSourceFree(meta); - VIR_FORCE_CLOSE(fd); - if (disk) - virStorageFileDeinit(disk->src); - - /* If we failed to get data from a domain because it's inactive and - * it's not a persistent domain, then force failure. - */ - if (activeFail && vm && !vm->persistent) { - virReportError(VIR_ERR_OPERATION_INVALID, "%s", - _("domain is not running")); - ret = -1; - } virObjectUnlock(vm); - virObjectUnref(cfg); return ret; } -- 2.0.4

While this could be exposed as a public API, it's not done yet as there's no demand for that yet. Anyway, this is just preparing the environment for easier volume creation on the destination. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/storage/storage_driver.c | 36 ++++++++++++++++++++++++++++++++++++ src/storage/storage_driver.h | 4 ++++ 2 files changed, 40 insertions(+) diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 23b63f5..a4f1030 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1466,6 +1466,42 @@ storageVolLookupByPath(virConnectPtr conn, return ret; } +virStoragePoolPtr +storagePoolLookupByPath(virConnectPtr conn, + const char *path) +{ + size_t i; + virStoragePoolPtr ret = NULL; + char *cleanpath; + + cleanpath = virFileSanitizePath(path); + if (!cleanpath) + return NULL; + + storageDriverLock(); + for (i = 0; i < driver->pools.count && !ret; i++) { + virStoragePoolObjPtr pool = driver->pools.objs[i]; + + virStoragePoolObjLock(pool); + + if (!virStoragePoolObjIsActive(pool)) { + virStoragePoolObjUnlock(pool); + continue; + } + + if (STREQ(path, pool->def->target.path)) { + ret = virGetStoragePool(conn, pool->def->name, pool->def->uuid, + NULL, NULL); + } + + virStoragePoolObjUnlock(pool); + } + + VIR_FREE(cleanpath); + storageDriverUnlock(); + return ret; +} + static int storageVolDeleteInternal(virStorageVolPtr obj, diff --git a/src/storage/storage_driver.h b/src/storage/storage_driver.h index b805ddd..b51760a 100644 --- a/src/storage/storage_driver.h +++ b/src/storage/storage_driver.h @@ -57,6 +57,10 @@ int virStorageFileGetMetadata(virStorageSourcePtr src, int virStorageTranslateDiskSourcePool(virConnectPtr conn, virDomainDiskDefPtr def); +virStoragePoolPtr +storagePoolLookupByPath(virConnectPtr conn, + const char *path); + int storageRegister(void); #endif /* __VIR_STORAGE_DRIVER_H__ */ -- 2.0.4

Up 'til now, users need to precreate non-shared storage on migration themselves. This is not very friendly requirement and we should do something about it. In this patch, the migration cookie is extended, so that <nbd/> section does not only contain NBD port, but info on disks being migrated. This patch sends a list of pairs of: <disk target; disk size> to the destination. The actual storage allocation is left for next commit. Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_migration.c | 180 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 19 deletions(-) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index a1b1458..d4b3acf 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -141,6 +141,10 @@ typedef struct _qemuMigrationCookieNBD qemuMigrationCookieNBD; typedef qemuMigrationCookieNBD *qemuMigrationCookieNBDPtr; struct _qemuMigrationCookieNBD { int port; /* on which port does NBD server listen for incoming data */ + + size_t ndisks; /* Number of items in @target and @alloc arrays */ + char **target; /* Disk target */ + size_t *alloc; /* And its allocation */ }; typedef struct _qemuMigrationCookie qemuMigrationCookie; @@ -206,6 +210,19 @@ qemuMigrationCookieNetworkFree(qemuMigrationCookieNetworkPtr network) } +static void qemuMigrationCookieNBDFree(qemuMigrationCookieNBDPtr nbd) +{ + if (!nbd) + return; + + while (nbd->ndisks) + VIR_FREE(nbd->target[--nbd->ndisks]); + VIR_FREE(nbd->target); + VIR_FREE(nbd->alloc); + VIR_FREE(nbd); +} + + static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig) { if (!mig) @@ -213,13 +230,13 @@ static void qemuMigrationCookieFree(qemuMigrationCookiePtr mig) qemuMigrationCookieGraphicsFree(mig->graphics); qemuMigrationCookieNetworkFree(mig->network); + qemuMigrationCookieNBDFree(mig->nbd); VIR_FREE(mig->localHostname); VIR_FREE(mig->remoteHostname); VIR_FREE(mig->name); VIR_FREE(mig->lockState); VIR_FREE(mig->lockDriver); - VIR_FREE(mig->nbd); VIR_FREE(mig->jobInfo); VIR_FREE(mig); } @@ -523,18 +540,86 @@ qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig, } +/** + * qemuMigrationGetDiskSize: + * @disk: disk to query + * @alloc: disk size in bytes + * + * For given disk get its image size. + * + * Returns: 0 on success, + * -1 on failure, + * 1 if disk size doesn't matter + */ +static int +qemuMigrationGetDiskSize(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + size_t *alloc) +{ + int ret = -1; + virDomainBlockInfo info; + + if (!virStorageSourceIsLocalStorage(disk->src) || + !disk->src->path) + return 1; + + if (qemuDomainGetBlockInfoImpl(driver, vm, disk, + &info, disk->src->path) < 0) + goto cleanup; + + *alloc = info.capacity; + ret = 0; + cleanup: + return ret; +} + + static int qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig, - virQEMUDriverPtr driver ATTRIBUTE_UNUSED, + virQEMUDriverPtr driver, virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; + size_t i; /* It is not a bug if there already is a NBD data */ if (!mig->nbd && VIR_ALLOC(mig->nbd) < 0) return -1; + if (vm->def->ndisks && + (VIR_ALLOC_N(mig->nbd->target, vm->def->ndisks) < 0 || + VIR_ALLOC_N(mig->nbd->alloc, vm->def->ndisks) < 0)) + return -1; + mig->nbd->ndisks = 0; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + int rc; + size_t alloc; + + /* skip shared, RO and source-less disks */ + if (disk->src->shared || disk->src->readonly || + !virDomainDiskGetSource(disk)) + continue; + + if ((rc = qemuMigrationGetDiskSize(driver, vm, disk, &alloc)) < 0) { + /* error reported */ + return -1; + } else if (rc > 0) { + /* Don't add this disk */ + continue; + } + + if (VIR_STRDUP(mig->nbd->target[mig->nbd->ndisks], + disk->dst) < 0) + return -1; + + mig->nbd->alloc[mig->nbd->ndisks] = alloc; + mig->nbd->ndisks++; + } + mig->nbd->port = priv->nbdPort; mig->flags |= QEMU_MIGRATION_COOKIE_NBD; @@ -763,7 +848,18 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, virBufferAddLit(buf, "<nbd"); if (mig->nbd->port) virBufferAsprintf(buf, " port='%d'", mig->nbd->port); - virBufferAddLit(buf, "/>\n"); + if (mig->nbd->ndisks) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < mig->nbd->ndisks; i++) + virBufferAsprintf(buf, "<disk target='%s' alloc='%zu'/>\n", + mig->nbd->target[i], + mig->nbd->alloc[i]); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</nbd>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } } if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo) @@ -891,6 +987,65 @@ qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt) } +static qemuMigrationCookieNBDPtr +qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt) +{ + qemuMigrationCookieNBDPtr ret = NULL; + char *port = NULL, *alloc = NULL; + size_t i; + int n; + xmlNodePtr *disks = NULL; + xmlNodePtr save_ctxt = ctxt->node; + + if (VIR_ALLOC(ret) < 0) + goto error; + + port = virXPathString("string(./nbd/@port)", ctxt); + if (port && virStrToLong_i(port, NULL, 10, &ret->port) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed nbd port '%s'"), + port); + goto error; + } + + /* Now check if source sent a list of disks to prealloc. We might be + * talking to an older server, so it's not an error if the list is + * missing. */ + if ((n = virXPathNodeSet("./nbd/disk", ctxt, &disks)) > 0) { + if (VIR_ALLOC_N(ret->target, n) < 0 || + VIR_ALLOC_N(ret->alloc, n) < 0) + goto error; + ret->ndisks = n; + + for (i = 0; i < n; i++) { + ctxt->node = disks[i]; + VIR_FREE(alloc); + + ret->target[i] = virXPathString("string(./@target)", ctxt); + alloc = virXPathString("string(./@alloc)", ctxt); + if (virStrToLong_ull(alloc, NULL, 10, (unsigned long long *) + &ret->alloc[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed disk allocation: '%s'"), + alloc); + goto error; + } + } + } + + cleanup: + VIR_FREE(port); + VIR_FREE(alloc); + VIR_FREE(disks); + ctxt->node = save_ctxt; + return ret; + error: + qemuMigrationCookieNBDFree(ret); + ret = NULL; + goto cleanup; +} + + static qemuDomainJobInfoPtr qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt) { @@ -1123,22 +1278,9 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, goto error; if (flags & QEMU_MIGRATION_COOKIE_NBD && - virXPathBoolean("boolean(./nbd)", ctxt)) { - char *port; - - if (VIR_ALLOC(mig->nbd) < 0) - goto error; - - port = virXPathString("string(./nbd/@port)", ctxt); - if (port && virStrToLong_i(port, NULL, 10, &mig->nbd->port) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Malformed nbd port '%s'"), - port); - VIR_FREE(port); - goto error; - } - VIR_FREE(port); - } + virXPathBoolean("boolean(./nbd)", ctxt) && + (!(mig->nbd = qemuMigrationCookieNBDXMLParse(ctxt)))) + goto error; if (flags & QEMU_MIGRATION_COOKIE_STATS && virXPathBoolean("boolean(./statistics)", ctxt) && -- 2.0.4

On 11/27/14 14:55, Michal Privoznik wrote:
Up 'til now, users need to precreate non-shared storage on migration themselves. This is not very friendly requirement and we should do something about it. In this patch, the migration cookie is extended, so that <nbd/> section does not only contain NBD port, but info on disks being migrated. This patch sends a list of pairs of:
<disk target; disk size>
to the destination. The actual storage allocation is left for next commit.
Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_migration.c | 180 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 161 insertions(+), 19 deletions(-)
diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index a1b1458..d4b3acf 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -141,6 +141,10 @@ typedef struct _qemuMigrationCookieNBD qemuMigrationCookieNBD; typedef qemuMigrationCookieNBD *qemuMigrationCookieNBDPtr; struct _qemuMigrationCookieNBD { int port; /* on which port does NBD server listen for incoming data */ + + size_t ndisks; /* Number of items in @target and @alloc arrays */ + char **target; /* Disk target */ + size_t *alloc; /* And its allocation */
I think this already warrants usage of a struct to group them together.
};
typedef struct _qemuMigrationCookie qemuMigrationCookie;
@@ -523,18 +540,86 @@ qemuMigrationCookieAddNetwork(qemuMigrationCookiePtr mig, }
+/** + * qemuMigrationGetDiskSize: + * @disk: disk to query + * @alloc: disk size in bytes
Allocation and capacity are two different stats of a disk volume. You should stick with capacity here to avoid confusion
+ * + * For given disk get its image size. + * + * Returns: 0 on success, + * -1 on failure, + * 1 if disk size doesn't matter + */ +static int +qemuMigrationGetDiskSize(virQEMUDriverPtr driver, + virDomainObjPtr vm, + virDomainDiskDefPtr disk, + size_t *alloc) +{ + int ret = -1; + virDomainBlockInfo info; + + if (!virStorageSourceIsLocalStorage(disk->src) || + !disk->src->path) + return 1; + + if (qemuDomainGetBlockInfoImpl(driver, vm, disk, + &info, disk->src->path) < 0) + goto cleanup;
Since you are requesting this only for a live domain and the only stat you care about is capacity of the guest visible portion of the disk it would be better to use a monitor call to determine the data from the disk rather than use the weird function that parses the information from the file on the disk.
+ + *alloc = info.capacity; + ret = 0; + cleanup: + return ret; +} + + static int qemuMigrationCookieAddNBD(qemuMigrationCookiePtr mig, - virQEMUDriverPtr driver ATTRIBUTE_UNUSED, + virQEMUDriverPtr driver, virDomainObjPtr vm) { qemuDomainObjPrivatePtr priv = vm->privateData; + size_t i;
/* It is not a bug if there already is a NBD data */ if (!mig->nbd && VIR_ALLOC(mig->nbd) < 0) return -1;
+ if (vm->def->ndisks && + (VIR_ALLOC_N(mig->nbd->target, vm->def->ndisks) < 0 || + VIR_ALLOC_N(mig->nbd->alloc, vm->def->ndisks) < 0))
Allocing an array of strutcts would look nicer.
+ return -1; + mig->nbd->ndisks = 0; + + for (i = 0; i < vm->def->ndisks; i++) { + virDomainDiskDefPtr disk = vm->def->disks[i]; + int rc; + size_t alloc; + + /* skip shared, RO and source-less disks */ + if (disk->src->shared || disk->src->readonly || + !virDomainDiskGetSource(disk))
I think also networked disks don't make sense here. We also have a function (virStorageSourceIsEmpty or similar) to check whether the source doesn't denote an empty drive. One additional thing that might break is if some of the storage is on a shared storage system, while other is not.
+ continue; + + if ((rc = qemuMigrationGetDiskSize(driver, vm, disk, &alloc)) < 0) { + /* error reported */
We report errors by default. No need to note it explicitly.
+ return -1; + } else if (rc > 0) { + /* Don't add this disk */ + continue; + } + + if (VIR_STRDUP(mig->nbd->target[mig->nbd->ndisks], + disk->dst) < 0) + return -1; + + mig->nbd->alloc[mig->nbd->ndisks] = alloc; + mig->nbd->ndisks++; + } + mig->nbd->port = priv->nbdPort; mig->flags |= QEMU_MIGRATION_COOKIE_NBD;
@@ -763,7 +848,18 @@ qemuMigrationCookieXMLFormat(virQEMUDriverPtr driver, virBufferAddLit(buf, "<nbd"); if (mig->nbd->port) virBufferAsprintf(buf, " port='%d'", mig->nbd->port); - virBufferAddLit(buf, "/>\n"); + if (mig->nbd->ndisks) { + virBufferAddLit(buf, ">\n"); + virBufferAdjustIndent(buf, 2); + for (i = 0; i < mig->nbd->ndisks; i++) + virBufferAsprintf(buf, "<disk target='%s' alloc='%zu'/>\n",
Again ... capacity is a better word for this IMO.
+ mig->nbd->target[i], + mig->nbd->alloc[i]); + virBufferAdjustIndent(buf, -2); + virBufferAddLit(buf, "</nbd>\n"); + } else { + virBufferAddLit(buf, "/>\n"); + } }
if (mig->flags & QEMU_MIGRATION_COOKIE_STATS && mig->jobInfo) @@ -891,6 +987,65 @@ qemuMigrationCookieNetworkXMLParse(xmlXPathContextPtr ctxt) }
+static qemuMigrationCookieNBDPtr +qemuMigrationCookieNBDXMLParse(xmlXPathContextPtr ctxt) +{ + qemuMigrationCookieNBDPtr ret = NULL; + char *port = NULL, *alloc = NULL; + size_t i; + int n; + xmlNodePtr *disks = NULL; + xmlNodePtr save_ctxt = ctxt->node; + + if (VIR_ALLOC(ret) < 0) + goto error; + + port = virXPathString("string(./nbd/@port)", ctxt); + if (port && virStrToLong_i(port, NULL, 10, &ret->port) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed nbd port '%s'"), + port); + goto error; + } + + /* Now check if source sent a list of disks to prealloc. We might be + * talking to an older server, so it's not an error if the list is + * missing. */ + if ((n = virXPathNodeSet("./nbd/disk", ctxt, &disks)) > 0) { + if (VIR_ALLOC_N(ret->target, n) < 0 || + VIR_ALLOC_N(ret->alloc, n) < 0)
capacity ...
+ goto error; + ret->ndisks = n; + + for (i = 0; i < n; i++) { + ctxt->node = disks[i]; + VIR_FREE(alloc); + + ret->target[i] = virXPathString("string(./@target)", ctxt); + alloc = virXPathString("string(./@alloc)", ctxt); + if (virStrToLong_ull(alloc, NULL, 10, (unsigned long long *) + &ret->alloc[i]) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Malformed disk allocation: '%s'"), + alloc); + goto error; + } + } + } + + cleanup: + VIR_FREE(port); + VIR_FREE(alloc); + VIR_FREE(disks); + ctxt->node = save_ctxt; + return ret; + error: + qemuMigrationCookieNBDFree(ret); + ret = NULL; + goto cleanup; +} + + static qemuDomainJobInfoPtr qemuMigrationCookieStatisticsXMLParse(xmlXPathContextPtr ctxt) { @@ -1123,22 +1278,9 @@ qemuMigrationCookieXMLParse(qemuMigrationCookiePtr mig, goto error;
if (flags & QEMU_MIGRATION_COOKIE_NBD && - virXPathBoolean("boolean(./nbd)", ctxt)) { - char *port; - - if (VIR_ALLOC(mig->nbd) < 0) - goto error; - - port = virXPathString("string(./nbd/@port)", ctxt); - if (port && virStrToLong_i(port, NULL, 10, &mig->nbd->port) < 0) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("Malformed nbd port '%s'"), - port); - VIR_FREE(port); - goto error; - } - VIR_FREE(port); - } + virXPathBoolean("boolean(./nbd)", ctxt) && + (!(mig->nbd = qemuMigrationCookieNBDXMLParse(ctxt)))) + goto error;
if (flags & QEMU_MIGRATION_COOKIE_STATS && virXPathBoolean("boolean(./statistics)", ctxt) &&
Peter

Based on previous commit, we can now precreate missing volumes. While digging out the functionality from storage driver would be nicer, if you've seen the code it's nearly impossible. So I'm going from the other end: 1) For given disk target, disk path is looked up. 2) If the path exists, and has correct size, there's nothing that we need to do, and next disk is evaluated. 3) For the disk path, storage pool is looked up, a volume XML is constructed and then passed to virStorageVolCreateXML() which has all the knowledge how to create raw images, (encrypted) qcow(2) images, etc. One of the advantages of this approach is, we don't have to care about image conversion - qemu does that for us. So for instance, users can transform qcow2 into raw on migration (if the correct XML is passed to the migration API). Signed-off-by: Michal Privoznik <mprivozn@redhat.com> --- src/qemu/qemu_migration.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/src/qemu/qemu_migration.c b/src/qemu/qemu_migration.c index d4b3acf..4e041e6 100644 --- a/src/qemu/qemu_migration.c +++ b/src/qemu/qemu_migration.c @@ -58,6 +58,7 @@ #include "virtypedparam.h" #include "virprocess.h" #include "nwfilter_conf.h" +#include "storage/storage_driver.h" #define VIR_FROM_THIS VIR_FROM_QEMU @@ -1473,6 +1474,176 @@ qemuMigrationRestoreDomainState(virConnectPtr conn, virDomainObjPtr vm) return ret; } + +static int +qemuMigrationPrecreateDisk(virConnectPtr conn, + virDomainDiskDefPtr disk, + size_t alloc) +{ + int ret = -1; + virStoragePoolPtr pool = NULL; + virStorageVolPtr vol = NULL; + char *volName = NULL, *basePath = NULL; + char *volStr = NULL; + virBuffer buf = VIR_BUFFER_INITIALIZER; + const char *format = NULL; + unsigned int flags = 0; + + VIR_DEBUG("Precreate disk type=%s", virStorageTypeToString(disk->src->type)); + + switch ((virStorageType) disk->src->type) { + case VIR_STORAGE_TYPE_FILE: + if (!virDomainDiskGetSource(disk)) { + VIR_DEBUG("Dropping sourceless disk '%s'", + disk->dst); + return 0; + } + + if (VIR_STRDUP(basePath, disk->src->path) < 0) + goto cleanup; + + if (!(volName = strrchr(basePath, '/'))) { + virReportError(VIR_ERR_INVALID_ARG, + _("malformed disk path: %s"), + disk->src->path); + goto cleanup; + } + + *volName = '\0'; + volName++; + + if (!(pool = storagePoolLookupByPath(conn, basePath))) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to find pool to %s"), + basePath); + goto cleanup; + } + format = virStorageFileFormatTypeToString(disk->src->format); + if (disk->src->format == VIR_STORAGE_FILE_QCOW2) + flags |= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA; + break; + + case VIR_STORAGE_TYPE_VOLUME: + if (!(pool = virStoragePoolLookupByName(conn, disk->src->srcpool->pool))) + goto cleanup; + format = virStorageFileFormatTypeToString(disk->src->format); + volName = disk->src->srcpool->volume; + if (disk->src->format == VIR_STORAGE_FILE_QCOW2) + flags |= VIR_STORAGE_VOL_CREATE_PREALLOC_METADATA; + break; + + case VIR_STORAGE_TYPE_BLOCK: + case VIR_STORAGE_TYPE_DIR: + case VIR_STORAGE_TYPE_NETWORK: + case VIR_STORAGE_TYPE_NONE: + case VIR_STORAGE_TYPE_LAST: + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unsupported disk type '%s'"), + virStorageTypeToString(disk->src->type)); + goto cleanup; + break; + } + + virBufferAddLit(&buf, "<volume>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<name>%s</name>", volName); + virBufferAsprintf(&buf, "<capacity>%zu</capacity>\n", alloc); + virBufferAddLit(&buf, "<target>\n"); + virBufferAdjustIndent(&buf, 2); + virBufferAsprintf(&buf, "<format type='%s'/>\n", format); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</target>\n"); + virBufferAdjustIndent(&buf, -2); + virBufferAddLit(&buf, "</volume>\n"); + + if (!(volStr = virBufferContentAndReset(&buf))) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("unable to create volume XML")); + goto cleanup; + } + + if (!(vol = virStorageVolCreateXML(pool, volStr, flags))) + goto cleanup; + + ret = 0; + cleanup: + VIR_FREE(basePath); + VIR_FREE(volStr); + if (vol) + virStorageVolFree(vol); + if (pool) + virStoragePoolFree(pool); + return ret; +} + + +static int +qemuMigrationPrecreateStorage(virConnectPtr conn, + virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuMigrationCookieNBDPtr nbd) +{ + int ret = -1; + size_t i = 0; + + if (!nbd || !nbd->ndisks) + return 0; + + for (i = 0; i < nbd->ndisks; i++) { + virDomainDiskDefPtr disk; + int indx; + const char *diskSrcPath; + + VIR_DEBUG("Looking up disk target '%s' (alloc=%zu)", + nbd->target[i], nbd->alloc[i]); + + if ((indx = virDomainDiskIndexByName(vm->def, + nbd->target[i], false)) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to find disk by target: %s"), + nbd->target[i]); + goto cleanup; + } + + disk = vm->def->disks[indx]; + diskSrcPath = virDomainDiskGetSource(disk); + + VIR_DEBUG("Proceeding with disk source %s", NULLSTR(diskSrcPath)); + + if (diskSrcPath && + virFileExists(diskSrcPath)) { + /* If the file exists already, check it has the correct size */ + int rc; + size_t alloc; + + if ((rc = qemuMigrationGetDiskSize(driver, vm, disk, &alloc)) < 0) { + /* error reported */ + goto cleanup; + } + + VIR_DEBUG("Disk exists with alloc %zu (required %zu)", + alloc, nbd->alloc[i]); + if (alloc >= nbd->alloc[i]) { + /* Okay, there's sufficient space. Continue with next disk */ + continue; + } + + /* Unfortunately, there's not enough space. Fallthrough to disk + * creation. */ + } + + if (qemuMigrationPrecreateDisk(conn, disk, nbd->alloc[i]) < 0) { + /* error reported by helper */ + goto cleanup; + } + } + + ret = 0; + cleanup: + return ret; +} + + /** * qemuMigrationStartNBDServer: * @driver: qemu driver @@ -2856,6 +3027,9 @@ qemuMigrationPrepareAny(virQEMUDriverPtr driver, goto cleanup; } + if (qemuMigrationPrecreateStorage(dconn, driver, vm, mig->nbd) < 0) + goto cleanup; + if (qemuMigrationJobStart(driver, vm, QEMU_ASYNC_JOB_MIGRATION_IN) < 0) goto cleanup; qemuMigrationJobSetPhase(driver, vm, QEMU_MIGRATION_PHASE_PREPARE); -- 2.0.4
participants (2)
-
Michal Privoznik
-
Peter Krempa