]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/volumes/fs/volume.py
import ceph quincy 17.2.1
[ceph.git] / ceph / src / pybind / mgr / volumes / fs / volume.py
CommitLineData
81eedcae
TL
1import json
2import errno
3import logging
f67539c2 4from typing import TYPE_CHECKING
81eedcae
TL
5
6import cephfs
81eedcae 7
f67539c2
TL
8from mgr_util import CephfsClient
9
92f5a8d4
TL
10from .fs_util import listdir
11
f67539c2 12from .operations.volume import create_volume, \
1d09f67e 13 delete_volume, rename_volume, list_volumes, open_volume, get_pool_names
adb31ebb 14from .operations.group import open_group, create_group, remove_group, open_group_unique
92f5a8d4
TL
15from .operations.subvolume import open_subvol, create_subvol, remove_subvol, \
16 create_clone
1d09f67e 17from .operations.trash import Trash
92f5a8d4
TL
18
19from .vol_spec import VolSpec
cd265ab1 20from .exception import VolumeException, ClusterError, ClusterTimeout, EvictionError
92f5a8d4 21from .async_cloner import Cloner
494da23a 22from .purge_queue import ThreadPoolPurgeQueueMixin
adb31ebb 23from .operations.template import SubvolumeOpType
81eedcae 24
f67539c2
TL
25if TYPE_CHECKING:
26 from volumes import Module
27
81eedcae
TL
28log = logging.getLogger(__name__)
29
cd265ab1
TL
30ALLOWED_ACCESS_LEVELS = ('r', 'rw')
31
32
92f5a8d4
TL
33def octal_str_to_decimal_int(mode):
34 try:
35 return int(mode, 8)
36 except ValueError:
37 raise VolumeException(-errno.EINVAL, "Invalid mode '{0}'".format(mode))
38
f67539c2 39
92f5a8d4
TL
40def name_to_json(names):
41 """
42 convert the list of names to json
43 """
44 namedict = []
45 for i in range(len(names)):
46 namedict.append({'name': names[i].decode('utf-8')})
47 return json.dumps(namedict, indent=4, sort_keys=True)
494da23a 48
f67539c2
TL
49
50class VolumeClient(CephfsClient["Module"]):
81eedcae 51 def __init__(self, mgr):
f67539c2 52 super().__init__(mgr)
92f5a8d4
TL
53 # volume specification
54 self.volspec = VolSpec(mgr.rados.conf_get('client_snapdir'))
522d829b 55 self.cloner = Cloner(self, self.mgr.max_concurrent_clones, self.mgr.snapshot_clone_delay)
494da23a
TL
56 self.purge_queue = ThreadPoolPurgeQueueMixin(self, 4)
57 # on startup, queue purge job for available volumes to kickstart
58 # purge for leftover subvolume entries in trash. note that, if the
59 # trash directory does not exist or if there are no purge entries
60 # available for a volume, the volume is removed from the purge
61 # job list.
62 fs_map = self.mgr.get('fs_map')
63 for fs in fs_map['filesystems']:
92f5a8d4
TL
64 self.cloner.queue_job(fs['mdsmap']['fs_name'])
65 self.purge_queue.queue_job(fs['mdsmap']['fs_name'])
66
92f5a8d4 67 def shutdown(self):
f67539c2 68 # Overrides CephfsClient.shutdown()
92f5a8d4
TL
69 log.info("shutting down")
70 # first, note that we're shutting down
71 self.stopping.set()
f67539c2
TL
72 # stop clones
73 self.cloner.shutdown()
74 # stop purge threads
75 self.purge_queue.shutdown()
76 # last, delete all libcephfs handles from connection pool
522d829b 77 self.connection_pool.del_all_connections()
81eedcae 78
eafe8130
TL
79 def cluster_log(self, msg, lvl=None):
80 """
81 log to cluster log with default log level as WARN.
82 """
83 if not lvl:
f67539c2 84 lvl = self.mgr.ClusterLogPrio.WARN
eafe8130
TL
85 self.mgr.cluster_log("cluster", lvl, msg)
86
81eedcae
TL
87 def volume_exception_to_retval(self, ve):
88 """
89 return a tuple representation from a volume exception
90 """
91 return ve.to_tuple()
92
92f5a8d4 93 ### volume operations -- create, rm, ls
81eedcae 94
9f95a23c 95 def create_fs_volume(self, volname, placement):
92f5a8d4
TL
96 if self.is_stopping():
97 return -errno.ESHUTDOWN, "", "shutdown in progress"
9f95a23c 98 return create_volume(self.mgr, volname, placement)
81eedcae 99
92f5a8d4
TL
100 def delete_fs_volume(self, volname, confirm):
101 if self.is_stopping():
102 return -errno.ESHUTDOWN, "", "shutdown in progress"
81eedcae 103
eafe8130
TL
104 if confirm != "--yes-i-really-mean-it":
105 return -errno.EPERM, "", "WARNING: this will *PERMANENTLY DESTROY* all data " \
106 "stored in the filesystem '{0}'. If you are *ABSOLUTELY CERTAIN* " \
107 "that is what you want, re-issue the command followed by " \
92f5a8d4 108 "--yes-i-really-mean-it.".format(volname)
eafe8130 109
f6b5b4d7
TL
110 ret, out, err = self.mgr.check_mon_command({
111 'prefix': 'config get',
112 'key': 'mon_allow_pool_delete',
113 'who': 'mon',
114 'format': 'json',
115 })
116 mon_allow_pool_delete = json.loads(out)
117 if not mon_allow_pool_delete:
118 return -errno.EPERM, "", "pool deletion is disabled; you must first " \
119 "set the mon_allow_pool_delete config option to true before volumes " \
120 "can be deleted"
121
122 metadata_pool, data_pools = get_pool_names(self.mgr, volname)
123 if not metadata_pool:
124 return -errno.ENOENT, "", "volume {0} doesn't exist".format(volname)
92f5a8d4 125 self.purge_queue.cancel_jobs(volname)
522d829b 126 self.connection_pool.del_connections(volname, wait=True)
f6b5b4d7 127 return delete_volume(self.mgr, volname, metadata_pool, data_pools)
eafe8130 128
92f5a8d4 129 def list_fs_volumes(self):
9f95a23c 130 if self.stopping.is_set():
92f5a8d4
TL
131 return -errno.ESHUTDOWN, "", "shutdown in progress"
132 volumes = list_volumes(self.mgr)
133 return 0, json.dumps(volumes, indent=4, sort_keys=True), ""
81eedcae 134
1d09f67e
TL
135 def rename_fs_volume(self, volname, newvolname, sure):
136 if self.is_stopping():
137 return -errno.ESHUTDOWN, "", "shutdown in progress"
138
139 if not sure:
140 return (
141 -errno.EPERM, "",
142 "WARNING: This will rename the filesystem and possibly its "
143 "pools. It is a potentially disruptive operation, clients' "
144 "cephx credentials need reauthorized to access the file system "
145 "and its pools with the new name. Add --yes-i-really-mean-it "
146 "if you are sure you wish to continue.")
147
148 return rename_volume(self.mgr, volname, newvolname)
149
92f5a8d4 150 ### subvolume operations
81eedcae 151
92f5a8d4
TL
152 def _create_subvolume(self, fs_handle, volname, group, subvolname, **kwargs):
153 size = kwargs['size']
154 pool = kwargs['pool_layout']
155 uid = kwargs['uid']
156 gid = kwargs['gid']
157 mode = kwargs['mode']
e306af50 158 isolate_nspace = kwargs['namespace_isolated']
81eedcae 159
92f5a8d4 160 oct_mode = octal_str_to_decimal_int(mode)
81eedcae 161 try:
92f5a8d4 162 create_subvol(
cd265ab1 163 self.mgr, fs_handle, self.volspec, group, subvolname, size, isolate_nspace, pool, oct_mode, uid, gid)
92f5a8d4
TL
164 except VolumeException as ve:
165 # kick the purge threads for async removal -- note that this
166 # assumes that the subvolume is moved to trashcan for cleanup on error.
167 self.purge_queue.queue_job(volname)
168 raise ve
81eedcae 169
92f5a8d4 170 def create_subvolume(self, **kwargs):
494da23a
TL
171 ret = 0, "", ""
172 volname = kwargs['vol_name']
173 subvolname = kwargs['sub_name']
174 groupname = kwargs['group_name']
e306af50
TL
175 size = kwargs['size']
176 pool = kwargs['pool_layout']
177 uid = kwargs['uid']
178 gid = kwargs['gid']
1d09f67e 179 mode = kwargs['mode']
e306af50 180 isolate_nspace = kwargs['namespace_isolated']
494da23a 181
81eedcae 182 try:
92f5a8d4
TL
183 with open_volume(self, volname) as fs_handle:
184 with open_group(fs_handle, self.volspec, groupname) as group:
185 try:
cd265ab1 186 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.CREATE) as subvolume:
e306af50 187 # idempotent creation -- valid. Attributes set is supported.
adb31ebb
TL
188 attrs = {
189 'uid': uid if uid else subvolume.uid,
190 'gid': gid if gid else subvolume.gid,
1d09f67e 191 'mode': octal_str_to_decimal_int(mode),
adb31ebb
TL
192 'data_pool': pool,
193 'pool_namespace': subvolume.namespace if isolate_nspace else None,
194 'quota': size
195 }
196 subvolume.set_attrs(subvolume.path, attrs)
92f5a8d4
TL
197 except VolumeException as ve:
198 if ve.errno == -errno.ENOENT:
199 self._create_subvolume(fs_handle, volname, group, subvolname, **kwargs)
200 else:
201 raise
81eedcae 202 except VolumeException as ve:
92f5a8d4 203 # volume/group does not exist or subvolume creation failed
81eedcae
TL
204 ret = self.volume_exception_to_retval(ve)
205 return ret
206
92f5a8d4 207 def remove_subvolume(self, **kwargs):
adb31ebb
TL
208 ret = 0, "", ""
209 volname = kwargs['vol_name']
210 subvolname = kwargs['sub_name']
211 groupname = kwargs['group_name']
212 force = kwargs['force']
213 retainsnaps = kwargs['retain_snapshots']
92f5a8d4
TL
214
215 try:
216 with open_volume(self, volname) as fs_handle:
217 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 218 remove_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, force, retainsnaps)
92f5a8d4
TL
219 # kick the purge threads for async removal -- note that this
220 # assumes that the subvolume is moved to trash can.
221 # TODO: make purge queue as singleton so that trash can kicks
222 # the purge threads on dump.
223 self.purge_queue.queue_job(volname)
224 except VolumeException as ve:
20effc67 225 if ve.errno == -errno.EAGAIN and not force:
92f5a8d4
TL
226 ve = VolumeException(ve.errno, ve.error_str + " (use --force to override)")
227 ret = self.volume_exception_to_retval(ve)
228 elif not (ve.errno == -errno.ENOENT and force):
229 ret = self.volume_exception_to_retval(ve)
230 return ret
231
cd265ab1
TL
232 def authorize_subvolume(self, **kwargs):
233 ret = 0, "", ""
234 volname = kwargs['vol_name']
235 subvolname = kwargs['sub_name']
236 authid = kwargs['auth_id']
237 groupname = kwargs['group_name']
238 accesslevel = kwargs['access_level']
239 tenant_id = kwargs['tenant_id']
240 allow_existing_id = kwargs['allow_existing_id']
241
242 try:
243 with open_volume(self, volname) as fs_handle:
244 with open_group(fs_handle, self.volspec, groupname) as group:
245 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.ALLOW_ACCESS) as subvolume:
246 key = subvolume.authorize(authid, accesslevel, tenant_id, allow_existing_id)
247 ret = 0, key, ""
248 except VolumeException as ve:
249 ret = self.volume_exception_to_retval(ve)
250 return ret
251
252 def deauthorize_subvolume(self, **kwargs):
253 ret = 0, "", ""
254 volname = kwargs['vol_name']
255 subvolname = kwargs['sub_name']
256 authid = kwargs['auth_id']
257 groupname = kwargs['group_name']
258
259 try:
260 with open_volume(self, volname) as fs_handle:
261 with open_group(fs_handle, self.volspec, groupname) as group:
262 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.DENY_ACCESS) as subvolume:
263 subvolume.deauthorize(authid)
264 except VolumeException as ve:
265 ret = self.volume_exception_to_retval(ve)
266 return ret
267
268 def authorized_list(self, **kwargs):
269 ret = 0, "", ""
270 volname = kwargs['vol_name']
271 subvolname = kwargs['sub_name']
272 groupname = kwargs['group_name']
273
274 try:
275 with open_volume(self, volname) as fs_handle:
276 with open_group(fs_handle, self.volspec, groupname) as group:
277 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.AUTH_LIST) as subvolume:
278 auths = subvolume.authorized_list()
279 ret = 0, json.dumps(auths, indent=4, sort_keys=True), ""
280 except VolumeException as ve:
281 ret = self.volume_exception_to_retval(ve)
282 return ret
283
284 def evict(self, **kwargs):
285 ret = 0, "", ""
286 volname = kwargs['vol_name']
287 subvolname = kwargs['sub_name']
288 authid = kwargs['auth_id']
289 groupname = kwargs['group_name']
290
291 try:
292 with open_volume(self, volname) as fs_handle:
293 with open_group(fs_handle, self.volspec, groupname) as group:
294 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.EVICT) as subvolume:
295 key = subvolume.evict(volname, authid)
296 ret = 0, "", ""
297 except (VolumeException, ClusterTimeout, ClusterError, EvictionError) as e:
298 if isinstance(e, VolumeException):
299 ret = self.volume_exception_to_retval(e)
300 elif isinstance(e, ClusterTimeout):
301 ret = -errno.ETIMEDOUT , "", "Timedout trying to talk to ceph cluster"
302 elif isinstance(e, ClusterError):
303 ret = e._result_code , "", e._result_str
304 elif isinstance(e, EvictionError):
305 ret = -errno.EINVAL, "", str(e)
306 return ret
307
92f5a8d4
TL
308 def resize_subvolume(self, **kwargs):
309 ret = 0, "", ""
310 volname = kwargs['vol_name']
311 subvolname = kwargs['sub_name']
312 newsize = kwargs['new_size']
313 noshrink = kwargs['no_shrink']
314 groupname = kwargs['group_name']
315
81eedcae 316 try:
92f5a8d4
TL
317 with open_volume(self, volname) as fs_handle:
318 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 319 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.RESIZE) as subvolume:
92f5a8d4
TL
320 nsize, usedbytes = subvolume.resize(newsize, noshrink)
321 ret = 0, json.dumps(
322 [{'bytes_used': usedbytes},{'bytes_quota': nsize},
323 {'bytes_pcent': "undefined" if nsize == 0 else '{0:.2f}'.format((float(usedbytes) / nsize) * 100.0)}],
324 indent=4, sort_keys=True), ""
81eedcae
TL
325 except VolumeException as ve:
326 ret = self.volume_exception_to_retval(ve)
327 return ret
328
f6b5b4d7
TL
329 def subvolume_pin(self, **kwargs):
330 ret = 0, "", ""
331 volname = kwargs['vol_name']
332 subvolname = kwargs['sub_name']
333 pin_type = kwargs['pin_type']
334 pin_setting = kwargs['pin_setting']
335 groupname = kwargs['group_name']
336
337 try:
338 with open_volume(self, volname) as fs_handle:
339 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 340 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.PIN) as subvolume:
f6b5b4d7
TL
341 subvolume.pin(pin_type, pin_setting)
342 ret = 0, json.dumps({}), ""
343 except VolumeException as ve:
344 ret = self.volume_exception_to_retval(ve)
345 return ret
346
92f5a8d4 347 def subvolume_getpath(self, **kwargs):
494da23a
TL
348 ret = None
349 volname = kwargs['vol_name']
350 subvolname = kwargs['sub_name']
351 groupname = kwargs['group_name']
92f5a8d4 352
81eedcae 353 try:
92f5a8d4
TL
354 with open_volume(self, volname) as fs_handle:
355 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 356 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.GETPATH) as subvolume:
92f5a8d4
TL
357 subvolpath = subvolume.path
358 ret = 0, subvolpath.decode("utf-8"), ""
eafe8130
TL
359 except VolumeException as ve:
360 ret = self.volume_exception_to_retval(ve)
361 return ret
362
1911f103
TL
363 def subvolume_info(self, **kwargs):
364 ret = None
365 volname = kwargs['vol_name']
366 subvolname = kwargs['sub_name']
367 groupname = kwargs['group_name']
368
369 try:
370 with open_volume(self, volname) as fs_handle:
371 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 372 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.INFO) as subvolume:
1911f103
TL
373 mon_addr_lst = []
374 mon_map_mons = self.mgr.get('mon_map')['mons']
375 for mon in mon_map_mons:
376 ip_port = mon['addr'].split("/")[0]
377 mon_addr_lst.append(ip_port)
378
379 subvol_info_dict = subvolume.info()
380 subvol_info_dict["mon_addrs"] = mon_addr_lst
381 ret = 0, json.dumps(subvol_info_dict, indent=4, sort_keys=True), ""
382 except VolumeException as ve:
383 ret = self.volume_exception_to_retval(ve)
384 return ret
385
33c7a0ef
TL
386 def set_user_metadata(self, **kwargs):
387 ret = 0, "", ""
388 volname = kwargs['vol_name']
389 subvolname = kwargs['sub_name']
390 groupname = kwargs['group_name']
391 keyname = kwargs['key_name']
392 value = kwargs['value']
393
394 try:
395 with open_volume(self, volname) as fs_handle:
396 with open_group(fs_handle, self.volspec, groupname) as group:
397 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_SET) as subvolume:
398 subvolume.set_user_metadata(keyname, value)
399 except VolumeException as ve:
400 ret = self.volume_exception_to_retval(ve)
401 return ret
402
403 def get_user_metadata(self, **kwargs):
404 ret = 0, "", ""
405 volname = kwargs['vol_name']
406 subvolname = kwargs['sub_name']
407 groupname = kwargs['group_name']
408 keyname = kwargs['key_name']
409
410 try:
411 with open_volume(self, volname) as fs_handle:
412 with open_group(fs_handle, self.volspec, groupname) as group:
413 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_GET) as subvolume:
414 value = subvolume.get_user_metadata(keyname)
415 ret = 0, value, ""
416 except VolumeException as ve:
417 ret = self.volume_exception_to_retval(ve)
418 return ret
419
420 def list_user_metadata(self, **kwargs):
421 ret = 0, "", ""
422 volname = kwargs['vol_name']
423 subvolname = kwargs['sub_name']
424 groupname = kwargs['group_name']
425
426 try:
427 with open_volume(self, volname) as fs_handle:
428 with open_group(fs_handle, self.volspec, groupname) as group:
429 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_LIST) as subvolume:
430 subvol_metadata_dict = subvolume.list_user_metadata()
431 ret = 0, json.dumps(subvol_metadata_dict, indent=4, sort_keys=True), ""
432 except VolumeException as ve:
433 ret = self.volume_exception_to_retval(ve)
434 return ret
435
436 def remove_user_metadata(self, **kwargs):
437 ret = 0, "", ""
438 volname = kwargs['vol_name']
439 subvolname = kwargs['sub_name']
440 groupname = kwargs['group_name']
441 keyname = kwargs['key_name']
442 force = kwargs['force']
443
444 try:
445 with open_volume(self, volname) as fs_handle:
446 with open_group(fs_handle, self.volspec, groupname) as group:
447 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.USER_METADATA_REMOVE) as subvolume:
448 subvolume.remove_user_metadata(keyname)
449 except VolumeException as ve:
450 if not (ve.errno == -errno.ENOENT and force):
451 ret = self.volume_exception_to_retval(ve)
452 return ret
453
92f5a8d4 454 def list_subvolumes(self, **kwargs):
eafe8130 455 ret = 0, "", ""
92f5a8d4 456 volname = kwargs['vol_name']
eafe8130
TL
457 groupname = kwargs['group_name']
458
459 try:
92f5a8d4
TL
460 with open_volume(self, volname) as fs_handle:
461 with open_group(fs_handle, self.volspec, groupname) as group:
462 subvolumes = group.list_subvolumes()
463 ret = 0, name_to_json(subvolumes), ""
81eedcae
TL
464 except VolumeException as ve:
465 ret = self.volume_exception_to_retval(ve)
466 return ret
467
468 ### subvolume snapshot
469
92f5a8d4 470 def create_subvolume_snapshot(self, **kwargs):
494da23a
TL
471 ret = 0, "", ""
472 volname = kwargs['vol_name']
473 subvolname = kwargs['sub_name']
474 snapname = kwargs['snap_name']
475 groupname = kwargs['group_name']
81eedcae 476
494da23a 477 try:
92f5a8d4
TL
478 with open_volume(self, volname) as fs_handle:
479 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 480 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_CREATE) as subvolume:
92f5a8d4 481 subvolume.create_snapshot(snapname)
81eedcae
TL
482 except VolumeException as ve:
483 ret = self.volume_exception_to_retval(ve)
484 return ret
485
92f5a8d4 486 def remove_subvolume_snapshot(self, **kwargs):
494da23a
TL
487 ret = 0, "", ""
488 volname = kwargs['vol_name']
489 subvolname = kwargs['sub_name']
490 snapname = kwargs['snap_name']
491 groupname = kwargs['group_name']
492 force = kwargs['force']
92f5a8d4
TL
493
494 try:
495 with open_volume(self, volname) as fs_handle:
496 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 497 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_REMOVE) as subvolume:
92f5a8d4
TL
498 subvolume.remove_snapshot(snapname)
499 except VolumeException as ve:
adb31ebb
TL
500 # ESTALE serves as an error to state that subvolume is currently stale due to internal removal and,
501 # we should tickle the purge jobs to purge the same
502 if ve.errno == -errno.ESTALE:
503 self.purge_queue.queue_job(volname)
504 elif not (ve.errno == -errno.ENOENT and force):
92f5a8d4
TL
505 ret = self.volume_exception_to_retval(ve)
506 return ret
507
e306af50
TL
508 def subvolume_snapshot_info(self, **kwargs):
509 ret = 0, "", ""
510 volname = kwargs['vol_name']
511 subvolname = kwargs['sub_name']
512 snapname = kwargs['snap_name']
513 groupname = kwargs['group_name']
514
515 try:
516 with open_volume(self, volname) as fs_handle:
517 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 518 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_INFO) as subvolume:
e306af50
TL
519 snap_info_dict = subvolume.snapshot_info(snapname)
520 ret = 0, json.dumps(snap_info_dict, indent=4, sort_keys=True), ""
521 except VolumeException as ve:
522 ret = self.volume_exception_to_retval(ve)
523 return ret
524
33c7a0ef
TL
525 def set_subvolume_snapshot_metadata(self, **kwargs):
526 ret = 0, "", ""
527 volname = kwargs['vol_name']
528 subvolname = kwargs['sub_name']
529 snapname = kwargs['snap_name']
530 groupname = kwargs['group_name']
531 keyname = kwargs['key_name']
532 value = kwargs['value']
533
534 try:
535 with open_volume(self, volname) as fs_handle:
536 with open_group(fs_handle, self.volspec, groupname) as group:
537 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_SET) as subvolume:
538 if not snapname.encode('utf-8') in subvolume.list_snapshots():
539 raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname))
540 subvolume.set_snapshot_metadata(snapname, keyname, value)
541 except VolumeException as ve:
542 ret = self.volume_exception_to_retval(ve)
543 return ret
544
545 def get_subvolume_snapshot_metadata(self, **kwargs):
546 ret = 0, "", ""
547 volname = kwargs['vol_name']
548 subvolname = kwargs['sub_name']
549 snapname = kwargs['snap_name']
550 groupname = kwargs['group_name']
551 keyname = kwargs['key_name']
552
553 try:
554 with open_volume(self, volname) as fs_handle:
555 with open_group(fs_handle, self.volspec, groupname) as group:
556 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_GET) as subvolume:
557 if not snapname.encode('utf-8') in subvolume.list_snapshots():
558 raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname))
559 value = subvolume.get_snapshot_metadata(snapname, keyname)
560 ret = 0, value, ""
561 except VolumeException as ve:
562 ret = self.volume_exception_to_retval(ve)
563 return ret
564
565 def list_subvolume_snapshot_metadata(self, **kwargs):
566 ret = 0, "", ""
567 volname = kwargs['vol_name']
568 subvolname = kwargs['sub_name']
569 snapname = kwargs['snap_name']
570 groupname = kwargs['group_name']
571
572 try:
573 with open_volume(self, volname) as fs_handle:
574 with open_group(fs_handle, self.volspec, groupname) as group:
575 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_LIST) as subvolume:
576 if not snapname.encode('utf-8') in subvolume.list_snapshots():
577 raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname))
578 snap_metadata_dict = subvolume.list_snapshot_metadata(snapname)
579 ret = 0, json.dumps(snap_metadata_dict, indent=4, sort_keys=True), ""
580 except VolumeException as ve:
581 ret = self.volume_exception_to_retval(ve)
582 return ret
583
584 def remove_subvolume_snapshot_metadata(self, **kwargs):
585 ret = 0, "", ""
586 volname = kwargs['vol_name']
587 subvolname = kwargs['sub_name']
588 snapname = kwargs['snap_name']
589 groupname = kwargs['group_name']
590 keyname = kwargs['key_name']
591 force = kwargs['force']
592
593 try:
594 with open_volume(self, volname) as fs_handle:
595 with open_group(fs_handle, self.volspec, groupname) as group:
596 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_METADATA_REMOVE) as subvolume:
597 if not snapname.encode('utf-8') in subvolume.list_snapshots():
598 raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(snapname))
599 subvolume.remove_snapshot_metadata(snapname, keyname)
600 except VolumeException as ve:
601 if not (ve.errno == -errno.ENOENT and force):
602 ret = self.volume_exception_to_retval(ve)
603 return ret
604
92f5a8d4
TL
605 def list_subvolume_snapshots(self, **kwargs):
606 ret = 0, "", ""
607 volname = kwargs['vol_name']
608 subvolname = kwargs['sub_name']
609 groupname = kwargs['group_name']
610
81eedcae 611 try:
92f5a8d4
TL
612 with open_volume(self, volname) as fs_handle:
613 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 614 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_LIST) as subvolume:
92f5a8d4
TL
615 snapshots = subvolume.list_snapshots()
616 ret = 0, name_to_json(snapshots), ""
81eedcae
TL
617 except VolumeException as ve:
618 ret = self.volume_exception_to_retval(ve)
619 return ret
620
92f5a8d4 621 def protect_subvolume_snapshot(self, **kwargs):
f6b5b4d7 622 ret = 0, "", "Deprecation warning: 'snapshot protect' call is deprecated and will be removed in a future release"
92f5a8d4 623 volname = kwargs['vol_name']
eafe8130
TL
624 subvolname = kwargs['sub_name']
625 groupname = kwargs['group_name']
626
627 try:
92f5a8d4
TL
628 with open_volume(self, volname) as fs_handle:
629 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 630 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_PROTECT) as subvolume:
f6b5b4d7 631 log.warning("snapshot protect call is deprecated and will be removed in a future release")
eafe8130
TL
632 except VolumeException as ve:
633 ret = self.volume_exception_to_retval(ve)
634 return ret
635
92f5a8d4 636 def unprotect_subvolume_snapshot(self, **kwargs):
f6b5b4d7 637 ret = 0, "", "Deprecation warning: 'snapshot unprotect' call is deprecated and will be removed in a future release"
92f5a8d4
TL
638 volname = kwargs['vol_name']
639 subvolname = kwargs['sub_name']
92f5a8d4
TL
640 groupname = kwargs['group_name']
641
642 try:
643 with open_volume(self, volname) as fs_handle:
644 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 645 with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.SNAP_UNPROTECT) as subvolume:
f6b5b4d7 646 log.warning("snapshot unprotect call is deprecated and will be removed in a future release")
92f5a8d4
TL
647 except VolumeException as ve:
648 ret = self.volume_exception_to_retval(ve)
649 return ret
eafe8130 650
adb31ebb
TL
651 def _prepare_clone_subvolume(self, fs_handle, volname, s_subvolume, s_snapname, t_group, t_subvolname, **kwargs):
652 t_pool = kwargs['pool_layout']
653 s_subvolname = kwargs['sub_name']
654 s_groupname = kwargs['group_name']
655 t_groupname = kwargs['target_group_name']
656
cd265ab1
TL
657 create_clone(self.mgr, fs_handle, self.volspec, t_group, t_subvolname, t_pool, volname, s_subvolume, s_snapname)
658 with open_subvol(self.mgr, fs_handle, self.volspec, t_group, t_subvolname, SubvolumeOpType.CLONE_INTERNAL) as t_subvolume:
92f5a8d4 659 try:
adb31ebb
TL
660 if t_groupname == s_groupname and t_subvolname == s_subvolname:
661 t_subvolume.attach_snapshot(s_snapname, t_subvolume)
662 else:
663 s_subvolume.attach_snapshot(s_snapname, t_subvolume)
92f5a8d4
TL
664 self.cloner.queue_job(volname)
665 except VolumeException as ve:
666 try:
adb31ebb 667 t_subvolume.remove()
92f5a8d4
TL
668 self.purge_queue.queue_job(volname)
669 except Exception as e:
adb31ebb 670 log.warning("failed to cleanup clone subvolume '{0}' ({1})".format(t_subvolname, e))
92f5a8d4
TL
671 raise ve
672
adb31ebb
TL
673 def _clone_subvolume_snapshot(self, fs_handle, volname, s_group, s_subvolume, **kwargs):
674 s_snapname = kwargs['snap_name']
675 target_subvolname = kwargs['target_sub_name']
676 target_groupname = kwargs['target_group_name']
677 s_groupname = kwargs['group_name']
92f5a8d4 678
adb31ebb
TL
679 if not s_snapname.encode('utf-8') in s_subvolume.list_snapshots():
680 raise VolumeException(-errno.ENOENT, "snapshot '{0}' does not exist".format(s_snapname))
92f5a8d4 681
adb31ebb 682 with open_group_unique(fs_handle, self.volspec, target_groupname, s_group, s_groupname) as target_group:
92f5a8d4 683 try:
cd265ab1 684 with open_subvol(self.mgr, fs_handle, self.volspec, target_group, target_subvolname, SubvolumeOpType.CLONE_CREATE):
92f5a8d4
TL
685 raise VolumeException(-errno.EEXIST, "subvolume '{0}' exists".format(target_subvolname))
686 except VolumeException as ve:
687 if ve.errno == -errno.ENOENT:
adb31ebb
TL
688 self._prepare_clone_subvolume(fs_handle, volname, s_subvolume, s_snapname,
689 target_group, target_subvolname, **kwargs)
92f5a8d4
TL
690 else:
691 raise
692
693 def clone_subvolume_snapshot(self, **kwargs):
694 ret = 0, "", ""
695 volname = kwargs['vol_name']
adb31ebb
TL
696 s_subvolname = kwargs['sub_name']
697 s_groupname = kwargs['group_name']
92f5a8d4
TL
698
699 try:
700 with open_volume(self, volname) as fs_handle:
adb31ebb 701 with open_group(fs_handle, self.volspec, s_groupname) as s_group:
cd265ab1 702 with open_subvol(self.mgr, fs_handle, self.volspec, s_group, s_subvolname, SubvolumeOpType.CLONE_SOURCE) as s_subvolume:
adb31ebb 703 self._clone_subvolume_snapshot(fs_handle, volname, s_group, s_subvolume, **kwargs)
92f5a8d4
TL
704 except VolumeException as ve:
705 ret = self.volume_exception_to_retval(ve)
706 return ret
81eedcae 707
92f5a8d4 708 def clone_status(self, **kwargs):
494da23a
TL
709 ret = 0, "", ""
710 volname = kwargs['vol_name']
92f5a8d4
TL
711 clonename = kwargs['clone_name']
712 groupname = kwargs['group_name']
713
714 try:
715 with open_volume(self, volname) as fs_handle:
716 with open_group(fs_handle, self.volspec, groupname) as group:
cd265ab1 717 with open_subvol(self.mgr, fs_handle, self.volspec, group, clonename, SubvolumeOpType.CLONE_STATUS) as subvolume:
92f5a8d4
TL
718 ret = 0, json.dumps({'status' : subvolume.status}, indent=2), ""
719 except VolumeException as ve:
720 ret = self.volume_exception_to_retval(ve)
721 return ret
722
9f95a23c
TL
723 def clone_cancel(self, **kwargs):
724 ret = 0, "", ""
725 volname = kwargs['vol_name']
726 clonename = kwargs['clone_name']
727 groupname = kwargs['group_name']
728
729 try:
730 self.cloner.cancel_job(volname, (clonename, groupname))
731 except VolumeException as ve:
732 ret = self.volume_exception_to_retval(ve)
733 return ret
734
92f5a8d4
TL
735 ### group operations
736
737 def create_subvolume_group(self, **kwargs):
738 ret = 0, "", ""
739 volname = kwargs['vol_name']
494da23a
TL
740 groupname = kwargs['group_name']
741 pool = kwargs['pool_layout']
92f5a8d4
TL
742 uid = kwargs['uid']
743 gid = kwargs['gid']
494da23a 744 mode = kwargs['mode']
81eedcae 745
494da23a 746 try:
92f5a8d4
TL
747 with open_volume(self, volname) as fs_handle:
748 try:
749 with open_group(fs_handle, self.volspec, groupname):
750 # idempotent creation -- valid.
751 pass
752 except VolumeException as ve:
753 if ve.errno == -errno.ENOENT:
754 oct_mode = octal_str_to_decimal_int(mode)
755 create_group(fs_handle, self.volspec, groupname, pool, oct_mode, uid, gid)
756 else:
757 raise
81eedcae 758 except VolumeException as ve:
92f5a8d4 759 # volume does not exist or subvolume group creation failed
81eedcae
TL
760 ret = self.volume_exception_to_retval(ve)
761 return ret
762
92f5a8d4 763 def remove_subvolume_group(self, **kwargs):
494da23a 764 ret = 0, "", ""
92f5a8d4 765 volname = kwargs['vol_name']
494da23a
TL
766 groupname = kwargs['group_name']
767 force = kwargs['force']
92f5a8d4 768
81eedcae 769 try:
92f5a8d4
TL
770 with open_volume(self, volname) as fs_handle:
771 remove_group(fs_handle, self.volspec, groupname)
81eedcae 772 except VolumeException as ve:
92f5a8d4
TL
773 if not (ve.errno == -errno.ENOENT and force):
774 ret = self.volume_exception_to_retval(ve)
81eedcae
TL
775 return ret
776
92f5a8d4
TL
777 def getpath_subvolume_group(self, **kwargs):
778 volname = kwargs['vol_name']
494da23a 779 groupname = kwargs['group_name']
92f5a8d4 780
494da23a 781 try:
92f5a8d4
TL
782 with open_volume(self, volname) as fs_handle:
783 with open_group(fs_handle, self.volspec, groupname) as group:
784 return 0, group.path.decode('utf-8'), ""
494da23a
TL
785 except VolumeException as ve:
786 return self.volume_exception_to_retval(ve)
787
92f5a8d4
TL
788 def list_subvolume_groups(self, **kwargs):
789 volname = kwargs['vol_name']
790 ret = 0, '[]', ""
f67539c2 791 volume_exists = False
eafe8130 792 try:
92f5a8d4 793 with open_volume(self, volname) as fs_handle:
f67539c2 794 volume_exists = True
1d09f67e 795 groups = listdir(fs_handle, self.volspec.base_dir, filter_entries=[Trash.GROUP_NAME.encode('utf-8')])
92f5a8d4 796 ret = 0, name_to_json(groups), ""
eafe8130 797 except VolumeException as ve:
f67539c2 798 if not ve.errno == -errno.ENOENT or not volume_exists:
92f5a8d4 799 ret = self.volume_exception_to_retval(ve)
eafe8130
TL
800 return ret
801
f6b5b4d7
TL
802 def pin_subvolume_group(self, **kwargs):
803 ret = 0, "", ""
804 volname = kwargs['vol_name']
805 groupname = kwargs['group_name']
806 pin_type = kwargs['pin_type']
807 pin_setting = kwargs['pin_setting']
808
809 try:
810 with open_volume(self, volname) as fs_handle:
811 with open_group(fs_handle, self.volspec, groupname) as group:
812 group.pin(pin_type, pin_setting)
813 ret = 0, json.dumps({}), ""
814 except VolumeException as ve:
815 ret = self.volume_exception_to_retval(ve)
816 return ret
817
81eedcae
TL
818 ### group snapshot
819
92f5a8d4 820 def create_subvolume_group_snapshot(self, **kwargs):
adb31ebb 821 ret = -errno.ENOSYS, "", "subvolume group snapshots are not supported"
494da23a
TL
822 volname = kwargs['vol_name']
823 groupname = kwargs['group_name']
adb31ebb 824 # snapname = kwargs['snap_name']
92f5a8d4 825
81eedcae 826 try:
92f5a8d4
TL
827 with open_volume(self, volname) as fs_handle:
828 with open_group(fs_handle, self.volspec, groupname) as group:
adb31ebb
TL
829 # as subvolumes are marked with the vxattr ceph.dir.subvolume deny snapshots
830 # at the subvolume group (see: https://tracker.ceph.com/issues/46074)
831 # group.create_snapshot(snapname)
832 pass
81eedcae
TL
833 except VolumeException as ve:
834 ret = self.volume_exception_to_retval(ve)
835 return ret
836
92f5a8d4 837 def remove_subvolume_group_snapshot(self, **kwargs):
494da23a
TL
838 ret = 0, "", ""
839 volname = kwargs['vol_name']
840 groupname = kwargs['group_name']
841 snapname = kwargs['snap_name']
842 force = kwargs['force']
494da23a
TL
843
844 try:
92f5a8d4
TL
845 with open_volume(self, volname) as fs_handle:
846 with open_group(fs_handle, self.volspec, groupname) as group:
847 group.remove_snapshot(snapname)
494da23a 848 except VolumeException as ve:
92f5a8d4
TL
849 if not (ve.errno == -errno.ENOENT and force):
850 ret = self.volume_exception_to_retval(ve)
494da23a
TL
851 return ret
852
92f5a8d4
TL
853 def list_subvolume_group_snapshots(self, **kwargs):
854 ret = 0, "", ""
855 volname = kwargs['vol_name']
856 groupname = kwargs['group_name']
494da23a 857
81eedcae 858 try:
92f5a8d4
TL
859 with open_volume(self, volname) as fs_handle:
860 with open_group(fs_handle, self.volspec, groupname) as group:
861 snapshots = group.list_snapshots()
862 ret = 0, name_to_json(snapshots), ""
81eedcae
TL
863 except VolumeException as ve:
864 ret = self.volume_exception_to_retval(ve)
865 return ret