]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/volumes: Add snapshot info command 35672/head
authorKotresh HR <khiremat@redhat.com>
Wed, 22 Apr 2020 10:40:27 +0000 (16:10 +0530)
committerKotresh HR <khiremat@redhat.com>
Fri, 19 Jun 2020 10:48:13 +0000 (16:18 +0530)
The following command is added

"ceph fs subvolume snapshot info <vol_name> <sub_name> <snap_name> [<group_name>]"

The output is in json format with following fields

    created_at: time of creation of snapshot in the format "YYYY-MM-DD HH:MM:SS:ffffff"
    data_pool: data pool the snapshot belongs to
    has_pending_clones: "yes" if snapshot clone is in progress otherwise "no"
    protected: "yes" if snapshot is protected otherwise "no"
    size: snapshot size in bytes

Fixes: https://tracker.ceph.com/issues/45237
Signed-off-by: Kotresh HR <khiremat@redhat.com>
(cherry picked from commit 171930ef773ee6630c76eb9581c725594fb4e884)

doc/cephfs/fs-volumes.rst
qa/tasks/cephfs/test_volumes.py
src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py
src/pybind/mgr/volumes/fs/volume.py
src/pybind/mgr/volumes/module.py

index 17e6e0c2478b37e5cc46ca314eb1ebba07cda2fd..709f401e2b427f51ea2fff5fda0c2a2473b3cbab 100644 (file)
@@ -195,6 +195,18 @@ List snapshots of a subvolume using::
 
     $ ceph fs subvolume snapshot ls <vol_name> <subvol_name> [--group_name <subvol_group_name>]
 
+Fetch the metadata of a snapshot using::
+
+    $ ceph fs subvolume snapshot info <vol_name> <subvol_name> <snap_name> [--group_name <subvol_group_name>]
+
+The output format is json and contains fields as follows.
+
+* created_at: time of creation of snapshot in the format "YYYY-MM-DD HH:MM:SS:ffffff"
+* data_pool: data pool the snapshot belongs to
+* has_pending_clones: "yes" if snapshot clone is in progress otherwise "no"
+* protected: "yes" if snapshot is protected otherwise "no"
+* size: snapshot size in bytes
+
 Cloning Snapshots
 -----------------
 
index a869b79e496d97ee8a1a4b8072d77bb270a93b42..3f821b3059fa3453e098e6459b879cc43bbea9cd 100644 (file)
@@ -164,6 +164,14 @@ class TestVolumes(CephFSTestCase):
         subvol_md = self._fs_cmd(*args)
         return subvol_md
 
+    def _get_subvolume_snapshot_info(self, vol_name, subvol_name, snapname, group_name=None):
+        args = ["subvolume", "snapshot", "info", vol_name, subvol_name, snapname]
+        if group_name:
+            args.append(group_name)
+        args = tuple(args)
+        snap_md = self._fs_cmd(*args)
+        return snap_md
+
     def _delete_test_volume(self):
         self._fs_cmd("volume", "rm", self.volname, "--yes-i-really-mean-it")
 
@@ -1184,6 +1192,49 @@ class TestVolumes(CephFSTestCase):
         # verify trash dir is clean
         self._wait_for_trash_empty()
 
+    def test_subvolume_snapshot_info(self):
+
+        """
+        tests the 'fs subvolume snapshot info' command
+        """
+
+        snap_metadata = ["created_at", "data_pool", "has_pending_clones", "protected", "size"]
+
+        subvolume = self._generate_random_subvolume_name()
+        snapshot = self._generate_random_snapshot_name()
+
+        # create subvolume
+        self._fs_cmd("subvolume", "create", self.volname, subvolume)
+
+        # do some IO
+        self._do_subvolume_io(subvolume, number_of_files=1)
+
+        # snapshot subvolume
+        self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
+
+        # now, protect snapshot
+        self._fs_cmd("subvolume", "snapshot", "protect", self.volname, subvolume, snapshot)
+
+        snap_info = json.loads(self._get_subvolume_snapshot_info(self.volname, subvolume, snapshot))
+        self.assertNotEqual(len(snap_info), 0)
+        for md in snap_metadata:
+            if md not in snap_info:
+                raise RuntimeError("%s not present in the metadata of subvolume snapshot" % md)
+        self.assertEqual(snap_info["protected"], "yes")
+        self.assertEqual(snap_info["has_pending_clones"], "no")
+
+        # now, unprotect snapshot
+        self._fs_cmd("subvolume", "snapshot", "unprotect", self.volname, subvolume, snapshot)
+
+        # remove snapshot
+        self._fs_cmd("subvolume", "snapshot", "rm", self.volname, subvolume, snapshot)
+
+        # remove subvolume
+        self._fs_cmd("subvolume", "rm", self.volname, subvolume)
+
+        # verify trash dir is clean
+        self._wait_for_trash_empty()
+
     def test_subvolume_snapshot_create_idempotence(self):
         subvolume = self._generate_random_subvolume_name()
         snapshot = self._generate_random_snapshot_name()
