*openUBMC 支持redfish接口指定硬盘进行升级详细设计说明书*

<table>
    <tr>
        <td>所属SIG组:</td>
        <td>hardware</td>
    </tr>
    <tr>
        <td>落入版本:</td>
        <td>26.03</td>
    </tr>
    <tr>
        <td>设计人员:</td>
        <td>yelmh</td>
    </tr>
    <tr>
        <td>日期:</td>
        <td>2026/01/30</td>
    </tr>
</table>


**Copyright © 2026 openUBMC Community**

您对&quot;本文档&quot;的复制，使用，修改及分发受木兰宽松许可证, 第2版协议(以下简称&quot;MulanPSL2&quot;)的约束。
为了方便用户理解，您可以通过访问<https://license.coscl.org.cn/MulanPSL2>了解MulanPSL2的概要 (但不是替代)。
MulanPSL2的完整协议内容您可以访问如下网址获取：<https://license.coscl.org.cn/MulanPSL2>。

**改版记录**

<table>
    <tr>
        <th>日期</th>
        <th>修订版本</th>
        <th>修订描述</th>
        <th>作者</th>
        <th>审核</th>
    </tr>
    <tr>
        <td>2026/01/30</td>
        <td>1.0</td>
        <td>新增支持Redfish接口指定硬盘进行升级</td>
        <td>yelmh</td>
        <td>xxx</td>
    </tr>
</table>

**List of abbreviations**  **缩略语清单** ：

<table>
    <tr>
        <th>Abbreviations 缩略语</th>
        <th>Full spelling 英文全名</th>
        <th>Chinese explanation 中文解释</th>
    </tr>
    <tr>
        <td>Redfish</td>
        <td>Redfish RESTful API</td>
        <td>DMTF定义的RESTful管理接口规范</td>
    </tr>
    <tr>
        <td>URI</td>
        <td>Uniform Resource Identifier</td>
        <td>统一资源标识符</td>
    </tr>
</table>

[TOC]

# 1.功能分析
## 1.1 功能背景
在已有“带外升级自研RAID卡管理的硬盘”能力基础上，用户通过Redfish接口发起硬盘固件升级时，当前仅支持对符合Model匹配条件的全部硬盘进行升级（全量升级）。实际运维场景中，用户可能需要对指定的一块或多块硬盘进行升级，而非全部硬盘。本需求通过扩展Redfish UpdateService.SimpleUpdate接口的Targets参数，支持用户指定目标硬盘URI列表，实现指定硬盘升级能力，提升升级的灵活性与精确性。

## 1.2 功能描述
扩展Redfish接口`/redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate`（POST方法），新增请求体扩展参数`Targets`。该参数为字符串数组，元素为硬盘的Redfish URI（形如`/redfish/v1/Chassis/1/Drives/HDDPlaneDiskX`），来源于FirmwareInventory资源的RelatedItem字段。当用户传入Targets时，仅对指定的硬盘进行升级；当Targets为空或未传入时，沿用原有全量匹配逻辑（根据update.cfg的Model列表筛选所有符合条件的硬盘）。

方案概述：
1. **Redfish接口层**：SimpleUpdate请求体新增`Targets`参数（type: array of string），符合DMTF UpdateService v1.2.0规范。
2. **rackmount层**：在调用固件管理StartUpgrade前，将Targets字符串数组转换为逗号分隔的字符串，作为扩展参数填充到parameters中。
3. **固件管理层**：firmware_mgmt通过prepare/process/finish信号与storage交互，parameters透传Targets字符串。
4. **storage层**：在Process阶段解析parameters.Targets，若为非空字符串则进行指定硬盘校验与升级，否则走全量匹配。

## 1.3 功能场景
1) **场景触发条件及对象**：运维人员/网管通过Redfish接口发起硬盘固件升级，在请求体中传入Targets参数指定目标硬盘URI列表。
2) **使用时间及频度**：按版本发布/问题修复节奏，或针对单盘/多盘故障修复场景进行指定升级。

