2222import stat
2323import time
2424import logging
25+ import uuid
2526
2627import shutil
2728from pathlib import Path
3536
3637log = logging .getLogger ()
3738
38- def mounts_by_local (mounts ) :
39+ def mounts_by_local (mounts : list [ NSMount ]) -> dict [ str , NSMount ] :
3940 """convert list of mounts to dict of mounts, local_mount as key"""
40- return {str (Path ( m .local_mount ) .resolve ()): m for m in mounts }
41+ return {str (m .local_mount .resolve ()): m for m in mounts }
4142
4243
43- def _get_default_mounts (lkp : util .Lookup ) -> List [ NSDict ]:
44+ def _get_default_mounts (lkp : util .Lookup ) -> list [ NSMount ]:
4445 if lkp .cfg .disable_default_mounts :
4546 return []
4647 return [
47- NSDict (
48- server_ip = "$controller" ,
49- remote_mount = path ,
50- local_mount = path ,
51- fs_type = "nfs" ,
52- mount_options = "defaults,hard,intr" ,
48+ NSMount (
49+ server_ip = lkp . controller_mount_server_ip () ,
50+ remote_mount = path ,
51+ local_mount = path ,
52+ fs_type = "nfs" ,
53+ mount_options = "defaults,hard,intr" ,
5354 )
5455 for path in (
5556 dirs .home ,
5657 dirs .apps ,
5758 )
5859 ]
5960
60- def resolve_network_storage (nodeset = None ) -> List [NSMount ]:
61+ def get_slurm_bucket_mount () -> NSMount :
62+ bucket , path = util ._get_bucket_and_common_prefix ()
63+ return NSMount (
64+ fs_type = "gcsfuse" ,
65+ server_ip = "" ,
66+ remote_mount = Path (bucket ),
67+ local_mount = dirs .slurm_bucket_mount ,
68+ mount_options = f"defaults,_netdev,implicit_dirs,only_dir={ path } " ,
69+ )
70+
71+ def resolve_network_storage () -> List [NSMount ]:
6172 """Combine appropriate network_storage fields to a single list"""
6273 lkp = lookup ()
6374
6475 # create dict of mounts, local_mount: mount_info
6576 mounts = mounts_by_local (_get_default_mounts (lkp ))
6677
78+ if lkp .is_controller and util .should_mount_slurm_bucket ():
79+ mounts .update (mounts_by_local ([get_slurm_bucket_mount ()]))
80+
6781 # On non-controller instances, entries in network_storage could overwrite
6882 # default exports from the controller. Be careful, of course
69- mounts .update (mounts_by_local (lkp .cfg .network_storage ))
83+ common = [lkp .normalize_ns_mount (m ) for m in lkp .cfg .network_storage ]
84+ mounts .update (mounts_by_local (common ))
7085
7186 if lkp .is_login_node :
72- login_ns = lkp .cfg .login_groups [util .instance_login_group ()].network_storage
87+ login_group = lkp .cfg .login_groups [util .instance_login_group ()]
88+ login_ns = [lkp .normalize_ns_mount (m ) for m in login_group .network_storage ]
7389 mounts .update (mounts_by_local (login_ns ))
7490
7591 if lkp .instance_role == "compute" :
@@ -78,74 +94,46 @@ def resolve_network_storage(nodeset=None) -> List[NSMount]:
7894 except Exception :
7995 pass # external nodename, skip lookup
8096 else :
81- mounts .update (mounts_by_local (nodeset .network_storage ))
82-
83- return [lkp .normalize_ns_mount (mnt ) for mnt in mounts .values ()]
84-
97+ nodeset_ns = [lkp .normalize_ns_mount (m ) for m in nodeset .network_storage ]
98+ mounts .update (mounts_by_local (nodeset_ns ))
8599
86- def separate_external_internal_mounts (mounts ):
87- """separate into cluster-external and internal mounts"""
100+ return list (mounts .values ())
88101
89- def internal_mount (mount ):
90- # NOTE: Valid Lustre server_ip can take the form of '<IP>@tcp'
91- server_ip = mount .server_ip .split ("@" )[0 ]
92- mount_addr = util .host_lookup (server_ip )
93- return mount_addr == lookup ().control_host_addr
94-
95- return separate (internal_mount , mounts )
96102
103+ def is_controller_mount (mount ) -> bool :
104+ # NOTE: Valid Lustre server_ip can take the form of '<IP>@tcp'
105+ server_ip = mount .server_ip .split ("@" )[0 ]
106+ mount_addr = util .host_lookup (server_ip )
107+ return mount_addr == lookup ().control_host_addr
97108
98109def setup_network_storage ():
99110 """prepare network fs mounts and add them to fstab"""
100111 log .info ("Set up network storage" )
101- # filter mounts into two dicts, cluster-internal and external mounts
102-
112+
103113 all_mounts = resolve_network_storage ()
104- ext_mounts , int_mounts = separate_external_internal_mounts (all_mounts )
105-
106114 if lookup ().is_controller :
107- mounts = ext_mounts
115+ mounts , _ = separate ( is_controller_mount , all_mounts )
108116 else :
109- mounts = ext_mounts + int_mounts
117+ mounts = all_mounts
110118
111119 # Determine fstab entries and write them out
112120 fstab_entries = []
113121 for mount in mounts :
114- local_mount = Path (mount .local_mount )
115- remote_mount = mount .remote_mount
122+ local_mount = mount .local_mount
116123 fs_type = mount .fs_type
117124 server_ip = mount .server_ip or ""
125+ src = mount .remote_mount if fs_type == "gcsfuse" else f"{ server_ip } :{ mount .remote_mount } "
126+
127+ log .info (f"Setting up mount ({ fs_type } ) { src } to { local_mount } " )
118128 util .mkdirp (local_mount )
119129
120- log .info (
121- "Setting up mount ({}) {}{} to {}" .format (
122- fs_type ,
123- server_ip + ":" if fs_type != "gcsfuse" else "" ,
124- remote_mount ,
125- local_mount ,
126- )
127- )
128-
129130 mount_options = mount .mount_options .split ("," ) if mount .mount_options else []
130- if not mount_options or "_netdev" not in mount_options :
131+ if "_netdev" not in mount_options :
131132 mount_options += ["_netdev" ]
132-
133- if fs_type == "gcsfuse" :
134- fstab_entries .append (
135- "{0} {1} {2} {3} 0 0" .format (
136- remote_mount , local_mount , fs_type , "," .join (mount_options )
137- )
138- )
139- else :
140- fstab_entries .append (
141- "{0}:{1} {2} {3} {4} 0 0" .format (
142- server_ip ,
143- remote_mount ,
144- local_mount ,
145- fs_type ,
146- "," .join (mount_options ),
147- )
148- )
133+ options_line = "," .join (mount_options )
134+
135+
136+ fstab_entries .append (f"{ src } { local_mount } { fs_type } { options_line } 0 0" )
149137
150138 fstab = Path ("/etc/fstab" )
151139 if not Path (fstab .with_suffix (".bak" )).is_file ():
@@ -157,16 +145,16 @@ def setup_network_storage():
157145 f .write (entry )
158146 f .write ("\n " )
159147
160- mount_fstab (mounts_by_local ( mounts ) , log )
148+ mount_fstab (mounts , log )
161149 if lookup ().cfg .enable_slurm_auth :
162150 slurm_key_mount_handler ()
163151 else :
164152 munge_mount_handler ()
165153
166154
167- def mount_fstab (mounts , log ):
155+ def mount_fstab (mounts : list [ NSMount ] , log ):
168156 """Wait on each mount, then make sure all fstab is mounted"""
169- def mount_path (path ):
157+ def mount_path (path : Path ):
170158 log .info (f"Waiting for '{ path } ' to be mounted..." )
171159 try :
172160 run (f"mount { path } " , timeout = 120 )
@@ -184,8 +172,8 @@ def mount_path(path):
184172 with Executors .thread_pool ().with_timeout (MAX_MOUNT_TIMEOUT ).with_retry (
185173 retry_policy = retry_policy
186174 ) as exe :
187- for path in mounts :
188- future = exe .submit (mount_path , path )
175+ for m in mounts :
176+ future = exe .submit (mount_path , m . local_mount )
189177 future_list .append (future )
190178
191179 # Iterate over futures, checking for exceptions
@@ -306,36 +294,34 @@ def setup_nfs_exports():
306294 lkp = util .lookup ()
307295 assert lkp .is_controller
308296
309-
310297 # The controller only needs to set up exports for cluster-internal mounts
311- # switch the key to remote mount path since that is what needs exporting
312- mounts = resolve_network_storage ()
313-
314- if lkp .cfg .enable_slurm_auth :
315- mounts .append (lkp .slurm_key_mount )
316- else :
317- mounts .append (lkp .munge_mount )
318-
319- # controller mounts
320- _ , con_mounts = separate_external_internal_mounts (mounts )
321- con_mounts = {m .remote_mount : m for m in con_mounts }
322- for nodeset in lkp .cfg .nodeset .values ():
323- # get internal mounts for each nodeset by calling
324- # resolve_network_storage as from a node in each nodeset
325- ns_mounts = resolve_network_storage (nodeset = nodeset )
326- _ , int_mounts = separate_external_internal_mounts (ns_mounts )
327- con_mounts .update ({m .remote_mount : m for m in int_mounts })
298+ exported_mounts = [m for m in resolve_network_storage () if is_controller_mount (m )]
299+
300+ # key by remote mount path since that is what needs exporting
301+ to_export = {m .remote_mount : "*(rw,no_subtree_check,no_root_squash)" for m in exported_mounts }
302+
303+ key_mount = lkp .slurm_key_mount if lkp .cfg .enable_slurm_auth else lkp .munge_mount
304+ if is_controller_mount (key_mount ):
305+ # Export key mount as read-only
306+ to_export [key_mount .remote_mount ] = "*(ro,no_subtree_check,no_root_squash)"
307+
308+ if util .should_mount_slurm_bucket ():
309+ mnt = get_slurm_bucket_mount ()
310+ # FSID is required for virtual filesystem that is not based on a device
311+ # Also export it as read-only
312+ fsid = str (uuid .uuid4 ())
313+ to_export [mnt .local_mount ] = f"*(ro,no_subtree_check,no_root_squash,fsid={ fsid } )"
328314
329315 # export path if corresponding selector boolean is True
330- exports = []
331- for path in con_mounts :
316+ lines = []
317+ for path , options in to_export . items () :
332318 util .mkdirp (Path (path ))
333319 run (rf"sed -i '\#{ path } #d' /etc/exports" , timeout = 30 )
334- exports .append (f"{ path } *(rw,no_subtree_check,no_root_squash) " )
320+ lines .append (f"{ path } { options } " )
335321
336322 exportsd = Path ("/etc/exports.d" )
337323 util .mkdirp (exportsd )
338324 with (exportsd / "slurm.exports" ).open ("w" ) as f :
339325 f .write ("\n " )
340- f .write ("\n " .join (exports ))
326+ f .write ("\n " .join (lines ))
341327 run ("exportfs -a" , timeout = 30 )
0 commit comments