index 15f8d8bea113299d94f941377630b636df742c97..9e916fc1304370a9afe4ec122996b926853dd422 100644 (file)
@@ -3,6 +3,7 @@ import stat
 import uuid
 import errno
 import logging
+from datetime import datetime
 
 import cephfs
 
@@ -222,6 +223,25 @@ class SubvolumeV1(SubvolumeBase, SubvolumeTemplate):
         snappath = self.snapshot_path(snapname)
         rmsnap(self.fs, snappath)
 
+    def snapshot_info(self, snapname):
+        snappath = self.snapshot_path(snapname)
+        snap_info = {}
+        try:
+            snap_attrs = {'created_at':'ceph.snap.btime', 'size':'ceph.dir.rbytes',
+                          'data_pool':'ceph.dir.layout.pool'}
+            for key, val in snap_attrs.items():
+                snap_info[key] = self.fs.getxattr(snappath, val)
+            return {'size': int(snap_info['size']),
+                    'created_at': str(datetime.fromtimestamp(float(snap_info['created_at']))),
+                    'data_pool': snap_info['data_pool'].decode('utf-8'),
+                    'protected': "yes" if self.is_snapshot_protected(snapname) else "no",
+                    'has_pending_clones': "yes" if self.has_pending_clones(snapname) else "no"}
+        except cephfs.Error as e:
+            if e.errno == errno.ENOENT:
+                raise VolumeException(-errno.ENOENT,
+                                      "snapshot '{0}' doesnot exist".format(snapname))
+            raise VolumeException(-e.args[0], e.args[1])
+
     def list_snapshots(self):
         try:
             dirpath = os.path.join(self.path,
index 36a12143c1d55dff7aa70c5822c4e6cff7b2813e..8d7f7ea828007d98c96338c4bf18ac11fc6a6866 100644 (file)
@@ -292,6 +292,23 @@ class VolumeClient(object):
                 ret = self.volume_exception_to_retval(ve)
         return ret
 
+    def subvolume_snapshot_info(self, **kwargs):
+        ret        = 0, "", ""
+        volname    = kwargs['vol_name']
+        subvolname = kwargs['sub_name']
+        snapname   = kwargs['snap_name']
+        groupname  = kwargs['group_name']
+
+        try:
+            with open_volume(self, volname) as fs_handle:
+                with open_group(fs_handle, self.volspec, groupname) as group:
+                    with open_subvol(fs_handle, self.volspec, group, subvolname) as subvolume:
+                        snap_info_dict = subvolume.snapshot_info(snapname)
+                        ret = 0, json.dumps(snap_info_dict, indent=4, sort_keys=True), ""
+        except VolumeException as ve:
+                ret = self.volume_exception_to_retval(ve)
+        return ret
+
     def list_subvolume_snapshots(self, **kwargs):
         ret        = 0, "", ""
         volname    = kwargs['vol_name']
index e2384d73369f8fc61e4e206c27a90c86ed05d361..40867f263d39a986734cdff0e9271a8c1f642f6a 100644 (file)
@@ -153,6 +153,16 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
                     "and optionally, in a specific subvolume group",
             'perm': 'rw'
         },
+        {
+            'cmd': 'fs subvolume snapshot info '
+                   'name=vol_name,type=CephString '
+                   'name=sub_name,type=CephString '
+                   'name=snap_name,type=CephString '
+                   'name=group_name,type=CephString,req=false ',
+            'desc': "Get the metadata of a CephFS subvolume snapshot "
+                    "and optionally, in a specific subvolume group",
+            'perm': 'r'
+        },
         {
             'cmd': 'fs subvolume snapshot rm '
                    'name=vol_name,type=CephString '
@@ -360,6 +370,12 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
                                                  group_name=cmd.get('group_name', None),
                                                  force=cmd.get('force', False))
 
+    def _cmd_fs_subvolume_snapshot_info(self, inbuf, cmd):
+        return self.vc.subvolume_snapshot_info(vol_name=cmd['vol_name'],
+                                               sub_name=cmd['sub_name'],
+                                               snap_name=cmd['snap_name'],
+                                               group_name=cmd.get('group_name', None))
+
     def _cmd_fs_subvolume_snapshot_ls(self, inbuf, cmd):
         return self.vc.list_subvolume_snapshots(vol_name=cmd['vol_name'],
                                                 sub_name=cmd['sub_name'],