典型场景：
- 用户从`/redfish/v1/UpdateService/FirmwareInventory/:id`（GET）响应中获取硬盘固件的RelatedItem字段（如`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1`），将其作为Targets元素传入SimpleUpdate，仅升级该指定硬盘。
- 用户传入多个Targets URI，仅对指定的多块硬盘进行升级。
- 用户不传Targets或传空数组，沿用全量匹配逻辑，对符合Model的所有硬盘升级。

## 1.4 功能列表
| 功能编号 | 功能标题 | 功能描述 |
| ------- | ------- | -------- |
| 1 | Redfish Targets参数扩展 | SimpleUpdate请求体支持Targets（字符串数组），元素为硬盘Redfish URI |
| 2 | rackmount参数转换 | 将Targets数组转换为逗号分隔字符串，填充到StartUpgrade扩展参数 |
| 3 | storage指定硬盘校验 | 解析Targets字符串，校验URI格式、硬盘存在性、在位、自研RAID卡管理、Model匹配 |
| 4 | 指定硬盘升级执行 | 仅对校验通过的指定硬盘下发升级，任一指定硬盘不满足则整体失败 |

# 2.功能设计
## 2.1 总体方案分析
在现有“带外升级自研RAID卡管理的硬盘”架构基础上，增加“指定硬盘”分支逻辑。数据流为：Redfish请求体Targets（array）→ rackmount转换为字符串 → firmware_mgmt透传parameters → storage解析并校验 → 指定硬盘升级或全量升级。

### 2.1.1 方案详细设计
#### 2.1.1.1 方案概述
关键点如下：
1. **Targets参数定义**：符合DMTF UpdateService v1.2.0，Targets为可选参数，type为array of string，元素为uri-reference格式，表示固件应用目标。若不存在或为空，则对全部适用目标升级。
2. **RelatedItem与Targets来源**：硬盘固件在FirmwareInventory中注册时，RelatedItem字段指向关联的硬盘资源。GET `/redfish/v1/UpdateService/FirmwareInventory/:id` 响应中，硬盘固件的RelatedItem为`/redfish/v1/Chassis/1/Drives/HDDPlaneDiskX`格式，该URI可直接作为Targets元素。
3. **rackmount转换**：StartUpgrade的扩展参数类型为键值对，其中parameters中Targets需为字符串。rackmount在调用StartUpgrade前，将ReqBody.Targets（数组）用逗号拼接为字符串，如`"/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1,/redfish/v1/Chassis/1/Drives/HDDPlaneDisk2"`。
4. **storage校验逻辑**：对Targets字符串按逗号分割，对每个URI严格校验：格式必须为`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk`+数字；通过drive_collection:get_drive_by_drive_id(drive_id)查找硬盘；校验在位、自研RAID卡管理、Model匹配。任一不满足则返回错误，不进行升级。
5. **全量/指定分支**：parameters.Targets为非空字符串时走指定硬盘逻辑；为空或未传时走全量匹配（get_upgrade_target_drives）。

#### 2.1.1.2 开发视图
```mermaid
classDiagram
    class Redfish_SimpleUpdate {
        + POST /redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate
        + ReqBody.Targets: string[]
        + 请求体参数校验
    }

    class rackmount_update_service {
        + get_expand_parameters(..., targets)
        + Targets数组转逗号分隔字符串
        + 填充ret_paras.Targets
    }

    class firmware_mgmt {
        + StartUpgrade(ImageURI, parameters)
        + PrepareSignal / ProcessSignal / FinishSignal
        + parameters透传
    }

    class storage_drive_upgrade {
        + on_upgrade_process(..., parameters)
        + parse_drive_id_from_redfish_uri(uri)
        + get_drive_by_redfish_uri(uri)
        + get_and_validate_target_drives_from_parameters(targets_str, models)
        + 指定硬盘校验与升级
    }

    Redfish_SimpleUpdate --> rackmount_update_service : ReqBody.Targets
    rackmount_update_service --> firmware_mgmt : parameters.Targets(string)
    firmware_mgmt --> storage_drive_upgrade : ProcessSignal(parameters)
    storage_drive_upgrade --> firmware_mgmt : ProcessReply / UpdateUpgradeStatus
```

