Redfish 事件接收服务搭建指南(Ubuntu Server 实测)

本文记录在 Ubuntu Server 测试控制台上搭建 Redfish 事件接收服务(HTTPS + Flask)的完整过程,用于接收 BMC 主动推送的 Redfish 事件,并自动解析测试用例要求的 16 个关键字段。


一、背景

在 Redfish 接口测试中,BMC 会通过订阅(Subscription)机制,将事件主动 POST 到我们指定的 Destination 地址。因此需要在一台"测试控制台"上搭建一个 HTTPS 事件接收服务,用来:

  • 接收 BMC 推送的 Redfish 事件;

  • 将事件原文写入日志文件,便于事后分析;

  • 实时解析并打印测试用例要求的 16 个关键字段。

关键要求 :BMC 要求事件接收地址必须是合法的 HTTPS 地址,所以服务必须启用 SSL 证书(测试环境使用自签名证书即可)。


二、环境说明

项目 说明
操作系统 Ubuntu Server 20.04 / 22.04 / 24.04 LTS
测试控制台 IP 192.168.2.100(示例,请按实际替换)
Python 系统预装 Python 3
依赖 Flask、OpenSSL
监听端口 8443

目录规划

在测试控制台的家目录下准备以下文件(本文以 ~/redfish-event-listener/ 为例):

复制代码
redfish-event-listener/
├── event_listener.py   # 事件接收服务脚本
├── cert.pem            # 自签名证书
├── server.key          # 证书私钥
└── logs/               # 日志目录(脚本自动创建)

三、安装 Python 环境

Ubuntu Server 默认已预装 Python 3,先确认版本:

复制代码
python3 --version

预期输出:

复制代码
Python 3.10.x   # 或 3.12.x,视 Ubuntu 版本而定

如未安装(极少见),执行:

复制代码
sudo apt update
sudo apt install -y python3 python3-pip python3-venv

四、安装 Flask 和 OpenSSL 依赖

方案 A:使用虚拟环境(推荐,避免污染系统 Python)

Ubuntu 22.04 及之后版本因 PEP 668 限制,直接用 pip3 install 安装系统级包会报错 externally-managed-environment,推荐用虚拟环境:

复制代码
cd ~/redfish-event-listener
python3 -m venv venv
source venv/bin/activate
pip install flask

之后每次启动服务前,需要先 source venv/bin/activate 激活虚拟环境。

方案 B:使用系统包管理器安装

复制代码
sudo apt update
sudo apt install -y python3-flask openssl

这种方式不需要虚拟环境,系统会统一管理 Flask 的版本。

方案 C:强制 pip 安装(不推荐,仅供快速试验)

复制代码
pip3 install flask --break-system-packages

⚠️ 使用 --break-system-packages 会绕过系统包管理保护,建议只在临时验证时使用。


五、生成自签名 SSL 证书

BMC 要求事件接收地址为合法的 HTTPS 地址。在测试控制台上生成自签名证书:

复制代码
mkdir -p ~/redfish-event-listener
cd ~/redfish-event-listener

openssl req -x509 -newkey rsa:2048 \
  -keyout server.key \
  -out cert.pem \
  -days 365 \
  -nodes \
  -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=Test/CN=192.168.2.100"

⚠️ 注意 :其中 CN 必须设置为测试控制台的真实 IP 地址 。上例假设控制台 IP 为 192.168.2.100,请根据实际情况替换。

生成完成后,cert.pem 与 server.key 应与 event_listener.py 位于同一目录下。


六、编写事件接收服务脚本

创建 event_listener.py:

bash 复制代码
#!/usr/bin/env python3
# Redfish 事件接收服务
#  - 监听 HTTPS 端口,接收 BMC 推送的 Redfish 事件
#  - 将事件写入 logs/events.log,并打印到控制台
#  - 自动解析测试用例要求的 16 个关键字段

import os
import ssl
import json
import datetime

from flask import Flask, request

# 基础路径(基于脚本所在目录)
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
CERT_FILE = os.path.join(BASE_DIR, "cert.pem")
KEY_FILE = os.path.join(BASE_DIR, "server.key")
LOG_DIR = os.path.join(BASE_DIR, "logs")
LOG_FILE = os.path.join(LOG_DIR, "events.log")

os.makedirs(LOG_DIR, exist_ok=True)

app = Flask(__name__)


def log_event(event_data: dict):
    """将事件写入日志文件并打印到控制台"""
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    separator = "=" * 80

    with open(LOG_FILE, "a", encoding="utf-8") as f:
        f.write(f"\n{separator}\n")
        f.write(f"[接收时间] {timestamp}\n")
        f.write(json.dumps(event_data, indent=2, ensure_ascii=False))
        f.write(f"\n{separator}\n")

    print(f"\n{separator}")
    print(f"[接收时间] {timestamp}")
    print(json.dumps(event_data, indent=2, ensure_ascii=False))
    print(separator)


