From 9a86695fec5caccb1db74c2803d818ba3f5e8fd9 Mon Sep 17 00:00:00 2001 From: Venky Shankar Date: Tue, 14 Jan 2020 23:46:09 -0500 Subject: [PATCH] mgr/volumes: remove stale subvolume module this was lying around post versioning changes. Signed-off-by: Venky Shankar (cherry picked from commit 5595476af7fe4dc2dfdeae17d5507d1d8eeeb3f5) --- src/pybind/mgr/volumes/fs/subvolume.py | 440 ------------------------- 1 file changed, 440 deletions(-) delete mode 100644 src/pybind/mgr/volumes/fs/subvolume.py diff --git a/src/pybind/mgr/volumes/fs/subvolume.py b/src/pybind/mgr/volumes/fs/subvolume.py deleted file mode 100644 index 6b88da20d5258..0000000000000 --- a/src/pybind/mgr/volumes/fs/subvolume.py +++ /dev/null @@ -1,440 +0,0 @@ -""" -Copyright (C) 2019 Red Hat, Inc. - -LGPL2.1. See file COPYING. -""" - -import logging -import os -import errno - -import cephfs - -from .subvolspec import SubvolumeSpec -from .exception import VolumeException - -log = logging.getLogger(__name__) - -class SubVolume(object): - """ - Combine libcephfs and librados interfaces to implement a - 'Subvolume' concept implemented as a cephfs directory. - - Additionally, subvolumes may be in a 'Group'. Conveniently, - subvolumes are a lot like manila shares, and groups are a lot - like manila consistency groups. - - Refer to subvolumes with SubvolumePath, which specifies the - subvolume and group IDs (both strings). The group ID may - be None. - - In general, functions in this class are allowed raise rados.Error - or cephfs.Error exceptions in unexpected situations. - """ - - - def __init__(self, mgr, fs_handle): - self.fs = fs_handle - self.rados = mgr.rados - - def _get_single_dir_entry(self, dir_path, exclude=[]): - """ - Return a directory entry in a given directory excluding passed - in entries. - """ - exclude.extend((b".", b"..")) - try: - with self.fs.opendir(dir_path) as d: - entry = self.fs.readdir(d) - while entry: - if entry.d_name not in exclude and entry.is_dir(): - return entry.d_name - entry = self.fs.readdir(d) - return None - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - - - ### basic subvolume operations - - def create_subvolume(self, spec, size=None, pool=None, namespace_isolated=True, - uid=None, gid=None, mode=0o755): - """ - Set up metadata, pools and auth for a subvolume. - - This function is idempotent. It is safe to call this again - for an already-created subvolume, even if it is in use. - - :param spec: subvolume path specification - :param size: In bytes, or None for no size limit - :param pool: the RADOS pool where the data objects of the subvolumes will be stored - :param namespace_isolated: If true, use separate RADOS namespace for this subvolume - :param uid: the user identifier - :param gid: the group identifier - :mode: the user permissions - :return: None - """ - subvolpath = spec.subvolume_path - log.info("creating subvolume with path: {0}".format(subvolpath)) - - self.fs.mkdirs(subvolpath, mode) - - try: - if size is not None: - try: - self.fs.setxattr(subvolpath, 'ceph.quota.max_bytes', str(size).encode('utf-8'), 0) - except cephfs.InvalidValue as e: - raise VolumeException(-errno.EINVAL, "Invalid size: '{0}'".format(size)) - if pool: - try: - self.fs.setxattr(subvolpath, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0) - except cephfs.InvalidValue: - raise VolumeException(-errno.EINVAL, - "Invalid pool layout '{0}'. It must be a valid data pool".format(pool)) - - xattr_key = xattr_val = None - if namespace_isolated: - # enforce security isolation, use separate namespace for this subvolume - xattr_key = 'ceph.dir.layout.pool_namespace' - xattr_val = spec.fs_namespace - elif not pool: - # If subvolume's namespace layout is not set, then the subvolume's pool - # layout remains unset and will undesirably change with ancestor's - # pool layout changes. - xattr_key = 'ceph.dir.layout.pool' - xattr_val = self._get_ancestor_xattr(subvolpath, "ceph.dir.layout.pool") - # TODO: handle error... - self.fs.setxattr(subvolpath, xattr_key, xattr_val.encode('utf-8'), 0) - - groupstat = self.fs.stat(spec.group_path) - if uid is None: - uid = int(groupstat.st_uid) - else: - try: - uid = int(uid) - if uid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "Invalid uid") - - if gid is None: - gid = int(groupstat.st_gid) - else: - try: - gid = int(gid) - if gid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "Invalid gid") - - try: - self.fs.chown(subvolpath, uid, gid) - except cephfs.InvalidValue: - raise VolumeException(-e.args[0], e.args[1]) - - except Exception as e: - try: - # cleanup subvol path on best effort basis - log.debug("cleaning up subvolume with path: {0}".format(subvolpath)) - self.fs.rmdir(subvolpath) - except Exception: - log.debug("failed to clean up subvolume with path: {0}".format(subvolpath)) - pass - finally: - raise e - - def remove_subvolume(self, spec, force): - """ - Make a subvolume inaccessible to guests. This function is idempotent. - This is the fast part of tearing down a subvolume. The subvolume will - get purged in the background. - - :param spec: subvolume path specification - :param force: flag to ignore non-existent path (never raise exception) - :return: None - """ - - subvolpath = spec.subvolume_path - log.info("deleting subvolume with path: {0}".format(subvolpath)) - - # Create the trash directory if it doesn't already exist - trashdir = spec.trash_dir - self.fs.mkdirs(trashdir, 0o700) - - # mangle the trash directroy entry to a random string so that subsequent - # subvolume create and delete with same name moves the subvolume directory - # to a unique trash dir (else, rename() could fail if the trash dir exist). - trashpath = spec.unique_trash_path - try: - self.fs.rename(subvolpath, trashpath) - except cephfs.ObjectNotFound: - if not force: - raise VolumeException( - -errno.ENOENT, "Subvolume '{0}' not found, cannot remove it".format(spec.subvolume_id)) - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - - def resize_subvolume(self, subvolpath, newsize, noshrink): - """ - :param subvolpath: subvolume path - :param newsize: new size In bytes - :return: new quota size and used bytes as a tuple - """ - if newsize <= 0: - raise VolumeException(-errno.EINVAL, "Provide a valid size") - - try: - maxbytes = int(self.fs.getxattr(subvolpath, 'ceph.quota.max_bytes').decode('utf-8')) - except cephfs.NoData: - maxbytes = 0 - - subvolstat = self.fs.stat(subvolpath) - if newsize > 0 and newsize < subvolstat.st_size: - if noshrink: - raise VolumeException(-errno.EINVAL, "Can't resize the subvolume. The new size '{0}' would be lesser than the current " - "used size '{1}'".format(newsize, subvolstat.st_size)) - - if newsize == maxbytes: - return newsize, subvolstat.st_size - - try: - self.fs.setxattr(subvolpath, 'ceph.quota.max_bytes', str(newsize).encode('utf-8'), 0) - except Exception as e: - raise VolumeException(-e.args[0], "Cannot set new size for the subvolume. '{0}'".format(e.args[1])) - return newsize, subvolstat.st_size - - def resize_infinite(self, subvolpath, newsize): - """ - :param subvolpath: the subvolume path - :param newsize: the string inf - :return: new quota size and used bytes as a tuple - """ - - if not (newsize == "inf" or newsize == "infinite"): - raise VolumeException(-errno.EINVAL, "Invalid parameter '{0}'".format(newsize)) - - subvolstat = self.fs.stat(subvolpath) - size = 0 - try: - self.fs.setxattr(subvolpath, 'ceph.quota.max_bytes', str(size).encode('utf-8'), 0) - except Exception as e: - raise VolumeException(-errno.ENOENT, "Cannot resize the subvolume to infinite size. '{0}'".format(e.args[1])) - return size, subvolstat.st_size - - def purge_subvolume(self, spec, should_cancel): - """ - Finish clearing up a subvolume from the trash directory. - """ - - def rmtree(root_path): - log.debug("rmtree {0}".format(root_path)) - try: - with self.fs.opendir(root_path) as dir_handle: - d = self.fs.readdir(dir_handle) - while d and not should_cancel(): - if d.d_name not in (b".", b".."): - d_full = os.path.join(root_path, d.d_name) - if d.is_dir(): - rmtree(d_full) - else: - self.fs.unlink(d_full) - d = self.fs.readdir(dir_handle) - except cephfs.ObjectNotFound: - return - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - # remove the directory only if we were not asked to cancel - # (else we would fail to remove this anyway) - if not should_cancel(): - self.fs.rmdir(root_path) - - trashpath = spec.trash_path - # catch any unlink errors - try: - rmtree(trashpath) - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - - def get_subvolume_path(self, spec): - path = spec.subvolume_path - try: - self.fs.stat(path) - except cephfs.ObjectNotFound: - return None - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - return path - - def get_dir_entries(self, path): - """ - Get the directory names in a given path - :param path: the given path - :return: the list of directory names - """ - dirs = [] - try: - with self.fs.opendir(path) as dir_handle: - d = self.fs.readdir(dir_handle) - while d: - if (d.d_name not in (b".", b"..")) and d.is_dir(): - dirs.append(d.d_name) - d = self.fs.readdir(dir_handle) - except cephfs.ObjectNotFound: - # When the given path is not found, we just return an empty list - return [] - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - return dirs - - ### group operations - - def create_group(self, spec, pool=None, uid=None, gid=None, mode=0o755): - """ - Create a subvolume group. - - :param spec: subvolume path specification - :param pool: the RADOS pool where the data objects of the subvolumes will be stored - :param uid: the user identifier - :param gid: the group identifier - :mode: the user permissions - :return: None - """ - path = spec.group_path - self.fs.mkdirs(path, mode) - try: - if not pool: - pool = self._get_ancestor_xattr(path, "ceph.dir.layout.pool") - try: - self.fs.setxattr(path, 'ceph.dir.layout.pool', pool.encode('utf-8'), 0) - except cephfs.InvalidValue: - raise VolumeException(-errno.EINVAL, - "Invalid pool layout '{0}'. It must be a valid data pool".format(pool)) - - if uid is None: - uid = 0 - else: - try: - uid = int(uid) - if uid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "Invalid uid") - - if gid is None: - gid = 0 - else: - try: - gid = int(gid) - if gid < 0: - raise ValueError - except ValueError: - raise VolumeException(-errno.EINVAL, "Invalid gid") - - try: - self.fs.chown(path, uid, gid) - except cephfs.InvalidValue: - raise VolumeException(-e.args[0], e.args[1]) - - except Exception as e: - try: - # cleanup group path on best effort basis - log.debug("cleaning up subvolumegroup with path: {0}".format(path)) - self.fs.rmdir(path) - except Exception: - log.debug("failed to clean up subvolumegroup with path: {0}".format(path)) - pass - finally: - raise e - - def remove_group(self, spec, force): - path = spec.group_path - try: - self.fs.rmdir(path) - except cephfs.ObjectNotFound: - if not force: - raise VolumeException(-errno.ENOENT, "Subvolume group '{0}' not found".format(spec.group_id)) - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - - def get_group_path(self, spec): - path = spec.group_path - try: - self.fs.stat(path) - except cephfs.ObjectNotFound: - return None - return path - - def _get_ancestor_xattr(self, path, attr): - """ - Helper for reading layout information: if this xattr is missing - on the requested path, keep checking parents until we find it. - """ - try: - return self.fs.getxattr(path, attr).decode('utf-8') - except cephfs.NoData: - if path == "/": - raise - else: - return self._get_ancestor_xattr(os.path.split(path)[0], attr) - - ### snapshot operations - - def _snapshot_create(self, snappath, mode=0o755): - """ - Create a snapshot, or do nothing if it already exists. - """ - try: - self.fs.stat(snappath) - except cephfs.ObjectNotFound: - self.fs.mkdir(snappath, mode) - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - else: - log.warn("Snapshot '{0}' already exists".format(snappath)) - - def _snapshot_delete(self, snappath, force): - """ - Remove a snapshot, or do nothing if it doesn't exist. - """ - try: - self.fs.stat(snappath) - self.fs.rmdir(snappath) - except cephfs.ObjectNotFound: - if not force: - raise VolumeException(-errno.ENOENT, "Snapshot '{0}' not found, cannot remove it".format(snappath)) - except cephfs.Error as e: - raise VolumeException(-e.args[0], e.args[1]) - - def create_subvolume_snapshot(self, spec, snapname, mode=0o755): - snappath = spec.make_subvol_snap_path(self.rados.conf_get('client_snapdir'), snapname) - self._snapshot_create(snappath, mode) - - def remove_subvolume_snapshot(self, spec, snapname, force): - snappath = spec.make_subvol_snap_path(self.rados.conf_get('client_snapdir'), snapname) - self._snapshot_delete(snappath, force) - - def create_group_snapshot(self, spec, snapname, mode=0o755): - snappath = spec.make_group_snap_path(self.rados.conf_get('client_snapdir'), snapname) - self._snapshot_create(snappath, mode) - - def remove_group_snapshot(self, spec, snapname, force): - snappath = spec.make_group_snap_path(self.rados.conf_get('client_snapdir'), snapname) - return self._snapshot_delete(snappath, force) - - def get_trash_entry(self, spec, exclude): - try: - trashdir = spec.trash_dir - return self._get_single_dir_entry(trashdir, exclude) - except VolumeException as ve: - if ve.errno == -errno.ENOENT: - # trash dir does not exist yet, signal success - return None - raise - - ### context manager routines - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - pass -- 2.39.5