]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/snap_schedule/module.py
a068b4ac31d8c9b6906ac19d6fe04deba4ed0d7e
[ceph.git] / ceph / src / pybind / mgr / snap_schedule / module.py
1 """
2 Copyright (C) 2019 SUSE
3
4 LGPL2.1. See file COPYING.
5 """
6 import errno
7 import json
8 import sqlite3
9 from typing import Any, Dict, Optional, Tuple
10 from .fs.schedule_client import SnapSchedClient
11 from mgr_module import MgrModule, CLIReadCommand, CLIWriteCommand, Option
12 from mgr_util import CephfsConnectionException
13 from threading import Event
14
15
16 class Module(MgrModule):
17 MODULE_OPTIONS = [
18 Option(
19 'allow_m_granularity',
20 type='bool',
21 default=False,
22 desc='allow minute scheduled snapshots',
23 runtime=True,
24 ),
25 Option(
26 'dump_on_update',
27 type='bool',
28 default=False,
29 desc='dump database to debug log on update',
30 runtime=True,
31 ),
32
33 ]
34
35 def __init__(self, *args: Any, **kwargs: Any) -> None:
36 super(Module, self).__init__(*args, **kwargs)
37 self._initialized = Event()
38 self.client = SnapSchedClient(self)
39
40 def resolve_subvolume_path(self, fs: str, subvol: Optional[str], path: str) -> str:
41 if not subvol:
42 return path
43
44 rc, subvol_path, err = self.remote('fs', 'subvolume', 'getpath',
45 fs, subvol)
46 if rc != 0:
47 # TODO custom exception?
48 raise Exception(f'Could not resolve {path} in {fs}, {subvol}')
49 return subvol_path + path
50
51 @property
52 def default_fs(self) -> str:
53 fs_map = self.get('fs_map')
54 if fs_map['filesystems']:
55 return fs_map['filesystems'][0]['mdsmap']['fs_name']
56 else:
57 self.log.error('No filesystem instance could be found.')
58 raise CephfsConnectionException(
59 -errno.ENOENT, "no filesystem found")
60
61 def serve(self) -> None:
62 self._initialized.set()
63
64 def handle_command(self, inbuf: str, cmd: Dict[str, str]) -> Tuple[int, str, str]:
65 self._initialized.wait()
66 return -errno.EINVAL, "", "Unknown command"
67
68 @CLIReadCommand('fs snap-schedule status')
69 def snap_schedule_get(self,
70 path: str = '/',
71 subvol: Optional[str] = None,
72 fs: Optional[str] = None,
73 format: Optional[str] = 'plain') -> Tuple[int, str, str]:
74 '''
75 List current snapshot schedules
76 '''
77 use_fs = fs if fs else self.default_fs
78 try:
79 ret_scheds = self.client.get_snap_schedules(use_fs, path)
80 except CephfsConnectionException as e:
81 return e.to_tuple()
82 if format == 'json':
83 json_report = ','.join([ret_sched.report_json() for ret_sched in ret_scheds])
84 return 0, f'[{json_report}]', ''
85 return 0, '\n===\n'.join([ret_sched.report() for ret_sched in ret_scheds]), ''
86
87 @CLIReadCommand('fs snap-schedule list')
88 def snap_schedule_list(self, path: str,
89 subvol: Optional[str] = None,
90 recursive: bool = False,
91 fs: Optional[str] = None,
92 format: Optional[str] = 'plain') -> Tuple[int, str, str]:
93 '''
94 Get current snapshot schedule for <path>
95 '''
96 try:
97 use_fs = fs if fs else self.default_fs
98 scheds = self.client.list_snap_schedules(use_fs, path, recursive)
99 self.log.debug(f'recursive is {recursive}')
100 except CephfsConnectionException as e:
101 return e.to_tuple()
102 if not scheds:
103 if format == 'json':
104 output: Dict[str, str] = {}
105 return 0, json.dumps(output), ''
106 return -errno.ENOENT, '', f'SnapSchedule for {path} not found'
107 if format == 'json':
108 # json_list = ','.join([sched.json_list() for sched in scheds])
109 schedule_list = [sched.schedule for sched in scheds]
110 retention_list = [sched.retention for sched in scheds]
111 out = {'path': path, 'schedule': schedule_list, 'retention': retention_list}
112 return 0, json.dumps(out), ''
113 return 0, '\n'.join([str(sched) for sched in scheds]), ''
114
115 @CLIWriteCommand('fs snap-schedule add')
116 def snap_schedule_add(self,
117 path: str,
118 snap_schedule: str,
119 start: Optional[str] = None,
120 fs: Optional[str] = None,
121 subvol: Optional[str] = None) -> Tuple[int, str, str]:
122 '''
123 Set a snapshot schedule for <path>
124 '''
125 try:
126 use_fs = fs if fs else self.default_fs
127 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
128 self.client.store_snap_schedule(use_fs,
129 abs_path,
130 (abs_path, snap_schedule,
131 use_fs, path, start, subvol))
132 suc_msg = f'Schedule set for path {path}'
133 except sqlite3.IntegrityError:
134 existing_scheds = self.client.get_snap_schedules(use_fs, path)
135 report = [s.report() for s in existing_scheds]
136 error_msg = f'Found existing schedule {report}'
137 self.log.error(error_msg)
138 return -errno.EEXIST, '', error_msg
139 except ValueError as e:
140 return -errno.ENOENT, '', str(e)
141 except CephfsConnectionException as e:
142 return e.to_tuple()
143 return 0, suc_msg, ''
144
145 @CLIWriteCommand('fs snap-schedule remove')
146 def snap_schedule_rm(self,
147 path: str,
148 repeat: Optional[str] = None,
149 start: Optional[str] = None,
150 subvol: Optional[str] = None,
151 fs: Optional[str] = None) -> Tuple[int, str, str]:
152 '''
153 Remove a snapshot schedule for <path>
154 '''
155 try:
156 use_fs = fs if fs else self.default_fs
157 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
158 self.client.rm_snap_schedule(use_fs, abs_path, repeat, start)
159 except CephfsConnectionException as e:
160 return e.to_tuple()
161 except ValueError as e:
162 return -errno.ENOENT, '', str(e)
163 return 0, 'Schedule removed for path {}'.format(path), ''
164
165 @CLIWriteCommand('fs snap-schedule retention add')
166 def snap_schedule_retention_add(self,
167 path: str,
168 retention_spec_or_period: str,
169 retention_count: Optional[str] = None,
170 fs: Optional[str] = None,
171 subvol: Optional[str] = None) -> Tuple[int, str, str]:
172 '''
173 Set a retention specification for <path>
174 '''
175 try:
176 use_fs = fs if fs else self.default_fs
177 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
178 self.client.add_retention_spec(use_fs, abs_path,
179 retention_spec_or_period,
180 retention_count)
181 except CephfsConnectionException as e:
182 return e.to_tuple()
183 except ValueError as e:
184 return -errno.ENOENT, '', str(e)
185 return 0, 'Retention added to path {}'.format(path), ''
186
187 @CLIWriteCommand('fs snap-schedule retention remove')
188 def snap_schedule_retention_rm(self,
189 path: str,
190 retention_spec_or_period: str,
191 retention_count: Optional[str] = None,
192 fs: Optional[str] = None,
193 subvol: Optional[str] = None) -> Tuple[int, str, str]:
194 '''
195 Remove a retention specification for <path>
196 '''
197 try:
198 use_fs = fs if fs else self.default_fs
199 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
200 self.client.rm_retention_spec(use_fs, abs_path,
201 retention_spec_or_period,
202 retention_count)
203 except CephfsConnectionException as e:
204 return e.to_tuple()
205 except ValueError as e:
206 return -errno.ENOENT, '', str(e)
207 return 0, 'Retention removed from path {}'.format(path), ''
208
209 @CLIWriteCommand('fs snap-schedule activate')
210 def snap_schedule_activate(self,
211 path: str,
212 repeat: Optional[str] = None,
213 start: Optional[str] = None,
214 subvol: Optional[str] = None,
215 fs: Optional[str] = None) -> Tuple[int, str, str]:
216 '''
217 Activate a snapshot schedule for <path>
218 '''
219 try:
220 use_fs = fs if fs else self.default_fs
221 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
222 self.client.activate_snap_schedule(use_fs, abs_path, repeat, start)
223 except CephfsConnectionException as e:
224 return e.to_tuple()
225 except ValueError as e:
226 return -errno.ENOENT, '', str(e)
227 return 0, 'Schedule activated for path {}'.format(path), ''
228
229 @CLIWriteCommand('fs snap-schedule deactivate')
230 def snap_schedule_deactivate(self,
231 path: str,
232 repeat: Optional[str] = None,
233 start: Optional[str] = None,
234 subvol: Optional[str] = None,
235 fs: Optional[str] = None) -> Tuple[int, str, str]:
236 '''
237 Deactivate a snapshot schedule for <path>
238 '''
239 try:
240 use_fs = fs if fs else self.default_fs
241 abs_path = self.resolve_subvolume_path(use_fs, subvol, path)
242 self.client.deactivate_snap_schedule(use_fs, abs_path, repeat, start)
243 except CephfsConnectionException as e:
244 return e.to_tuple()
245 except ValueError as e:
246 return -errno.ENOENT, '', str(e)
247 return 0, 'Schedule deactivated for path {}'.format(path), ''