[libvirt] [PATCH v2 3/5] storage: vstorage pool operations

John Ferlan jferlan at redhat.com
Wed Jan 18 21:04:05 UTC 2017



On 01/17/2017 09:10 AM, Olga Krishtal wrote:
> Added create/define/etc pool operations for vstorage backend.
> 
> The vstorage storage pool looks like netfs ones. Due to this
> some of pool/volume functions were taken with no changes:
> refresh pool function.

Not exactly what I was expecting - perhaps I didn't explain well enough.
I was hoping you would create common API helpers and call from *_fs.c
and *_vstorage.c.

Of course, this is where that set of changes would start overlapping
with a what pkrempa is doing by creating a common storage_util.c
(currently the common place would be storage_backend.c) [1].

Since I'm watching [1] and I'd like for you to see progress here, I can
create the common functions once [1] is completed - which should be
soon. I'll mark places below with [2] for your reference.

> 
> Signed-off-by: Olga Krishtal <okrishtal at virtuozzo.com>
> ---
>  src/storage/storage_backend_vstorage.c | 530 +++++++++++++++++++++++++++++++++
>  1 file changed, 530 insertions(+)
> 
> diff --git a/src/storage/storage_backend_vstorage.c b/src/storage/storage_backend_vstorage.c
> index 3a57385..8332f4d 100644
> --- a/src/storage/storage_backend_vstorage.c
> +++ b/src/storage/storage_backend_vstorage.c
> @@ -6,11 +6,541 @@
>  #include "storage_backend_vstorage.h"
>  #include "virlog.h"
>  #include "virstring.h"
> +#include <mntent.h>
> +#include <pwd.h>
> +#include <grp.h>

> +#include <sys/statvfs.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>

[2] These 3 won't be necessary...

>  
>  #define VIR_FROM_THIS VIR_FROM_STORAGE
>  
> +#define VIR_STORAGE_VOL_FS_OPEN_FLAGS    (VIR_STORAGE_VOL_OPEN_DEFAULT | \
> +                                          VIR_STORAGE_VOL_OPEN_DIR)
> +#define VIR_STORAGE_VOL_FS_PROBE_FLAGS   (VIR_STORAGE_VOL_FS_OPEN_FLAGS | \
> +                                          VIR_STORAGE_VOL_OPEN_NOERROR)

[2] These flags move to storage_backend.h near where
VIR_STORAGE_VOL_OPEN_DEFAULT is defined...

> +
> +
> +

Some strange spacing here. I think if you move the VIR_LOG_INIT up
betwen the #define's and have the right spacing it'll be OK.


>  VIR_LOG_INIT("storage.storage_backend_vstorage");

Need at least an empty line between

