From 478a38b5a4300f884d60d947b2cee8b38702c84d Mon Sep 17 00:00:00 2001 From: Venky Shankar Date: Mon, 2 Dec 2019 03:11:45 -0500 Subject: [PATCH] mgr/volumes: add clone specific commands Signed-off-by: Venky Shankar (cherry picked from commit b2145b73d7640851c27ac4bb09fd98e146de953d) --- src/pybind/mgr/volumes/fs/volume.py | 102 +++++++++++++++++++++++++++- src/pybind/mgr/volumes/module.py | 58 ++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) diff --git a/src/pybind/mgr/volumes/fs/volume.py b/src/pybind/mgr/volumes/fs/volume.py index d287e8d55965f..f5c0102d979b6 100644 --- a/src/pybind/mgr/volumes/fs/volume.py +++ b/src/pybind/mgr/volumes/fs/volume.py @@ -10,7 +10,8 @@ from .fs_util import listdir from .operations.volume import ConnectionPool, open_volume, create_volume, \ delete_volume, list_volumes from .operations.group import open_group, create_group, remove_group -from .operations.subvolume import open_subvol, create_subvol, remove_subvol +from .operations.subvolume import open_subvol, create_subvol, remove_subvol, \ + create_clone from .vol_spec import VolSpec from .exception import VolumeException @@ -270,6 +271,105 @@ class VolumeClient(object): ret = self.volume_exception_to_retval(ve) return ret + def protect_subvolume_snapshot(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: + subvolume.protect_snapshot(snapname) + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def unprotect_subvolume_snapshot(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: + subvolume.unprotect_snapshot(snapname) + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def _prepare_clone_subvolume(self, fs_handle, volname, subvolume, snapname, target_group, target_subvolname, target_pool): + create_clone(fs_handle, self.volspec, target_group, target_subvolname, target_pool, volname, subvolume, snapname) + with open_subvol(fs_handle, self.volspec, target_group, target_subvolname, need_complete=False) as target_subvolume: + try: + subvolume.attach_snapshot(snapname, target_subvolume) + except VolumeException as ve: + try: + target_subvolume.remove() + self.purge_queue.queue_purge_job(volname) + except Exception as e: + log.warn("failed to cleanup clone subvolume '{0}' ({1})".format(target_subvolname, e)) + raise ve + + def _clone_subvolume_snapshot(self, fs_handle, volname, subvolume, **kwargs): + snapname = kwargs['snap_name'] + target_pool = kwargs['pool_layout'] + target_subvolname = kwargs['target_sub_name'] + target_groupname = kwargs['target_group_name'] + + if not snapname.encode('utf-8') in subvolume.list_snapshots(): + raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname)) + if not subvolume.is_snapshot_protected(snapname): + raise VolumeException(-errno.EINVAL, "snapshot '{0}' is not protected".format(snapname)) + + # TODO: when the target group is same as source, reuse group object. + with open_group(fs_handle, self.volspec, target_groupname) as target_group: + try: + with open_subvol(fs_handle, self.volspec, target_group, target_subvolname, need_complete=False): + raise VolumeException(-errno.EEXIST, "subvolume '{0}' exists".format(target_subvolname)) + except VolumeException as ve: + if ve.errno == -errno.ENOENT: + self._prepare_clone_subvolume(fs_handle, volname, subvolume, snapname, + target_group, target_subvolname, target_pool) + else: + raise + + def clone_subvolume_snapshot(self, **kwargs): + ret = 0, "", "" + volname = kwargs['vol_name'] + subvolname = kwargs['sub_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: + self._clone_subvolume_snapshot(fs_handle, volname, subvolume, **kwargs) + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + + def clone_status(self, **kwargs): + ret = 0, "", "" + volname = kwargs['vol_name'] + clonename = kwargs['clone_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, clonename, + need_complete=False, expected_types=["clone"]) as subvolume: + ret = 0, json.dumps({'status' : subvolume.status}, indent=2), "" + except VolumeException as ve: + ret = self.volume_exception_to_retval(ve) + return ret + ### group operations def create_subvolume_group(self, **kwargs): diff --git a/src/pybind/mgr/volumes/module.py b/src/pybind/mgr/volumes/module.py index e38b805b8bf9b..bbde50080131f 100644 --- a/src/pybind/mgr/volumes/module.py +++ b/src/pybind/mgr/volumes/module.py @@ -163,6 +163,46 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): 'desc': "Resize a CephFS subvolume", 'perm': 'rw' }, + { + 'cmd': 'fs subvolume snapshot protect ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Protect snapshot of a CephFS subvolume in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'rw' + }, + { + 'cmd': 'fs subvolume snapshot unprotect ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Unprotect a snapshot of a CephFS subvolume in a volume, " + "and optionally, in a specific subvolume group", + 'perm': 'rw' + }, + { + 'cmd': 'fs subvolume snapshot clone ' + 'name=vol_name,type=CephString ' + 'name=sub_name,type=CephString ' + 'name=snap_name,type=CephString ' + 'name=target_sub_name,type=CephString ' + 'name=pool_layout,type=CephString,req=false ' + 'name=group_name,type=CephString,req=false ' + 'name=target_group_name,type=CephString,req=false ', + 'desc': "Clone a snapshot to target subvolume", + 'perm': 'rw' + }, + { + 'cmd': 'fs clone status ' + 'name=vol_name,type=CephString ' + 'name=clone_name,type=CephString ' + 'name=group_name,type=CephString,req=false ', + 'desc': "Get status on a cloned subvolume.", + 'perm': 'r' + }, # volume ls [recursive] # subvolume ls @@ -304,3 +344,21 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule): return self.vc.resize_subvolume(vol_name=cmd['vol_name'], sub_name=cmd['sub_name'], new_size=cmd['new_size'], group_name=cmd.get('group_name', None), no_shrink=cmd.get('no_shrink', False)) + + def _cmd_fs_subvolume_snapshot_protect(self, inbuf, cmd): + return self.vc.protect_subvolume_snapshot(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_unprotect(self, inbuf, cmd): + return self.vc.unprotect_subvolume_snapshot(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_clone(self, inbuf, cmd): + return self.vc.clone_subvolume_snapshot( + vol_name=cmd['vol_name'], sub_name=cmd['sub_name'], snap_name=cmd['snap_name'], + group_name=cmd.get('group_name', None), pool_layout=cmd.get('pool_layout', None), + target_sub_name=cmd['target_sub_name'], target_group_name=cmd.get('target_group_name', None)) + + 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)) -- 2.39.5