#### 2.1.1.3 运行视图
```mermaid
sequenceDiagram
    participant User as 用户/北向
    participant Redfish as Redfish接口
    participant Rackmount as rackmount
    participant FW as firmware_mgmt
    participant Storage as storage(drive_upgrade)

    User->>Redfish: POST SimpleUpdate { ImageURI, Targets: ["/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1", ...] }
    Redfish->>Rackmount: 请求路由
    Rackmount->>Rackmount: get_expand_parameters(..., ReqBody.Targets)
    Note over Rackmount: Targets数组 → "uri1,uri2,..." 字符串
    Rackmount->>FW: StartUpgrade(ImageURI, { Targets: "uri1,uri2,...", ... })
    FW->>FW: 解压包、解析update.cfg
    FW->>Storage: UpgradeProcessSignal(system_id, Drive, file_path, parameters)
    Storage->>Storage: 解析 parameters.Targets
    alt Targets 非空
        Storage->>Storage: get_and_validate_target_drives_from_parameters(targets_str, cfg.model)
        Storage->>Storage: 校验每个URI：格式/存在/在位/自研/Model
        alt 校验通过
            Storage->>Storage: 仅对指定硬盘下发升级
        else 任一校验失败
            Storage->>FW: ProcessReply(ERR)
        end
    else Targets 为空或未传
        Storage->>Storage: get_upgrade_target_drives(cfg.model) 全量匹配
        Storage->>Storage: 对全部匹配硬盘下发升级
    end
    Storage->>FW: ProcessReply / UpdateUpgradeStatus
    FW->>Redfish: 任务结果
    Redfish->>User: 响应
```

#### 2.1.1.4 关键数据结构与接口
**Redfish SimpleUpdate 请求体 Targets 参数（DMTF v1.2.0）**：
```json
"Targets": {
    "description": "The array of URIs indicating where the update image is to be applied.",
    "format": "uri-reference",
    "items": { "type": "string" },
    "longDescription": "This array property shall contain zero or more URIs indicating where the update image is to be applied. These targets should correspond to SoftwareInventory instances or their RelatedItems. If this property is not present or contains zero targets, the Service shall apply the software image to all applicable targets (as determined by the Service).",
    "type": "array",
    "versionAdded": "v1_2_0"
}
```

**FirmwareInventory RelatedItem 字段（硬盘固件）**：
```json
"RelatedItem": {
    "description": "The ID(s) of the resources associated with this software inventory item.",
    "items": { "$ref": "http://redfish.dmtf.org/schemas/v1/odata.v4_0_2.json#/definitions/idRef" },
    "longDescription": "The value of this property shall be an array of IDs containing pointers consistent with JSON pointer syntax to the resource that is associated with this software inventory item.",
    "readonly": true,
    "type": "array"
}
```
硬盘固件RelatedItem示例：`["/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1"]`

**硬盘 Redfish URI 格式**：`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk` + 数字（如HDDPlaneDisk1、HDDPlaneDisk2），与drive_object中NodeId（`HDDPlaneDisk%d`）及FirmwareInventory RelatedItem输出一致。

### 2.1.2 内部依赖分析
- **rackmount**：依赖UpdateService映射配置中ReqBody.Targets的解析，以及get_expand_parameters对Targets的转换逻辑。
- **firmware_mgmt**：需在Prepare/Process/Finish信号中透传parameters，当前实现已支持parameters透传。
- **storage**：依赖drive_collection的get_drive_by_drive_id接口，用于根据解析出的drive_id查找硬盘对象；依赖现有get_upgrade_target_drives全量匹配逻辑作为Targets为空时的回退。

