在 OpenBMC 测试自动化框架中,Robot Framework 经常被用于执行 BMC 功能测试、压力测试以及稳定性测试。
为了提升测试效率,openbmc-test-automation 提供了基于 Python multiprocessing 的并发执行能力,通过 Execute Process 同时启动多个测试任务,对 BMC 的 REST、SSH、IPMI 服务进行压力验证。
然而,在实际使用过程中,发现该多进程测试框架存在两个比较隐蔽的问题:
- 多进程执行结果返回异常,导致测试结果误判;
- 测试执行成功,但生成的 Robot Framework
output.xml文件损坏。
本文记录问题定位过程、根因分析以及最终解决方案。
一、问题背景
测试用例:
extended/test_bmc_stress_buster.robot
该测试用于验证 BMC 在高并发访问下的稳定性。
例如 IPMI stress 测试:
Stress BMC IPMI Server
[Documentation] Execute maximum allowed IPMI operation.
${dict}= Execute Process
... ${IPMI_BUSTER_MAX}
... IPMI Check Status
Dictionary Should Not Contain Value
... ${dict}
... False
其中:
IPMI_BUSTER_MAX控制并发数量;Execute Process来自:
lib/jobs_processing.py
它会创建多个 Python 子进程,每个子进程执行 Robot keyword。
整体流程:
Robot Test Case
|
v
Execute Process
|
v
jobs_processing.py
|
v
multiprocessing.Process
|
v
Robot Keyword Execution
二、第一个问题:Execute Process 返回结果异常
1. 问题现象
测试执行:
Stress BMC IPMI Server
出现:
Stress BMC IPMI Server | FAIL |
One or more IPMI operations has failed.
但是实际观察 IPMI 操作:
- 命令可以正常执行;
- BMC 有正常响应;
- 单独执行 keyword 可以成功。
查看返回结果:
${dict} =
{
'256555': 'False',
'256557': 'False',
'256559': 'False'
}
最终导致:
Dictionary Should Not Contain Value
... ${dict}
... False
失败。
问题看起来像是 IPMI 执行失败,但实际并不是。
三、定位 jobs_processing.py
继续检查:
lib/jobs_processing.py
关键代码:
def execute_keyword(keyword_name, return_dict):
pid = os.getpid()
status = BuiltIn().run_keyword_and_return_status(keyword_name)
return_dict[str(pid)] = str(status)
这里没有问题。
子进程执行结果:
True / False
可以正常写入。
继续查看:
def execute_process(num_process, keyword_name):
manager = Manager()
return_dict = manager.dict()
...
return return_dict
问题出现在这里。
四、Manager.dict() 与普通 dict 的区别
Python multiprocessing 提供:
Manager()
用于创建多个进程之间共享的数据。
例如:
manager = Manager()
return_dict = manager.dict()
这个对象并不是普通 Python dictionary。
它实际上是:
DictProxy Object
结构类似:
Process A
|
Process B
|
Process C
|
v
Manager Server
|
v
Dict Proxy
它通过 IPC(进程间通信)访问真实数据。
因此:
return return_dict
返回的是:
multiprocessing.managers.DictProxy
而不是:
dict
五、解决方案
将返回值转换成普通 Python dictionary。
修改:
- return return_dict
+ return dict(return_dict)
修改后:
def execute_process(num_process, keyword_name):
manager = Manager()
return_dict = manager.dict()
...
return dict(return_dict)
六、修改效果
修改后:
返回:
{
"256555": "True",
"256557": "True",
"256559": "True"
}
Robot Framework 可以正常处理:
Dictionary Should Not Contain Value
... ${dict}
... False
测试结果恢复正常。
七、第二个问题:output.xml 损坏
修复返回结果问题后,又发现另一个问题。
这次测试本身:
PASS
例如:
Stress BMC IPMI Server :: Execute maximum allowed IPMI operation. | PASS |
但是测试结束后:
Robot Framework 无法解析:
output.xml
错误:
[ ERROR ] Reading XML source 'output.xml' failed:
ParseError:
XML or text declaration not at start of entity
八、分析损坏的 output.xml
打开:
output.xml
发现:
文件中出现重复 XML 声明:
<?xml version="1.0" encoding="UTF-8"?>
<robot>
...
<?xml version="1.0" encoding="UTF-8"?>
<robot>
正常 XML:
XML declaration
|
v
<robot>
|
v
test cases
而异常 XML:
XML declaration
|
v
<robot>
|
v
XML declaration
|
v
<robot>
因此 XML parser 报错。
九、为什么直接执行 ipmitool 没问题?
原测试:
Run IPMI Standard Command
... chassis status
执行路径:
Robot keyword
|
Run IPMI Standard Command
|
IPMI library
|
ipmitool
替换为:
${cmd}= Catenate
... ipmitool
... chassis status
Run And Return Rc And Output
... ${cmd}
执行:
Robot
|
Shell
|
ipmitool
测试通过:
- IPMI 操作成功;
- output.xml 正常。
说明:
问题并不是 IPMI 命令本身。
更可能与:
- Robot keyword 调用方式;
- 多进程执行;
- keyword 输出处理;
有关。
十、Workaround
将:
Run IPMI Standard Command
... chassis status
替换:
IPMI Check Status
${cmd}= Catenate
... ipmitool
... -I lanplus
... -C ${IPMI_CIPHER_LEVEL}
... -N 3
... -p ${IPMI_PORT}
... -U ${OPENBMC_USERNAME}
... -P ${OPENBMC_PASSWORD}
... -H ${OPENBMC_HOST}
... chassis status
${rc} ${output}=
... Run And Return Rc And Output
... ${cmd}
Should Be Equal As Integers
... ${rc}
... 0
十一、经验总结
1. multiprocessing 返回对象要注意类型
在 Python multiprocessing 中:
Manager().dict()
不是:
dict
而是:
DictProxy
跨框架传递时,最好转换:
dict(proxy)
避免调用方无法正确处理。
2. 自动化框架问题不一定来自测试目标
本次问题初看像:
IPMI stress失败
实际上:
第一个问题:
multiprocessing result handling
第二个问题:
Robot output generation
测试目标 BMC/IPMI 本身没有问题。
3. PASS 不代表测试链路完整成功
Robot Framework 流程:
Test Execution
|
v
Generate output.xml
|
v
Generate log/report
任何一步失败都会影响 CI。
因此:
Test PASS
不一定代表:
Automation Pipeline PASS
十二、总结
本次 OpenBMC stress 测试问题最终发现两个独立问题:
| 问题 | 位置 | 解决方式 |
|---|---|---|
| Execute Process 返回结果异常 | jobs_processing.py | return dict(return_dict) |
| output.xml 损坏 | Robot 执行链路 | 使用直接 ipmitool workaround |
其中第一个问题已经通过代码修改解决;第二个问题仍需要进一步分析 Robot Framework 多进程执行和输出处理机制。
这类问题说明,在复杂自动化测试框架中,测试框架自身的稳定性与被测对象稳定性同样重要。