]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/snap_schedule: add subvol and subvol group arguments to cli
authorMilind Changire <mchangir@redhat.com>
Thu, 9 Nov 2023 07:55:02 +0000 (13:25 +0530)
committerMilind Changire <mchangir@redhat.com>
Sat, 6 Jan 2024 03:28:36 +0000 (08:58 +0530)
Signed-off-by: Milind Changire <mchangir@redhat.com>
src/pybind/mgr/snap_schedule/module.py

index 8ff7ff3728899dabb23928310f28fb28cc41e176..d8f04a62b94ecaa6f2715de6ac61ee42879dc566 100644 (file)
@@ -6,7 +6,7 @@ LGPL2.1.  See file COPYING.
 import errno
 import json
 import sqlite3
-from typing import Any, Dict, Optional, Tuple
+from typing import Any, Dict, Optional, Tuple, Union
 from .fs.schedule_client import SnapSchedClient
 from mgr_module import MgrModule, CLIReadCommand, CLIWriteCommand, Option
 from mgr_util import CephfsConnectionException
@@ -37,6 +37,44 @@ class Module(MgrModule):
         self._initialized = Event()
         self.client = SnapSchedClient(self)
 
+    def _subvolume_exist(self, fs: str, subvol: Union[str, None], group: Union[str, None]) -> bool:
+        rc, subvolumes, err = self.remote('volumes', 'subvolume_ls', fs, group)
+        if rc == 0:
+            for svj in json.loads(subvolumes):
+                if subvol == svj['name']:
+                    return True
+
+        return False
+
+    def _subvolume_flavor(self, fs: str, subvol: Union[str, None], group: Union[str, None]) -> str:
+        rc, subvol_info, err = self.remote('volumes', 'subvolume_info', fs, subvol, group)
+        svi_json = json.loads(subvol_info)
+        return svi_json.get('flavor', 'bad_flavor')  # 1 or 2 etc.
+
+    def _resolve_subvolume_path(self, fs: str, path: str, subvol: Union[str, None], group: Union[str, None]) -> Tuple[int, str, str]:
+        if subvol is None and group is None:
+            return 0, path, ''
+
+        rc = -1
+        subvol_path = ''
+        if self._subvolume_exist(fs, subvol, group):
+            rc, subvol_path, err = self.remote('volumes', 'subvolume_getpath', fs, subvol, group)
+            if rc != 0:
+                return rc, '', f'Could not resolve subvol:{subvol} path in fs:{fs}'
+            else:
+                subvol_flavor = self._subvolume_flavor(fs, subvol, group)
+                if subvol_flavor == 1:  # v1
+                    return 0, subvol_path, f'Ignoring user specified path:{path} for subvol'
+                if subvol_flavor == 2:  # v2
+                    err = '';
+                    if path != "/..":
+                        err = f'Ignoring user specified path:{path} for subvol'
+                    return 0, subvol_path + "/..", err
+
+                return -errno.EINVAL, '', f'Unhandled subvol flavor:{subvol_flavor}'
+        else:
+            return -errno.EINVAL, '', f'No such subvol: {group}::{subvol}'
+
     @property
     def _default_fs(self) -> Tuple[int, str, str]:
         fs_map = self.get('fs_map')
@@ -53,11 +91,11 @@ class Module(MgrModule):
             rc, fs, err = self._default_fs
             if rc < 0:
                 return rc, fs, err
-        if not self.has_fs(fs):
+        if not self._has_fs(fs):
             return -errno.EINVAL, '', f"no such file system: {fs}"
         return 0, fs, 'Success'
 
-    def has_fs(self, fs_name: str) -> bool:
+    def _has_fs(self, fs_name: str) -> bool:
         return fs_name in self.client.get_all_filesystems()
 
     def serve(self) -> None:
