Skip to content

Commit a08ddab

Browse files
committed
mgr/volumes: add earmarking for subvol
- Implemented earmarking functionality for subvolumes and subvolume groups, ensuring that they do not share the same subvolume or subvolume group to avoid data corruption due to unsupported mixed protocol use. - Added a command to set set/rm earmark from a given subvolume / subvolume group - Adding __init__.py for python-common/ceph/fs dir to have it as a package. - Fixed subvolume info command when no earmark is set. Fixes: https://tracker.ceph.com/issues/67460 Signed-off-by: Avan Thakkar <[email protected]>
1 parent fb471bd commit a08ddab

File tree

9 files changed

+272
-14
lines changed

9 files changed

+272
-14
lines changed

src/pybind/mgr/volumes/fs/operations/subvolume.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from .template import SubvolumeOpType
66
from .versions import loaded_subvolumes
77

8-
def create_subvol(mgr, fs, vol_spec, group, subvolname, size, isolate_nspace, pool, mode, uid, gid):
8+
def create_subvol(mgr, fs, vol_spec, group, subvolname, size, isolate_nspace, pool, mode, uid, gid, earmark):
99
"""
1010
create a subvolume (create a subvolume with the max known version).
1111
@@ -18,10 +18,11 @@ def create_subvol(mgr, fs, vol_spec, group, subvolname, size, isolate_nspace, po
1818
:param mode: the user permissions
1919
:param uid: the user identifier
2020
:param gid: the group identifier
21+
:param earmark: metadata string to identify if subvolume is associated with nfs/smb
2122
:return: None
2223
"""
2324
subvolume = loaded_subvolumes.get_subvolume_object_max(mgr, fs, vol_spec, group, subvolname)
24-
subvolume.create(size, isolate_nspace, pool, mode, uid, gid)
25+
subvolume.create(size, isolate_nspace, pool, mode, uid, gid, earmark)
2526

2627

2728
def create_clone(mgr, fs, vol_spec, group, subvolname, pool, source_volume, source_subvolume, snapname):

src/pybind/mgr/volumes/fs/operations/template.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ class SubvolumeOpType(Enum):
6868
SNAP_METADATA_GET = 'snap-metadata-get'
6969
SNAP_METADATA_LIST = 'snap-metadata-ls'
7070
SNAP_METADATA_REMOVE = 'snap-metadata-rm'
71+
EARMARK_GET = 'earmark-get'
72+
EARMARK_SET = 'earmark-set'
73+
EARMARK_CLEAR = 'earmark-clear'
7174

7275
class SubvolumeTemplate(object):
7376
VERSION = None # type: int

src/pybind/mgr/volumes/fs/operations/versions/subvolume_base.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from .auth_metadata import AuthMetadataManager
1919
from .subvolume_attrs import SubvolumeStates
2020

21+
from ceph.fs.earmarking import CephFSVolumeEarmarking, EarmarkException # type: ignore
22+
2123
log = logging.getLogger(__name__)
2224

2325

@@ -192,6 +194,14 @@ def get_attrs(self, pathname):
192194
except cephfs.NoData:
193195
attrs["quota"] = None
194196

197+
try:
198+
fs_earmark = CephFSVolumeEarmarking(self.fs, pathname)
199+
attrs["earmark"] = fs_earmark.get_earmark()
200+
except cephfs.NoData:
201+
attrs["earmark"] = ''
202+
except EarmarkException:
203+
attrs["earmark"] = ''
204+
195205
return attrs
196206

197207
def set_attrs(self, path, attrs):
@@ -277,6 +287,12 @@ def set_attrs(self, path, attrs):
277287
if mode is not None:
278288
self.fs.lchmod(path, mode)
279289

290+
# set earmark
291+
earmark = attrs.get("earmark")
292+
if earmark is not None:
293+
fs_earmark = CephFSVolumeEarmarking(self.fs, path)
294+
fs_earmark.set_earmark(earmark)
295+
280296
def _resize(self, path, newsize, noshrink):
281297
try:
282298
newsize = int(newsize)
@@ -418,6 +434,14 @@ def info(self):
418434
except cephfs.Error as e:
419435
raise VolumeException(-e.args[0], e.args[1])
420436