def extract_event_fields(event_data: dict):
    """提取并展示测试用例要求的 16 个关键字段"""
    print("\n-- 关键字段验证 --")

    events = event_data.get("Events", [])
    if not events:
        print("[警告] 事件体中没有 Events 数组")
        return

    for idx, event in enumerate(events):
        print(f"\n[事件 #{idx + 1}]")
        print(f"  EventType      : {event.get('EventType', 'N/A')}")
        print(f"  EventId        : {event.get('EventId', 'N/A')}")
        print(f"  EventTimestamp : {event.get('EventTimestamp', 'N/A')}")
        print(f"  Severity       : {event.get('Severity', 'N/A')}")
        print(f"  Message        : {event.get('Message', 'N/A')}")

        oem = event.get("Oem", {}).get("Public", {})
        print(f"  Oem.Public.alarmStatus       : {oem.get('alarmStatus', 'N/A')}")
        print(f"  Oem.Public.specificProblemID : {oem.get('specificProblemID', 'N/A')}")
        print(f"  Oem.Public.specificProblem   : {oem.get('specificProblem', 'N/A')}")
        print(f"  Oem.Public.neUID             : {oem.get('neUID', 'N/A')}")
        print(f"  Oem.Public.neName            : {oem.get('neName', 'N/A')}")
        print(f"  Oem.Public.neType            : {oem.get('neType', 'N/A')}")
        print(f"  Oem.Public.objectUID         : {oem.get('objectUID', 'N/A')}")
        print(f"  Oem.Public.objectName        : {oem.get('objectName', 'N/A')}")
        print(f"  Oem.Public.objectType        : {oem.get('objectType', 'N/A')}")
        print(f"  Oem.Public.locationInfo      : {oem.get('locationInfo', 'N/A')}")
        print(f"  Oem.Public.Addinfo           : {oem.get('Addinfo', 'N/A')}")


@app.route("/event-receiver", methods=["POST"])
def receive_event():
    """接收 BMC 推送的 Redfish 事件"""
    try:
        event_data = request.get_json(force=True)
        if event_data is None:
            event_data = {}

        log_event(event_data)
        extract_event_fields(event_data)

        # 按 Redfish 规范返回 204 No Content
        return "", 204
    except Exception as e:
        print(f"[错误] 解析事件失败: {e}")
        return "", 400


def main():
    # 检查证书文件是否存在
    if not os.path.exists(CERT_FILE):
        print(f"[错误] 找不到证书文件: {CERT_FILE}")
        print("请确保证书 cert.pem 与 event_listener.py 在同一目录下。")
        return

    if not os.path.exists(KEY_FILE):
        print(f"[错误] 找不到私钥文件: {KEY_FILE}")
        print("请确保私钥 server.key 与 event_listener.py 在同一目录下。")
        return

    # 配置 SSL
    context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    context.load_cert_chain(certfile=CERT_FILE, keyfile=KEY_FILE)

    print("=" * 60)
    print("Redfish 事件接收服务已启动")
    print("监听地址: https://0.0.0.0:8443/event-receiver")
    print(f"证书文件: {CERT_FILE}")
    print(f"私钥文件: {KEY_FILE}")
    print(f"日志文件: {LOG_FILE}")
    print("=" * 60)

    app.run(
        host="0.0.0.0",
        port=8443,
        ssl_context=context,
        debug=False,
        threaded=True
    )


if __name__ == "__main__":
    main()

脚本要点说明

  1. 路径自适配:所有文件路径均基于脚本所在目录计算,脚本放哪都能跑。

  2. 日志自动落盘 :logs/ 目录不存在时自动创建,事件以 ==== 分隔追加写入 events.log。

  3. 返回 204 :Redfish 事件推送要求服务端返回 204 No Content,脚本已按要求处理。

  4. 多线程 :threaded=True,避免 BMC 并发推送时阻塞。


七、启动事件接收服务

复制代码
cd ~/redfish-event-listener

# 如果使用虚拟环境(方案 A),先激活
source venv/bin/activate

python3 event_listener.py

服务启动后输出:

复制代码
============================================================
Redfish 事件接收服务已启动
监听地址: https://0.0.0.0:8443/event-receiver
证书文件: /home/ubuntu/redfish-event-listener/cert.pem
私钥文件: /home/ubuntu/redfish-event-listener/server.key
日志文件: /home/ubuntu/redfish-event-listener/logs/events.log
============================================================

八、开放防火墙端口

Ubuntu Server 默认使用 UFW 管理防火墙(很多镜像默认状态为 inactive)。先查看状态:

