Hi everyone, In Boxes we'll need to change the size of the storage volumes (we use qcow2 files) but turns out that there is no virStorageVolResize() yet[1]. In my chat with Daniel on IRC, he mentioned that this would be a trivial task so I thought I should try to do it myself. I've been looking into this for several hours now and haven't gotten very far. I guess Daniel overestimated my skills of deciphering complicated code. :) Attached is my very much WIP patch that at least builds but doesn't exactly work yet: virsh # vol-resize 'Microsoft Windows XP.qcow2' '4G' gnome-boxes error: Failed to change size of volume 'Microsoft Windows XP.qcow2' to 4G error: this function is not supported by the connection driver: virStorageVolResize --------------------- If anyone can have a look and tell me if I'm going anywhere towards the right direction and what level of indirection I'm missing here, that would be awesome! -- Regards, Zeeshan Ali (Khattak) FSF member#5124 [1] Yes, I know there is a virDomainBlockResize but thats limited to running domains and I haven't yet figured if it can be nicely binded in libvirt-glib. Probably we'll want one function in libvirt-glib that will abstract both virDomainBlockResize() and virStorageVolResize().
diff --git a/src/driver.h b/src/driver.h index 24636a4..4b0b3d9 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1252,6 +1252,10 @@ typedef int unsigned long long offset, unsigned long long length, unsigned int flags); +typedef int + (*virDrvStorageVolResize) (virStorageVolPtr vol, + unsigned long long capacity, + unsigned int flags); typedef int (*virDrvStoragePoolIsActive)(virStoragePoolPtr pool); @@ -1313,6 +1317,7 @@ struct _virStorageDriver { virDrvStorageVolGetInfo volGetInfo; virDrvStorageVolGetXMLDesc volGetXMLDesc; virDrvStorageVolGetPath volGetPath; + virDrvStorageVolResize volResize; virDrvStoragePoolIsActive poolIsActive; virDrvStoragePoolIsPersistent poolIsPersistent; }; diff --git a/src/libvirt.c b/src/libvirt.c index 7b8adf7..2901234 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -12791,6 +12791,7 @@ virStorageVolGetPath(virStorageVolPtr vol) return ret; } + VIR_WARN("driver: %s",conn->storageDriver->name); virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); error: @@ -12798,6 +12799,51 @@ error: return NULL; } +/** + * virStorageVolResize: + * @vol: pointer to storage volume + * @capacity: new capacity + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Changes the capacity of the storage volume @vol to @capacity. The new + * capacity must not exceed the sum of current capacity of the volume and + * remainining free space of its parent pool. Also the new capacity must + * be greater than or equal to current allocation of the volume. + * + * Returns 0 on success, or -1 on error. + */ +int +virStorageVolResize(virStorageVolPtr vol, + unsigned long long capacity, + unsigned int flags) +{ + virConnectPtr conn; + VIR_DEBUG("vol=%p", vol); + + virResetLastError(); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = vol->conn; + + if (conn->storageDriver && conn->storageDriver->volResize) { + int ret; + ret = conn->storageDriver->volResize(vol, capacity, flags); + if (!ret) + goto error; + return ret; + } + + virLibConnError(VIR_ERR_NO_SUPPORT, __FUNCTION__); + +error: + virDispatchError(vol->conn); + return -1; +} /** * virNodeNumOfDevices: diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 4ca7216..5155022 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -516,4 +516,9 @@ LIBVIRT_0.9.9 { virDomainSetNumaParameters; } LIBVIRT_0.9.8; +LIBVIRT_0.9.10 { + global: + virStorageVolResize; +} LIBVIRT_0.9.9; + # .... define new API here using predicted next version number .... diff --git a/src/storage/storage_backend.h b/src/storage/storage_backend.h index 75ed676..a37bf7c 100644 --- a/src/storage/storage_backend.h +++ b/src/storage/storage_backend.h @@ -44,6 +44,11 @@ typedef int (*virStorageBackendDeleteVol)(virConnectPtr conn, virStoragePoolObjP typedef int (*virStorageBackendBuildVolFrom)(virConnectPtr conn, virStoragePoolObjPtr pool, virStorageVolDefPtr origvol, virStorageVolDefPtr newvol, unsigned int flags); +typedef int (*virStorageBackendVolumeResize)(virConnectPtr conn, + virStoragePoolObjPtr pool, + virStorageVolDefPtr vol, + unsigned long long capacity, + unsigned int flags); /* File creation/cloning functions used for cloning between backends */ int virStorageBackendCreateRaw(virConnectPtr conn, @@ -78,6 +83,7 @@ struct _virStorageBackend { virStorageBackendCreateVol createVol; virStorageBackendRefreshVol refreshVol; virStorageBackendDeleteVol deleteVol; + virStorageBackendVolumeResize resizeVol; }; virStorageBackendPtr virStorageBackendForType(int type); diff --git a/src/storage/storage_backend_fs.c b/src/storage/storage_backend_fs.c index d8dc29c..20f5534 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1187,6 +1187,21 @@ virStorageBackendFileSystemVolRefresh(virConnectPtr conn, return 0; } +/** + * Resize a volume + */ +static int +virStorageBackendFileSystemVolResize(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned long long capacity, + unsigned int flags ATTRIBUTE_UNUSED) +{ + return virStorageFileResize(vol->target.path, + vol->target.format, + capacity); +} + virStorageBackend virStorageBackendDirectory = { .type = VIR_STORAGE_POOL_DIR, @@ -1199,6 +1214,7 @@ virStorageBackend virStorageBackendDirectory = { .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, + .resizeVol = virStorageBackendFileSystemVolResize, }; #if WITH_STORAGE_FS @@ -1216,6 +1232,7 @@ virStorageBackend virStorageBackendFileSystem = { .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, + .resizeVol = virStorageBackendFileSystemVolResize, }; virStorageBackend virStorageBackendNetFileSystem = { .type = VIR_STORAGE_POOL_NETFS, @@ -1232,5 +1249,6 @@ virStorageBackend virStorageBackendNetFileSystem = { .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, + .resizeVol = virStorageBackendFileSystemVolResize, }; #endif /* WITH_STORAGE_FS */ diff --git a/src/storage/storage_driver.c b/src/storage/storage_driver.c index 8c2d6e1..dda1bab 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1695,7 +1695,74 @@ out: return ret; } +static int +storageVolumeResize(virStorageVolPtr obj, + unsigned long long capacity, + unsigned int flags) +{ + virStorageDriverStatePtr driver = obj->conn->storagePrivateData; + virStorageBackendPtr backend; + virStoragePoolObjPtr pool = NULL; + virStorageVolDefPtr vol = NULL; + int ret = -1; + + virCheckFlags(0, -1); + + storageDriverLock(driver); + pool = virStoragePoolObjFindByName(&driver->pools, obj->pool); + storageDriverUnlock(driver); + + if (!pool) { + virStorageReportError(VIR_ERR_NO_STORAGE_POOL, + "%s", _("no storage pool with matching uuid")); + goto out; + } + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(VIR_ERR_OPERATION_INVALID, + "%s", _("storage pool is not active")); + goto out; + } + + if ((backend = virStorageBackendForType(pool->def->type)) == NULL) + goto out; + + vol = virStorageVolDefFindByName(pool, obj->name); + + if (vol == NULL) { + virStorageReportError(VIR_ERR_NO_STORAGE_VOL, + _("no storage vol with matching name '%s'"), + obj->name); + goto out; + } + + if (vol->building) { + virStorageReportError(VIR_ERR_OPERATION_INVALID, + _("volume '%s' is still being allocated."), + vol->name); + goto out; + } + + if (!backend->resizeVol) { + virStorageReportError(VIR_ERR_NO_SUPPORT, + "%s", _("storage pool does not support changing " + "of volume capacity")); + + goto out; + } + + if (backend->resizeVol(obj->conn, pool, vol, capacity, flags) < 0) + goto out; + + vol->capacity = capacity; + ret = 0; + +out: + if (pool) + virStoragePoolObjUnlock(pool); + + return ret; +} /* If the volume we're wiping is already a sparse file, we simply * truncate and extend it to its original size, filling it with @@ -2178,6 +2245,7 @@ static virStorageDriver storageDriver = { .volGetInfo = storageVolumeGetInfo, /* 0.4.0 */ .volGetXMLDesc = storageVolumeGetXMLDesc, /* 0.4.0 */ .volGetPath = storageVolumeGetPath, /* 0.4.0 */ + .volResize = storageVolumeResize, /* 0.9.10 */ .poolIsActive = storagePoolIsActive, /* 0.7.3 */ .poolIsPersistent = storagePoolIsPersistent, /* 0.7.3 */ diff --git a/src/util/storage_file.c b/src/util/storage_file.c index ba9cfc5..6a654a8 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -931,6 +931,107 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) VIR_FREE(meta); } +int +virStorageFileResizeFromFD(const char *path, + int fd, + int format, + unsigned long long capacity) +{ + unsigned char *head = NULL; + ssize_t len = STORAGE_MAX_HEAD; + int ret = -1; + struct stat sb; + + if (fstat(fd, &sb) < 0) { + virReportSystemError(errno, + _("cannot stat file '%s'"), + path); + return -1; + } + + /* No header to probe for directories */ + if (S_ISDIR(sb.st_mode)) { + return 0; + } + + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + virReportSystemError(errno, _("cannot seek to start of '%s'"), path); + return -1; + } + + if (VIR_ALLOC_N(head, len) < 0) { + virReportOOMError(); + return -1; + } + + if ((len = read(fd, head, len)) < 0) { + virReportSystemError(errno, _("cannot read header '%s'"), path); + goto cleanup; + } + + if (format == VIR_STORAGE_FILE_AUTO) + format = virStorageFileProbeFormatFromBuf(path, head, len); + + if (format < 0 || + format >= VIR_STORAGE_FILE_LAST) { + virReportSystemError(EINVAL, _("unknown storage file format %d"), + format); + goto cleanup; + } + + if (fileTypeInfo[format].sizeOffset != -1) { + unsigned long long *file_capacity; + + if ((fileTypeInfo[format].sizeOffset + 8) > len) + return 1; + + file_capacity = (unsigned long long *)(head + fileTypeInfo[format].sizeOffset); + if (fileTypeInfo[format].endian == LV_LITTLE_ENDIAN) + *file_capacity = htole64(capacity); + else + *file_capacity = htobe64(capacity); + + *file_capacity /= fileTypeInfo[format].sizeMultiplier; + } + + /* Move to start of file to write the header back with adjusted capacity */ + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + virReportSystemError(errno, _("cannot seek to start of '%s'"), path); + return -1; + } + + if ((len = write(fd, head, len)) < 0) { + virReportSystemError(errno, _("cannot write header '%s'"), path); + goto cleanup; + } + +cleanup: + VIR_FREE(head); + return ret; +} + +/** + * virStorageFileResize: + */ +int +virStorageFileResize(const char *path, + int format, + unsigned long long capacity) +{ + int fd, ret; + + if ((fd = open(path, O_RDONLY)) < 0) { + virReportSystemError(errno, _("cannot open file '%s'"), path); + return -1; + } + + ret = virStorageFileResizeFromFD(path, fd, format, capacity); + + VIR_FORCE_CLOSE(fd); + + return ret; +} + #ifdef __linux__ # ifndef NFS_SUPER_MAGIC diff --git a/src/util/storage_file.h b/src/util/storage_file.h index b8920d0..02716f2 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -72,6 +72,14 @@ int virStorageFileGetMetadataFromFD(const char *path, void virStorageFileFreeMetadata(virStorageFileMetadata *meta); +int virStorageFileResize(const char *path, + int format, + unsigned long long capacity); +int virStorageFileResizeFromFD(const char *path, + int fd, + int format, + unsigned long long capacity); + enum { VIR_STORAGE_FILE_SHFS_NFS = (1 << 0), VIR_STORAGE_FILE_SHFS_GFS2 = (1 << 1), diff --git a/tools/virsh.c b/tools/virsh.c index d635b56..e9a40a2 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -11209,6 +11209,58 @@ cmdVolInfo(vshControl *ctl, const vshCmd *cmd) return ret; } +/* + * "vol-resize" command + */ +static const vshCmdInfo info_vol_resize[] = { + {"help", N_("resize a vol")}, + {"desc", N_("Resizes a storage vol.")}, + {NULL, NULL} +}; + +static const vshCmdOptDef opts_vol_resize[] = { + {"vol", VSH_OT_DATA, VSH_OFLAG_REQ, N_("vol name, key or path")}, + {"capacity", VSH_OT_DATA, VSH_OFLAG_REQ, N_("new capacity for the vol with optional k,M,G,T suffix")}, + {"pool", VSH_OT_STRING, 0, N_("pool name or uuid")}, + {NULL, 0, 0, NULL} +}; + +static bool +cmdVolResize(vshControl *ctl, const vshCmd *cmd) +{ + virStorageVolPtr vol; + const char *capacityStr = NULL; + unsigned long long capacity = 0; + bool ret = true; + + if (!vshConnectionUsability(ctl, ctl->conn)) + return false; + + if (!(vol = vshCommandOptVol(ctl, cmd, "vol", "pool", NULL))) + return false; + + if (vshCommandOptString(cmd, "capacity", &capacityStr) <= 0) + goto cleanup; + if (cmdVolSize(capacityStr, &capacity) < 0) { + vshError(ctl, _("Malformed size %s"), capacityStr); + goto cleanup; + } + + if (virStorageVolResize(vol, capacity, 0) == 0) { + vshPrint(ctl, "Size of volume '%s' successfully changed to %s\n", + virStorageVolGetName(vol), capacityStr); + ret = true; + } else { + vshError(ctl, "Failed to change size of volume '%s' to %s\n", + virStorageVolGetName(vol), capacityStr); + ret = false; + } + +cleanup: + virStorageVolFree(vol); + return ret; +} + /* * "vol-dumpxml" command @@ -16065,6 +16117,7 @@ static const vshCmdDef storageVolCmds[] = { {"vol-pool", cmdVolPool, opts_vol_pool, info_vol_pool, 0}, {"vol-upload", cmdVolUpload, opts_vol_upload, info_vol_upload, 0}, {"vol-wipe", cmdVolWipe, opts_vol_wipe, info_vol_wipe, 0}, + {"vol-resize", cmdVolResize, opts_vol_resize, info_vol_resize, 0}, {NULL, NULL, NULL, NULL, 0} };
-- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list