Skip to content

Instantly share code, notes, and snippets.

@dsroberts
Last active September 15, 2023 01:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dsroberts/a4a4d3a8d10c726c0b268c07c62a7872 to your computer and use it in GitHub Desktop.
Save dsroberts/a4a4d3a8d10c726c0b268c07c62a7872 to your computer and use it in GitHub Desktop.
payu/envmod.py for Lmod on setonix
# 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