Last active
September 15, 2023 01:59
-
-
Save dsroberts/a4a4d3a8d10c726c0b268c07c62a7872 to your computer and use it in GitHub Desktop.
payu/envmod.py for Lmod on setonix
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
"""envmodules | |
========== | |
A modular port of the Environment Modules Python ``init`` script | |
""" | |
import os | |
import shlex | |
import subprocess | |
# Python 2.6 subprocess.check_output support | |
if not hasattr(subprocess, 'check_output'): | |
from backports import check_output | |
subprocess.check_output = check_output | |
DEFAULT_BASEPATH = '/opt/Modules' | |
DEFAULT_VERSION = 'v4.3.0' | |
def setup(basepath=DEFAULT_BASEPATH): | |
"""Set the environment modules used by the Environment Module system.""" | |
module_version = os.environ.get('MODULE_VERSION', DEFAULT_VERSION) | |
moduleshome = os.environ.get('MODULESHOME', None) | |
if moduleshome is None: | |
moduleshome = os.path.join(basepath, module_version) | |
# Abort if MODULESHOME does not exist | |
if not os.path.isdir(moduleshome): | |
print('payu: warning: MODULESHOME does not exist; disabling ' | |
'environment modules.') | |
try: | |
del(os.environ['MODULESHOME']) | |
except KeyError: | |
pass | |
return | |
else: | |
print('payu: Found modules in {}'.format(moduleshome)) | |
os.environ['MODULE_VERSION'] = module_version | |
os.environ['MODULE_VERSION_STACK'] = module_version | |
os.environ['MODULESHOME'] = moduleshome | |
if 'MODULEPATH' not in os.environ: | |
module_initpath = os.path.join(moduleshome, 'init', '.modulespath') | |
with open(module_initpath) as initpaths: | |
modpaths = [ | |
line.partition('#')[0].strip() | |
for line in initpaths.readlines() if not line.startswith('#') | |
] | |
os.environ['MODULEPATH'] = ':'.join(modpaths) | |
os.environ['LOADEDMODULES'] = os.environ.get('LOADEDMODULES', '') | |
# Environment modules with certain characters will cause corruption | |
# when MPI jobs get launched on other nodes (possibly a PBS issue). | |
# | |
# Bash processes obscure the issue on Raijin, since it occurs in an | |
# environment module function, and bash moves those to the end of | |
# the environment variable list. | |
# | |
# Raijin's mpirun wrapper is a bash script, and therefore "fixes" by doing | |
# the shuffle and limiting the damage to other bash functions, but some | |
# wrappers (e.g. OpenMPI 2.1.x) may not be present. So we manually patch | |
# the problematic variable here. But a more general solution would be nice | |
# someday. | |
if 'BASH_FUNC_module()' in os.environ: | |
bash_func_module = os.environ['BASH_FUNC_module()'] | |
os.environ['BASH_FUNC_module()'] = bash_func_module.replace('\n', ';') | |
def module(command, *args): | |
"""Run the modulecmd tool and use its Python-formatted output to set the | |
environment variables.""" | |
if 'MODULESHOME' not in os.environ: | |
print('payu: warning: No Environment Modules found; skipping {0} call.' | |
''.format(command)) | |
return | |
#modulecmd = ('{0}/bin/modulecmd'.format(os.environ['MODULESHOME'])) | |
### For Lmod | |
modulecmd=('{0}/libexec/lmod'.format(os.environ['MODULESHOME'])) | |
cmd = '{0} python {1} {2}'.format(modulecmd, command, ' '.join(args)) | |
envs, _ = subprocess.Popen(shlex.split(cmd), | |
stdout=subprocess.PIPE).communicate() | |
exec(envs) | |
def lib_update(required_libs, lib_name): | |
# Local import to avoid reversion interference | |
# TODO: Bad design, fixme! | |
# NOTE: We may be able to move this now that reversion is going away | |
from payu import fsops | |
for lib_filename, lib_path in required_libs.items(): | |
if lib_filename.startswith(lib_name) and lib_path.startswith('/software/setonix'): | |
### Load nci's /apps/ version of module if required | |
# Load Pawseys /software/setonix version of module if required | |
# pylint: disable=unbalanced-tuple-unpacking | |
#mod_name, mod_version = fsops.splitpath(lib_path)[2:4] | |
### Work backwards through the path elements until we | |
### find something that looks right: | |
for dir in reversed(fsops.splitpath(lib_path)): | |
dir_arr=dir.split('-') | |
### Assume we're in the right place if the last token matches | |
### the pattern of a spack id. | |
if len(dir_arr[-1]) == 32 and dir_arr[-1].isalnum() and dir_arr[-1].islower(): | |
break | |
### Bad assumption - version names never contain hyphens | |
mod_name='-'.join(dir_arr[:-2]) | |
mod_version=dir_arr[-2] | |
module('unload', mod_name) | |
module('load', os.path.join(mod_name, mod_version)) | |
return '{0}/{1}'.format(mod_name, mod_version) | |
# If there are no libraries, return an empty string | |
return '' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment