From 69ba333181fa39cfedbb35d38d3b3abd5e7fa2d2 Mon Sep 17 00:00:00 2001 From: Rishabh Dave Date: Fri, 11 Apr 2025 00:25:03 +0530 Subject: [PATCH] mgr/vol: handle snaps when multiple incarnation exists When a directory for previous incarnation exists for a subvolume along with its current incarnation, check all incarnaitions for existence of a snapshot with given name. Signed-off-by: Rishabh Dave --- .../fs/operations/versions/subvolume_v1.py | 4 +- .../fs/operations/versions/subvolume_v2.py | 7 ++- .../fs/operations/versions/subvolume_v3.py | 50 ++++++++++++++++++- 3 files changed, 56 insertions(+), 5 deletions(-) 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 506796a583f..cf18888c427 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py @@ -801,10 +801,10 @@ class SubvolumeV1(SubvolumeBase, SubvolumeTemplate): return pending_clones_info - def remove_snapshot(self, snapname, force=False): + def remove_snapshot(self, snapname, force=False, uuid=None): if self.has_pending_clones(snapname): raise VolumeException(-errno.EAGAIN, "snapshot '{0}' has pending clones".format(snapname)) - snappath = self.snapshot_path(snapname) + snappath = self.snapshot_path(snapname, uuid=uuid) try: self.metadata_mgr.remove_section(self.get_snap_section_name(snapname)) self.metadata_mgr.flush() diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py index d23d326c567..b72a6ceb03b 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py @@ -414,8 +414,11 @@ class SubvolumeV2(SubvolumeV1): return {'type': self.subvol_type.value, 'features': self.features, 'state': SubvolumeStates.STATE_RETAINED.value} - def remove_snapshot(self, snapname, force=False): - super(SubvolumeV2, self).remove_snapshot(snapname, force) + def create_snapshot(self, snap_name): + super(SubvolumeV2, self).create_snapshot(snap_name) + + def remove_snapshot(self, snapname, force=False, uuid=None): + super(SubvolumeV2, self).remove_snapshot(snapname, force=force, uuid=uuid) if self.purgeable: self.trash_base_dir() # tickle the volume purge job to purge this entry, using ESTALE diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v3.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v3.py index 597e4b3c3cc..0152eedac25 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v3.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v3.py @@ -227,9 +227,57 @@ class SubvolumeV3(SubvolumeV2): # following are methods that help or do snapshot creation + def snapshot_path(self, snap_name, uuid=None): + ''' + Path to a specific snapshot named 'snap_name'. + ''' + if uuid: + return join(self.roots_dir, uuid, + self.vol_spec.snapshot_prefix.encode('utf-8'), + snap_name.encode('utf-8')) + else: + return join(self.snapshot_base_path(), snap_name.encode('utf-8')) + def snapshot_base_path(self): return self.snap_dir + def get_incar_uuid_for_snap(self, snap_name): + ''' + Return incarnation's UUID in which the snapshot name is present. + When multiple incarnations for a subvolume exists, check if a snap + exists in one of the incarnations. + ''' + # list of all incarnations/UUID dirs of this subvolume. + incars = listdir(self.fs, self.roots_dir) + + for incar_uuid in incars: + # construct path to ".snap" directory for given UUID. + snap_dir = join(self.roots_dir, incar_uuid, + self.vol_spec.snapshot_dir_prefix.encode('utf-8')) + all_snap_names = listdir(self.fs, snap_dir) + # encode since listdir() call above returns list of bytes and list + # of str + if snap_name.encode('utf-8') in all_snap_names: + return incar_uuid + + return None + + def create_snapshot(self, snap_name): + if self.get_incar_uuid_for_snap(snap_name) != None: + raise VolumeException(errno.EEXIST, + f'subvolume \'{snap_name}\' already exists') + + super(SubvolumeV3, self).create_snapshot(snap_name) + + def remove_snapshot(self, snap_name, force): + uuid = self.get_incar_uuid_for_snap(snap_name) + if uuid == None: + raise VolumeException(errno.ENOENT, + f'subvolume \'{snap_name}\' does not exists') + + super(SubvolumeV3, self).remove_snapshot(snap_name, force=force, + uuid=uuid) + def remove_but_retain_snaps(self): assert self.state != SubvolumeStates.STATE_RETAINED @@ -242,7 +290,6 @@ class SubvolumeV3(SubvolumeV2): except MetadataMgrException as e: log.error(f"failed to write config: {e}") raise VolumeException(e.args[0], e.args[1]) - # in subvol v3, self.mnt_dir (AKA data dir) is renamed to ".unlinked" if # subvol is deleted but snapshots are retained. def trash_incarnation_dir(self): @@ -258,6 +305,7 @@ class SubvolumeV3(SubvolumeV2): SubvolumeStates.STATE_RETAINED.value) self.metadata_mgr.flush() + # Following methods help clone operation - -- 2.47.3