> +/**
> + * @conn connection to report errors against
> + * @pool storage pool to build
> + * @flags controls the pool formatting behaviour
> + *
> + *
> + * If no flag is set, it only makes the directory;
> + * If VIR_STORAGE_POOL_BUILD_NO_OVERWRITE set, it determines if
> + * the directory exists and if yes - we use it. Otherwise - the new one
> + * will be created.
> + * If VIR_STORAGE_POOL_BUILD_OVERWRITE is set, the dircetory for pool
> + * will used but the content and permissions will be update

see [3]

> + *
> + * Returns 0 on success, -1 on error
> + */
> +
> +static int
> +virStorageBackendVzPoolBuild(virConnectPtr conn ATTRIBUTE_UNUSED,
> +                                 virStoragePoolObjPtr pool,
> +                                 unsigned int flags)
> +{

[2 - snip  most into virStorageBackendBuildFileDirCommon()]

keep:
    unsigned int dir_create_flags = 0;

> +    int ret = -1;
> +    char *parent = NULL;
> +    char *p = NULL;
> +    mode_t mode;
> +    unsigned int dir_create_flags;
> +
> +    virCheckFlags(VIR_STORAGE_POOL_BUILD_OVERWRITE |
> +                  VIR_STORAGE_POOL_BUILD_NO_OVERWRITE, ret);
> +
> +    VIR_EXCLUSIVE_FLAGS_GOTO(VIR_STORAGE_POOL_BUILD_OVERWRITE,
> +                             VIR_STORAGE_POOL_BUILD_NO_OVERWRITE,
> +                             error);
> +
> +    if (VIR_STRDUP(parent, pool->def->target.path) < 0)
> +        goto error;
> +    if (!(p = strrchr(parent, '/'))) {
> +        virReportError(VIR_ERR_INVALID_ARG,
> +                       _("path '%s' is not absolute"),
> +                       pool->def->target.path);
> +        goto error;
> +    }
> +
> +    if (p != parent) {
> +        /* assure all directories in the path prior to the final dir
> +         * exist, with default uid/gid/mode. */
> +        *p = '\0';
> +        if (virFileMakePath(parent) < 0) {
> +            virReportSystemError(errno, _("cannot create path '%s'"),
> +                                 parent);
> +            goto error;
> +        }
> +    }
> +
> +    dir_create_flags = VIR_DIR_CREATE_ALLOW_EXIST;
> +    mode = pool->def->target.perms.mode;
> +
> +    if (mode == (mode_t) -1 && !virFileExists(pool->def->target.path))
> +        mode = VIR_STORAGE_DEFAULT_POOL_PERM_MODE;
> +
> +    /* Now create the final dir in the path with the uid/gid/mode
> +     * requested in the config. If the dir already exists, just set
> +     * the perms. */
> +    if (virDirCreate(pool->def->target.path,
> +                     mode,
> +                     pool->def->target.perms.uid,
> +                     pool->def->target.perms.gid,
> +                     dir_create_flags) < 0)
> +        goto error;

[2 end snip]

> +
> +    /* Delete directory content if flag is set*/

should be "set */"

> +    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE)
> +       if (virFileDeleteTree(pool->def->target.path))
> +           goto error;

[3] This block doesn't make sense in light of the virDirCreate just
above *and* the intro comments.

If you want to support [NO_]OVERWRITE, then prior to calling the common
function we can play with flags, thus you end up with just:

    if (flags & VIR_STORAGE_POOL_BUILD_OVERWRITE)
        dir_create_flags |= VIR_DIR_CREATE_ALLOW_EXIST;

    return virStorageBackendBuildFileDirCommon(pool, dir_create_flags);


The *_fs.c code would be

    unsigned int dir_create_flags = VIR_DIR_CREATE_ALLOW_EXIST;
...

    if (virStorageBackendBuildFileDirCommon(pool, dir_create_flags) < 0)
        goto error;
...

and the common code would receive dir_create_flags

The comments would then need to be updated a bit...  If you have a
suggestion or would prefer to just follow the *_fs.c model - I can
adjust that too - let me know.