@@ -71,6 +109,8 @@ class Module(MgrModule):
     def snap_schedule_get(self,
                           path: str = '/',
                           fs: Optional[str] = None,
+                          subvol: Optional[str] = None,
+                          group: Optional[str] = None,
                           format: Optional[str] = 'plain') -> Tuple[int, str, str]:
         '''
         List current snapshot schedules
@@ -78,8 +118,12 @@ class Module(MgrModule):
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        errstr = 'Success'
         try:
-            ret_scheds = self.client.get_snap_schedules(fs, path)
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
+            ret_scheds = self.client.get_snap_schedules(fs, abs_path)
         except CephfsConnectionException as e:
             return e.to_tuple()
         except Exception as e:
@@ -87,12 +131,15 @@ class Module(MgrModule):
         if format == 'json':
             json_report = ','.join([ret_sched.report_json() for ret_sched in ret_scheds])
             return 0, f'[{json_report}]', ''
+        self.log.info(errstr)
         return 0, '\n===\n'.join([ret_sched.report() for ret_sched in ret_scheds]), ''
 
     @CLIReadCommand('fs snap-schedule list')
     def snap_schedule_list(self, path: str,
                            recursive: bool = False,
                            fs: Optional[str] = None,
+                           subvol: Optional[str] = None,
+                           group: Optional[str] = None,
                            format: Optional[str] = 'plain') -> Tuple[int, str, str]:
         '''
         Get current snapshot schedule for <path>
@@ -100,8 +147,12 @@ class Module(MgrModule):
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            scheds = self.client.list_snap_schedules(fs, path, recursive)
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
+            scheds = self.client.list_snap_schedules(fs, abs_path, recursive)
             self.log.debug(f'recursive is {recursive}')
         except CephfsConnectionException as e:
             return e.to_tuple()
@@ -116,7 +167,7 @@ class Module(MgrModule):
             # json_list = ','.join([sched.json_list() for sched in scheds])
             schedule_list = [sched.schedule for sched in scheds]
             retention_list = [sched.retention for sched in scheds]
-            out = {'path': path, 'schedule': schedule_list, 'retention': retention_list}
+            out = {'path': abs_path, 'schedule': schedule_list, 'retention': retention_list}
             return 0, json.dumps(out), ''
         return 0, '\n'.join([str(sched) for sched in scheds]), ''
 
@@ -125,23 +176,27 @@ class Module(MgrModule):
                           path: str,
                           snap_schedule: str,
                           start: Optional[str] = None,
-                          fs: Optional[str] = None) -> Tuple[int, str, str]:
+                          fs: Optional[str] = None,
+                          subvol: Optional[str] = None,
+                          group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Set a snapshot schedule for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
-            subvol = None
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.store_snap_schedule(fs,
                                             abs_path,
                                             (abs_path, snap_schedule,
-                                             fs, path, start, subvol))
-            suc_msg = f'Schedule set for path {path}'
+                                             fs, abs_path, start, subvol, group))
+            suc_msg = f'Schedule set for path {abs_path}'
         except sqlite3.IntegrityError:
-            existing_scheds = self.client.get_snap_schedules(fs, path)
+            existing_scheds = self.client.get_snap_schedules(fs, abs_path)
             report = [s.report() for s in existing_scheds]
             error_msg = f'Found existing schedule {report}'
             self.log.error(error_msg)
@@ -159,15 +214,20 @@ class Module(MgrModule):
                          path: str,
                          repeat: Optional[str] = None,
                          start: Optional[str] = None,
-                         fs: Optional[str] = None) -> Tuple[int, str, str]:
+                         fs: Optional[str] = None,
+                         subvol: Optional[str] = None,
+                         group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Remove a snapshot schedule for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.rm_snap_schedule(fs, abs_path, repeat, start)
         except ValueError as e:
             return -errno.ENOENT, '', str(e)
@@ -175,22 +235,27 @@ class Module(MgrModule):
             return e.to_tuple()
         except Exception as e:
             return -errno.EIO, '', str(e)