### 2.1.3 外部依赖分析
不涉及平台SDK新增依赖。

### 2.1.4 北向接口分析
| 特性 | SNMP | CLI | WEB | KVM_VMM | IPMI(RMCP/RMCP+) | HMM | Redfish |
| ---- | ---- | ---- | --- | ------ | ---------------- | --- | ------- |
| 支持Redfish指定硬盘升级 | 不涉及 | 不涉及 | 可联动 | 不涉及 | 不涉及 | 不涉及 | 支持 |

**Redfish接口详细说明**：
- **接口**：`POST /redfish/v1/UpdateService/Actions/UpdateService.SimpleUpdate`
- **新增参数**：`Targets`（可选，type: array of string）
- **参数说明**：Targets为字符串数组，每个元素为uri-reference，表示固件应用目标。对于硬盘固件，应使用FirmwareInventory GET响应中RelatedItem字段的URI（如`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1`）。若不存在或为空，则对全部适用目标升级。
- **Targets来源**：用户可通过`GET /redfish/v1/UpdateService/FirmwareInventory/:id`获取硬盘固件信息，响应体RelatedItem字段即为目标硬盘URI，可直接填入Targets。

### 2.1.5 兼容性分析
- 不传Targets或Targets为空数组时，行为与现有全量升级完全一致，无影响。
- 传入Targets时，仅影响本次升级的目标选择，不改变其他固件类型（如RAID、BMC等）的升级流程。

### 2.1.6 定制化接口分析
不涉及

### 2.1.7 导入导出分析
不涉及

### 2.1.8 传感器分析
不涉及

### 2.1.9 精准告警事件分析
不涉及

### 2.1.10 系统锁定分析
不涉及

### 2.1.11 用例场景分析
**以下错误信息为实例，以实现为准**
| 用例编号 | 用例描述 | 前置条件 | 执行步骤 | 预期结果 |
| --- | --- | --- | --- | --- |
| 1 | 指定单盘升级成功 | BMC正常；自研RAID卡在位；目标硬盘在位、Model匹配 | 1. GET FirmwareInventory获取硬盘固件RelatedItem；2. POST SimpleUpdate传入Targets=[该URI]；3. 查询升级进度与结果 | 仅对指定硬盘升级成功，其他硬盘不受影响 |
| 2 | 指定多盘升级成功 | 同上；多块硬盘满足条件 | 1. POST SimpleUpdate传入Targets=[uri1,uri2,...]；2. 查询进度 | 仅对指定多盘升级成功 |
| 3 | Targets为空走全量匹配 | 同上 | 1. POST SimpleUpdate不传Targets或Targets=[]；2. 查询进度 | 对符合Model的全部硬盘升级，与现有行为一致 |
| 4 | 指定盘URI格式非法 | 同上 | 1. POST SimpleUpdate传入Targets=["/invalid/uri"] | Process阶段返回ERR，错误信息为invalid URI format |
| 5 | 指定盘不存在 | 同上 | 1. POST SimpleUpdate传入Targets=["/redfish/v1/Chassis/1/Drives/HDDPlaneDisk99"]（无此盘） | Process阶段返回ERR，错误信息为drive not found |
| 6 | 指定盘不在位 | 同上；目标盘Presence=0 | 1. POST SimpleUpdate传入该盘URI | Process阶段返回ERR，错误信息为drive not present |
| 7 | 指定盘非自研RAID卡管理 | 目标盘RefControllerTypeId非自研范围 | 1. POST SimpleUpdate传入该盘URI | Process阶段返回ERR，错误信息为not self-developed RAID |
| 8 | 指定盘Model不匹配 | 目标盘Model不在update.cfg的Model列表 | 1. POST SimpleUpdate传入该盘URI | Process阶段返回ERR，错误信息为model not match |
| 9 | Targets仅逗号无有效盘 | 传入Targets="," | 1. POST SimpleUpdate传入Targets="," | Process阶段返回ERR，错误信息为no valid targets |

