跳转至

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 两项,根因同为「同一路径两个属主、互不知情」,且都引入了行为回退:

  1. 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/... 无实体)而删除。任何一方复跑都会推翻另一方。
  2. 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. 目标

  1. /opt/modulefiles/synopsys/vcs/2023.12.lua 不再由任一侧产出或删除——跑过 --module-env 与跑过 fix_modules.py --apply 后状态一致(都不存在);
  2. module load siemens && module load calibre 在 6 台 + 提交节点成功,且加载到有实体的 calibre/2015.2_36.27;module avail 无指向不存在目录的 calibre / vcs 条目;
  3. module load synopsys && module load eda2018/1.0 && command -v vcs 仍可用;
  4. 全程幂等:复跑零变更、全跳过;不停服、不重启任何 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, 退役 NFS synopsys_compat,制品源迁入工程 artifacts/share/。
  • D 采用交接单方案 A(不改名 + 加别名):eda2018/1.0 加同目录版本别名 eda2018/2018; vcs-el7/1.0 加全局别名 vcs/el7。

C.3 目标

  1. /opt/modulefiles 下只含模块定义与 .modulerc*(C-1 验收判据);
  2. module load synopsys && module load eda2018/1.0 正常(LD_PRELOAD 生效、dc_shell 可用);
  3. 新旧写法并存:eda2018/1.0↔eda2018/2018、vcs-el7/1.0↔vcs/el7 均可加载;
  4. 幂等:两个部署工程复跑零变更。

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 同步,请勿在站点侧修改)