]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/volumes: make clone state transition to fail when UUID dir is missing... 65467/head
authorRishabh Dave <ridave@redhat.com>
Wed, 10 Sep 2025 07:59:00 +0000 (13:29 +0530)
committerRishabh Dave <ridave@redhat.com>
Mon, 13 Oct 2025 07:12:25 +0000 (12:42 +0530)
for the source subvolume. Also add tests for the same.

Fixes: https://tracker.ceph.com/issues/72957
Signed-off-by: Rishabh Dave <ridave@redhat.com>
qa/tasks/cephfs/test_volumes.py
src/pybind/mgr/volumes/fs/async_cloner.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_v2.py

index d9b7b0a398d43261fae1852b8d3c2c40d22c6387..e517e63b483f1a716cc4f8a3c7f13af7af14e913 100644 (file)
@@ -9672,3 +9672,26 @@ class TestCorruptedSubvolumes(TestVolumesHelper):
 
         # cleanup
         self.run_ceph_cmd(f'fs subvolume rm {self.volname} {sv1} --force')
+
+    def test_clone_when_src_subvol_has_missing_UUID_dir(self):
+        sv1 = 'sv1'
+        ss1 = 'ss1'
+        c1 = 'c1'
+
+        self.run_ceph_cmd(f'fs subvolume create {self.volname} {sv1}')
+        sv_path = self.get_ceph_cmd_stdout('fs subvolume getpath '
+                                           f'{self.volname} {sv1}').strip()
+        sv_path = sv_path[1:]
+        self.run_ceph_cmd(f'fs subvolume snapshot create {self.volname} {sv1} {ss1}')
+        self.run_ceph_cmd('config set mgr mgr/volumes/snapshot_clone_delay 2')
+        self.run_ceph_cmd(f'fs subvolume snapshot clone {self.volname} {sv1} {ss1} {c1}')
+
+        self.mount_a.run_shell(f'sudo rmdir {sv_path}', omit_sudo=False)
+
+        time.sleep(2)
+        self._wait_for_clone_to_fail(c1, timo=20)
+
+        # cleanup
+        self.run_ceph_cmd(f'fs subvolume snapshot rm {self.volname} {sv1} {ss1} '
+                           '--force')
+        self.run_ceph_cmd(f'fs subvolume rm {self.volname} {sv1} --force')
index 6fe2523ad2d86c23e87fc57d11491463391fc8a2..93c61a9267bf5135b9fe4de6657e7a9b6b04ce6e 100644 (file)
@@ -229,12 +229,12 @@ def do_clone(fs_client, volspec, volname, groupname, subvolname, should_cancel):
             set_quota_on_clone(fs_handle, (subvol0, subvol1, subvol2))
 
 def update_clone_failure_status(fs_client, volspec, volname, groupname, subvolname, ve):
-    with open_clone_subvol_pair_in_vol(fs_client, volspec, volname, groupname,
-            subvolname, lockless=False) as (subvol0, subvol1, subvol2):
+    with open_at_volume(fs_client, volspec, volname, groupname, subvolname,
+                        SubvolumeOpType.CLONE_INTERNAL) as clone:
         if ve.errno == -errno.EINTR:
-            subvol0.add_clone_failure(-ve.errno, "user interrupted clone operation")
+            clone.add_clone_failure(-ve.errno, "user interrupted clone operation")
         else:
-            subvol0.add_clone_failure(-ve.errno, ve.error_str)
+            clone.add_clone_failure(-ve.errno, ve.error_str)
 
 def log_clone_failure(volname, groupname, subvolname, ve):
     if ve.errno == -errno.EINTR:
@@ -262,7 +262,7 @@ def handle_clone_failed(fs_client, volspec, volname, index, groupname, subvolnam
     try:
         # detach source but leave the clone section intact for later inspection
         with open_clone_subvol_pair_in_vol(fs_client, volspec, volname, groupname,
-                subvolname) as (subvol0, subvol1, subvol2):
+                subvolname, failed=True) as (subvol0, subvol1, subvol2):
             subvol1.detach_snapshot(subvol2, index)
     except (MetadataMgrException, VolumeException) as e:
         log.error("failed to detach clone from snapshot: {0}".format(e))