## 2.2 非功能质量属性设计
### 2.2.1 扩展性分析
指定硬盘逻辑与全量逻辑分支清晰，后续若支持其他固件类型（如RAID、NVMe）的Targets指定，可复用相同参数透传与校验框架。

### 2.2.2 重用性分析
parse_drive_id_from_redfish_uri、get_drive_by_redfish_uri、get_and_validate_target_drives_from_parameters等函数可复用于其他需要按URI定位硬盘的场景。

### 2.2.3 可测试性分析
1. 通过Redfish POST SimpleUpdate传入不同Targets组合验证指定/全量分支。
2. 构造非法URI、不存在盘、不在位、非自研、Model不匹配等异常验证错误路径。
3. 单元测试对get_and_validate_target_drives_from_parameters及parse_drive_id等函数进行覆盖。

### 2.2.4 资料分析
需补充Redfish UpdateService.SimpleUpdate的Targets参数说明，以及FirmwareInventory RelatedItem与Targets的对应关系说明。

### 2.2.5 资源使用分析
不新增线程或显著内存占用，仅增加字符串解析与校验逻辑。

### 2.2.6 可靠性分析
| 编号 | 场景 | 问题描述 | 可靠性影响 | 建议解决方案 | 备注 |
| --- | --- | --- | --- | --- | --- |
| 1 | 指定盘校验失败 | 任一Targets中硬盘不满足条件 | 整体升级失败 | 严格校验，任一不满足即返回ERR，不进行升级 | 已实现 |
| 2 | URI格式非法 | 用户传入非标准格式URI | 解析失败 | 严格匹配`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk`+数字，非法则返回明确错误 | 已实现 |

### 2.2.7 安全性分析
| 安全合规项 | 是否涉及 | 现有的防护措施 |
| --- | --- | --- |
| 访问控制 | 不涉及 | - |
| 新增对外接口入参校验 | 涉及 | URI格式校验、硬盘存在性/在位/自研/Model校验 |

# 3.功能实现
## 3.1 支持Redfish接口指定硬盘进行升级
### 3.1.1 功能实现设计
整体实现分为三层：Redfish/rackmount参数处理、firmware_mgmt透传、storage解析与校验。

**1. rackmount层（update_service.lua）**：
- 在`get_expand_parameters(is_clear_conf, active_mode, force_update, stage, targets)`中，新增`targets`参数。
- 若`targets`非空，遍历数组，将每个元素用逗号拼接为字符串`temp_targets`，去掉末尾多余逗号后赋值`ret_paras.Targets`。
- UpdateService.json中GetExpandParameters的Formula传入`ReqBody.Targets`作为第五个参数。
- StartUpgrade的Params第二项为`GetExpandParameters()`的返回值，即包含`Targets`字符串的parameters。

**2. firmware_mgmt层**：
- Prepare/Process/Finish信号回调中，parameters作为参数传递，无需修改，透传即可。

**3. storage层（drive_upgrade.lua）**：
- **parse_drive_id_from_redfish_uri(uri)**：严格解析URI，格式必须为`upgrade_defs.REDFISH_DRIVE_URI_PREFIX`+数字（如`/redfish/v1/Chassis/1/Drives/HDDPlaneDisk1`→1），否则返回nil。
- **get_drive_by_redfish_uri(uri)**：调用parse_drive_id_from_redfish_uri解析drive_id，再通过`c_drive_collection:get_drive_by_drive_id(drive_id)`获取硬盘对象。
- **get_and_validate_target_drives_from_parameters(targets_str, models)**：对targets_str按逗号分割，对每个URI依次校验：parse格式、get_drive存在、Presence==1、test_controller_vendor为自研、Model在models中。全部通过则返回target_drives表，否则返回nil与错误信息。
- **drive_upgrade_process分支**：若`parameters.Targets`为非空字符串，调用`get_and_validate_target_drives_from_parameters`；校验失败则`self.target_drives={}`并返回ERR；成功则`self.target_drives=specified`。否则调用`get_upgrade_target_drives(cfg.model)`全量匹配。

