On 19/01/17 00:04, John Ferlan wrote:
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.
I will follow changes
> Signed-off-by: Olga Krishtal <okrishtal(a)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.
follow the *_fs.c model seems more reasonable
> +
> + 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,
> };
>
--
Best regards,
Olga