From: Venky Shankar Date: Mon, 2 Dec 2019 07:56:48 +0000 (-0500) Subject: mgr/volumes: interface for creating a cloned subvolume X-Git-Tag: v15.1.1~578^2~10 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=7089808bf82321712676803dd4bfdd62cf5fc00e;p=ceph-ci.git mgr/volumes: interface for creating a cloned subvolume Signed-off-by: Venky Shankar --- diff --git a/src/pybind/mgr/volumes/fs/operations/group.py b/src/pybind/mgr/volumes/fs/operations/group.py index a75e9d134b0..cfca8ff83c0 100644 --- a/src/pybind/mgr/volumes/fs/operations/group.py +++ b/src/pybind/mgr/volumes/fs/operations/group.py @@ -29,6 +29,10 @@ class Group(GroupTemplate): def path(self): return os.path.join(self.vol_spec.base_dir.encode('utf-8'), self.groupname.encode('utf-8')) + @property + def group_name(self): + return self.groupname + @property def uid(self): return self.user_id diff --git a/src/pybind/mgr/volumes/fs/operations/subvolume.py b/src/pybind/mgr/volumes/fs/operations/subvolume.py index 83f7ce691d4..8c3db11c421 100644 --- a/src/pybind/mgr/volumes/fs/operations/subvolume.py +++ b/src/pybind/mgr/volumes/fs/operations/subvolume.py @@ -28,6 +28,23 @@ def create_subvol(fs, vol_spec, group, subvolname, size, isolate_nspace, pool, m subvolume = loaded_subvolumes.get_subvolume_object_max(fs, vol_spec, group, subvolname) subvolume.create(size, isolate_nspace, pool, mode, uid, gid) +def create_clone(fs, vol_spec, group, subvolname, pool, source_volume, source_subvolume, snapname): + """ + create a cloned subvolume. + + :param fs: ceph filesystem handle + :param vol_spec: volume specification + :param group: group object for the clone + :param subvolname: clone subvolume nam + :param pool: the RADOS pool where the data objects of the cloned subvolume will be stored + :param source_volume: source subvolumes volume name + :param source_subvolume: source (parent) subvolume object + :param snapname: source subvolume snapshot + :return None + """ + subvolume = loaded_subvolumes.get_subvolume_object_max(fs, vol_spec, group, subvolname) + subvolume.create_clone(pool, source_volume, source_subvolume, snapname) + def remove_subvol(fs, vol_spec, group, subvolname): """ remove a subvolume. diff --git a/src/pybind/mgr/volumes/fs/operations/template.py b/src/pybind/mgr/volumes/fs/operations/template.py index 1bd1b0add62..c5ecf47e7e4 100644 --- a/src/pybind/mgr/volumes/fs/operations/template.py +++ b/src/pybind/mgr/volumes/fs/operations/template.py @@ -58,6 +58,18 @@ class SubvolumeTemplate(object): """ raise VolumeException(-errno.ENOTSUP, "operation not supported.") + def create_clone(self, pool, source_volname, source_subvolume, snapname): + """ + prepare a subvolume to be cloned. + + :param pool: the RADOS pool where the data objects of the cloned subvolume will be stored + :param source_volname: source volume of snapshot + :param source_subvolume: source subvolume of snapshot + :param snapname: snapshot name to be cloned from + :return: None + """ + raise VolumeException(-errno.ENOTSUP, "operation not supported.") + def remove(self): """ make a subvolume inaccessible to guests. diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py index 16b8c6675f4..466db0f6eb9 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py @@ -17,6 +17,7 @@ class SubvolumeBase(object): LEGACY_CONF_DIR = "_legacy" SUBVOLUME_TYPE_NORMAL = "subvolume" + SUBVOLUME_TYPE_CLONE = "clone" def __init__(self, fs, vol_spec, group, subvolname, legacy=False): self.fs = fs @@ -76,6 +77,14 @@ class SubvolumeBase(object): def namespace(self): return "{0}{1}".format(self.vol_spec.fs_namespace, self.subvolname) + @property + def group_name(self): + return self.group.group_name + + @property + def subvol_name(self): + return self.subvolname + @property def legacy_mode(self): return self.legacy diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py index 45947eb5743..5a28f6292fa 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py @@ -1,4 +1,5 @@ import os +import stat import uuid import errno import logging @@ -59,12 +60,62 @@ class SubvolumeV1(SubvolumeBase, SubvolumeTemplate): e = VolumeException(-e.args[0], e.args[1]) raise e + def add_clone_source(self, volname, subvolume, snapname, flush=False): + self.metadata_mgr.add_section("source") + self.metadata_mgr.update_section("source", "volume", volname) + if not subvolume.group.is_default_group(): + self.metadata_mgr.update_section("source", "group", subvolume.group_name) + self.metadata_mgr.update_section("source", "subvolume", subvolume.subvol_name) + self.metadata_mgr.update_section("source", "snapshot", snapname) + if flush: + self.metadata_mgr.flush() + + def remove_clone_source(self, flush=False): + self.metadata_mgr.remove_section("source") + if flush: + self.metadata_mgr.flush() + + def create_clone(self, pool, source_volname, source_subvolume, snapname): + subvolume_type = SubvolumeBase.SUBVOLUME_TYPE_CLONE + try: + initial_state = OpSm.get_init_state(subvolume_type) + except OpSmException as oe: + raise VolumeException(-errno.EINVAL, "clone failed: internal error") + + subvol_path = os.path.join(self.base_path, str(uuid.uuid4()).encode('utf-8')) + try: + # create directory and set attributes + self.fs.mkdirs(subvol_path, source_subvolume.mode) + self._set_attrs(subvol_path, None, None, pool, source_subvolume.uid, source_subvolume.gid) + + # persist subvolume metadata and clone source + qpath = subvol_path.decode('utf-8') + self.metadata_mgr.init(SubvolumeV1.VERSION, subvolume_type, qpath, initial_state) + self.add_clone_source(source_volname, source_subvolume, snapname) + self.metadata_mgr.flush() + except (VolumeException, MetadataMgrException, cephfs.Error) as e: + try: + log.info("cleaning up subvolume with path: {0}".format(self.subvolname)) + self.remove() + except VolumeException as ve: + log.info("failed to cleanup subvolume '{0}' ({1})".format(self.subvolname, ve)) + + if isinstance(e, MetadataMgrException): + log.error("metadata manager exception: {0}".format(e)) + e = VolumeException(-errno.EINVAL, "exception in subvolume metadata") + elif isinstance(e, cephfs.Error): + e = VolumeException(-e.args[0], e.args[1]) + raise e + def open(self): try: self.metadata_mgr.refresh() subvol_path = self.path log.debug("refreshed metadata, checking subvolume path '{0}'".format(subvol_path)) - self.fs.stat(subvol_path) + st = self.fs.stat(subvol_path) + self.uid = int(st.st_uid) + self.gid = int(st.st_gid) + self.mode = int(st.st_mode & ~stat.S_IFMT(st.st_mode)) except MetadataMgrException as me: if me.errno == -errno.ENOENT: raise VolumeException(-errno.ENOENT, "subvolume '{0}' does not exist".format(self.subvolname))