### 3.1.2 功能详细设计
#### 3.1.2.1 Redfish请求体与rackmount映射
- UpdateService.json中SimpleUpdate的ReqBody.Properties新增：
  ```json
  "Targets": {
      "Type": "array",
      "Required": false,
      "Items": { "Type": "string" }
  }
  ```
- GetExpandParameters的Formula为：`orchestrator.update_service.get_expand_parameters(ReqBody.RestoreFactorySettings, ReqBody.ActiveMode, ReqBody.ForceUpdate, ReqBody.Stage, ReqBody.Targets)`
- get_expand_parameters中Targets转换逻辑：
  ```lua
  local temp_targets = ''
  if targets then
      for _, v in ipairs(targets) do
          temp_targets = temp_targets .. v .. ','
      end
  end
  if #temp_targets > 0 then
      temp_targets = string.sub(temp_targets, 1, #temp_targets - 1)
      ret_paras.Targets = temp_targets
  end
  ```

#### 3.1.2.2 storage指定硬盘校验流程
1. **输入**：`parameters.Targets`（逗号分隔字符串）、`cfg.model`（update.cfg中Model数组）
2. **校验顺序**：models非空 → 对每个URI：格式解析 → 硬盘存在 → 在位 → 自研RAID卡 → Model匹配
3. **输出**：成功返回`(target_drives, nil)`，失败返回`(nil, err_msg)`
4. **错误信息**：invalid URI format、drive not found、drive not present、not self-developed RAID、model not match、no valid targets

#### 3.1.2.3 常量定义（upgrade_defs.lua）
- `REDFISH_DRIVE_URI_PREFIX = '/redfish/v1/Chassis/1/Drives/HDDPlaneDisk'`：用于URI格式匹配与解析。

#### 3.1.2.4 drive_collection扩展
- 新增`get_drive_by_drive_id(drive_id)`：根据硬盘Id（数字）从集合中查找并返回硬盘对象，供get_drive_by_redfish_uri使用。

### 3.1.3 开发者测试
#### 3.1.3.1 单元测试
| 组件名 | 用例类型 | 测试用例描述 | 预置条件 | 操作步骤 | 预期结果 |
| ------- | -------- | ------------ | -------- | -------- | -------- |
| Storage | UT | 指定单盘升级成功 | mock drive Id=1, Model匹配, 自研RAID | on_upgrade_process传入Targets=uri1 | ProcessReply OK，target_drives含1个 |
| Storage | UT | 指定多盘升级成功 | mock drive Id=1,2, Model匹配 | on_upgrade_process传入Targets=uri1,uri2 | ProcessReply OK，target_drives含2个 |
| Storage | UT | Targets格式非法返回ERR | - | on_upgrade_process传入Targets="/invalid/uri" | ProcessReply ERR，target_drives=0 |
| Storage | UT | Targets合法但drive_id找不到 | 无Id=99的盘 | on_upgrade_process传入Targets=uri99 | ProcessReply ERR |
| Storage | UT | 指定盘不在位 | Presence=0 | on_upgrade_process传入该盘URI | ProcessReply ERR |
| Storage | UT | 指定盘Model不匹配 | Model不在cfg.model | on_upgrade_process传入该盘URI | ProcessReply ERR |
| Storage | UT | 指定盘非自研RAID | RefControllerTypeId非96-113 | on_upgrade_process传入该盘URI | ProcessReply ERR |
| Storage | UT | Targets为空走全量匹配 | 有匹配硬盘 | on_upgrade_process传入Targets="" | 走get_upgrade_target_drives |
| Storage | UT | Targets仅逗号无有效盘 | - | on_upgrade_process传入Targets="," | ProcessReply ERR |

#### 3.1.3.2 集成测试
不涉及