> +
> +    ret = 0;
> +
> + error:
> +    VIR_FREE(parent);
> +    return ret;
> +}
> +
> +static int
> +virStorageBackendVzPoolStart(virConnectPtr conn ATTRIBUTE_UNUSED,
> +                             virStoragePoolObjPtr pool)
> +{
> +    int ret = -1;
> +    virCommandPtr cmd = NULL;
> +    char *grp_name = NULL;
> +    char *usr_name = NULL;
> +    char *mode = NULL;
> +
> +    /* Check the permissions */
> +    if (pool->def->target.perms.mode == (mode_t) - 1)
> +        pool->def->target.perms.mode = VIR_STORAGE_DEFAULT_POOL_PERM_MODE;
> +    if (pool->def->target.perms.uid == (uid_t) -1)
> +        pool->def->target.perms.uid = geteuid();
> +    if (pool->def->target.perms.gid == (gid_t) -1)
> +        pool->def->target.perms.gid = getegid();
> +
> +    /* Convert ids to names because vstorage uses names */
> +
> +    grp_name = virGetGroupName(pool->def->target.perms.gid);
> +    if (!grp_name)
> +        return -1;
> +
> +    usr_name = virGetUserName(pool->def->target.perms.uid);
> +    if (!usr_name)
> +        return -1;

Leak grp_name

> +
> +    if (virAsprintf(&mode, "%o", pool->def->target.perms.mode) < 0)
> +        return -1;

Leak grp_name, usr_name

Each of these changes to a goto cleanup;

> +
> +    cmd = virCommandNewArgList(VSTORAGE_MOUNT,
> +                               "-c", pool->def->source.name,
> +                               pool->def->target.path,
> +                               "-m", mode,
> +                               "-g", grp_name, "-u", usr_name,
> +                               NULL);
> +
> +    if (virCommandRun(cmd, NULL) < 0)
> +        goto cleanup;
> +    ret = 0;
> +
> + cleanup:
> +    virCommandFree(cmd);
> +    VIR_FREE(mode);
> +    VIR_FREE(grp_name);
> +    VIR_FREE(usr_name);
> +    return ret;
> +}
> +
> +static int
> +virStorageBackendVzIsMounted(virStoragePoolObjPtr pool)
> +{
> +    int ret = -1;
> +    char *src = NULL;

Never used - I'll remove

> +    FILE *mtab;
> +    struct mntent ent;
> +    char buf[1024];
> +    char *cluster = NULL;
> +
> +    if (virAsprintf(&cluster, "vstorage://%s", pool->def->source.name) < 0)
> +        return -1;
> +
> +    if ((mtab = fopen(_PATH_MOUNTED, "r")) == NULL) {
> +        virReportSystemError(errno,
> +                             _("cannot read mount list '%s'"),
> +                             _PATH_MOUNTED);
> +        goto cleanup;
> +    }
> +
> +    while ((getmntent_r(mtab, &ent, buf, sizeof(buf))) != NULL) {
> +
> +        if (STREQ(ent.mnt_dir, pool->def->target.path) &&
> +            STREQ(ent.mnt_fsname, cluster)) {
> +            ret = 1;
> +            goto cleanup;
> +        }
> +
> +        VIR_FREE(src);
> +    }
> +
> +    ret = 0;
> +
> + cleanup:
> +    VIR_FORCE_FCLOSE(mtab);
> +    VIR_FREE(src);
> +    VIR_FREE(cluster);
> +    return ret;
> +}
> +
> +static int
> +virStorageBackendVzUmount(virStoragePoolObjPtr pool)
> +{
> +    virCommandPtr cmd = NULL;
> +    int ret = -1;
> +    int rc;
> +
> +    /* Short-circuit if already unmounted */
> +    if ((rc = virStorageBackendVzIsMounted(pool)) != 1)
> +        return rc;
> +

[2] The following has commonality with _fs.c - use "return
virStorageBackendUmountCommon(pool);"

> +    cmd = virCommandNewArgList(UMOUNT,
> +                               pool->def->target.path,
> +                               NULL);
> +
> +    if (virCommandRun(cmd, NULL) < 0)
> +        goto cleanup;
> +
> +    ret = 0;
> + cleanup:
> +    virCommandFree(cmd);
> +    return ret;
> +}
> +
> +static int
> +virStorageBackendVzPoolStop(virConnectPtr conn ATTRIBUTE_UNUSED,
> +                            virStoragePoolObjPtr pool)
> +{
> +    if (virStorageBackendVzUmount(pool) < 0)
> +        return -1;
> +
> +    return 0;
> +}
> +
> +/**
> + * @conn connection to report errors against
> + * @pool storage pool to delete
> + *
> + * Deletes vstorage based storage pool.
> + * At this moment vstorage is in umounted state - so we do not
> + * need to delete volumes first.
> + * Returns 0 on success, -1 on error
> + */
> +static int
> +virStorageBackendVzDelete(virConnectPtr conn ATTRIBUTE_UNUSED,
> +                          virStoragePoolObjPtr pool,
> +                          unsigned int flags)
> +{
> +    virCheckFlags(0, -1);

[2] This could be a return virStorageBackendDeleteDirCommon(pool);

> +
> +    if (rmdir(pool->def->target.path) < 0) {
> +        virReportSystemError(errno,
> +                             _("failed to remove pool '%s'"),
> +                             pool->def->target.path);
> +        return -1;
> +    }
> +
> +    return 0;
> +}
> +
> +/*
> + * Check wether the cluster is mounted

whether

> + */
> +static int
> +virStorageBackendVzCheck(virStoragePoolObjPtr pool,
> +                         bool *isActive)
> +{
> +    int ret = -1;
> +    *isActive = false;
> +    if ((ret = virStorageBackendVzIsMounted(pool)) != 0) {
> +        if (ret < 0)
> +            return -1;
> +        *isActive = true;
> +    }
> +
> +    return 0;
> +}
> +