437+
try:
438+
fs_earmark = CephFSVolumeEarmarking(self.fs, subvolpath)
439+
earmark = fs_earmark.get_earmark()
440+
except cephfs.NoData:
441+
earmark = ''
442+
except EarmarkException:
443+
earmark = ''
444+
421445
return {'path': subvolpath,
422446
'type': etype.value,
423447
'uid': int(st["uid"]),
@@ -434,7 +458,9 @@ def info(self):
434458
if nsize == 0
435459
else '{0:.2f}'.format((float(usedbytes) / nsize) * 100.0),
436460
'pool_namespace': pool_namespace,
437-
'features': self.features, 'state': self.state.value}
461+
'features': self.features,
462+
'state': self.state.value,
463+
'earmark': earmark}
438464

439465
def set_user_metadata(self, keyname, value):
440466
try:

src/pybind/mgr/volumes/fs/operations/versions/subvolume_v1.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def snapshot_data_path(self, snapname):
8585
""" Path to user data directory within a subvolume snapshot named 'snapname' """
8686
return self.snapshot_path(snapname)
8787

88-
def create(self, size, isolate_nspace, pool, mode, uid, gid):
88+
def create(self, size, isolate_nspace, pool, mode, uid, gid, earmark):
8989
subvolume_type = SubvolumeTypes.TYPE_NORMAL
9090
try:
9191
initial_state = SubvolumeOpSm.get_init_state(subvolume_type)
@@ -103,7 +103,8 @@ def create(self, size, isolate_nspace, pool, mode, uid, gid):
103103
'gid': gid,
104104
'data_pool': pool,
105105
'pool_namespace': self.namespace if isolate_nspace else None,
106-
'quota': size
106+
'quota': size,
107+
'earmark': earmark
107108
}
108109
self.set_attrs(subvol_path, attrs)
109110

src/pybind/mgr/volumes/fs/operations/versions/subvolume_v2.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def _set_incarnation_metadata(self, subvolume_type, qpath, initial_state):
154154
self.metadata_mgr.update_global_section(MetadataManager.GLOBAL_META_KEY_PATH, qpath)
155155
self.metadata_mgr.update_global_section(MetadataManager.GLOBAL_META_KEY_STATE, initial_state.value)
156156

157-
def create(self, size, isolate_nspace, pool, mode, uid, gid):
157+
def create(self, size, isolate_nspace, pool, mode, uid, gid, earmark):
158158
subvolume_type = SubvolumeTypes.TYPE_NORMAL
159159
try:
160160
initial_state = SubvolumeOpSm.get_init_state(subvolume_type)
@@ -175,7 +175,8 @@ def create(self, size, isolate_nspace, pool, mode, uid, gid):
175175
'gid': gid,
176176
'data_pool': pool,
177177
'pool_namespace': self.namespace if isolate_nspace else None,
178-
'quota': size
178+
'quota': size,
179+
'earmark': earmark
179180
}
180181
self.set_attrs(subvol_path, attrs)
181182

src/pybind/mgr/volumes/fs/volume.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
import mgr_util
55
import inspect
66
import functools
7-
from typing import TYPE_CHECKING, Any, Callable, Optional
7+
from typing import TYPE_CHECKING, Any, Callable, Optional, Tuple
88
from urllib.parse import urlsplit, urlunsplit
99

1010
import cephfs
1111

12+
from ceph.fs.earmarking import CephFSVolumeEarmarking, EarmarkException # type: ignore
13+
1214
from mgr_util import CephfsClient
1315

1416
from .fs_util import listdir, has_subdir
@@ -229,11 +231,13 @@ def _create_subvolume(self, fs_handle, volname, group, subvolname, **kwargs):
229231
gid = kwargs['gid']
230232
mode = kwargs['mode']
231233
isolate_nspace = kwargs['namespace_isolated']
234+
earmark = kwargs['earmark'] or '' # if not set, default to empty string --> no earmark
232235

233236
oct_mode = octal_str_to_decimal_int(mode)
237+
234238
try:
235239
create_subvol(
236-
self.mgr, fs_handle, self.volspec, group, subvolname, size, isolate_nspace, pool, oct_mode, uid, gid)
240+
self.mgr, fs_handle, self.volspec, group, subvolname, size, isolate_nspace, pool, oct_mode, uid, gid, earmark)
237241
except VolumeException as ve:
238242
# kick the purge threads for async removal -- note that this
239243
# assumes that the subvolume is moved to trashcan for cleanup on error.
@@ -251,6 +255,7 @@ def create_subvolume(self, **kwargs):
251255
gid = kwargs['gid']
252256
mode = kwargs['mode']
253257
isolate_nspace = kwargs['namespace_isolated']
258+
earmark = kwargs['earmark'] or '' # if not set, default to empty string --> no earmark
254259

