1313 Set ,
1414 cast )
1515from os .path import normpath
16+ from ceph .fs .earmarking import EarmarkTopScope
1617import cephfs
1718
19+ from mgr_util import CephFSEarmarkResolver
1820from rados import TimedOut , ObjectNotFound , Rados
1921
2022from object_format import ErrorResponse
@@ -535,7 +537,8 @@ def get_export_by_pseudo(
535537
536538 # This method is used by the dashboard module (../dashboard/controllers/nfs.py)
537539 # Do not change interface without updating the Dashboard code
538- def apply_export (self , cluster_id : str , export_config : str ) -> AppliedExportResults :
540+ def apply_export (self , cluster_id : str , export_config : str ,
541+ earmark_resolver : Optional [CephFSEarmarkResolver ] = None ) -> AppliedExportResults :
539542 try :
540543 exports = self ._read_export_config (cluster_id , export_config )
541544 except Exception as e :
@@ -544,7 +547,7 @@ def apply_export(self, cluster_id: str, export_config: str) -> AppliedExportResu
544547
545548 aeresults = AppliedExportResults ()
546549 for export in exports :
547- changed_export = self ._change_export (cluster_id , export )
550+ changed_export = self ._change_export (cluster_id , export , earmark_resolver )
548551 # This will help figure out which export blocks in conf/json file
549552 # are problematic.
550553 if changed_export .get ("state" , "" ) == "error" :
@@ -573,9 +576,10 @@ def _read_export_config(self, cluster_id: str, export_config: str) -> List[Dict]
573576 return j # j is already a list object
574577 return [j ] # return a single object list, with j as the only item
575578
576- def _change_export (self , cluster_id : str , export : Dict ) -> Dict [str , Any ]:
579+ def _change_export (self , cluster_id : str , export : Dict ,
580+ earmark_resolver : Optional [CephFSEarmarkResolver ] = None ) -> Dict [str , Any ]:
577581 try :
578- return self ._apply_export (cluster_id , export )
582+ return self ._apply_export (cluster_id , export , earmark_resolver )
579583 except NotImplementedError :
580584 # in theory, the NotImplementedError here may be raised by a hook back to
581585 # an orchestration module. If the orchestration module supports it the NFS
@@ -651,10 +655,34 @@ def _create_user_key(self, cluster_id: str, entity: str, path: str, fs_name: str
651655 log .info (f"Export user created is { json_res [0 ]['entity' ]} " )
652656 return json_res [0 ]['key' ]
653657
658+ def _check_earmark (self , earmark_resolver : CephFSEarmarkResolver , path : str ,
659+ fs_name : str ) -> None :
660+ earmark = earmark_resolver .get_earmark (
661+ path ,
662+ fs_name ,
663+ )
664+ if not earmark :
665+ earmark_resolver .set_earmark (
666+ path ,
667+ fs_name ,
668+ EarmarkTopScope .NFS .value ,
669+ )
670+ else :
671+ if not earmark_resolver .check_earmark (
672+ earmark , EarmarkTopScope .NFS
673+ ):
674+ raise NFSException (
675+ 'earmark has already been set by ' + earmark .split ('.' )[0 ],
676+ - errno .EAGAIN
677+ )
678+ return None
679+
654680 def create_export_from_dict (self ,
655681 cluster_id : str ,
656682 ex_id : int ,
657- ex_dict : Dict [str , Any ]) -> Export :
683+ ex_dict : Dict [str , Any ],
684+ earmark_resolver : Optional [CephFSEarmarkResolver ] = None
685+ ) -> Export :
658686 pseudo_path = ex_dict .get ("pseudo" )
659687 if not pseudo_path :
660688 raise NFSInvalidOperation ("export must specify pseudo path" )
@@ -677,6 +705,11 @@ def create_export_from_dict(self,
677705 raise FSNotFound (fs_name )
678706
679707 validate_cephfs_path (self .mgr , fs_name , path )
708+
709+ # Check if earmark is set for the path, given path is of subvolume
710+ if earmark_resolver :
711+ self ._check_earmark (earmark_resolver , path , fs_name )
712+
680713 if fsal ["cmount_path" ] != "/" :
681714 _validate_cmount_path (fsal ["cmount_path" ], path ) # type: ignore
682715
@@ -707,7 +740,9 @@ def create_cephfs_export(self,
707740 access_type : str ,
708741 clients : list = [],
709742 sectype : Optional [List [str ]] = None ,
710- cmount_path : Optional [str ] = "/" ) -> Dict [str , Any ]:
743+ cmount_path : Optional [str ] = "/" ,
744+ earmark_resolver : Optional [CephFSEarmarkResolver ] = None
745+ ) -> Dict [str , Any ]:
711746
712747 validate_cephfs_path (self .mgr , fs_name , path )
713748 if cmount_path != "/" :
@@ -731,7 +766,8 @@ def create_cephfs_export(self,
731766 },
732767 "clients" : clients ,
733768 "sectype" : sectype ,
734- }
769+ },
770+ earmark_resolver
735771 )
736772 log .debug ("creating cephfs export %s" , export )
737773 self ._ensure_cephfs_export_user (export )
@@ -795,6 +831,7 @@ def _apply_export(
795831 self ,
796832 cluster_id : str ,
797833 new_export_dict : Dict ,
834+ earmark_resolver : Optional [CephFSEarmarkResolver ] = None
798835 ) -> Dict [str , str ]:
799836 for k in ['path' , 'pseudo' ]:
800837 if k not in new_export_dict :
@@ -834,7 +871,8 @@ def _apply_export(
834871 new_export = self .create_export_from_dict (
835872 cluster_id ,
836873 new_export_dict .get ('export_id' , self ._gen_export_id (cluster_id )),
837- new_export_dict
874+ new_export_dict ,
875+ earmark_resolver
838876 )
839877
840878 if not old_export :
0 commit comments