]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/volumes: interface for creating a cloned subvolume
authorVenky Shankar <vshankar@redhat.com>
Mon, 2 Dec 2019 07:56:48 +0000 (02:56 -0500)
committerRamana Raja <rraja@redhat.com>
Wed, 12 Feb 2020 10:11:59 +0000 (05:11 -0500)
Signed-off-by: Venky Shankar <vshankar@redhat.com>
(cherry picked from commit 7089808bf82321712676803dd4bfdd62cf5fc00e)

src/pybind/mgr/volumes/fs/operations/group.py
src/pybind/mgr/volumes/fs/operations/subvolume.py
src/pybind/mgr/volumes/fs/operations/template.py
src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py
src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py

index a75e9d134b054f2cb85e4dc3ef2c099f49d35927..cfca8ff83c0a835468c1b439f5fa63c7d3a78708 100644 (file)
@@ -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
index 83f7ce691d4ea50ed347a85b11c45d81c7e5e8a3..8c3db11c42192da7470bb14ee8bceadeb67f6675 100644 (file)
@@ -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.
index 1bd1b0add620fd60c9e2bdaa613bab63d598ff55..c5ecf47e7e44009a7d500be8cd3145192daa8b6d 100644 (file)
@@ -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.
index 16b8c6675f4a5d77d157f1e1b8a9c21b1f12498b..466db0f6eb9a6d38593079db917e189a85588519 100644 (file)
@@ -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
index 45947eb57432334e3bdd0a63cbca28b327dfa718..5a28f6292fad35a5e70bcdeef1281a0640bb42a7 100644 (file)
@@ -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))