4 from typing
import TYPE_CHECKING
8 from mgr_util
import CephfsClient
10 from .fs_util
import listdir
12 from .operations
.volume
import create_volume
, \
13 delete_volume
, list_volumes
, open_volume
, get_pool_names
14 from .operations
.group
import open_group
, create_group
, remove_group
, open_group_unique
15 from .operations
.subvolume
import open_subvol
, create_subvol
, remove_subvol
, \
18 from .vol_spec
import VolSpec
19 from .exception
import VolumeException
, ClusterError
, ClusterTimeout
, EvictionError
20 from .async_cloner
import Cloner
21 from .purge_queue
import ThreadPoolPurgeQueueMixin
22 from .operations
.template
import SubvolumeOpType
25 from volumes
import Module
27 log
= logging
.getLogger(__name__
)
29 ALLOWED_ACCESS_LEVELS
= ('r', 'rw')
32 def octal_str_to_decimal_int(mode
):
36 raise VolumeException(-errno
.EINVAL
, "Invalid mode '{0}'".format(mode
))
39 def name_to_json(names
):
41 convert the list of names to json
44 for i
in range(len(names
)):
45 namedict
.append({'name': names
[i
].decode('utf-8')})
46 return json
.dumps(namedict
, indent
=4, sort_keys
=True)
49 class VolumeClient(CephfsClient
["Module"]):
50 def __init__(self
, mgr
):
52 # volume specification
53 self
.volspec
= VolSpec(mgr
.rados
.conf_get('client_snapdir'))
54 self
.cloner
= Cloner(self
, self
.mgr
.max_concurrent_clones
)
55 self
.purge_queue
= ThreadPoolPurgeQueueMixin(self
, 4)
56 # on startup, queue purge job for available volumes to kickstart
57 # purge for leftover subvolume entries in trash. note that, if the
58 # trash directory does not exist or if there are no purge entries
59 # available for a volume, the volume is removed from the purge
61 fs_map
= self
.mgr
.get('fs_map')
62 for fs
in fs_map
['filesystems']:
63 self
.cloner
.queue_job(fs
['mdsmap']['fs_name'])
64 self
.purge_queue
.queue_job(fs
['mdsmap']['fs_name'])
67 # Overrides CephfsClient.shutdown()
68 log
.info("shutting down")
69 # first, note that we're shutting down
72 self
.cloner
.shutdown()
74 self
.purge_queue
.shutdown()
75 # last, delete all libcephfs handles from connection pool
76 self
.connection_pool
.del_all_handles()
78 def cluster_log(self
, msg
, lvl
=None):
80 log to cluster log with default log level as WARN.
83 lvl
= self
.mgr
.ClusterLogPrio
.WARN
84 self
.mgr
.cluster_log("cluster", lvl
, msg
)
86 def volume_exception_to_retval(self
, ve
):
88 return a tuple representation from a volume exception
92 ### volume operations -- create, rm, ls
94 def create_fs_volume(self
, volname
, placement
):
95 if self
.is_stopping():
96 return -errno
.ESHUTDOWN
, "", "shutdown in progress"
97 return create_volume(self
.mgr
, volname
, placement
)
99 def delete_fs_volume(self
, volname
, confirm
):
100 if self
.is_stopping():
101 return -errno
.ESHUTDOWN
, "", "shutdown in progress"
103 if confirm
!= "--yes-i-really-mean-it":
104 return -errno
.EPERM
, "", "WARNING: this will *PERMANENTLY DESTROY* all data " \
105 "stored in the filesystem '{0}'. If you are *ABSOLUTELY CERTAIN* " \
106 "that is what you want, re-issue the command followed by " \
107 "--yes-i-really-mean-it.".format(volname
)
109 ret
, out
, err
= self
.mgr
.check_mon_command({
110 'prefix': 'config get',
111 'key': 'mon_allow_pool_delete',
115 mon_allow_pool_delete
= json
.loads(out
)
116 if not mon_allow_pool_delete
:
117 return -errno
.EPERM
, "", "pool deletion is disabled; you must first " \
118 "set the mon_allow_pool_delete config option to true before volumes " \
121 metadata_pool
, data_pools
= get_pool_names(self
.mgr
, volname
)
122 if not metadata_pool
:
123 return -errno
.ENOENT
, "", "volume {0} doesn't exist".format(volname
)
124 self
.purge_queue
.cancel_jobs(volname
)
125 self
.connection_pool
.del_fs_handle(volname
, wait
=True)
126 return delete_volume(self
.mgr
, volname
, metadata_pool
, data_pools
)
128 def list_fs_volumes(self
):
129 if self
.stopping
.is_set():
130 return -errno
.ESHUTDOWN
, "", "shutdown in progress"
131 volumes
= list_volumes(self
.mgr
)
132 return 0, json
.dumps(volumes
, indent
=4, sort_keys
=True), ""
134 ### subvolume operations
136 def _create_subvolume(self
, fs_handle
, volname
, group
, subvolname
, **kwargs
):
137 size
= kwargs
['size']
138 pool
= kwargs
['pool_layout']
141 mode
= kwargs
['mode']
142 isolate_nspace
= kwargs
['namespace_isolated']
144 oct_mode
= octal_str_to_decimal_int(mode
)
147 self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, size
, isolate_nspace
, pool
, oct_mode
, uid
, gid
)
148 except VolumeException
as ve
:
149 # kick the purge threads for async removal -- note that this
150 # assumes that the subvolume is moved to trashcan for cleanup on error.
151 self
.purge_queue
.queue_job(volname
)
154 def create_subvolume(self
, **kwargs
):
156 volname
= kwargs
['vol_name']
157 subvolname
= kwargs
['sub_name']
158 groupname
= kwargs
['group_name']
159 size
= kwargs
['size']
160 pool
= kwargs
['pool_layout']
163 isolate_nspace
= kwargs
['namespace_isolated']
166 with
open_volume(self
, volname
) as fs_handle
:
167 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
169 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.CREATE
) as subvolume
:
170 # idempotent creation -- valid. Attributes set is supported.
172 'uid': uid
if uid
else subvolume
.uid
,
173 'gid': gid
if gid
else subvolume
.gid
,
175 'pool_namespace': subvolume
.namespace
if isolate_nspace
else None,
178 subvolume
.set_attrs(subvolume
.path
, attrs
)
179 except VolumeException
as ve
:
180 if ve
.errno
== -errno
.ENOENT
:
181 self
._create
_subvolume
(fs_handle
, volname
, group
, subvolname
, **kwargs
)
184 except VolumeException
as ve
:
185 # volume/group does not exist or subvolume creation failed
186 ret
= self
.volume_exception_to_retval(ve
)
189 def remove_subvolume(self
, **kwargs
):
191 volname
= kwargs
['vol_name']
192 subvolname
= kwargs
['sub_name']
193 groupname
= kwargs
['group_name']
194 force
= kwargs
['force']
195 retainsnaps
= kwargs
['retain_snapshots']
198 with
open_volume(self
, volname
) as fs_handle
:
199 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
200 remove_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, force
, retainsnaps
)
201 # kick the purge threads for async removal -- note that this
202 # assumes that the subvolume is moved to trash can.
203 # TODO: make purge queue as singleton so that trash can kicks
204 # the purge threads on dump.
205 self
.purge_queue
.queue_job(volname
)
206 except VolumeException
as ve
:
207 if ve
.errno
== -errno
.EAGAIN
:
208 ve
= VolumeException(ve
.errno
, ve
.error_str
+ " (use --force to override)")
209 ret
= self
.volume_exception_to_retval(ve
)
210 elif not (ve
.errno
== -errno
.ENOENT
and force
):
211 ret
= self
.volume_exception_to_retval(ve
)
214 def authorize_subvolume(self
, **kwargs
):
216 volname
= kwargs
['vol_name']
217 subvolname
= kwargs
['sub_name']
218 authid
= kwargs
['auth_id']
219 groupname
= kwargs
['group_name']
220 accesslevel
= kwargs
['access_level']
221 tenant_id
= kwargs
['tenant_id']
222 allow_existing_id
= kwargs
['allow_existing_id']
225 with
open_volume(self
, volname
) as fs_handle
:
226 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
227 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.ALLOW_ACCESS
) as subvolume
:
228 key
= subvolume
.authorize(authid
, accesslevel
, tenant_id
, allow_existing_id
)
230 except VolumeException
as ve
:
231 ret
= self
.volume_exception_to_retval(ve
)
234 def deauthorize_subvolume(self
, **kwargs
):
236 volname
= kwargs
['vol_name']
237 subvolname
= kwargs
['sub_name']
238 authid
= kwargs
['auth_id']
239 groupname
= kwargs
['group_name']
242 with
open_volume(self
, volname
) as fs_handle
:
243 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
244 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.DENY_ACCESS
) as subvolume
:
245 subvolume
.deauthorize(authid
)
246 except VolumeException
as ve
:
247 ret
= self
.volume_exception_to_retval(ve
)
250 def authorized_list(self
, **kwargs
):
252 volname
= kwargs
['vol_name']
253 subvolname
= kwargs
['sub_name']
254 groupname
= kwargs
['group_name']
257 with
open_volume(self
, volname
) as fs_handle
:
258 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
259 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.AUTH_LIST
) as subvolume
:
260 auths
= subvolume
.authorized_list()
261 ret
= 0, json
.dumps(auths
, indent
=4, sort_keys
=True), ""
262 except VolumeException
as ve
:
263 ret
= self
.volume_exception_to_retval(ve
)
266 def evict(self
, **kwargs
):
268 volname
= kwargs
['vol_name']
269 subvolname
= kwargs
['sub_name']
270 authid
= kwargs
['auth_id']
271 groupname
= kwargs
['group_name']
274 with
open_volume(self
, volname
) as fs_handle
:
275 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
276 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.EVICT
) as subvolume
:
277 key
= subvolume
.evict(volname
, authid
)
279 except (VolumeException
, ClusterTimeout
, ClusterError
, EvictionError
) as e
:
280 if isinstance(e
, VolumeException
):
281 ret
= self
.volume_exception_to_retval(e
)
282 elif isinstance(e
, ClusterTimeout
):
283 ret
= -errno
.ETIMEDOUT
, "", "Timedout trying to talk to ceph cluster"
284 elif isinstance(e
, ClusterError
):
285 ret
= e
._result
_code
, "", e
._result
_str
286 elif isinstance(e
, EvictionError
):
287 ret
= -errno
.EINVAL
, "", str(e
)
290 def resize_subvolume(self
, **kwargs
):
292 volname
= kwargs
['vol_name']
293 subvolname
= kwargs
['sub_name']
294 newsize
= kwargs
['new_size']
295 noshrink
= kwargs
['no_shrink']
296 groupname
= kwargs
['group_name']
299 with
open_volume(self
, volname
) as fs_handle
:
300 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
301 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.RESIZE
) as subvolume
:
302 nsize
, usedbytes
= subvolume
.resize(newsize
, noshrink
)
304 [{'bytes_used': usedbytes
},{'bytes_quota': nsize
},
305 {'bytes_pcent': "undefined" if nsize
== 0 else '{0:.2f}'.format((float(usedbytes
) / nsize
) * 100.0)}],
306 indent
=4, sort_keys
=True), ""
307 except VolumeException
as ve
:
308 ret
= self
.volume_exception_to_retval(ve
)
311 def subvolume_pin(self
, **kwargs
):
313 volname
= kwargs
['vol_name']
314 subvolname
= kwargs
['sub_name']
315 pin_type
= kwargs
['pin_type']
316 pin_setting
= kwargs
['pin_setting']
317 groupname
= kwargs
['group_name']
320 with
open_volume(self
, volname
) as fs_handle
:
321 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
322 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.PIN
) as subvolume
:
323 subvolume
.pin(pin_type
, pin_setting
)
324 ret
= 0, json
.dumps({}), ""
325 except VolumeException
as ve
:
326 ret
= self
.volume_exception_to_retval(ve
)
329 def subvolume_getpath(self
, **kwargs
):
331 volname
= kwargs
['vol_name']
332 subvolname
= kwargs
['sub_name']
333 groupname
= kwargs
['group_name']
336 with
open_volume(self
, volname
) as fs_handle
:
337 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
338 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.GETPATH
) as subvolume
:
339 subvolpath
= subvolume
.path
340 ret
= 0, subvolpath
.decode("utf-8"), ""
341 except VolumeException
as ve
:
342 ret
= self
.volume_exception_to_retval(ve
)
345 def subvolume_info(self
, **kwargs
):
347 volname
= kwargs
['vol_name']
348 subvolname
= kwargs
['sub_name']
349 groupname
= kwargs
['group_name']
352 with
open_volume(self
, volname
) as fs_handle
:
353 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
354 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.INFO
) as subvolume
:
356 mon_map_mons
= self
.mgr
.get('mon_map')['mons']
357 for mon
in mon_map_mons
:
358 ip_port
= mon
['addr'].split("/")[0]
359 mon_addr_lst
.append(ip_port
)
361 subvol_info_dict
= subvolume
.info()
362 subvol_info_dict
["mon_addrs"] = mon_addr_lst
363 ret
= 0, json
.dumps(subvol_info_dict
, indent
=4, sort_keys
=True), ""
364 except VolumeException
as ve
:
365 ret
= self
.volume_exception_to_retval(ve
)
368 def list_subvolumes(self
, **kwargs
):
370 volname
= kwargs
['vol_name']
371 groupname
= kwargs
['group_name']
374 with
open_volume(self
, volname
) as fs_handle
:
375 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
376 subvolumes
= group
.list_subvolumes()
377 ret
= 0, name_to_json(subvolumes
), ""
378 except VolumeException
as ve
:
379 ret
= self
.volume_exception_to_retval(ve
)
382 ### subvolume snapshot
384 def create_subvolume_snapshot(self
, **kwargs
):
386 volname
= kwargs
['vol_name']
387 subvolname
= kwargs
['sub_name']
388 snapname
= kwargs
['snap_name']
389 groupname
= kwargs
['group_name']
392 with
open_volume(self
, volname
) as fs_handle
:
393 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
394 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_CREATE
) as subvolume
:
395 subvolume
.create_snapshot(snapname
)
396 except VolumeException
as ve
:
397 ret
= self
.volume_exception_to_retval(ve
)
400 def remove_subvolume_snapshot(self
, **kwargs
):
402 volname
= kwargs
['vol_name']
403 subvolname
= kwargs
['sub_name']
404 snapname
= kwargs
['snap_name']
405 groupname
= kwargs
['group_name']
406 force
= kwargs
['force']
409 with
open_volume(self
, volname
) as fs_handle
:
410 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
411 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_REMOVE
) as subvolume
:
412 subvolume
.remove_snapshot(snapname
)
413 except VolumeException
as ve
:
414 # ESTALE serves as an error to state that subvolume is currently stale due to internal removal and,
415 # we should tickle the purge jobs to purge the same
416 if ve
.errno
== -errno
.ESTALE
:
417 self
.purge_queue
.queue_job(volname
)
418 elif not (ve
.errno
== -errno
.ENOENT
and force
):
419 ret
= self
.volume_exception_to_retval(ve
)
422 def subvolume_snapshot_info(self
, **kwargs
):
424 volname
= kwargs
['vol_name']
425 subvolname
= kwargs
['sub_name']
426 snapname
= kwargs
['snap_name']
427 groupname
= kwargs
['group_name']
430 with
open_volume(self
, volname
) as fs_handle
:
431 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
432 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_INFO
) as subvolume
:
433 snap_info_dict
= subvolume
.snapshot_info(snapname
)
434 ret
= 0, json
.dumps(snap_info_dict
, indent
=4, sort_keys
=True), ""
435 except VolumeException
as ve
:
436 ret
= self
.volume_exception_to_retval(ve
)
439 def list_subvolume_snapshots(self
, **kwargs
):
441 volname
= kwargs
['vol_name']
442 subvolname
= kwargs
['sub_name']
443 groupname
= kwargs
['group_name']
446 with
open_volume(self
, volname
) as fs_handle
:
447 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
448 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_LIST
) as subvolume
:
449 snapshots
= subvolume
.list_snapshots()
450 ret
= 0, name_to_json(snapshots
), ""
451 except VolumeException
as ve
:
452 ret
= self
.volume_exception_to_retval(ve
)
455 def protect_subvolume_snapshot(self
, **kwargs
):
456 ret
= 0, "", "Deprecation warning: 'snapshot protect' call is deprecated and will be removed in a future release"
457 volname
= kwargs
['vol_name']
458 subvolname
= kwargs
['sub_name']
459 groupname
= kwargs
['group_name']
462 with
open_volume(self
, volname
) as fs_handle
:
463 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
464 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_PROTECT
) as subvolume
:
465 log
.warning("snapshot protect call is deprecated and will be removed in a future release")
466 except VolumeException
as ve
:
467 ret
= self
.volume_exception_to_retval(ve
)
470 def unprotect_subvolume_snapshot(self
, **kwargs
):
471 ret
= 0, "", "Deprecation warning: 'snapshot unprotect' call is deprecated and will be removed in a future release"
472 volname
= kwargs
['vol_name']
473 subvolname
= kwargs
['sub_name']
474 groupname
= kwargs
['group_name']
477 with
open_volume(self
, volname
) as fs_handle
:
478 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
479 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, subvolname
, SubvolumeOpType
.SNAP_UNPROTECT
) as subvolume
:
480 log
.warning("snapshot unprotect call is deprecated and will be removed in a future release")
481 except VolumeException
as ve
:
482 ret
= self
.volume_exception_to_retval(ve
)
485 def _prepare_clone_subvolume(self
, fs_handle
, volname
, s_subvolume
, s_snapname
, t_group
, t_subvolname
, **kwargs
):
486 t_pool
= kwargs
['pool_layout']
487 s_subvolname
= kwargs
['sub_name']
488 s_groupname
= kwargs
['group_name']
489 t_groupname
= kwargs
['target_group_name']
491 create_clone(self
.mgr
, fs_handle
, self
.volspec
, t_group
, t_subvolname
, t_pool
, volname
, s_subvolume
, s_snapname
)
492 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, t_group
, t_subvolname
, SubvolumeOpType
.CLONE_INTERNAL
) as t_subvolume
:
494 if t_groupname
== s_groupname
and t_subvolname
== s_subvolname
:
495 t_subvolume
.attach_snapshot(s_snapname
, t_subvolume
)
497 s_subvolume
.attach_snapshot(s_snapname
, t_subvolume
)
498 self
.cloner
.queue_job(volname
)
499 except VolumeException
as ve
:
502 self
.purge_queue
.queue_job(volname
)
503 except Exception as e
:
504 log
.warning("failed to cleanup clone subvolume '{0}' ({1})".format(t_subvolname
, e
))
507 def _clone_subvolume_snapshot(self
, fs_handle
, volname
, s_group
, s_subvolume
, **kwargs
):
508 s_snapname
= kwargs
['snap_name']
509 target_subvolname
= kwargs
['target_sub_name']
510 target_groupname
= kwargs
['target_group_name']
511 s_groupname
= kwargs
['group_name']
513 if not s_snapname
.encode('utf-8') in s_subvolume
.list_snapshots():
514 raise VolumeException(-errno
.ENOENT
, "snapshot '{0}' does not exist".format(s_snapname
))
516 with
open_group_unique(fs_handle
, self
.volspec
, target_groupname
, s_group
, s_groupname
) as target_group
:
518 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, target_group
, target_subvolname
, SubvolumeOpType
.CLONE_CREATE
):
519 raise VolumeException(-errno
.EEXIST
, "subvolume '{0}' exists".format(target_subvolname
))
520 except VolumeException
as ve
:
521 if ve
.errno
== -errno
.ENOENT
:
522 self
._prepare
_clone
_subvolume
(fs_handle
, volname
, s_subvolume
, s_snapname
,
523 target_group
, target_subvolname
, **kwargs
)
527 def clone_subvolume_snapshot(self
, **kwargs
):
529 volname
= kwargs
['vol_name']
530 s_subvolname
= kwargs
['sub_name']
531 s_groupname
= kwargs
['group_name']
534 with
open_volume(self
, volname
) as fs_handle
:
535 with
open_group(fs_handle
, self
.volspec
, s_groupname
) as s_group
:
536 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, s_group
, s_subvolname
, SubvolumeOpType
.CLONE_SOURCE
) as s_subvolume
:
537 self
._clone
_subvolume
_snapshot
(fs_handle
, volname
, s_group
, s_subvolume
, **kwargs
)
538 except VolumeException
as ve
:
539 ret
= self
.volume_exception_to_retval(ve
)
542 def clone_status(self
, **kwargs
):
544 volname
= kwargs
['vol_name']
545 clonename
= kwargs
['clone_name']
546 groupname
= kwargs
['group_name']
549 with
open_volume(self
, volname
) as fs_handle
:
550 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
551 with
open_subvol(self
.mgr
, fs_handle
, self
.volspec
, group
, clonename
, SubvolumeOpType
.CLONE_STATUS
) as subvolume
:
552 ret
= 0, json
.dumps({'status' : subvolume
.status
}, indent
=2), ""
553 except VolumeException
as ve
:
554 ret
= self
.volume_exception_to_retval(ve
)
557 def clone_cancel(self
, **kwargs
):
559 volname
= kwargs
['vol_name']
560 clonename
= kwargs
['clone_name']
561 groupname
= kwargs
['group_name']
564 self
.cloner
.cancel_job(volname
, (clonename
, groupname
))
565 except VolumeException
as ve
:
566 ret
= self
.volume_exception_to_retval(ve
)
571 def create_subvolume_group(self
, **kwargs
):
573 volname
= kwargs
['vol_name']
574 groupname
= kwargs
['group_name']
575 pool
= kwargs
['pool_layout']
578 mode
= kwargs
['mode']
581 with
open_volume(self
, volname
) as fs_handle
:
583 with
open_group(fs_handle
, self
.volspec
, groupname
):
584 # idempotent creation -- valid.
586 except VolumeException
as ve
:
587 if ve
.errno
== -errno
.ENOENT
:
588 oct_mode
= octal_str_to_decimal_int(mode
)
589 create_group(fs_handle
, self
.volspec
, groupname
, pool
, oct_mode
, uid
, gid
)
592 except VolumeException
as ve
:
593 # volume does not exist or subvolume group creation failed
594 ret
= self
.volume_exception_to_retval(ve
)
597 def remove_subvolume_group(self
, **kwargs
):
599 volname
= kwargs
['vol_name']
600 groupname
= kwargs
['group_name']
601 force
= kwargs
['force']
604 with
open_volume(self
, volname
) as fs_handle
:
605 remove_group(fs_handle
, self
.volspec
, groupname
)
606 except VolumeException
as ve
:
607 if not (ve
.errno
== -errno
.ENOENT
and force
):
608 ret
= self
.volume_exception_to_retval(ve
)
611 def getpath_subvolume_group(self
, **kwargs
):
612 volname
= kwargs
['vol_name']
613 groupname
= kwargs
['group_name']
616 with
open_volume(self
, volname
) as fs_handle
:
617 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
618 return 0, group
.path
.decode('utf-8'), ""
619 except VolumeException
as ve
:
620 return self
.volume_exception_to_retval(ve
)
622 def list_subvolume_groups(self
, **kwargs
):
623 volname
= kwargs
['vol_name']
625 volume_exists
= False
627 with
open_volume(self
, volname
) as fs_handle
:
629 groups
= listdir(fs_handle
, self
.volspec
.base_dir
)
630 ret
= 0, name_to_json(groups
), ""
631 except VolumeException
as ve
:
632 if not ve
.errno
== -errno
.ENOENT
or not volume_exists
:
633 ret
= self
.volume_exception_to_retval(ve
)
636 def pin_subvolume_group(self
, **kwargs
):
638 volname
= kwargs
['vol_name']
639 groupname
= kwargs
['group_name']
640 pin_type
= kwargs
['pin_type']
641 pin_setting
= kwargs
['pin_setting']
644 with
open_volume(self
, volname
) as fs_handle
:
645 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
646 group
.pin(pin_type
, pin_setting
)
647 ret
= 0, json
.dumps({}), ""
648 except VolumeException
as ve
:
649 ret
= self
.volume_exception_to_retval(ve
)
654 def create_subvolume_group_snapshot(self
, **kwargs
):
655 ret
= -errno
.ENOSYS
, "", "subvolume group snapshots are not supported"
656 volname
= kwargs
['vol_name']
657 groupname
= kwargs
['group_name']
658 # snapname = kwargs['snap_name']
661 with
open_volume(self
, volname
) as fs_handle
:
662 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
663 # as subvolumes are marked with the vxattr ceph.dir.subvolume deny snapshots
664 # at the subvolume group (see: https://tracker.ceph.com/issues/46074)
665 # group.create_snapshot(snapname)
667 except VolumeException
as ve
:
668 ret
= self
.volume_exception_to_retval(ve
)
671 def remove_subvolume_group_snapshot(self
, **kwargs
):
673 volname
= kwargs
['vol_name']
674 groupname
= kwargs
['group_name']
675 snapname
= kwargs
['snap_name']
676 force
= kwargs
['force']
679 with
open_volume(self
, volname
) as fs_handle
:
680 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
681 group
.remove_snapshot(snapname
)
682 except VolumeException
as ve
:
683 if not (ve
.errno
== -errno
.ENOENT
and force
):
684 ret
= self
.volume_exception_to_retval(ve
)
687 def list_subvolume_group_snapshots(self
, **kwargs
):
689 volname
= kwargs
['vol_name']
690 groupname
= kwargs
['group_name']
693 with
open_volume(self
, volname
) as fs_handle
:
694 with
open_group(fs_handle
, self
.volspec
, groupname
) as group
:
695 snapshots
= group
.list_snapshots()
696 ret
= 0, name_to_json(snapshots
), ""
697 except VolumeException
as ve
:
698 ret
= self
.volume_exception_to_retval(ve
)