]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/volumes: introduce "clone cancel" volume command
authorVenky Shankar <vshankar@redhat.com>
Tue, 14 Jan 2020 09:20:50 +0000 (04:20 -0500)
committerRamana Raja <rraja@redhat.com>
Wed, 18 Mar 2020 05:33:11 +0000 (11:03 +0530)
Signed-off-by: Venky Shankar <vshankar@redhat.com>
src/pybind/mgr/volumes/fs/async_cloner.py
src/pybind/mgr/volumes/fs/volume.py
src/pybind/mgr/volumes/module.py

index 1bd575a133f8b74bb70680cf1a47bcb28ab5bf22..d9d3c87a5a58f9908a3b629f0e6b640c0dc0641d 100644 (file)
@@ -236,10 +236,72 @@ class Cloner(AsyncJobs):
             'pending'     : handle_clone_pending,
             'in-progress' : handle_clone_in_progress,
             'complete'    : handle_clone_complete,
-            'failed'      : handle_clone_failed
+            'failed'      : handle_clone_failed,
+            'canceled'    : handle_clone_failed,
         }
         super(Cloner, self).__init__(volume_client, "cloner", tp_size)
 
+    def is_clone_cancelable(self, clone_state):
+        return not (OpSm.is_final_state(clone_state) or OpSm.is_failed_state(clone_state))
+
+    def get_clone_tracking_index(self, fs_handle, clone_subvolume):
+        with open_clone_index(fs_handle, self.vc.volspec) as index:
+            return index.find_clone_entry_index(clone_subvolume.base_path)
+
+    def _cancel_pending_clone(self, fs_handle, clone_subvolume, status, track_idx):
+        clone_state = status['state']
+        assert self.is_clone_cancelable(clone_state)
+
+        s_groupname = status['source'].get('group', None)
+        s_subvolname = status['source']['subvolume']
+        s_snapname = status['source']['snapshot']
+
+        with open_group(fs_handle, self.vc.volspec, s_groupname) as s_group:
+            with open_subvol(fs_handle, self.vc.volspec, s_group, s_subvolname) as s_subvolume:
+                next_state = OpSm.get_next_state("clone", clone_state, -errno.EINTR)
+                clone_subvolume.state = (next_state, True)
+                s_subvolume.detach_snapshot(s_snapname, track_idx.decode('utf-8'))
+
+    def cancel_job(self, volname, job):
+        """
+        override base class `cancel_job`. interpret @job as (clone, group) tuple.
+        """
+        clonename = job[0]
+        groupname = job[1]
+        track_idx = None
+
+        try:
+            with open_volume(self.vc, volname) as fs_handle:
+                with open_group(fs_handle, self.vc.volspec, groupname) as group:
+                    with open_subvol(fs_handle, self.vc.volspec, group, clonename,
+                                     need_complete=False, expected_types=["clone"]) as clone_subvolume:
+                        status = clone_subvolume.status
+                        clone_state = status['state']
+                        if not self.is_clone_cancelable(clone_state):
+                            raise VolumeException(-errno.EINVAL, "cannot cancel -- clone finished (check clone status)")
+                        track_idx = self.get_clone_tracking_index(fs_handle, clone_subvolume)
+                        if not track_idx:
+                            log.warn("cannot lookup clone tracking index for {0}".format(clone_subvolume.base_path))
+                            raise VolumeException(-errno.EINVAL, "error canceling clone")
+                        if OpSm.is_init_state("clone", clone_state):
+                            # clone has not started yet -- cancel right away.
+                            self._cancel_pending_clone(fs_handle, clone_subvolume, status, track_idx)
+                            return
+            # cancelling an on-going clone would persist "canceled" state in subvolume metadata.
+            # to persist the new state, async cloner accesses the volume in exclusive mode.
+            # accessing the volume in exclusive mode here would lead to deadlock.
+            assert track_idx is not None
+            with self.lock:
+                with open_volume_lockless(self.vc, volname) as fs_handle:
+                    with open_group(fs_handle, self.vc.volspec, groupname) as group:
+                        with open_subvol(fs_handle, self.vc.volspec, group, clonename,
+                                         need_complete=False, expected_types=["clone"]) as clone_subvolume:
+                            if not self._cancel_job(volname, (track_idx, clone_subvolume.base_path)):
+                                raise VolumeException(-errno.EINVAL, "cannot cancel -- clone finished (check clone status)")
+        except (IndexException, MetadataMgrException) as e:
+            log.error("error cancelling clone {0}: ({1})".format(job, e))
+            raise VolumeException(-errno.EINVAL, "error canceling clone")
+
     def get_next_job(self, volname, running_jobs):
         return get_next_clone_entry(self.vc, volname, running_jobs)
 
index 2015fe3dc212a169c0a7709b95bb138f8db3836b..2f2d4e20e55b65407d0466686ec9ddbd5f2293bd 100644 (file)
@@ -377,6 +377,18 @@ class VolumeClient(object):
             ret = self.volume_exception_to_retval(ve)
         return ret
 
+    def clone_cancel(self, **kwargs):
+        ret       = 0, "", ""
+        volname   = kwargs['vol_name']
+        clonename = kwargs['clone_name']
+        groupname = kwargs['group_name']
+
+        try:
+            self.cloner.cancel_job(volname, (clonename, groupname))
+        except VolumeException as ve:
+            ret = self.volume_exception_to_retval(ve)
+        return ret
+
     ### group operations
 
     def create_subvolume_group(self, **kwargs):
index f12112961674f95f5f6ce88559070772e64ec333..0d8015bf48f4d1b09deaebd628ba64711e6ed197 100644 (file)
@@ -204,6 +204,14 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
             'desc': "Get status on a cloned subvolume.",
             'perm': 'r'
         },
+        {
+            'cmd': 'fs clone cancel '
+                   'name=vol_name,type=CephString '
+                   'name=clone_name,type=CephString '
+                   'name=group_name,type=CephString,req=false ',
+            'desc': "Cancel an pending or ongoing clone operation.",
+            'perm': 'r'
+        },
 
         # volume ls [recursive]
         # subvolume ls <volume>
@@ -364,3 +372,7 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
     def _cmd_fs_clone_status(self, inbuf, cmd):
         return self.vc.clone_status(
             vol_name=cmd['vol_name'], clone_name=cmd['clone_name'],  group_name=cmd.get('group_name', None))
+
+    def _cmd_fs_clone_cancel(self, inbuf, cmd):
+        return self.vc.clone_cancel(
+            vol_name=cmd['vol_name'], clone_name=cmd['clone_name'],  group_name=cmd.get('group_name', None))