复制代码
sudo ufw status

如状态为 inactive,需要先启用(注意:启用时务必先放行 SSH,避免把自己锁在外面):

复制代码
sudo ufw allow ssh
sudo ufw enable

然后放行 8443 端口:

复制代码
sudo ufw allow 8443/tcp
sudo ufw reload

再次确认:

复制代码
sudo ufw status

应能看到 8443/tcp ALLOW 的规则。

如服务器用的是 iptables 或云厂商安全组,也需要在对应位置放行 8443/tcp。


九、本地验证服务连通性

在测试控制台上另开一个终端,用 curl 模拟推送一条事件:

复制代码
curl -k -X POST https://127.0.0.1:8443/event-receiver \
  -H "Content-Type: application/json" \
  -d '{"Events": [{"EventType": "Alert", "Message": "test"}]}'

预期结果:

  • 事件接收服务终端打印出测试事件内容与关键字段验证结果;

  • curl 返回 HTTP 204;

  • logs/events.log 中新增一条记录。


十、关键提醒(避坑指南)

  1. Destination 必须是真实可达的地址

    在 BMC 上创建事件订阅时,Destination 必须填测试控制台真实可达的 HTTPS 地址,例如:

    复制代码
    https://192.168.2.100:8443/event-receiver

    IP 与端口需和本服务一致,且证书 CN 要与之对应。

  2. 防火墙必须放行端口

    Ubuntu 默认 UFW inactive,很多人忘了这一步;若启用了云服务器,还要在云厂商安全组同步放行。BMC 是主动连接方,控制台侧 8443 不通则订阅永远收不到事件。

  3. PEP 668 限制(Ubuntu 22.04+)

    直接 pip3 install flask 会报 externally-managed-environment 错误,请使用本文提供的虚拟环境或 apt install python3-flask 方案。

  4. 证书与脚本必须同目录

    cert.pem、server.key 与 event_listener.py 放同一目录,否则脚本启动时会直接报错退出。

  5. 端口冲突

    若 8443 已被占用,可修改脚本中的 port,同时同步修改订阅 Destination 和防火墙规则。

  6. 自签名证书的 CN 与 SAN

    现代 TLS 客户端校验更严格,若 BMC 对证书校验较严,可在生成证书时额外指定 subjectAltName(SAN)为控制台 IP,兼容性更好。例如:

    复制代码
    openssl req -x509 -newkey rsa:2048 \
      -keyout server.key -out cert.pem -days 365 -nodes \
      -subj "/C=CN/ST=Guangdong/L=Shenzhen/O=Test/CN=192.168.2.100" \
      -addext "subjectAltName=IP:192.168.2.100"

十一、总结

整个流程可以概括为 5 步:

  1. 确认 Python3 环境 → 2. 安装 Flask/OpenSSL → 3. 生成自签名证书 → 4. 部署事件接收脚本 → 5. 放行防火墙并用 curl 自测。

在 Ubuntu Server 上特别需要注意 PEP 668 安装限制 和 UFW 默认未启用 这两个坑,其余步骤与常规 Linux 发行版基本一致。验证通过后,即可在 BMC 侧创建 EventDestination 订阅,把事件推送到本服务,开始联调 Redfish 告警上报功能。

如果这篇文章对你有帮助,欢迎点赞收藏 👍,有问题也欢迎在评论区交流~

相关推荐
fengyangaiGEO1 小时前
如何通过GEO优化实现外贸B2B询盘增长?
人工智能·python
jianxuan1 小时前
Spring Boot + Maven 接入 Allatori:从普通打包到可选混淆发布
spring boot·python·maven
德迅云安全-上官1 小时前
服务器遭攻击如何快速响应?从技术原理到接入防御架构的实战指南
运维·服务器·架构
不一样的故事1261 小时前
Prompt 工程(Prompt Engineering)
前端·人工智能·python
我命由我123451 小时前
6-2.Python 异常处理 - 异常捕获(捕获指定异常、常见异常类型、异常信息)
java·运维·开发语言·python·http·https·java-ee
zh路西法1 小时前
【模型式强化学习】(三):MBPO——短轨迹生成与模型数据驱动的策略优化
python·强化学习·pe·sac·pets·mbpo·mbrl
倔强的石头1061 小时前
【Linux指南】进程通信系列(四):命名管道 FIFO —— 让陌生进程按名字见面
linux·服务器·网络
Angeldream1231 小时前
第 9 章 医学影像项目实战
大数据·图像处理·人工智能·python·医学影像处理
ZhengEnCi1 小时前
一亿条数据取前100条完全指南:从全量排序到分块流式最小堆的4种解法实测
python·算法·排序算法