[2 - start] - This whole hunk moves to common module

> +/* All the underlying functions were directly copied from
> + * filesystem backend with no changes.
> + */
> +
> +static int
> +virStorageBackendProbeTarget(virStorageSourcePtr target,
> +                             virStorageEncryptionPtr *encryption)
> +{
> +    int backingStoreFormat;
> +    int fd = -1;
> +    int ret = -1;
> +    int rc;
> +    virStorageSourcePtr meta = NULL;
> +    struct stat sb;
> +
> +    if (encryption)
> +        *encryption = NULL;
> +
> +    if ((rc = virStorageBackendVolOpen(target->path, &sb,
> +                                       VIR_STORAGE_VOL_FS_PROBE_FLAGS)) < 0)
> +        return rc; /* Take care to propagate rc, it is not always -1 */
> +    fd = rc;
> +
> +    if (virStorageBackendUpdateVolTargetInfoFD(target, fd, &sb) < 0)
> +        goto cleanup;
> +
> +    if (S_ISDIR(sb.st_mode)) {
> +        if (virStorageBackendIsPloopDir(target->path)) {
> +            if (virStorageBackendRedoPloopUpdate(target, &sb, &fd,
> +                                                 VIR_STORAGE_VOL_FS_PROBE_FLAGS) < 0)
> +                goto cleanup;
> +        } else {
> +            target->format = VIR_STORAGE_FILE_DIR;
> +            ret = 0;
> +            goto cleanup;
> +        }
> +    }
> +
> +    if (!(meta = virStorageFileGetMetadataFromFD(target->path,
> +                                                 fd,
> +                                                 VIR_STORAGE_FILE_AUTO,
> +                                                 &backingStoreFormat)))
> +        goto cleanup;
> +
> +    if (meta->backingStoreRaw) {
> +        if (!(target->backingStore = virStorageSourceNewFromBacking(meta)))
> +            goto cleanup;
> +
> +        target->backingStore->format = backingStoreFormat;
> +
> +        /* XXX: Remote storage doesn't play nicely with volumes backed by
> +         * remote storage. To avoid trouble, just fake the backing store is RAW
> +         * and put the string from the metadata as the path of the target. */
> +        if (!virStorageSourceIsLocalStorage(target->backingStore)) {
> +            virStorageSourceFree(target->backingStore);
> +
> +            if (VIR_ALLOC(target->backingStore) < 0)
> +                goto cleanup;
> +
> +            target->backingStore->type = VIR_STORAGE_TYPE_NETWORK;
> +            target->backingStore->path = meta->backingStoreRaw;
> +            meta->backingStoreRaw = NULL;
> +            target->backingStore->format = VIR_STORAGE_FILE_RAW;
> +        }
> +
> +        if (target->backingStore->format == VIR_STORAGE_FILE_AUTO) {
> +            if ((rc = virStorageFileProbeFormat(target->backingStore->path,
> +                                                -1, -1)) < 0) {
> +                /* If the backing file is currently unavailable or is
> +                 * accessed via remote protocol only log an error, fake the
> +                 * format as RAW and continue. Returning -1 here would
> +                 * disable the whole storage pool, making it unavailable for
> +                 * even maintenance. */
> +                target->backingStore->format = VIR_STORAGE_FILE_RAW;
> +                virReportError(VIR_ERR_INTERNAL_ERROR,
> +                               _("cannot probe backing volume format: %s"),
> +                               target->backingStore->path);
> +            } else {
> +                target->backingStore->format = rc;
> +            }
> +        }
> +    }
> +
> +    target->format = meta->format;
> +
> +    /* Default to success below this point */
> +    ret = 0;
> +
> +    if (meta->capacity)
> +        target->capacity = meta->capacity;
> +
> +    if (encryption && meta->encryption) {
> +        *encryption = meta->encryption;
> +        meta->encryption = NULL;
> +
> +        /* XXX ideally we'd fill in secret UUID here
> +         * but we cannot guarantee 'conn' is non-NULL
> +         * at this point in time :-(  So we only fill
> +         * in secrets when someone first queries a vol
> +         */
> +    }
> +
> +    virBitmapFree(target->features);
> +    target->features = meta->features;
> +    meta->features = NULL;
> +
> +    if (meta->compat) {
> +        VIR_FREE(target->compat);
> +        target->compat = meta->compat;
> +        meta->compat = NULL;
> +    }
> +
> + cleanup:
> +    VIR_FORCE_CLOSE(fd);
> +    virStorageSourceFree(meta);
> +    return ret;
> +
> +}

