From: "Zeeshan Ali (Khattak)" <zeeshanak@xxxxxxxxx> Add a new function to allow changing of capacity of storage volumes. --- include/libvirt/libvirt.h.in | 5 ++ src/driver.h | 5 ++ src/libvirt.c | 50 +++++++++++++++++++++++ src/libvirt_public.syms | 1 + src/remote/remote_driver.c | 1 + src/remote/remote_protocol.x | 9 ++++- src/remote_protocol-structs | 6 +++ src/storage/storage_backend.h | 6 +++ src/storage/storage_backend_fs.c | 59 ++++++++++++++++++++++++++++ src/storage/storage_driver.c | 80 ++++++++++++++++++++++++++++++++++++++ src/util/storage_file.c | 16 ++++++++ src/util/storage_file.h | 2 + tools/virsh.c | 53 +++++++++++++++++++++++++ 13 files changed, 292 insertions(+), 1 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index e99cd00..b169592 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -2386,6 +2386,11 @@ char * virStorageVolGetXMLDesc (virStorageVolPtr pool, char * virStorageVolGetPath (virStorageVolPtr vol); +int virStorageVolResize (virStorageVolPtr vol, + unsigned long long capacity, + unsigned int flags); + + /** * virKeycodeSet: * diff --git a/src/driver.h b/src/driver.h index df2aa60..c850926 100644 --- a/src/driver.h +++ b/src/driver.h @@ -1261,6 +1261,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); @@ -1323,6 +1327,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 e9d638b..d1962a2 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -12927,6 +12927,56 @@ 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 capacity=%llu flags=%x", vol, capacity, flags); + + virResetLastError(); + + if (!VIR_IS_STORAGE_VOL(vol)) { + virLibStorageVolError(VIR_ERR_INVALID_STORAGE_VOL, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + conn = vol->conn; + + if (conn->flags & VIR_CONNECT_RO) { + virLibConnError(VIR_ERR_OPERATION_DENIED, __FUNCTION__); + goto error; + } + + if (conn->storageDriver && conn->storageDriver->volResize) { + int ret; + ret = conn->storageDriver->volResize(vol, capacity, flags); + if (ret < 0) + 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 1340b0c..54a4f2c 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -520,6 +520,7 @@ LIBVIRT_0.9.10 { global: virDomainShutdownFlags; virStorageVolWipePattern; + virStorageVolResize; } LIBVIRT_0.9.9; # .... define new API here using predicted next version number .... diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c index f79f53e..2bb4cbf 100644 --- a/src/remote/remote_driver.c +++ b/src/remote/remote_driver.c @@ -4837,6 +4837,7 @@ static virStorageDriver storage_driver = { .volGetInfo = remoteStorageVolGetInfo, /* 0.4.1 */ .volGetXMLDesc = remoteStorageVolGetXMLDesc, /* 0.4.1 */ .volGetPath = remoteStorageVolGetPath, /* 0.4.1 */ + .volResize = remoteStorageVolResize, /* 0.9.10 */ .poolIsActive = remoteStoragePoolIsActive, /* 0.7.3 */ .poolIsPersistent = remoteStoragePoolIsPersistent, /* 0.7.3 */ }; diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x index 0f354bb..29f98fc 100644 --- a/src/remote/remote_protocol.x +++ b/src/remote/remote_protocol.x @@ -1676,6 +1676,12 @@ struct remote_storage_vol_get_path_ret { remote_nonnull_string name; }; +struct remote_storage_vol_resize_args { + remote_nonnull_storage_vol vol; + unsigned hyper capacity; + unsigned int flags; +}; + /* Node driver calls: */ struct remote_node_num_of_devices_args { @@ -2667,7 +2673,8 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_SET_INTERFACE_PARAMETERS = 256, /* autogen autogen */ REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, /* skipgen skipgen */ REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, /* autogen autogen */ - REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259 /* autogen autogen */ + REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259, /* autogen autogen */ + REMOTE_PROC_STORAGE_VOL_RESIZE = 300 /* autogen autogen */ /* * Notice how the entries are grouped in sets of 10 ? diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs index de85862..9a60fc2 100644 --- a/src/remote_protocol-structs +++ b/src/remote_protocol-structs @@ -1260,6 +1260,11 @@ struct remote_storage_vol_get_path_args { struct remote_storage_vol_get_path_ret { remote_nonnull_string name; }; +struct remote_storage_vol_resize_args { + remote_nonnull_storage_vol vol; + uint64_t capacity; + u_int flags; +}; struct remote_node_num_of_devices_args { remote_string cap; u_int flags; @@ -2101,4 +2106,5 @@ enum remote_procedure { REMOTE_PROC_DOMAIN_GET_INTERFACE_PARAMETERS = 257, REMOTE_PROC_DOMAIN_SHUTDOWN_FLAGS = 258, REMOTE_PROC_STORAGE_VOL_WIPE_PATTERN = 259, + REMOTE_PROC_STORAGE_VOL_RESIZE = 300, }; 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..31f7c0a 100644 --- a/src/storage/storage_backend_fs.c +++ b/src/storage/storage_backend_fs.c @@ -1187,6 +1187,62 @@ virStorageBackendFileSystemVolRefresh(virConnectPtr conn, return 0; } +static int +virStorageBackendFilesystemResizeQemuImg(const char *path, + unsigned long long capacity) +{ + int ret = -1; + char *img_tool; + virCommandPtr cmd = NULL; + + /* KVM is usually ahead of qemu on features, so try that first */ + img_tool = virFindFileInPath("kvm-img"); + if (!img_tool) + img_tool = virFindFileInPath("qemu-img"); + + if (!img_tool) { + virStorageReportError(VIR_ERR_INTERNAL_ERROR, + "%s", _("unable to find kvm-img or qemu-img")); + return -1; + } + + cmd = virCommandNew(img_tool); + virCommandAddArgList(cmd, "resize", path, NULL); + virCommandAddArgFormat(cmd, "%llu", capacity); + + ret = virCommandRun(cmd, NULL); + + VIR_FREE(img_tool); + virCommandFree(cmd); + + return ret; +} + +/** + * Resize a volume + */ +static int +virStorageBackendFileSystemVolResize(virConnectPtr conn ATTRIBUTE_UNUSED, + virStoragePoolObjPtr pool ATTRIBUTE_UNUSED, + virStorageVolDefPtr vol, + unsigned long long capacity, + unsigned int flags) +{ + virCheckFlags(0, -1); + + if (vol->target.format == VIR_STORAGE_FILE_RAW) { + return virStorageFileResize(vol->target.path, capacity); + } else if (vol->target.format == VIR_STORAGE_FILE_DIR) { + virStorageReportError(VIR_ERR_NO_SUPPORT, + "%s", _("Changing size of directory based " + "volumes is not supported")); + return -1; + } else { + return virStorageBackendFilesystemResizeQemuImg(vol->target.path, + capacity); + } +} + virStorageBackend virStorageBackendDirectory = { .type = VIR_STORAGE_POOL_DIR, @@ -1199,6 +1255,7 @@ virStorageBackend virStorageBackendDirectory = { .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, + .resizeVol = virStorageBackendFileSystemVolResize, }; #if WITH_STORAGE_FS @@ -1216,6 +1273,7 @@ virStorageBackend virStorageBackendFileSystem = { .createVol = virStorageBackendFileSystemVolCreate, .refreshVol = virStorageBackendFileSystemVolRefresh, .deleteVol = virStorageBackendFileSystemVolDelete, + .resizeVol = virStorageBackendFileSystemVolResize, }; virStorageBackend virStorageBackendNetFileSystem = { .type = VIR_STORAGE_POOL_NETFS, @@ -1232,5 +1290,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 a332ada..76730b4 100644 --- a/src/storage/storage_driver.c +++ b/src/storage/storage_driver.c @@ -1695,7 +1695,86 @@ 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, + _("no storage pool with matching uuid")); + goto out; + } + + if (!virStoragePoolObjIsActive(pool)) { + virStorageReportError(VIR_ERR_OPERATION_INVALID, + _("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 (capacity < vol->allocation) { + virStorageReportError(VIR_ERR_INVALID_ARG, + _("can't shrink capacity below " + "existing allocation")); + goto out; + } + + if (capacity > vol->allocation + pool->def->available) { + virStorageReportError(VIR_ERR_INVALID_ARG, + _("Not enough space left on storage pool")); + goto out; + } + + if (!backend->resizeVol) { + virStorageReportError(VIR_ERR_NO_SUPPORT, + _("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 @@ -2243,6 +2322,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..8260adb 100644 --- a/src/util/storage_file.c +++ b/src/util/storage_file.c @@ -931,6 +931,22 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta) VIR_FREE(meta); } +/** + * virStorageFileResize: + * + * Change the capacity of the raw storage file at 'path'. + */ +int +virStorageFileResize(const char *path, unsigned long long capacity) +{ + if (truncate(path, capacity) < 0) { + virReportSystemError(errno, _("Failed to truncate file '%s'"), path); + return -1; + } + + return 0; +} + #ifdef __linux__ # ifndef NFS_SUPER_MAGIC diff --git a/src/util/storage_file.h b/src/util/storage_file.h index b8920d0..96afb12 100644 --- a/src/util/storage_file.h +++ b/src/util/storage_file.h @@ -72,6 +72,8 @@ int virStorageFileGetMetadataFromFD(const char *path, void virStorageFileFreeMetadata(virStorageFileMetadata *meta); +int virStorageFileResize(const char *path, 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 74655c2..20d4bd0 100644 --- a/tools/virsh.c +++ b/tools/virsh.c @@ -11279,6 +11279,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 @@ -16141,6 +16193,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} }; -- 1.7.7.5 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list