SOP — 为 csh/tcsh 用户补站点 Lmod 脚本(九台 EDA 主机)¶
变更编号:
lmod_csh_profile_20260927|执行日期:2026-09-27|影响主机:9 台 触发:用户rain01在hpc-submit(192.168.31.28)上执行module av只见lmod/settarg
1. 背景¶
集群 LDAP 用户的 loginShell 是 /bin/csh。csh 的登录入口是 /etc/csh.login,它只遍历
/etc/profile.d/*.csh,不读 *.sh;而站点 Lmod 增强此前只提供了 bash 版
/etc/profile.d/zzz_site_lmod.sh(slurm 侧由 slurm_ins 部署器渲染,LSF 三台为手工补建)。
后果:csh 用户登录后 MODULEPATH 只有 /etc/modulefiles、/usr/share/modulefiles/*、
/usr/share/lmod/lmod/modulefiles/Core 五个 Lmod 默认路径,缺站点 /opt/modulefiles/Core,
module avail 里看不到 cadence / siemens / synopsys / pdk / slurm 任何家族入口。
这不是 Lmod 未装好:Lmod 本体、共享 modulefiles、bash 侧配置、NFS 挂载均正常,
root(bash 登录)下 MODULEPATH 首项就是 /opt/modulefiles/Core。缺口只在 csh 一侧。
2. 目标¶
- 九台 EDA 主机(slurm 侧
.16/.17/.18/.19/.20/.28+ LSF 侧.25/.26/.27)的/etc/profile.d/zzz_site_lmod.csh就位,内容九台逐字一致(同一 SHA256)。 - csh/tcsh 登录后
MODULEPATH首项为/opt/modulefiles/Core,module avail可见五个家族入口。 - slurm 侧的落地由生成器承载,
--module-env复跑不得丢失;复跑零变更、全跳过。 - 不触碰既有
zzz_site_lmod.sh、lmod_config.lua、modulefiles、slurm.conf,不重启任何服务。
3. 前置条件¶
- 以 root 在部署机(dell-R620,192.168.31.249)执行;
slurm_ins工程根/home/rain/Documents/slurm/slurm_ins,venv 为.venv/。 - 九台 SSH 免密可达;
/opt/modulefiles已由九台挂载(NFS:192.168.31.249:/data/nfs/modulefiles)。 - 九台已装
csh(/bin/csh,Rocky 9 的 tcsh 符号链接)与Lmod 8.7.65。 squeue为空(本次不改slurm.conf,理论不会重启 daemon;仍按集群惯例确认)。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | .16/.17(控制)、.18/.19/.20(计算)、.28(提交)、.25/.26/.27(LSF) |
| 新增文件 | /etc/profile.d/zzz_site_lmod.csh(644,root:root,约 665 字节) |
| 停服 | 不需要;不改 slurm.conf,--module-env 不会走到 scontrol reconfigure / slurmd 重启分支 |
| 窗口 | 任意;仅影响新登录的 csh 会话,已登录会话不受影响 |
| 在跑作业 | 不受影响 |
5. 分步操作¶
步骤 1:备份待改的部署器代码¶
改的是生成器与部署器两个文件,先落备份,失败可一键还原。
存放位置见第 7 章与 backup/README.md。
D=/home/rain/Documents/slurm/slurm_ins/lmod_csh_profile_20260927
TS=$(date +%Y%m%d-%H%M%S)
mkdir -p "$D/backup/slurm_ins" "$D/docs"
cd /home/rain/Documents/slurm/slurm_ins
cp -p module_env_assets.py "$D/backup/slurm_ins/module_env_assets.py.bak.$TS"
cp -p module_env_deployer.py "$D/backup/slurm_ins/module_env_deployer.py.bak.$TS"
预期:两个 .bak.<时间戳> 落盘。
步骤 2:给生成器与部署器加 csh 版站点脚本¶
module_env_assets.py(渲染层):
- 新增属性
site_profile_csh_path:由site_profile_path派生(.sh→.csh), 不新增 yaml 配置项(避免出现零引用字段); - 新增方法
render_site_profile_csh():csh 语法,两件事——非登录 shell 下先source /etc/profile.d/modules.csh拿到module;再把站点 Core 路径前置进MODULEPATH。
module_env_deployer.py(落地层)五处接线:
validate_config()—— 补 csh 路径的白名单校验;sync()的site_files—— 纳入 SHA256 比对,变成纳入幂等体系;_write_site_files()—— 纳入实际写入(mode 644);uninstall()—— 卸载时一并删除,避免残留孤儿配置;verify()—— 新增 csh 登录自检(见步骤 6 的判据说明)。
坑(本轮踩实):csh 侧最初用 Lmod 自带的 addto --prepend 拼 MODULEPATH,
实测 Lmod 8.7.65 的 addto 不认 --prepend(Lua 报错后无输出),
setenv MODULEPATH \...`把变量写成**空串** —— csh 用户比不装这份更糟。
故最终改为 csh 原生判重(switch/case看路径是否已在MODULEPATH中),
与 bash 版case` 判重同构,不依赖任何外部小工具。
渲染后先在本地核对内容,再动主机:
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python -m py_compile module_env_assets.py module_env_deployer.py
mkdir -p lmod_csh_profile_20260927/scratch
.venv/bin/python - <<'PY'
import yaml
from module_env_assets import ModuleEnvAssets
a = ModuleEnvAssets(yaml.safe_load(open('config/slurm_deploy.yaml', encoding='utf-8')), None)
open('lmod_csh_profile_20260927/scratch/zzz_site_lmod.csh', 'w', encoding='utf-8').write(
a.render_site_profile_csh())
PY
预期:语法检查无输出;scratch/zzz_site_lmod.csh 内容中 switch 分支的匹配串是字面路径
(不用 ${变量} 做 case 模式,避免 csh 版本差异导致永不匹配、反复叠加)。
实现体:module_env_assets.py(render_site_profile_csh)、module_env_deployer.py(五处接线)。
步骤 3:预演(slurm 侧六台)¶
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env --dry-run
预期:六台各一条 lmod 站点配置 - 待写入:zzz_site_lmod.csh;modulefiles、
lmod_config.lua、zzz_site_lmod.sh、task_prolog_lmod.sh 均「内容一致 → 跳过」;
slurm.conf 不出现在变更清单里;退出码 0。
失败判断:若出现 slurm.conf 变更项,立即停止 —— 那会触发 daemon 重启。
步骤 4:下发 slurm 侧六台¶
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env
预期:六台 .../zzz_site_lmod.csh - 写入,随后 health_check 六台
✅ csh 登录 MODULEPATH 含 /opt/modulefiles/Core,退出码 0。
失败判断:health_check 报 csh 登录环境未见 /opt/modulefiles/Core → 见第 7 章回滚。
步骤 5:下发 LSF 三台(.25/.26/.27)¶
这三台不在 slurm_ins 的 nodes 清单里(其 Lmod 是 2026-09-27 手工接入的,无部署器承载),
用本变更目录下的一次性脚本下发同一份内容:
cd /home/rain/Documents/slurm/slurm_ins/lmod_csh_profile_20260927
python3 apply_csh_profile.py --hosts 192.168.31.25,192.168.31.26,192.168.31.27 \
--content scratch/zzz_site_lmod.csh --dry-run # 预演
python3 apply_csh_profile.py --hosts 192.168.31.25,192.168.31.26,192.168.31.27 \
--content scratch/zzz_site_lmod.csh --apply # 执行
坑:该脚本内容经 stdin 传给远端 cat,不走命令行 —— csh 脚本里含反引号与 $,
拼进 shell 命令串会被本机/远端 shell 抢先展开。脚本内对目标路径做了白名单
(仅 /etc/profile.d/<普通文件名>),并在覆盖前自动备份到 /var/backups/lmod/。
预期:三台 已写入 ...(SHA256 …),权限 644,随后三台
✅ csh 登录 MODULEPATH 含 /opt/modulefiles/Core;退出码 0。
实现体:lmod_csh_profile_20260927/apply_csh_profile.py。
步骤 6:九台一致性与真实用户视角验证¶
for h in 192.168.31.16 192.168.31.17 192.168.31.18 192.168.31.19 192.168.31.20 \
192.168.31.28 192.168.31.25 192.168.31.26 192.168.31.27; do
echo -n "$h "
ssh -o BatchMode=yes root@$h "sha256sum /etc/profile.d/zzz_site_lmod.csh | awk '{print \$1}'"
done
ssh root@192.168.31.28 'su - rain01 -c "echo MP=\$MODULEPATH; module -t avail"'
预期:九台 SHA256 全部相同(本轮为 6e610e10…);真实 csh 用户 MODULEPATH 首项为
/opt/modulefiles/Core,module -t avail 列出 cadence、pdk、siemens、site、
slurm/26.05.2、synopsys。
验证判据的两处坑(部署器 verify() 里已固化):
- csh 不支持
-l选项(csh -lc→Unknown option),改用csh -c 'source /etc/csh.login >& /dev/null; echo $MODULEPATH'模拟登录加载; - 必须
env -i清空环境:sshd 会话自带BASH_ENV=/usr/share/lmod/lmod/init/bash与站点MODULEPATH,不清环境会把父进程的值当成本机登录结果,恒判通过(本轮首次自检即因此假通过)。
步骤 7:幂等复跑¶
cd /home/rain/Documents/slurm/slurm_ins
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env # 第二次
cd lmod_csh_profile_20260927
python3 apply_csh_profile.py --hosts 192.168.31.25,192.168.31.26,192.168.31.27 \
--content scratch/zzz_site_lmod.csh --apply # 第二次
预期:slurm 侧 ✅ 状态已一致,零变更,无需部署(幂等跳过)(待变更 0 / 幂等跳过 27);
LSF 侧三台 内容一致(SHA256 …),跳过。
6. 验证¶
| # | 检查项 | 命令 | 预期 |
|---|---|---|---|
| 1 | 九台文件同源 | 见步骤 6 第一条 | 9 个 SHA256 相同 |
| 2 | csh 登录可见站点路径(九台) | ssh root@<host> "env -i HOME=/root USER=root PATH=/usr/bin:/bin /bin/csh -c 'source /etc/csh.login >& /dev/null; echo \$MODULEPATH'" |
首项为 /opt/modulefiles/Core |
| 3 | 真实 csh 用户视角 | 见步骤 6 第二条 | module avail 见 cadence/pdk/siemens/synopsys/slurm |
| 4 | 反复登录不叠加 | 上条命令中重复 source 两次 |
两次 MODULEPATH 相同(无重复项) |
| 5 | bash 侧未被影响 | ssh root@<host> 'bash -lc "module avail"' |
与变更前一致,仍见全部家族 |
| 6 | 幂等复跑 | 见步骤 7 | 零变更、全跳过、退出码 0 |
| 7 | 服务未受影响 | ssh root@<host> '/usr/local/slurm/bin/sinfo -N -o "%N %T"' |
节点状态与变更前一致 |
7. 回滚¶
触发条件:第 6 章任一项不符(九台 SHA 不一致、csh 登录未见站点路径、bash 侧退化、 复跑出现非预期变更、节点状态异常)。
第一步:撤主机侧文件(九台,删除新增文件即回到变更前状态)
for h in 192.168.31.16 192.168.31.17 192.168.31.18 192.168.31.19 192.168.31.20 \
192.168.31.28; do
ssh -o BatchMode=yes root@$h "rm -f /etc/profile.d/zzz_site_lmod.csh && echo $h OK"
done
cd /home/rain/Documents/slurm/slurm_ins/lmod_csh_profile_20260927
python3 apply_csh_profile.py --hosts 192.168.31.25,192.168.31.26,192.168.31.27 --rollback
先撤主机、再回滚代码:主机侧文件由生成器按 SHA 判定,代码回滚后再跑部署器不会把它重新写回 (渲染函数已不存在 → 不进入比对清单),避免"回滚了又被推回来"。
第二步:还原部署器代码
D=/home/rain/Documents/slurm/slurm_ins/lmod_csh_profile_20260927/backup/slurm_ins
cd /home/rain/Documents/slurm/slurm_ins
cp -p "$D/module_env_assets.py.bak.<时间戳>" module_env_assets.py
cp -p "$D/module_env_deployer.py.bak.<时间戳>" module_env_deployer.py
.venv/bin/python -m py_compile module_env_assets.py module_env_deployer.py
第三步:回滚后验证:csh 登录 MODULEPATH 回到只有 Lmod 默认五项(即恢复本次变更前的行为),
bash 侧不受影响,--module-env --dry-run 不再出现 zzz_site_lmod.csh。
备份位置:本次两个代码备份在
lmod_csh_profile_20260927/backup/slurm_ins/,台账见
lmod_csh_profile_20260927/backup/README.md(含产生时间、回滚对象、可删时机)。
主机侧无备份文件:本次是纯新增,且 apply_csh_profile.py 在检测到同名文件已存在时
会先备份到目标机 /var/backups/lmod/(本轮九台均无同名文件,未产生备份)。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
csh 脚本语法错误会污染 csh 用户的 MODULEPATH(本轮首版即把变量写空) |
先在单台(.28)用 env -i 隔离验证语法、前置效果与重复加载幂等,确认后才批量;批量后立即九台复验 |
Lmod 版本差异导致 addto 等小工具行为不一致 |
实现刻意不依赖外部小工具,只用 csh 原生 switch/case 判重 |
自检判据被父环境污染(sshd 自带 BASH_ENV),"假通过" |
判据固定 env -i HOME=/root USER=root PATH=/usr/bin:/bin,只看清空环境后的真实登录结果 |
| LSF 三台无部署器承载,日后重建 / 重装会丢 | 内容与 slurm 侧逐字一致(同 SHA),并在 slurm_ins/docs/HANDOFF.md 与本文档登记:LSF 侧为一次性下发,重建时需重跑 apply_csh_profile.py |
| 生成器改动影响 slurm 侧后续部署 | 只新增渲染函数与五处接线,未改既有渲染逻辑;--module-env --dry-run 已确认既有项全部"内容一致 → 跳过" |
附录 A:交付物清单¶
| 文件 | 说明 |
|---|---|
docs/SOP.md |
本文档 |
scratch/zzz_site_lmod.csh |
下发的脚本内容(九台同源,SHA256 6e610e10…) |
apply_csh_profile.py |
LSF 三台幂等下发/回滚脚本(支持 --dry-run / --apply / --rollback) |
backup/README.md、backup/slurm_ins/*.bak.* |
代码备份台账与备份本体 |
../../../slurm_ins/module_env_assets.py、module_env_deployer.py |
被修改的部署器(slurm 侧生成器承载) |
附录 B:变更后事实(供交接引用)¶
- 九台
/etc/profile.d/zzz_site_lmod.csh就位,SHA2566e610e10863097ad5429ee4030ba159f0f7889530faeb9625d0018186a3e1443。 - slurm 侧复跑
--module-env零变更(待执行 0 / 幂等跳过 27);LSF 侧复跑三台全跳过。 - 未改动
slurm.conf,未重启slurmctld/slurmd。
来源:
/home/rain/Documents/slurm/slurm_ins/lmod_csh_profile_20260927/docs/SOP.md| 最后更新:2026-09-27 22:33(由build/sync.py同步,请勿在站点侧修改)