index 3c8d2241afb717ccde05bcf472c6266af1c27fc4..33322de011aee030e1fcd9e85d9558f0ae622790 100644 (file)
@@ -101,7 +101,8 @@ def open_subvol_in_group(mgr, vol_handle, vol_spec, group_name, subvol_name,
 
 @contextmanager
 def open_clone_subvol_pair_in_vol(vc, vol_spec, vol_name, group_name,
-                                  subvol_name, lockless=False):
+                                  subvol_name, lockless=False, failed=False):
+
     with open_subvol_in_vol(vc, vol_spec, vol_name, group_name, subvol_name,
                             SubvolumeOpType.CLONE_INTERNAL, lockless) \
                             as (vol_handle, _, dst_subvol):
@@ -112,9 +113,14 @@ def open_clone_subvol_pair_in_vol(vc, vol_spec, vol_name, group_name,
             # use the same subvolume to avoid metadata overwrites
             yield (dst_subvol, dst_subvol, src_snap_name)
         else:
+            if failed:
+                op_type = SubvolumeOpType.CLONE_FAILED
+            else:
+                op_type = SubvolumeOpType.CLONE_SOURCE
+
             with open_subvol_in_group(vc.mgr, vol_handle, vol_spec,
                                       src_group_name, src_subvol_name,
-                                      SubvolumeOpType.CLONE_SOURCE) \
+                                      op_type) \
                                       as src_subvol:
                 yield (dst_subvol, src_subvol, src_snap_name)
 
index fc1a70d206aab4ce2e49016e9f40a99362433e96..e205f6fb6b4bd3517895830ef97774aced264b1e 100644 (file)
@@ -58,6 +58,7 @@ class SubvolumeOpType(Enum):
     CLONE_CREATE          = 'clone-create'
     CLONE_STATUS          = 'clone-status'
     CLONE_CANCEL          = 'clone-cancel'
+    CLONE_FAILED          = 'clone-failed'
     CLONE_INTERNAL        = 'clone_internal'
     ALLOW_ACCESS          = 'allow-access'
     DENY_ACCESS           = 'deny-access'
index de17411f1e3bba1fdb6413d515897411f094ab99..4f9d313e994e6fd6c82d7b1203c3fb5eeffbcc16 100644 (file)
@@ -287,7 +287,8 @@ class SubvolumeV2(SubvolumeV1):
                 SubvolumeOpType.SNAP_INFO,
                 SubvolumeOpType.SNAP_PROTECT,
                 SubvolumeOpType.SNAP_UNPROTECT,
-                SubvolumeOpType.CLONE_SOURCE
+                SubvolumeOpType.CLONE_SOURCE,
+                SubvolumeOpType.CLONE_FAILED
             }
 
         return {SubvolumeOpType.REMOVE_FORCE,
@@ -295,7 +296,8 @@ class SubvolumeV2(SubvolumeV1):
                 SubvolumeOpType.CLONE_STATUS,
                 SubvolumeOpType.CLONE_CANCEL,
                 SubvolumeOpType.CLONE_INTERNAL,
-                SubvolumeOpType.CLONE_SOURCE}
+                SubvolumeOpType.CLONE_SOURCE,
+                SubvolumeOpType.CLONE_FAILED}
 
     def open(self, op_type):
         if not isinstance(op_type, SubvolumeOpType):
@@ -342,6 +344,11 @@ class SubvolumeV2(SubvolumeV1):
                           f"path '{subvol_path}' for subvolume "
                           f"{self.subvolname}'")
                 return
+            elif op_type == SubvolumeOpType.CLONE_FAILED:
+                log.debug('since clone failed, letting that register in .meta '
+                          'file and ignoring missing subvolume path '
+                          f'{subvol_path} for subvolume {self.subvolname}')
+                return
             log.debug("missing subvolume path '{0}' for subvolume '{1}'".format(subvol_path, self.subvolname))
             raise VolumeException(-errno.ENOENT, "mount path missing for subvolume '{0}'".format(self.subvolname))
         except cephfs.Error as e: