SOP — Lmod 跨工程收敛(A:vcs 写删拉锯 / B:calibre 默认版本回归)¶
变更日期:2026-10-05 实施人:rain(Agent 执行) 状态:已完成,验收通过 归档目录:
eda_tools_ins/lmod_cross_project_fix_20261005/触发来源:交接单eda_tools_ins/docs/LMOD-CROSS-PROJECT-TODO.md第 2 节 A、B 两项; eda_tools_ins(本工作区)作为项目方定案后推进。
1. 背景¶
/opt/modulefiles 是共享写入区,slurm_ins 生成器与 eda_tools_ins 两侧装载工程各写一部分。
本轮解决交接单 A、B 两项,根因同为「同一路径两个属主、互不知情」,且都引入了行为回退:
- A —
synopsys/vcs/2023.12.lua写删拉锯:slurm_ins --module-env按config/slurm_deploy.yaml生成该文件;eda_tools_ins/Synopsys的synopsys_module_fix_20260929/fix_modules.py又判其悬空(install_dir=/opt/eda/...无实体)而删除。任何一方复跑都会推翻另一方。 - B —
module load calibre行为回退:siemens/calibre/下有实体模块2015.2_36.27(Cadence 侧维护,在用)与占位模块2026.1(slurm 生成,/opt/eda无实体)。2026-10-05 slurm 新增的.modulerc.lua把默认版本指向悬空的 2026.1,导致module load calibre报 LmodError,可用版本反而 要写全calibre/2015.2_36.27。
定案(eda_tools_ins 侧作项目方):
- A 采用交接单方案 1:真实 VCS 实体为
/tools/synopsys/vcs-mx/N-2017.12-SP2,vcs命令入口由synopsys/eda2018/1.0提供 → slurm 侧从 yaml 删除该条目并退役清理模块, eda 侧fix_modules.py移除删除逻辑(改幂等确认)。 - B 采用「移除悬空 2026.1」:实测确认
2026.1无实体(见第 3 章)→ slurm 侧删 yaml 条目 并清理.modulerc.lua与2026.1.lua;此后该目录仅存一个版本,裸名加载恢复。
2. 目标¶
/opt/modulefiles/synopsys/vcs/2023.12.lua不再由任一侧产出或删除——跑过--module-env与跑过fix_modules.py --apply后状态一致(都不存在);module load siemens && module load calibre在 6 台 + 提交节点成功,且加载到有实体的calibre/2015.2_36.27;module avail无指向不存在目录的 calibre / vcs 条目;module load synopsys && module load eda2018/1.0 && command -v vcs仍可用;- 全程幂等:复跑零变更、全跳过;不停服、不重启任何 daemon。
3. 前置条件¶
- root SSH 可达 6 台:控制
.16/.17、计算.18/.19/.20、提交.28; slurm_ins/.venv与config/slurm_deploy.yaml就绪;- 执行前
squeue为空(本实现体在 TaskProlog 变化时会重启 slurmd;本次 TaskProlog 无变化, 实测未触发); - B 的实体确认(决定采取哪条分支):实测
.18上/opt/eda不存在、/tools/mentor/calibre仅有aoi_cal_2015.2_36.27→calibre/2026.1无实体,取「移除悬空 2026.1」分支。
4. 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | .16 .17 .18 .19 .20 .28 共 6 台 |
| 共享盘 | 内容主节点 .16:删除 /opt/modulefiles/synopsys/vcs/(目录)、/opt/modulefiles/siemens/calibre/2026.1.lua、/opt/modulefiles/siemens/calibre/.modulerc.lua(先备份) |
| 源码 | slurm_ins/config/slurm_deploy.yaml、module_env_assets.py、module_env_deployer.py;eda_tools_ins/Synopsys/synopsys_module_fix_20260929/fix_modules.py |
| 是否停服 | 否,不重启 daemon |
| 在跑作业 | 不受影响(只改模块可见性;执行前 squeue 为空) |
| 窗口期 | 随时;删除前有备份,可回滚 |
5. 分步操作¶
本次以生成器/脚本代码为准,下发由
slurm_ins部署器完成。以下给出等价底层操作 供阅读、复核与手工复现;幂等与错误处理由实现体保证。 实现体:slurm_ins/module_env_assets.py(渲染与退役清单)、module_env_deployer.py(判定/执行/清理/验证);Synopsys/synopsys_module_fix_20260929/fix_modules.py(SCL 修补)。
步骤 1:slurm 侧从 yaml 删除两个悬空工具条目¶
config/slurm_deploy.yaml 的 families 中,synopsys 与 siemens 两个家族的 tools 由
「各一条占位工具」改为空列表;家族入口保留(家族入口负责把 /opt/modulefiles/<family>
挂进 MODULEPATH,eda 侧模块才可见)。
坑:只删工具条目、不删家族条目。删家族条目会让 eda2018 / vcs-el7 / calibre/2015.2_36.27
在 module avail 里全部消失。
# 生成器:families.synopsys.tools = [] → 不再渲染 synopsys/vcs/2023.12.lua
# 生成器:families.siemens.tools = [] → 不再渲染 siemens/calibre/2026.1.lua 与 .modulerc.lua
步骤 2:在退役清单登记 3 个路径(生成器只写不删)¶
生成器不再产出条目后,历史文件仍留在共享盘继续被扫到,故在 module_env_assets.py 的
RETIRED_MODULE_PATHS 登记(清理由部署器备份后执行):
synopsys/vcs # 目录
siemens/calibre/2026.1.lua # 单文件
siemens/calibre/.modulerc.lua # 单文件
坑:只登这两个文件、不登 siemens/calibre/ 整个目录——该目录还装着 Cadence 侧维护、
在用的 2015.2_36.27.lua,登目录会误删在用模块。为此把清理判定由 [ -d ] 泛化为 [ -e ]
(目录与文件同款处理)。
步骤 3:eda 侧移除 fix_modules.py 的 vcs 删除逻辑¶
fix_modules.py 步骤 3 由「移出悬空 vcs module」改为幂等确认(不再删除、不再备份该文件);
VCS_LUA 常量保留用于确认,BK_VCS_DIR 常量删除。此后两处不再对同一文件做相反动作。
步骤 4:内容主节点备份并清理退役路径¶
高危:删除共享盘模块路径影响全体用户,故先打包备份、备份失败即中止(实现体用 && 串联)。
# 在内容主节点 192.168.31.16 上
B=/var/backups/slurm/modulefiles; mkdir -p "$B"
tar -C /opt/modulefiles -czf "$B/synopsys-vcs.removed.tar.gz" synopsys/vcs
tar -C /opt/modulefiles -czf "$B/siemens-calibre-2026.1.lua.removed.tar.gz" siemens/calibre/2026.1.lua
tar -C /opt/modulefiles -czf "$B/siemens-calibre-.modulerc.lua.removed.tar.gz" siemens/calibre/.modulerc.lua
rm -rf /opt/modulefiles/synopsys/vcs /opt/modulefiles/siemens/calibre/2026.1.lua /opt/modulefiles/siemens/calibre/.modulerc.lua
步骤 5:下发(实现体入口)¶
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env --dry-run # 预演
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env # 下发
预期:预演列出「待清理:synopsys/vcs, siemens/calibre/2026.1.lua, siemens/calibre/.modulerc.lua」,
其余零变更;下发退出码 0、末尾 ✅ 部署完成、✅ 环境模块校验通过。
6. 验证¶
| # | 命令 | 实测结果 |
|---|---|---|
| 1 | .16:/opt/modulefiles/synopsys/ |
仅 eda2018、vcs-el7(vcs/ 已删) ✅ |
| 2 | .16:/opt/modulefiles/siemens/calibre/ |
仅 2015.2_36.27.lua(2026.1.lua、.modulerc.lua 已删) ✅ |
| 3 | .16:/var/backups/slurm/modulefiles/ |
synopsys-vcs.removed.tar.gz(624B)、siemens-calibre-2026.1.lua.removed.tar.gz(596B)、siemens-calibre-.modulerc.lua.removed.tar.gz(337B) 均存在 ✅ |
| 4 | .18: module load siemens && module load calibre |
RC=0,CALIBRE_HOME=/tools/mentor/calibre/aoi_cal_2015.2_36.27/aoi_cal_2015.2_36.27(加载到有实体版本) ✅ |
| 5 | .18: module load synopsys && module load eda2018/1.0 && command -v vcs |
/tools/synopsys/vcs-mx/N-2017.12-SP2/bin/vcs,RC=0 ✅ |
| 6 | .18: module load siemens; module load synopsys; module -t avail |
siemens 段仅 calibre/2015.2_36.27;synopsys 段仅 vcs-el7/1.0(无 vcs/2023.12、无 calibre/2026.1) ✅ |
| 7 | .18: check_module_tree_syntax /opt/modulefiles |
仅剩 pdk/sky130(占位,见第 8 章,非本轮范围)2 条;本轮目标 0 条 ✅ |
| 8 | 幂等复跑 --module-env |
第二次「待执行 0 / 幂等跳过 27 / 退出码 0」 ✅ |
说明:在
.16(未挂/tools、/pdk)上跑语法自检会报 12 条「路径未配置」——那是挂载缺失 导致的 isDir 守卫触发,非语法错误;应改在有挂载的节点(如.18)上跑。
7. 回滚¶
系统侧(恢复变更前状态):
# 1) 还原被清理的 modulefile 路径(内容主节点 .16)
B=/var/backups/slurm/modulefiles
tar -C /opt/modulefiles -xzf "$B/synopsys-vcs.removed.tar.gz"
tar -C /opt/modulefiles -xzf "$B/siemens-calibre-2026.1.lua.removed.tar.gz"
tar -C /opt/modulefiles -xzf "$B/siemens-calibre-.modulerc.lua.removed.tar.gz"
# 2) 代码侧回退后复跑生成器(会重新产出 vcs/2023.12.lua 与 calibre/2026.1.lua + .modulerc.lua)
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env
# 3) 或直接用部署器回滚(会从备份包还原退役路径并撤销站点配置)
.venv/bin/python main.py --config config/slurm_deploy.yaml --module-env --rollback
代码侧:从 backup/ 恢复 4 个源文件(见第 8 章 / backup/README.md)后复跑。
8. 风险说明¶
| 风险 | 缓解 |
|---|---|
| 删共享盘路径影响用户脚本 | 执行前确认 6 台 squeue 为空;删除前 tar 备份、备份失败即中止;--rollback 可还原 |
误删 Cadence 在用的 calibre/2015.2_36.27.lua |
退役清单只登两个单文件,不登整个 siemens/calibre/ 目录;实施后用 .18 实测 module load calibre 命中 2015.2 验证 |
| 删家族条目导致 eda 侧模块整体不可见 | 只删 tools 数组内容,保留家族入口;验证 #6 覆盖 |
fix_modules.py 与生成器再次对同一文件相反动作 |
eda 侧删除逻辑已改为幂等确认;两处口径写进交接单第 5 节 |
清理机制由 [ -d ] 泛化为 [ -e ] 影响既有 Core/slurm 清理 |
判定与删除动作对目录行为不变;实施后幂等复跑零变更、无报错 |
~~本轮未处理:pdk/sky130/1.0 仍为占位悬空(交接单 C-2)~~ |
已同日处置,见附录 B |
本轮未处理(留待后续):交接单 D(命名/别名)、E(deprecate())、C-1(兼容层位置) |
属各自工程的跟进项,本轮不涉及 |
附录:本次交付物¶
| 文件 | 说明 |
|---|---|
本 SOP.md |
变更档案 |
backup/README.md |
备份台账(4 个源文件快照) |
backup/slurm_ins/{config/slurm_deploy.yaml,module_env_assets.py,module_env_deployer.py}.bak.* |
slurm 侧动手前快照 |
backup/eda_tools_ins/Synopsys/synopsys_module_fix_20260929/fix_modules.py.bak.* |
eda 侧动手前快照 |
远端备份包(.16:/var/backups/slurm/modulefiles/*.removed.tar.gz) |
共享盘退役路径备份,随 --module-env --rollback 自动还原 |
附录 B(2026-10-05 同日追加):C-2 pdk/sky130 悬空条目清理¶
交接单 C-2 要求"确认这三个工具的真实落点;确认不落地的条目直接删除"。A/B 已处置
synopsys/vcs/2023.12 与 siemens/calibre/2026.1,本条处置最后一个 pdk/sky130/1.0。
实体确认(实测,无实体):
- 本地
/data/nfs/pdk:仅FreePDK15 / FreePDK45 / ncsu-cdk-1.6.0.beta / SAED32_EDK / templates,无 sky130; - 计算节点
.18/.19/.20:/opt/eda均不存在,/pdk内容同上; - 全盘
find -iname '*sky130*'仅命中模块目录本身。 - 旁证:实际在用的 PDK 模块是
pdk/saed32/1.0(由Synopsys/synopsys_pdk_saed32_20260929维护)。
变更(与 A/B 同款机制,复用 obsolete_module_file_dirs):
config/slurm_deploy.yaml:families.pdk.tools由[sky130/1.0]改为[](家族入口保留,saed32才可见);module_env_assets.py:RETIRED_MODULE_PATHS增加pdk/sky130。
验证:/data/nfs/modulefiles/pdk 仅剩 saed32;.18 module load pdk; module -t avail
仅 saed32/1.0;.18 语法自检 No errors found(C-2 目标达成:module avail 无指向
不存在目录的条目);幂等复跑「待执行 0 / 幂等跳过 27」。备份包
.16:/var/backups/slurm/modulefiles/pdk-sky130.removed.tar.gz(573 B)。回滚同第 7 章。
未改
config/slurm_deploy.yaml.example(示例模板,非部署源;其中 sky130 作示意保留)。 日后 sky130 实体就位,按真实install_dir重新登记即可。
附录 C(2026-10-05 同日追加):C-1 兼容层位置迁移 + D 命名别名(E 仅登记)¶
交接单 docs/LMOD-CROSS-PROJECT-TODO.md 第 2 节 C-1 / D 两项定案并实施;
E 按交接单口径本轮不做、仅登记。
C.1 背景¶
- C-1:
/opt/modulefiles/synopsys_compat/(bin/uname、libexec/libpthread.so.0、lib64/{libtiff.so.3,libmng.so.1,liblcms.so.1})是兼容层数据、不是模块,却位于 module 树内, 会被module spider遍历;而同类兼容层(el7 binutils)在节点本地/opt/compat—— 两处不一致。 - D:
synopsys/vcs-el7/1.0、synopsys/eda2018/1.0不符合 Lmod 通行vendor/tool/version, 但改名会破坏存量用法(Synopsys/docs/*、design/作业脚本)。
C.2 定案(eda_tools_ins 侧作项目方)¶
- C-1 采用「迁到
/opt/compat」:实测六台 work +.28的/opt/compat已含全部制品 (el6 库 + shim + uname,sha256 与 NFS 源一致),NFS 那份是冗余副本 → 模块引用改指/opt/compat, 退役 NFSsynopsys_compat,制品源迁入工程artifacts/share/。 - D 采用交接单方案 A(不改名 + 加别名):
eda2018/1.0加同目录版本别名eda2018/2018;vcs-el7/1.0加全局别名vcs/el7。
C.3 目标¶
/opt/modulefiles下只含模块定义与.modulerc*(C-1 验收判据);module load synopsys && module load eda2018/1.0正常(LD_PRELOAD 生效、dc_shell 可用);- 新旧写法并存:
eda2018/1.0↔eda2018/2018、vcs-el7/1.0↔vcs/el7均可加载; - 幂等:两个部署工程复跑零变更。
C.4 影响范围¶
| 项 | 内容 |
|---|---|
| 主机 | 六台 work(.18/.19/.20/.25/.26/.27)+ 提交/GUI 节点 .28(读兼容层) |
| 共享盘 | 内容主节点 .249:删除 /data/nfs/modulefiles/synopsys_compat/(先备份);写入 eda2018 的 1.0.lua/.modulerc.lua 与 synopsys/.modulerc.lua |
| 源码 | Synopsys/rocky9_eda_compat_20260928/{docs/eda2018-1.0.lua, docs/eda2018-.modulerc.lua, config/rollout.yaml(+.example), deployer.py, utils/eda_state.py, artifacts/share/**};Synopsys/vcs_el7_binutils_20260929/{deploy_el7_binutils.py, templates/synopsys-.modulerc.lua.tpl} |
| 是否停服 | 否 |
| 在跑作业 | 不受影响(仅改模块可见性与模块内引用路径) |
C.5 分步操作¶
步骤 1:制品源迁入工程,改 eda2018 引用(rocky9_eda_compat_20260928)
# 制品源:NFS → 工程 artifacts/share(一处维护,随工程走)
cp -a /data/nfs/modulefiles/synopsys_compat/. \
rocky9_eda_compat_20260928/artifacts/share/
docs/eda2018-1.0.lua:compat 由 /opt/modulefiles/synopsys_compat 改为 /opt/compat;
守卫由 isDir(compat) 改为 isFile(compat .. "/libexec/libpthread.so.0");LD_PRELOAD 指向
/opt/compat/libexec/libpthread.so.0。
坑(关键):必须移除 prepend_path("PATH", compat .. "/bin") —— /opt/compat/bin 同时含
el7 binutils(ld/ar/as/...,供 vcs-el7),整目录前置会把老工具链混进所有 eda2018 会话;
而 uname 包装实际生效路径是各机 /usr/local/bin/uname(工具内部重建 PATH,使 PATH 前置失效)。
步骤 2:部署工程支持操作机本地制品源(deployer.py + utils/eda_state.py)
原实现假定 compat_share 目标机可见(远端 sha256sum / 远端 cp)。制品源迁入工程本地后目标机读不到,
判定为 unknown 并中止。新增配置 compat_share_local 与本地源探测:
utils/eda_state.py::local_src_sha_state():操作机算源 sha256,远端比对目标文件;deployer.py:self.share_local决定探测走local_src_sha_state/same_sha_state, 变更走上传(ssh.upload+ 远端chmod)/ 远端cp。
步骤 3:D 别名(各自属主工程写自己那份 rc)
# eda2018:module_version(同目录版本别名)
# → /data/nfs/modulefiles/synopsys/eda2018/.modulerc.lua
# module_version("eda2018/1.0", "2018")
# vcs-el7:module_alias(全局别名,落在顶层 vcs/el7)
# → /data/nfs/modulefiles/synopsys/.modulerc.lua
# module_alias("vcs/el7", "vcs-el7/1.0")
坑(关键):module_version("vcs-el7/1.0", "vcs/el7") 不会得到顶层 vcs/el7 —— 实测得到
vcs-el7/vcs/el7(别名被附加到 name 所在目录)。要跨到顶层须用全局别名
module_alias(alias, known_version),故 vcs-el7 的 rc 放在 synopsys/ 家族目录(只登记该模块别名,
属主仍是 vcs_el7_binutils_20260929);eda2018 的别名放自身目录即可(同目录别名)。
步骤 4:下发并退役 NFS 冗余目录
# 写入 modulefile 与 rc(属 root;源在工程,见 C.4)
sudo -n install -m 0644 -o root -g root <源> <目标>
# 退役(高危;备份见第 7 章)
sudo -n rm -rf /data/nfs/modulefiles/synopsys_compat
实现体:rocky9_eda_compat_20260928/{main.py,deployer.py}(节点侧制品与包)、
vcs_el7_binutils_20260929/deploy_el7_binutils.py(vcs-el7 的 modulefile 与 rc)。
C.6 验证(.18 实测)¶
| # | 命令 | 结果 |
|---|---|---|
| 1 | ls /opt/modulefiles |
cadence Core pdk siemens synopsys(无 synopsys_compat) ✅ |
| 2 | module load synopsys && module load eda2018/1.0; echo $LD_PRELOAD |
/opt/compat/libexec/libpthread.so.0:/lib64/libz.so.1 ✅ |
| 3 | 同上 command -v dc_shell / spyglass -version |
dc_shell 定位;SpyGlass Exit Code 0 ✅ |
| 4 | PATH 是否含 /opt/compat/bin |
否(未引入 el7 binutils) ✅ |
| 5 | module load eda2018/2018 |
成功,dc_shell 可用 ✅ |
| 6 | module load vcs/el7 |
成功,ld 2.27-44.base.el7、which=/opt/compat/bin/ld ✅ |
| 7 | module load vcs-el7/1.0(原名) |
成功 ✅ |
| 8 | check_module_tree_syntax /opt/modulefiles |
No errors found ✅ |
| 9 | rocky9 部署器 --dry-run rollout |
六台「状态一致」→ skipped,零变更 ✅ |
| 10 | deploy_el7_binutils.py --apply |
modulefile / rc「内容一致,跳过」;7 台兼容层已一致 ✅ |
C.7 回滚¶
# 1) 恢复 NFS 兼容层目录(备份包,825 kB)
sudo -n tar -C /data/nfs/modulefiles -xzf \
lmod_cross_project_fix_20261005/backup/data/nfs/modulefiles/synopsys_compat.retired.tar.gz
# 2) 恢复 eda2018 模块(旧版,引用 NFS)
sudo -n install -m 0644 -o root -g root \
lmod_cross_project_fix_20261005/backup/data/nfs/modulefiles/synopsys/eda2018/1.0.lua.bak.<TS> \
/data/nfs/modulefiles/synopsys/eda2018/1.0.lua
# 3) 移除 D 的两个 rc
sudo -n rm -f /data/nfs/modulefiles/synopsys/eda2018/.modulerc.lua \
/data/nfs/modulefiles/synopsys/.modulerc.lua
# 4) 源码回退:从 backup/ 恢复源文件后复跑
代码侧:backup/eda_tools_ins/Synopsys/...(见 backup/README.md 第 3 节)。
回滚触发判据:任一节点 module load eda2018/1.0 报「兼容层缺失」,或 dc_shell / SpyGlass 不可用 → 按上式回滚。
C.8 风险说明¶
| 风险 | 缓解 |
|---|---|
eda2018 前置 /opt/compat/bin 会引入 el7 老 ld/ar |
已移除 PATH 前置;验证 #4 覆盖 |
删 NFS synopsys_compat 影响别处引用 |
全仓确认仅 eda2018 模块引用该路径;制品源已迁入工程 artifacts/share;删除前 tar 备份 |
| 兼容层长期一致性(节点本地不再是单一 NFS 源) | 制品源在工程 artifacts/share,由 rocky9 部署器按 sha256 逐台收敛;--dry-run 可随时查漂移 |
.28 的 /opt/compat/bin/uname 为旧版(md5 不同) |
.28 不在 nodes.work(不参与 rollout);其 /usr/local/bin/uname 已新版 982 B,SpyGlass 实际生效路径不受影响 |
module_alias 在旧版 Lmod 不可用 |
本机实测支持(module avail 出现 vcs/el7(@vcs-el7/1.0)) |
E deprecate() |
本轮不做、仅登记(三个候选模块 IC617 / calibre2015.2 / eda2018 均在用,加标注会误导) |
来源:
/home/rain/Documents/eda_tools_ins/lmod_cross_project_fix_20261005/SOP.md| 最后更新:2026-10-05 17:31(由build/sync.py同步,请勿在站点侧修改)