255260
try:
256261
with open_volume(self, volname) as fs_handle:
@@ -264,7 +269,8 @@ def create_subvolume(self, **kwargs):
264269
'mode': octal_str_to_decimal_int(mode),
265270
'data_pool': pool,
266271
'pool_namespace': subvolume.namespace if isolate_nspace else None,
267-
'quota': size
272+
'quota': size,
273+
'earmark': earmark
268274
}
269275
subvolume.set_attrs(subvolume.path, attrs)
270276
except VolumeException as ve:
@@ -606,6 +612,68 @@ def subvolume_exists(self, **kwargs):
606612
ret = self.volume_exception_to_retval(ve)
607613
return ret
608614

615+
def get_earmark(self, **kwargs) -> Tuple[int, Optional[str], str]:
616+
ret: Tuple[int, Optional[str], str] = 0, "", ""
617+
volname = kwargs['vol_name']
618+
subvolname = kwargs['sub_name']
619+
groupname = kwargs['group_name']
620+
621+
try:
622+
with open_volume(self, volname) as fs_handle:
623+
with open_group(fs_handle, self.volspec, groupname) as group:
624+
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.EARMARK_GET) as subvolume:
625+
log.info("Getting earmark for subvolume %s", subvolume.path)
626+
fs_earmark = CephFSVolumeEarmarking(fs_handle, subvolume.path)
627+
earmark = fs_earmark.get_earmark()
628+
ret = 0, earmark, ""
629+
except VolumeException as ve:
630+
ret = self.volume_exception_to_retval(ve)
631+
except EarmarkException as ee:
632+
log.error(f"Earmark error occurred: {ee}")
633+
ret = ee.to_tuple()
634+
return ret
635+
636+
def set_earmark(self, **kwargs): # type: ignore
637+
ret = 0, "", ""
638+
volname = kwargs['vol_name']
639+
subvolname = kwargs['sub_name']
640+
groupname = kwargs['group_name']
641+
earmark = kwargs['earmark']
642+
643+
try:
644+
with open_volume(self, volname) as fs_handle:
645+
with open_group(fs_handle, self.volspec, groupname) as group:
646+
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.EARMARK_SET) as subvolume:
647+
log.info("Setting earmark %s for subvolume %s", earmark, subvolume.path)
648+
fs_earmark = CephFSVolumeEarmarking(fs_handle, subvolume.path)
649+
fs_earmark.set_earmark(earmark)
650+
except VolumeException as ve:
651+
ret = self.volume_exception_to_retval(ve)
652+
except EarmarkException as ee:
653+
log.error(f"Earmark error occurred: {ee}")
654+
ret = ee.to_tuple() # type: ignore
655+
return ret
656+
657+
def clear_earmark(self, **kwargs): # type: ignore
658+
ret = 0, "", ""
659+
volname = kwargs['vol_name']
660+
subvolname = kwargs['sub_name']
661+
groupname = kwargs['group_name']
662+
663+
try:
664+
with open_volume(self, volname) as fs_handle:
665+
with open_group(fs_handle, self.volspec, groupname) as group:
666+
with open_subvol(self.mgr, fs_handle, self.volspec, group, subvolname, SubvolumeOpType.EARMARK_CLEAR) as subvolume:
667+
log.info("Removing earmark for subvolume %s", subvolume.path)
668+
fs_earmark = CephFSVolumeEarmarking(fs_handle, subvolume.path)
669+
fs_earmark.clear_earmark()
670+
except VolumeException as ve:
671+
ret = self.volume_exception_to_retval(ve)
672+
except EarmarkException as ee:
673+
log.error(f"Earmark error occurred: {ee}")
674+
ret = ee.to_tuple() # type: ignore
675+
return ret
676+
609677
### subvolume snapshot
610678

611679
def create_subvolume_snapshot(self, **kwargs):

