跳转至

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(落地层)五处接线:

  1. validate_config() —— 补 csh 路径的白名单校验;
  2. sync() 的 site_files —— 纳入 SHA256 比对,变成纳入幂等体系;
  3. _write_site_files() —— 纳入实际写入(mode 644);
  4. uninstall() —— 卸载时一并删除,避免残留孤儿配置;
  5. 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 就位,SHA256 6e610e10863097ad5429ee4030ba159f0f7889530faeb9625d0018186a3e1443。
  • 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 同步,请勿在站点侧修改)