OpenLava EDA 实战(二):License 调度管理与动态资源采集
OpenLava EDA 实战(二):License 调度管理与动态资源采集
摘要:EDA 工具的 License 是昂贵且稀缺的资源,"作业启动了但 License 不够导致失败"是 EDA 集群最常见的痛点之一。本文讲解如何把 FlexLM License 数量纳入 OpenLava 的调度体系:动态采集 License 余量、配置可消耗资源、作业按 License 需求调度、避免 License 抢用冲突。同时分享 License 监控告警、统计分析的实践方案。
上一篇讲了 EDA 集群搭建和工具接入。这一篇解决 EDA 集群最核心的痛点:License 调度。
零、读前必看
0.1 适合谁
- ✅ 管理 EDA 集群,经常遇到 License 不够用的问题
- ✅ 想让 OpenLava 自动根据 License 余量调度作业
- ✅ 需要 License 使用率监控和统计
0.2 你将学到
| 主题 | 重点 |
|---|---|
| 痛点分析 | 为什么需要 License 调度 |
| 动态资源采集 | elm 外部资源采集脚本编写 |
| 可消耗资源配置 | lsf.shared / lsf.cluster 配置 |
| 作业提交 | -R "rusage[vcs=1]" 调度 |
| License 监控 | 使用率监控 + 告警 |
| 统计分析 | License 利用率报表 |
| 高级技巧 | License 队列、优先级、抢占 |
一、为什么需要 License 调度
1.1 痛点场景
场景:你有 5 个 VCS License,同时提交了 10 个 VCS 编译作业。
>
结果:5 个作业抢到 License 跑起来了,另外 5 个启动时报 "License checkout failed",直接失败退出。
>
浪费了调度时间、占了计算资源,作业还失败了。
1.2 理想状态
作业在排队时就知道 License 够不够,License 不足时就在队列里等,有了再调度。
作业 A(需要 1 VCS License)
│
▼
OpenLava 调度器检查:
- CPU 够不够? ✅
- 内存够不够? ✅
- VCS License 够不够? ❌(只剩 0.5 个)
│
▼
作业继续 PEND 等待
│
▼
等别的作业释放了 License → 再调度运行
1.3 OpenLava 的解决方案
通过外部资源管理器(ELM / External Load Manager),定期采集 License 余量,作为可消耗资源(Consumable Resource)纳入调度。
| 概念 | 说明 |
|---|---|
| ELM | External Load Manager,外部负载采集器 |
| 可消耗资源 | 作业用了就减少,释放了就归还的资源类型 |
| rusage | 作业声明自己消耗多少资源 |
二、FlexLM License 信息采集
2.1 lmstat 命令
FlexLM 提供 lmstat 工具查询 License 状态:
# 查看所有 License 状态
lmstat -a -c 27000@lic_server01
# 查看特定 feature
lmstat -f VCS_MX -c 27000@lic_server01
# Users of VCS_MX: (Total of 5 licenses issued; Total of 2 licenses in use)
#
# "VCS_MX" v2018.09, vendor: snpslmd, expiry: 31-dec-2025
# vendor_string:
# floating license
#
# user1 workstation1 workstation1 (v2018.09) (lic_srv/27000 1234), start Wed 1/15 10:00
# user2 workstation2 workstation2 (v2018.09) (lic_srv/27000 5678), start Wed 1/15 11:30
2.2 提取 License 余量
写个脚本解析 lmstat 输出,计算剩余 License 数量:
#!/bin/bash
# /data/eda/scripts/get_license_remain.sh
# 输出格式:resource_name value
LIC_SERVER="27000@lic_server01"
LMSTAT="/data/eda/tools/synopsys/scl/2019.06/linux64/bin/lmstat"
# 函数:提取某个 feature 的剩余数量
get_remain() {
local feature=$1
local output=$($LMSTAT -f "$feature" -c "$LIC_SERVER" 2>/dev/null)
local total=$(echo "$output" | grep "Total of.*licenses issued" | grep -oP 'Total of \K\d+' | head -1)
local in_use=$(echo "$output" | grep "Total of.*licenses in use" | grep -oP 'in use\).*Total of \K\d+' | head -1)
if [ -z "$total" ] || [ -z "$in_use" ]; then
echo "0" # 查不到就返回 0,保守起见
return
fi
local remain=$((total - in_use))
echo "$remain"
}
# 输出各 License 余量
echo "vcs_license $(get_remain VCS_MX)"
echo "verdi_license $(get_remain Verdi3)"
echo "dc_license $(get_remain Design-Compiler)"
echo "simvision_license $(get_remain SimVision)"
测试一下:
chmod +x /data/eda/scripts/get_license_remain.sh
/data/eda/scripts/get_license_remain.sh
# vcs_license 3
# verdi_license 4
# dc_license 1
# simvision_license 2
每一行的格式是 **`资源名 数值`**,这是 OpenLava ELM 期望的输出格式。
三、配置 OpenLava 动态资源
3.1 第一步:在 lsf.shared 里定义资源
编辑 /opt/openlava/etc/lsf.shared:
Begin Resource
RESOURCENAME TYPE INTERVAL INCREASING CONSUMABLE DESCRIPTION
# EDA License 资源(可消耗)
vcs_license Numeric 60 N Y (VCS license count)
verdi_license Numeric 60 N Y (Verdi license count)
dc_license Numeric 60 N Y (Design Compiler license)
simv_license Numeric 60 N Y (SimVision license count)
End Resource
关键字段:
TYPE = Numeric:数值型INTERVAL = 60:每 60 秒采集一次INCREASING = N:值越小越"好"(因为是余量,多了更好?这里要注意方向)CONSUMABLE = Y:可消耗资源,作业占了就减少
**关于 INCREASING 的说明**:
- License 余量是"越多越好"的资源,所以理论上 `INCREASING = Y`
- 但对调度而言,Consumable 资源主要看"够不够",不是用于排序选择
- 实际配置中设 N 也能工作,重点是 `CONSUMABLE = Y`
3.2 第二步:配置外部资源采集器(ELM)
编辑 /opt/openlava/etc/lsf.cluster.openlava,加入 ResourceMap:
Begin ResourceMap
RESOURCENAME LOCATION
vcs_license [all]
verdi_license [all]
dc_license [all]
simv_license [all]
End ResourceMap
`[all]` 表示这是集群级资源,所有节点共享,不绑定到具体节点。
3.3 第三步:配置 LIM 外部采集
在 lsf.conf 里配置外部采集脚本(不同版本 OpenLava 配置方式略有差异):
# /opt/openlava/etc/lsf.conf
LSF_ELM_ENABLE=Y
LSF_ELM_INTERVAL=60
LSF_ELM_SCRIPT=/data/eda/scripts/get_license_remain.sh
**注意**:不同 OpenLava 版本的 ELM 配置方式可能不同。有的版本是在 `lsf.cluster` 的 Resource 段里配 `external`,有的是独立的 `elim`(External Load Information Manager)机制。请参考你对应版本的文档。
3.4 第四步(备用方案):用 cron 周期更新
如果你的 OpenLava 版本没有标准 ELM 机制,可以用 cron + lsload / lsgrun 方式模拟:
#!/bin/bash
# /data/eda/scripts/update_license_resource.sh
# 定时采集 License 余量,更新到 OpenLava 资源
SCRIPT=/data/eda/scripts/get_license_remain.sh
OUTPUT=$($SCRIPT)
# 通过 lsadmin 命令或自定义方式更新
# 具体方式取决于你的 OpenLava 版本支持
echo "$OUTPUT" | while read resource value; do
echo "Updating $resource = $value"
# 方式A: 如果支持动态资源更新
# lsadmin limcontrol ...
# 方式B: 写入临时文件,由 LIM 读取
done
加到 crontab:
* * * * * /data/eda/scripts/update_license_resource.sh >> /var/log/license_update.log 2>&1
**说明**:OpenLava 的动态资源更新机制版本差异较大。如果官方 ELM 机制不可用,常见的替代方案是:
1. 自定义 elim 可执行文件,放在 LIM 能找到的路径
2. 用 `lsadmin reconfig` 重新加载静态配置(适合变化不频繁的)
3. 用第三方脚本 + 队列控制的方式间接管理
3.5 验证资源配置
# 1. 查看资源定义
lsinfo -r | grep -E "vcs|verdi|dc_license"
# 2. 查看资源值
lsload -l | head -20
# 应该能看到 vcs_license、verdi_license 等字段和对应的值
# 3. 确认值正确
lmstat -f VCS_MX -c 27000@lic_server01 | grep "Total of"
# 和 lsload 里看到的对比
四、按 License 调度作业
4.1 提交 VCS 作业
bsub -q normal \
-n 4 \
-R "rusage[mem=8192, vcs_license=1] span[hosts=1]" \
-J vcs_compile \
-o vcs_compile.log \
"vcs -full64 -f filelist.f -debug_access+all -o simv -j4"
关键点:-R "rusage[vcs_license=1]" —— 告诉调度器这个作业需要 1 个 VCS License。
调度器会:
- 看 CPU/内存够不够
- 看
vcs_license余量够不够 - 都够 → 调度运行
- 不够 → 继续 PEND 等
4.2 同时需要多个 License
# VCS 仿真 + Verdi(同时用两个 License)
bsub -n 2 \
-R "rusage[mem=16384, vcs_license=1, verdi_license=1]" \
-o sim.log \
"./simv +fsdb=wave.fsdb +UVM_VERBOSITY=MEDIUM"
4.3 查看 PEND 原因
bjobs -p <jobid>
# Job is waiting for the following resources:
# 1 host with vcs_license>=1
看到 vcs_license>=1,说明作业在等 License,而不是等 CPU/内存。
4.4 队列配置优化
建议按 License 类型划分队列,更精细地控制:
Begin Queue
QUEUE_NAME = vcs_normal
PRIORITY = 30
RESOURCE_REQ = rusage[vcs_license=1]
QJOB_LIMIT = 20
HOSTS = all
USERS = all
End Queue
Begin Queue
QUEUE_NAME = dc_synthesis
PRIORITY = 40
RESOURCE_REQ = rusage[dc_license=1]
QJOB_LIMIT = 5
HOSTS = all
USERS = dc_team
End Queue
好处:
- 不同工具的作业互不干扰
- 可以针对每种 License 设置不同的队列上限
- 用户体验更好(提交到对应的队列就行)
五、License 监控告警
5.1 使用率监控
#!/bin/bash
# /data/eda/scripts/check_license_usage.sh
# 检查各 License 使用率,超过阈值告警
LIC_SERVER="27000@lic_server01"
LMSTAT="/data/eda/tools/synopsys/scl/2019.06/linux64/bin/lmstat"
THRESHOLD=80 # 超过 80% 告警
check_feature() {
local feature=$1
local output=$($LMSTAT -f "$feature" -c "$LIC_SERVER" 2>/dev/null)
local total=$(echo "$output" | grep "licenses issued" | grep -oP 'Total of \K\d+' | head -1)
local in_use=$(echo "$output" | grep "licenses in use" | grep -oP 'in use\).*Total of \K\d+' | head -1)
if [ -z "$total" ] || [ "$total" -eq 0 ]; then
return
fi
local usage=$((in_use * 100 / total))
echo "$feature: $in_use/$total (${usage}%)"
if [ "$usage" -ge "$THRESHOLD" ]; then
echo "WARNING: $feature 使用率超过 ${THRESHOLD}%!"
# 这里可以接邮件/钉钉/企业微信告警
fi
}
for feature in VCS_MX Verdi3 Design-Compiler SimVision; do
check_feature "$feature"
done
5.2 Prometheus 监控(推荐)
如果你有 Prometheus,写个 exporter 更专业:
#!/usr/bin/env python3
"""
FlexLM License Exporter for Prometheus
"""
import subprocess
import re
from prometheus_client import Gauge, start_http_server
import time
LICENSE_SERVERS = ["27000@lic_server01"]
FEATURES = ["VCS_MX", "Verdi3", "Design-Compiler"]
LMSTAT = "/data/eda/tools/synopsys/scl/2019.06/linux64/bin/lmstat"
# 定义指标
license_total = Gauge('eda_license_total', 'Total licenses', ['feature', 'vendor'])
license_used = Gauge('eda_license_used', 'Used licenses', ['feature', 'vendor'])
license_available = Gauge('eda_license_available', 'Available licenses', ['feature', 'vendor'])
def collect_licenses():
for server in LICENSE_SERVERS:
for feature in FEATURES:
try:
result = subprocess.run(
[LMSTAT, '-f', feature, '-c', server],
capture_output=True, text=True, timeout=10
)
output = result.stdout
# 提取总数
total_match = re.search(r'Total of (\d+) licenses issued', output)
used_match = re.search(r'Total of (\d+) licenses in use', output)
if total_match and used_match:
total = int(total_match.group(1))
used = int(used_match.group(1))
avail = total - used
license_total.labels(feature=feature, vendor='synopsys').set(total)
license_used.labels(feature=feature, vendor='synopsys').set(used)
license_available.labels(feature=feature, vendor='synopsys').set(avail)
except Exception as e:
print(f"Error collecting {feature}: {e}")
if __name__ == '__main__':
start_http_server(9100) # 监听 9100 端口
while True:
collect_licenses()
time.sleep(60)
5.3 告警规则(Prometheus)
groups:
- name: license_alerts
rules:
- alert: LicenseUsageHigh
expr: eda_license_used / eda_license_total > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "License 使用率 > 90%"
description: "{{ $labels.feature }} 使用率 {{ $value | humanizePercentage }}"
- alert: LicenseServerDown
expr: eda_license_total == 0
for: 2m
labels:
severity: critical
annotations:
summary: "License 服务器不可达"
description: "{{ $labels.feature }} 查不到 License 总数"
- alert: LicenseExpiringSoon
expr: days_until_license_expiry < 30
for: 1h
labels:
severity: warning
annotations:
summary: "License 即将过期(<30天)"
六、License 使用统计
6.1 历史使用率统计
#!/bin/bash
# /data/eda/scripts/license_daily_report.sh
# 每日 License 使用统计
LOG_DIR=/data/eda/license_logs
DATE=$(date +%Y%m%d)
# 每 5 分钟采样一次,写入日志
# 可以用 awk 分析峰值、平均值
echo "=== License 日报 $(date +%Y-%m-%d) ==="
echo
for feature in VCS_MX Verdi3 Design-Compiler; do
echo "--- $feature ---"
# 从采样日志里统计
if [ -f "$LOG_DIR/${feature}_${DATE}.log" ]; then
echo "峰值: $(awk '{print $2}' "$LOG_DIR/${feature}_${DATE}.log" | sort -rn | head -1)"
echo "平均: $(awk '{sum+=$2; n++} END {printf "%.1f", sum/n}' "$LOG_DIR/${feature}_${DATE}.log")"
fi
done
6.2 月度趋势分析
把每天的数据存下来,月底生成趋势图:
import pandas as pd
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
# 读取一个月的采样数据
df = pd.read_csv('license_usage_202401.csv')
df['time'] = pd.to_datetime(df['time'])
df = df.set_index('time')
# 按小时取最大值
hourly_max = df.resample('H').max()
# 画趋势图
plt.figure(figsize=(12, 6))
for col in ['vcs_license', 'verdi_license']:
plt.plot(hourly_max.index, hourly_max[col], label=col)
plt.title('Monthly License Usage (Hourly Peak)')
plt.legend()
plt.savefig('license_monthly_trend.png')
用途:
- 评估 License 是否够用
- 决定要不要加买 License
- 做预算分析
七、高级技巧
7.1 License 队列分级
Begin Queue
QUEUE_NAME = vcs_priority
PRIORITY = 50
RESOURCE_REQ = rusage[vcs_license=1]
QJOB_LIMIT = 3
USERS = lead_engineer project_manager
PREEMPTION = PREEMPTIVE
End Queue
Begin Queue
QUEUE_NAME = vcs_normal
PRIORITY = 30
RESOURCE_REQ = rusage[vcs_license=1]
QJOB_LIMIT = 5
USERS = all
PREEMPTION = PREEMPTABLE
End Queue
逻辑:
- 高优先级队列的作业来了,可以抢占低优先级队列的 License
- 用于"紧急任务插队"场景
**慎用抢占**。被抢占的作业会失败退出,需要配合 checkpoint 或自动重跑。
7.2 License + FairShare
结合 FairShare,保证不同团队公平使用 License:
Begin Queue
QUEUE_NAME = vcs_shared
PRIORITY = 30
RESOURCE_REQ = rusage[vcs_license=1]
FAIRSHARE = USER_GROUP_SHARES[
[team_A, 40, [default, 1]]
[team_B, 30, [default, 1]]
[team_C, 30, [default, 1]]
]
End Queue
三个团队按 4:3:3 的比例共享 10 个 VCS License,不会被一个团队占满。
7.3 避免 License 泄漏
License 泄漏:作业被杀掉了,但 License 没及时归还,被 FlexLM 认为还在使用。
原因:
- 作业被
bkill -r强制杀掉,进程来不及释放 License - 节点宕机
解决方案:
# 1. 用 epilog 脚本,作业结束后确认 License 释放
# /data/eda/scripts/epilog_license_check.sh
# (具体取决于工具,有些需要手动 lmremove)
# 2. 配置 FlexLM 的 TIMEOUT(长作业不建议设太短)
# 在 License 文件里加:
# OPTIONS= /data/eda/license/synopsys.opt
# 在 synopsys.opt 里:
# TIMEOUT VCS_MX 3600
# 3. 定期检查 + 手动释放
# lmremove -c 27000@lic_server01 VCS_MX user1 host1 display1
7.4 License 借用(roaming)
用户带回家办公用,可借用 License。但要注意借用数量影响集群可用数量。
八、常见问题
Q1:License 采集脚本不执行?
检查:
- 脚本有执行权限:
chmod +x - LIM 用户(lsf)能执行脚本
- 脚本路径在 PATH 里或者写绝对路径
- 看
lim.log有没有报错
Q2:作业调度了但还是 License 失败?
可能原因:
- 采集延迟:采集是周期性的(比如 60 秒),调度时刻看到有余量,但实际启动时已经被别的作业抢了
- 调度器和真实 License 不同步:外部采集有延迟
- License 被集群外用户占用:比如有人在自己工作站上用
缓解措施:
- 缩短采集间隔(但别太短,增加 License Server 负担)
- 保守估计(比如配置时少算 1-2 个做缓冲)
- 对作业加启动重试机制
Q3:怎么统计每个用户用了多少 License?
# 实时看
lmstat -f VCS_MX -c 27000@lic_server01
# 历史统计
# 用采样日志,按用户聚合
也可以用专业的 License 管理工具(如 FlexNet Manager、Reprise RLM)。
九、写在最后
License 调度是 EDA 集群的核心竞争力——同样 5 个 VCS License,调度得好团队效率翻倍,调度不好天天有人喊"License 不够"。
核心思路就三点:
- 采得到:把 License 余量动态采集到 OpenLava
- 算得准:作业声明自己消耗多少 License,调度器据此调度
- 看得见:监控 + 统计 + 告警,掌握 License 使用情况
下一篇我们讲 VCS 仿真最佳实践:怎么组织 VCS 编译和仿真作业、并行编译、波形管理、调试技巧、回归测试等。
你们团队 License 够用吗?最紧缺的是哪个工具? 评论区聊聊。







