From 7b6b456a628a10f8dd73c3afaf7d79f97e7f1514 Mon Sep 17 00:00:00 2001 From: Nikhilkumar Shelke Date: Wed, 27 Apr 2022 21:50:33 +0530 Subject: [PATCH] mgr/volumes: set, get, list and remove custom metadata for snapshot If CephFS in ODF configured in external mode, user like to use subvolume snapshot metadata to store some Openshift specific information, as the PVC/PV/namespace the subvolumes/snapshot are coming from. For RBD volumes, it's possible to add metadata information to the images using the 'rbd image-meta' command. However, this feature is not available for CephFS volumes. We'd like to request this capability. Adding following commands: ceph fs subvolume snapshot metadata set [] ceph fs subvolume snapshot metadata get [] ceph fs subvolume snapshot metadata ls [] ceph fs subvolume snapshot metadata rm [] [--force] Fixes: https://tracker.ceph.com/issues/55401 Signed-off-by: Nikhilkumar Shelke (cherry picked from commit 559222cfe8d552cd2d7aef7361de4140820ae74a) --- .../mgr/volumes/fs/operations/template.py | 12 ++- .../fs/operations/versions/subvolume_base.py | 33 ++++++++ .../fs/operations/versions/subvolume_v1.py | 2 + src/pybind/mgr/volumes/fs/volume.py | 80 +++++++++++++++++++ src/pybind/mgr/volumes/module.py | 80 ++++++++++++++++++- 5 files changed, 202 insertions(+), 5 deletions(-) diff --git a/src/pybind/mgr/volumes/fs/operations/template.py b/src/pybind/mgr/volumes/fs/operations/template.py index 23ef2aba6f912..eb55bd7432519 100644 --- a/src/pybind/mgr/volumes/fs/operations/template.py +++ b/src/pybind/mgr/volumes/fs/operations/template.py @@ -60,10 +60,14 @@ class SubvolumeOpType(Enum): DENY_ACCESS = 'deny-access' AUTH_LIST = 'auth-list' EVICT = 'evict' - USER_METADATA_SET = 'metadata-set' - USER_METADATA_GET = 'metadata-get' - USER_METADATA_LIST = 'metadata-ls' - USER_METADATA_REMOVE = 'metadata-rm' + USER_METADATA_SET = 'user-metadata-set' + USER_METADATA_GET = 'user-metadata-get' + USER_METADATA_LIST = 'user-metadata-ls' + USER_METADATA_REMOVE = 'user-metadata-rm' + SNAP_METADATA_SET = 'snap-metadata-set' + SNAP_METADATA_GET = 'snap-metadata-get' + SNAP_METADATA_LIST = 'snap-metadata-ls' + SNAP_METADATA_REMOVE = 'snap-metadata-rm' class SubvolumeTemplate(object): VERSION = None # type: int diff --git a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py index d9b3b47b4c21c..d82ad8f5103d8 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py @@ -365,3 +365,36 @@ class SubvolumeBase(object): if me.errno == -errno.ENOENT: raise VolumeException(-errno.ENOENT, "subvolume metadata not does not exist") raise VolumeException(-me.args[0], me.args[1]) + + def get_snap_section_name(self, snapname): + section = "SNAP_METADATA" + "_" + snapname; + return section; + + def set_snapshot_metadata(self, snapname, keyname, value): + section = self.get_snap_section_name(snapname) + self.metadata_mgr.add_section(section) + self.metadata_mgr.update_section(section, keyname, str(value)) + self.metadata_mgr.flush() + + def get_snapshot_metadata(self, snapname, keyname): + try: + value = self.metadata_mgr.get_option(self.get_snap_section_name(snapname), keyname) + except MetadataMgrException as me: + if me.errno == -errno.ENOENT: + raise VolumeException(-errno.ENOENT, "key '{0}' does not exist.".format(keyname)) + raise VolumeException(-me.args[0], me.args[1]) + return value + + def list_snapshot_metadata(self, snapname): + return self.metadata_mgr.list_all_options_from_section(self.get_snap_section_name(snapname)) + + def remove_snapshot_metadata(self, snapname, keyname): + try: + ret = self.metadata_mgr.remove_option(self.get_snap_section_name(snapname), keyname) + if not ret: + raise VolumeException(-errno.ENOENT, "key '{0}' does not exist.".format(keyname)) + self.metadata_mgr.flush() + except MetadataMgrException as me: + if me.errno == -errno.ENOENT: + raise VolumeException(-errno.ENOENT, "snapshot metadata not does not exist") + raise VolumeException(-me.args[0], me.args[1]) 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 42c08e04712b1..d0dd96f91a52b 100644 --- a/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py +++ b/src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py @@ -722,6 +722,8 @@ class SubvolumeV1(SubvolumeBase, SubvolumeTemplate): raise VolumeException(-errno.EAGAIN, "snapshot '{0}' has pending clones".format(snapname)) snappath = self.snapshot_path(snapname) rmsnap(self.fs, snappath) + self.metadata_mgr.remove_section(self.get_snap_section_name(snapname)) + self.metadata_mgr.flush() def snapshot_info(self, snapname): if is_inherited_snap(snapname): diff --git a/src/pybind/mgr/volumes/fs/volume.py b/src/pybind/mgr/volumes/fs/volume.py index 7f9ce76070ad2..16969dda854ec 100644 --- a/src/pybind/mgr/volumes/fs/volume.py +++ b/src/pybind/mgr/volumes/fs/volume.py @@ -507,6 +507,86 @@ class VolumeClient(CephfsClient["Module"]): ret = self.volume_exception_to_retval(ve) return ret + def set_subvolume_snapshot_metadata(self, **kwargs): + ret = 0, "", "" + volname = kwargs['vol_name'] + subvolname = kwargs['sub_name'] + snapname = kwargs['snap_name'] + groupname = kwargs['group_name'] + keyname = kwargs['key_name'] + value = kwargs['value'] + + try: + with open_volume(self, volname) as fs_handle: + with open_group(fs_handle, self.volspec, groupname) as group: + with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_SET) as subvolume: + if not snapname.encode('utf-8') in subvolume.list_snapshots(): + raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname)) + subvolume.set_snapshot_metadata(snapname, keyname, value) + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def get_subvolume_snapshot_metadata(self, **kwargs): + ret = 0, "", "" + volname = kwargs['vol_name'] + subvolname = kwargs['sub_name'] + snapname = kwargs['snap_name'] + groupname = kwargs['group_name'] + keyname = kwargs['key_name'] + + try: + with open_volume(self, volname) as fs_handle: + with open_group(fs_handle, self.volspec, groupname) as group: + with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_GET) as subvolume: + if not snapname.encode('utf-8') in subvolume.list_snapshots(): + raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname)) + value = subvolume.get_snapshot_metadata(snapname, keyname) + ret = 0, value, "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def list_subvolume_snapshot_metadata(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(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_LIST) as subvolume: + if not snapname.encode('utf-8') in subvolume.list_snapshots(): + raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname)) + snap_metadata_dict = subvolume.list_snapshot_metadata(snapname) + ret = 0, json.dumps(snap_metadata_dict, indent=4, sort_keys=True), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def remove_subvolume_snapshot_metadata(self, **kwargs): + ret = 0, "", "" + volname = kwargs['vol_name'] + subvolname = kwargs['sub_name'] + snapname = kwargs['snap_name'] + groupname = kwargs['group_name'] + keyname = kwargs['key_name'] + force = kwargs['force'] + + try: + with open_volume(self, volname) as fs_handle: + with open_group(fs_handle, self.volspec, groupname) as group: + with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_REMOVE) as subvolume: + if not snapname.encode('utf-8') in subvolume.list_snapshots(): + raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname)) + subvolume.remove_snapshot_metadata(snapname, keyname) + except VolumeException as ve: + if not (ve.errno == -errno.ENOENT and force): + ret = self.volume_exception_to_retval(ve) + return ret + def list_subvolume_snapshots(self, **kwargs): ret = 0, "", "" volname = kwargs['vol_name'] diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index c146d0263821e..be0f6b088fb22 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -278,10 +278,55 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): '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 " + 'desc': "Get the information of a CephFS subvolume snapshot " "and optionally, in a specific subvolume group", 'perm': 'r' }, + { + 'cmd': 'fs subvolume snapshot metadata set ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=key_name,type=CephString ' + 'name=value,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Set custom metadata (key-value) for a CephFS subvolume snapshot in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'rw' + }, + { + 'cmd': 'fs subvolume snapshot metadata get ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=key_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Get custom metadata associated with the key of a CephFS subvolume snapshot in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'r' + }, + { + 'cmd': 'fs subvolume snapshot metadata ls ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "List custom metadata (key-value pairs) of a CephFS subvolume snapshot in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'r' + }, + { + 'cmd': 'fs subvolume snapshot metadata rm ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=key_name,type=CephString ' + 'name=group_name,type=CephString,req=false ' + 'name=force,type=CephBool,req=false ', + 'desc': "Remove custom metadata (key-value) associated with the key of a CephFS subvolume snapshot in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'rw' + }, { 'cmd': 'fs subvolume snapshot rm ' 'name=vol_name,type=CephString ' @@ -640,6 +685,39 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): snap_name=cmd['snap_name'], group_name=cmd.get('group_name', None)) + @mgr_cmd_wrap + def _cmd_fs_subvolume_snapshot_metadata_set(self, inbuf, cmd): + return self.vc.set_subvolume_snapshot_metadata(vol_name=cmd['vol_name'], + sub_name=cmd['sub_name'], + snap_name=cmd['snap_name'], + key_name=cmd['key_name'], + value=cmd['value'], + group_name=cmd.get('group_name', None)) + + @mgr_cmd_wrap + def _cmd_fs_subvolume_snapshot_metadata_get(self, inbuf, cmd): + return self.vc.get_subvolume_snapshot_metadata(vol_name=cmd['vol_name'], + sub_name=cmd['sub_name'], + snap_name=cmd['snap_name'], + key_name=cmd['key_name'], + group_name=cmd.get('group_name', None)) + + @mgr_cmd_wrap + def _cmd_fs_subvolume_snapshot_metadata_ls(self, inbuf, cmd): + return self.vc.list_subvolume_snapshot_metadata(vol_name=cmd['vol_name'], + sub_name=cmd['sub_name'], + snap_name=cmd['snap_name'], + group_name=cmd.get('group_name', None)) + + @mgr_cmd_wrap + def _cmd_fs_subvolume_snapshot_metadata_rm(self, inbuf, cmd): + return self.vc.remove_subvolume_snapshot_metadata(vol_name=cmd['vol_name'], + sub_name=cmd['sub_name'], + snap_name=cmd['snap_name'], + key_name=cmd['key_name'], + group_name=cmd.get('group_name', None), + force=cmd.get('force', False)) + @mgr_cmd_wrap def _cmd_fs_subvolume_snapshot_ls(self, inbuf, cmd): return self.vc.list_subvolume_snapshots(vol_name=cmd['vol_name'], -- 2.39.5