[2] - end

> +
> +/**
> + * The same as for fs/dir storage pools
> + * Iterate over the pool's directory and enumerate all disk images
> + * within it. This is non-recursive.
> + */

Scratch the comment

> +
> +static int
> +virStorageBackendVzRefresh(virConnectPtr conn ATTRIBUTE_UNUSED,
> +                                   virStoragePoolObjPtr pool)
> +{

[2] all the code moves:

    return virStorageBackendRefreshFileDirCommon(pool);

> +    DIR *dir;
> +    struct dirent *ent;
> +    struct statvfs sb;
> +    struct stat statbuf;
> +    virStorageVolDefPtr vol = NULL;
> +    virStorageSourcePtr target = NULL;
> +    int direrr;
> +    int fd = -1, ret = -1;
> +
> +    if (virDirOpen(&dir, pool->def->target.path) < 0)
> +        goto cleanup;
> +
> +    while ((direrr = virDirRead(dir, &ent, pool->def->target.path)) > 0) {
> +        int err;
> +
> +        if (virStringHasControlChars(ent->d_name)) {
> +            VIR_WARN("Ignoring file with control characters under '%s'",
> +                     pool->def->target.path);
> +            continue;
> +        }
> +
> +        if (VIR_ALLOC(vol) < 0)
> +            goto cleanup;
> +
> +        if (VIR_STRDUP(vol->name, ent->d_name) < 0)
> +            goto cleanup;
> +
> +        vol->type = VIR_STORAGE_VOL_FILE;
> +        vol->target.format = VIR_STORAGE_FILE_RAW; /* Real value is filled in during probe */
> +        if (virAsprintf(&vol->target.path, "%s/%s",
> +                        pool->def->target.path,
> +                        vol->name) == -1)
> +            goto cleanup;
> +
> +        if (VIR_STRDUP(vol->key, vol->target.path) < 0)
> +            goto cleanup;
> +
> +        if ((err = virStorageBackendProbeTarget(&vol->target,
> +                                                &vol->target.encryption)) < 0) {
> +            if (err == -2) {
> +                /* Silently ignore non-regular files,
> +                 * eg 'lost+found', dangling symbolic link */
> +                virStorageVolDefFree(vol);
> +                vol = NULL;
> +                continue;
> +            } else if (err == -3) {
> +                /* The backing file is currently unavailable, its format is not
> +                 * explicitly specified, the probe to auto detect the format
> +                 * failed: continue with faked RAW format, since AUTO will
> +                 * break virStorageVolTargetDefFormat() generating the line
> +                 * <format type='...'/>. */
> +            } else {
> +                goto cleanup;
> +            }
> +        }
> +
> +        /* directory based volume */
> +        if (vol->target.format == VIR_STORAGE_FILE_DIR)
> +            vol->type = VIR_STORAGE_VOL_DIR;
> +
> +        if (vol->target.format == VIR_STORAGE_FILE_PLOOP)
> +            vol->type = VIR_STORAGE_VOL_PLOOP;
> +
> +        if (vol->target.backingStore) {
> +            ignore_value(virStorageBackendUpdateVolTargetInfo(vol->target.backingStore,
> +                                                              false,
> +                                                              VIR_STORAGE_VOL_OPEN_DEFAULT, 0));

NB: I had a patch here today - this is why cut/copy/paste is not
preferred...

> +            /* If this failed, the backing file is currently unavailable,
> +             * the capacity, allocation, owner, group and mode are unknown.
> +             * An error message was raised, but we just continue. */
> +        }
> +
> +        if (VIR_APPEND_ELEMENT(pool->volumes.objs, pool->volumes.count, vol) < 0)
> +            goto cleanup;
> +    }
> +    if (direrr < 0)
> +        goto cleanup;
> +    VIR_DIR_CLOSE(dir);
> +    vol = NULL;
> +
> +    if (VIR_ALLOC(target))
> +        goto cleanup;
> +
> +    if ((fd = open(pool->def->target.path, O_RDONLY)) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot open path '%s'"),
> +                             pool->def->target.path);
> +        goto cleanup;
> +    }
> +
> +    if (fstat(fd, &statbuf) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot stat path '%s'"),
> +                             pool->def->target.path);
> +        goto cleanup;
> +    }
> +
> +    if (virStorageBackendUpdateVolTargetInfoFD(target, fd, &statbuf) < 0)
> +        goto cleanup;
> +
> +    /* VolTargetInfoFD doesn't update capacity correctly for the pool case */
> +    if (statvfs(pool->def->target.path, &sb) < 0) {
> +        virReportSystemError(errno,
> +                             _("cannot statvfs path '%s'"),
> +                             pool->def->target.path);
> +        goto cleanup;
> +    }
> +
> +    pool->def->capacity = ((unsigned long long)sb.f_frsize *
> +                           (unsigned long long)sb.f_blocks);
> +    pool->def->available = ((unsigned long long)sb.f_bfree *
> +                            (unsigned long long)sb.f_frsize);
> +    pool->def->allocation = pool->def->capacity - pool->def->available;
> +
> +    pool->def->target.perms.mode = target->perms->mode;
> +    pool->def->target.perms.uid = target->perms->uid;
> +    pool->def->target.perms.gid = target->perms->gid;
> +    VIR_FREE(pool->def->target.perms.label);
> +    if (VIR_STRDUP(pool->def->target.perms.label, target->perms->label) < 0)
> +        goto cleanup;
> +
> +    ret = 0;
> + cleanup:
> +    VIR_DIR_CLOSE(dir);
> +    VIR_FORCE_CLOSE(fd);
> +    virStorageVolDefFree(vol);
> +    virStorageSourceFree(target);
> +    if (ret < 0)
> +        virStoragePoolObjClearVols(pool);
> +    return ret;
> +}

[2 - end]


Once this is all done - this file is much shorter and you gain the
benefit of any adjustments to the common code - which will happen...
and this code wouldn't be updated...

I'll make the updates though - it won't happen overnight - should be by
the end of the week.

John
>  
>  virStorageBackend virStorageBackendVstorage = {
>      .type = VIR_STORAGE_POOL_VSTORAGE,
> +
> +    .buildPool = virStorageBackendVzPoolBuild,
> +    .startPool = virStorageBackendVzPoolStart,
> +    .stopPool = virStorageBackendVzPoolStop,
> +    .deletePool = virStorageBackendVzDelete,
> +    .refreshPool = virStorageBackendVzRefresh,
> +    .checkPool = virStorageBackendVzCheck,
>  };
> 




More information about the libvir-list mailing list