[libvirt] [libvirt-glib] Add virStorageVolResize()

Zeeshan Ali (Khattak) zeeshanak at gnome.org
Fri Jan 27 05:29:56 UTC 2012


From: "Zeeshan Ali (Khattak)" <zeeshanak at gnome.org>

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 |   18 +++++++
 src/storage/storage_driver.c     |   83 ++++++++++++++++++++++++++++++
 src/util/storage_file.c          |  105 ++++++++++++++++++++++++++++++++++++++
 src/util/storage_file.h          |    4 ++
 tools/virsh.c                    |   53 +++++++++++++++++++
 13 files changed, 345 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..44865e8 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", vol);
+
+    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..67c113e 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..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 a332ada..d3eff93 100644
--- a/src/storage/storage_driver.c
+++ b/src/storage/storage_driver.c
@@ -1695,7 +1695,89 @@ 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 (capacity < vol->allocation) {
+        virStorageReportError(VIR_ERR_INVALID_ARG,
+                              "%s", _("can not shrink capacity below "
+                                      "existing allocation"));
+
+        goto out;
+    }
+
+    if (capacity > vol->allocation + pool->def->available) {
+        virStorageReportError(VIR_ERR_INVALID_ARG,
+                              "%s", _("Not enough space left on storage pool"));
+
+        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
@@ -2243,6 +2325,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..f84feab 100644
--- a/src/util/storage_file.c
+++ b/src/util/storage_file.c
@@ -931,6 +931,111 @@ virStorageFileFreeMetadata(virStorageFileMetadata *meta)
     VIR_FREE(meta);
 }
 
+static int
+virStorageFileResizeForFD(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;
+    }
+
+    ret = 0;
+
+cleanup:
+    VIR_FREE(head);
+    return ret;
+}
+
+/**
+ * virStorageFileResize:
+ *
+ * Change the capacity of the storage file at 'path'.
+ */
+int
+virStorageFileResize(const char *path,
+                     int format,
+                     unsigned long long capacity)
+{
+    int fd, ret;
+
+    if ((fd = open(path, O_RDWR)) < 0) {
+        virReportSystemError(errno, _("cannot open file '%s'"), path);
+        return -1;
+    }
+
+    ret = virStorageFileResizeForFD(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..f6c6048 100644
--- a/src/util/storage_file.h
+++ b/src/util/storage_file.h
@@ -72,6 +72,10 @@ int virStorageFileGetMetadataFromFD(const char *path,
 
 void virStorageFileFreeMetadata(virStorageFileMetadata *meta);
 
+int virStorageFileResize(const char *path,
+                         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 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




More information about the libvir-list mailing list