-        return 0, 'Schedule removed for path {}'.format(path), ''
+        return 0, 'Schedule removed for path {}'.format(abs_path), ''
 
     @CLIWriteCommand('fs snap-schedule retention add')
     def snap_schedule_retention_add(self,
                                     path: str,
                                     retention_spec_or_period: str,
                                     retention_count: Optional[str] = None,
-                                    fs: Optional[str] = None) -> Tuple[int, str, str]:
+                                    fs: Optional[str] = None,
+                                    subvol: Optional[str] = None,
+                                    group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Set a retention specification for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.add_retention_spec(fs, abs_path,
                                            retention_spec_or_period,
                                            retention_count)
@@ -200,22 +265,27 @@ class Module(MgrModule):
             return e.to_tuple()
         except Exception as e:
             return -errno.EIO, '', str(e)
-        return 0, 'Retention added to path {}'.format(path), ''
+        return 0, 'Retention added to path {}'.format(abs_path), ''
 
     @CLIWriteCommand('fs snap-schedule retention remove')
     def snap_schedule_retention_rm(self,
                                    path: str,
                                    retention_spec_or_period: str,
                                    retention_count: Optional[str] = None,
-                                   fs: Optional[str] = None) -> Tuple[int, str, str]:
+                                   fs: Optional[str] = None,
+                                   subvol: Optional[str] = None,
+                                   group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Remove a retention specification for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.rm_retention_spec(fs, abs_path,
                                           retention_spec_or_period,
                                           retention_count)
@@ -225,22 +295,27 @@ class Module(MgrModule):
             return e.to_tuple()
         except Exception as e:
             return -errno.EIO, '', str(e)
-        return 0, 'Retention removed from path {}'.format(path), ''
+        return 0, 'Retention removed from path {}'.format(abs_path), ''
 
     @CLIWriteCommand('fs snap-schedule activate')
     def snap_schedule_activate(self,
                                path: str,
                                repeat: Optional[str] = None,
                                start: Optional[str] = None,
-                               fs: Optional[str] = None) -> Tuple[int, str, str]:
+                               fs: Optional[str] = None,
+                               subvol: Optional[str] = None,
+                               group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Activate a snapshot schedule for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.activate_snap_schedule(fs, abs_path, repeat, start)
         except ValueError as e:
             return -errno.ENOENT, '', str(e)
@@ -248,22 +323,27 @@ class Module(MgrModule):
             return e.to_tuple()
         except Exception as e:
             return -errno.EIO, '', str(e)
-        return 0, 'Schedule activated for path {}'.format(path), ''
+        return 0, 'Schedule activated for path {}'.format(abs_path), ''
 
     @CLIWriteCommand('fs snap-schedule deactivate')
     def snap_schedule_deactivate(self,
                                  path: str,
                                  repeat: Optional[str] = None,
                                  start: Optional[str] = None,
-                                 fs: Optional[str] = None) -> Tuple[int, str, str]:
+                                 fs: Optional[str] = None,
+                                 subvol: Optional[str] = None,
+                                 group: Optional[str] = None) -> Tuple[int, str, str]:
         '''
         Deactivate a snapshot schedule for <path>
         '''
         rc, fs, err = self._validate_fs(fs)
         if rc < 0:
             return rc, fs, err
+        abs_path = ""
         try:
-            abs_path = path
+            rc, abs_path, errstr = self._resolve_subvolume_path(fs, path, subvol, group)
+            if rc != 0:
+                return rc, '', errstr
             self.client.deactivate_snap_schedule(fs, abs_path, repeat, start)
         except ValueError as e:
             return -errno.ENOENT, '', str(e)
@@ -271,4 +351,4 @@ class Module(MgrModule):
             return e.to_tuple()
         except Exception as e:
             return -errno.EIO, '', str(e)
-        return 0, 'Schedule deactivated for path {}'.format(path), ''
+        return 0, 'Schedule deactivated for path {}'.format(abs_path), ''