src/pybind/mgr/volumes/module.py

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
141141
'name=uid,type=CephInt,req=false '
142142
'name=gid,type=CephInt,req=false '
143143
'name=mode,type=CephString,req=false '
144-
'name=namespace_isolated,type=CephBool,req=false ',
144+
'name=namespace_isolated,type=CephBool,req=false '
145+
'name=earmark,type=CephString,req=false ',
145146
'desc': "Create a CephFS subvolume in a volume, and optionally, "
146147
"with a specific size (in bytes), a specific data pool layout, "
147148
"a specific mode, in a specific subvolume group and in separate "
@@ -272,6 +273,31 @@ class Module(orchestrator.OrchestratorClientMixin, MgrModule):
272273
"and optionally, in a specific subvolume group",
273274
'perm': 'rw'
274275
},
276+
{
277+
'cmd': 'fs subvolume earmark get '
278+
'name=vol_name,type=CephString '
279+
'name=sub_name,type=CephString '
280+
'name=group_name,type=CephString,req=false ',
281+
'desc': "Get earmark for a subvolume",
282+
'perm': 'r'
283+
},
284+
{
285+
'cmd': 'fs subvolume earmark set '
286+
'name=vol_name,type=CephString '
287+
'name=sub_name,type=CephString '
288+
'name=group_name,type=CephString,req=false '
289+
'name=earmark,type=CephString ',
290+
'desc': "Set earmark for a subvolume",
291+
'perm': 'rw'
292+
},
293+
{
294+
'cmd': 'fs subvolume earmark rm '
295+
'name=vol_name,type=CephString '
296+
'name=sub_name,type=CephString '
297+
'name=group_name,type=CephString,req=false ',
298+
'desc': "Remove earmark from a subvolume",
299+
'perm': 'rw'
300+
},
275301
{
276302
'cmd': 'fs quiesce '
277303
'name=vol_name,type=CephString '
@@ -631,6 +657,7 @@ def _cmd_fs_subvolumegroup_resize(self, inbuf, cmd):
631657
group_name=cmd['group_name'],
632658
new_size=cmd['new_size'],
633659
no_shrink=cmd.get('no_shrink', False))
660+
634661
@mgr_cmd_wrap
635662
def _cmd_fs_subvolumegroup_ls(self, inbuf, cmd):
636663
return self.vc.list_subvolume_groups(vol_name=cmd['vol_name'])
@@ -652,7 +679,8 @@ def _cmd_fs_subvolume_create(self, inbuf, cmd):
652679
uid=cmd.get('uid', None),
653680
gid=cmd.get('gid', None),
654681
mode=cmd.get('mode', '755'),
655-
namespace_isolated=cmd.get('namespace_isolated', False))
682+
namespace_isolated=cmd.get('namespace_isolated', False),
683+
earmark=cmd.get('earmark', None))
656684

657685
@mgr_cmd_wrap
658686
def _cmd_fs_subvolume_rm(self, inbuf, cmd):
@@ -733,7 +761,7 @@ def _cmd_fs_subvolume_info(self, inbuf, cmd):
733761
def _cmd_fs_subvolume_exist(self, inbuf, cmd):
734762
return self.vc.subvolume_exists(vol_name=cmd['vol_name'],
735763
group_name=cmd.get('group_name', None))
736-
764+
737765
@mgr_cmd_wrap
738766
def _cmd_fs_subvolume_metadata_set(self, inbuf, cmd):
739767
return self.vc.set_user_metadata(vol_name=cmd['vol_name'],
@@ -762,7 +790,26 @@ def _cmd_fs_subvolume_metadata_rm(self, inbuf, cmd):
762790
key_name=cmd['key_name'],
763791
group_name=cmd.get('group_name', None),
764792
force=cmd.get('force', False))
765-
793+
794+
@mgr_cmd_wrap
795+
def _cmd_fs_subvolume_earmark_get(self, inbuf, cmd):
796+
return self.vc.get_earmark(vol_name=cmd['vol_name'],
797+
sub_name=cmd['sub_name'],
798+
group_name=cmd.get('group_name', None))
799+
800+
@mgr_cmd_wrap
801+
def _cmd_fs_subvolume_earmark_set(self, inbuf, cmd):
802+
return self.vc.set_earmark(vol_name=cmd['vol_name'],
803+
sub_name=cmd['sub_name'],
804+
group_name=cmd.get('group_name', None),
805+
earmark=cmd['earmark'])
806+
807+
@mgr_cmd_wrap
808+
def _cmd_fs_subvolume_earmark_rm(self, inbuf, cmd):
809+
return self.vc.clear_earmark(vol_name=cmd['vol_name'],
810+
sub_name=cmd['sub_name'],
811+
group_name=cmd.get('group_name', None))
812+
766813
@mgr_cmd_wrap
767814
def _cmd_fs_quiesce(self, inbuf, cmd):
768815
return self.vc.quiesce(cmd)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import logging
2+
3+
log = logging.getLogger(__name__)

0 commit comments

Comments
 (0)