* Copyright (c) Huawei Technologies Co., Ltd. 2026-2026. All rights reserved.
* ubs-engine is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#ifndef UBSE_URMA_CONTROLLER_MANAGER_H
#define UBSE_URMA_CONTROLLER_MANAGER_H
#include <cstdint>
#include <functional>
#include <map>
#include <queue>
#include <string>
#include "ubse_common_def.h"
#include "ubse_mti_interface.h"
#include "ubse_urma_def.h"
#include "ubse_urma_uvs_module.h"
#include "lock/ubse_lock.h"
namespace ubse::urmaController {
using common::def::UbseResult;
using ubse::adapter_plugins::mti::UbseMtiFeInfo;
using ubse::urma::FeTopoType;
using ubse::urma::UbseUrmaInfo;
using ubse::urma::UbseUrmaNodeInfo;
using ubse::urma::UbseUrmaUvsNodeInfo;
using ubse::urma::UrmaDevState;
struct UrmaDevRollbackState {
uint16_t feId;
uint64_t urmaId;
std::vector<std::string> urmaDevs;
};
class UbseUrmaControllerManager {
public:
* @brief 获取单例实例
* @return 单例引用
*/
static UbseUrmaControllerManager& GetInstance()
{
static UbseUrmaControllerManager instance;
return instance;
}
* @brief 存储通过RPC从其他节点(包括本节点)获取的URMA节点信息
* @details 将节点信息插入或更新到nodeInfos中。如果本地已有该节点的信息且时间戳更新,
* 则跳过本次更新,避免旧数据覆盖新数据。
* @param nodeId 节点唯一标识
* @param insertNodeInfo 待插入的节点URMA信息(move语义,调用后内容被转移)
*/
void InsertNewNodeInfo(const std::string& nodeId, UbseUrmaNodeInfo& insertNodeInfo);
* @brief 基于节点的FE信息计算并构建URMA bonding设备信息(左值引用版本)
* @details 根据组网类型(CLOS/非CLOS)对FE信息进行拆分和过滤,将每个IOU上配对的两组FE
* 的EID组逐一组成bonding设备。CLOS组网下会拆分为容器侧和主机侧两套bonding。
* 操作完成后更新节点的updateTimeStamp。
* @param nodeId 节点唯一标识
* @param feInfos 按IOU分组的前端引擎信息,格式为 feInfos[IOU索引][FE索引]
* @return UbseResult UBSE_OK表示成功,其他值表示失败
*/
UbseResult ConstructNewUrmaInfo(const std::string& nodeId, std::vector<std::vector<UbseMtiFeInfo>>& feInfos);
UbseResult ConstructNewUrmaInfo(const std::string& nodeId, std::vector<std::vector<UbseMtiFeInfo>>&& feInfos);
* @brief 根据基准节点的URMA bonding信息推算出其他节点的URMA设备信息
* @details 仅在CLOS组网下生效。以basedNodeId节点的bonding信息为模板,遍历指定范围的
* serverIdx,为每个节点重新生成唯一的devEid和primaryEid/portEid,实现跨节点的
* bonding信息复制。跳过基准节点自身(curServerIdx)。
* @param isInferHostOnly 是否只推算主机侧(hostUrmaList)的bonding,false则同时推算容器侧
* @param basedNodeId 作为推算模板的基准节点ID
* @param startIdx 起始server索引(从0开始)
* @param batchNodeNum 批量处理的节点数量
* @return UbseResult UBSE_OK表示成功,其他值表示失败
*/
UbseResult InferOtherNodesUrmaDevInfo(bool isInferHostOnly, const std::string& basedNodeId, uint32_t startIdx,
uint32_t batchNodeNum);
* @brief 删除除当前节点外其他所有节点的URMA信息
* @details 在下发拓扑后调用,通过extract保留当前节点信息,再swap清空整个nodeInfos,
* 最后重新插入当前节点信息。此方式可强制释放旧map占用的全部内存(含Rb-tree节点)。
* 仅在CLOS组网下执行清理操作。
* @param curNodeId 当前节点ID,该节点的信息将被保留
*/
void DeleteOtherNodesUrmaInfo(const std::string& curNodeId);
* @brief 根据URMA设备的EID设置其状态
* @details 先在nodeInfos中按EID查找对应的URMA设备名称,再通过写锁更新其状态。
* 先在读锁下查找设备名,再升级为写锁进行状态更新。
* @param urmaDevEid URMA设备的全局唯一EID标识
* @param state 目标状态值
*/
void SetUrmaDevStateByDevEid(const std::string& urmaDevEid, UrmaDevState state);
* @brief 根据URMA设备名称获取本节点的URMA设备详细信息
* @details 在读锁保护下查找当前节点的urmaList中匹配名称的设备信息。
* 仅在当前节点的nodeInfos中查找,不跨节点查询。
* @param urmaName URMA设备名称(如 "bonding_dev_0")
* @param urmaInfo 输出参数,返回匹配的URMA设备完整信息
* @return UbseResult UBSE_OK表示找到,UBSE_URMACONTRL_ERROR_DEV_NOT_EXIST表示设备不存在
*/
UbseResult GetLocalUrmaDevInfoByName(const std::string& urmaName, UbseUrmaInfo& urmaInfo);
* @brief 分配一个URMA设备,返回其关联的FE名称列表和EID
* @details 在读锁保护下查找当前节点的URMA设备,校验设备状态必须为ACTIVED才可分配。
* 返回结果包含设备的subPath以及所有eidGroups中FE的名称。
* @param urmaName URMA设备名称
* @param feNames 输出参数,依次返回subPath和各eidGroup关联的FE名称
* @param eid 输出参数,返回URMA设备的EID
* @return UbseResult UBSE_OK表示分配成功,
* UBSE_URMACONTRL_ERROR_DEV_NOT_EXIST表示设备不存在,
* UBSE_URMACONTRL_ERROR_DEV_NOT_INACTIVE表示设备未激活不可分配
*/
UbseResult AllocUrmaDev(const std::string& urmaName, std::vector<std::string>& feNames, std::string& eid);
* @brief 将URMA CTL的设备信息转换为拓扑下发时URMA UVS模块所需的格式
* @details 在写锁保护下执行:先调InferOtherNodesUrmaDevInfo推算其他节点拓扑(仅CLOS组网),
* 再遍历所有nodeInfos,将每个节点的urmaList和hostUrmaList中的设备信息转换为
* UbseUrmaUvsNodeInfo格式。转换完成后自动清理其他节点信息以释放内存。
* @param isBuildHostOnly 是否只构建主机侧拓扑(仅含hostUrmaList),
* false则同时构建容器侧和主机侧拓扑
* @param startServerIdx 推算其他节点拓扑时的起始server索引
* @param batchNodeNum 推算其他节点拓扑时的批量节点数
* @param uvsInfos 输出参数,所有节点的URMA UVS拓扑信息列表
* @return UbseResult UBSE_OK表示成功,其他值表示失败
*/
UbseResult BuildUvsTopoNodeInfo(bool isBuildHostOnly, uint32_t startServerIdx, uint32_t batchNodeNum,
std::vector<UbseUrmaUvsNodeInfo>& uvsInfos);
* @brief 根据URMA设备的EID设置其子路径
* @details 在写锁保护下遍历所有节点的urmaList,匹配EID后设置对应设备的subPath字段。
* 找到第一个匹配的设备后立即返回。
* @param urmaEid URMA设备的EID
* @param urmaSubPath 要设置的子路径字符串
*/
void SetUrmaSubPath(const std::string& urmaEid, const std::string& urmaSubPath);
* @brief 根据FE的primaryEid设置对应FE的名称
* @details 在写锁保护下遍历所有节点所有URMA设备的eidGroups,找到primaryEid匹配的
* eidGroup后设置其关联feInfo的name字段。找到第一个匹配后立即返回。
* @param feEid FE的primaryEid,用作匹配条件
* @param urmaEidName 要设置的FE名称
*/
void SetFeName(const std::string& feEid, const std::string& urmaEidName);
* @brief 获取或创建指定节点的URMA节点信息
* @details 先以读锁查找nodeInfos,若存在则直接返回副本;若不存在则升级为写锁,
* 创建并插入一个新的UbseUrmaNodeInfo,设置其nodeId后返回。
* @param nodeId 节点唯一标识
* @return UbseUrmaNodeInfo 节点的URMA信息副本
*/
UbseUrmaNodeInfo GetUrmaNodeInfo(const std::string& nodeId);
* @brief 设置当前节点所有URMA设备的状态
* @details 在写锁保护下遍历当前节点urmaList中的所有设备,将状态统一设置为指定值。
* 常用于批量激活或去激活操作。
* @param state 目标状态值
*/
void SetAllUrmaDevStateForNode(UrmaDevState state);
* @brief 获取指定节点的URMA信息更新时间戳
* @details 在读锁保护下查找节点信息,返回其updateTimeStamp。
* 如果节点不存在则返回0。
* @param nodeId 节点唯一标识
* @return uint64_t 时间戳(毫秒级Unix时间),节点不存在时返回0
*/
uint64_t GetUrmaUpdateTimeStamp(const std::string& nodeId);
* @brief 设置FE拓扑类型
* @details FE拓扑类型由CalculateFeTopoType根据每个IOU上的PFE/VFE数量计算得出,
* 用于指导bonding命名和拓扑构建逻辑。
* @param topoType FE拓扑类型(ALL_PFE / PFE_VFE_HYBRID / INVALID)
*/
void SetFeTopoType(FeTopoType topoType);
* @brief 获取当前FE拓扑类型
* @return FeTopoType 当前FE拓扑类型
*/
FeTopoType GetFeTopoType() const;
private:
* @brief 基于两个FE的EID组逐一创建URMA bonding设备并插入到指定的urmaList中
* @details 核心bonding逻辑:取lcneFe0和lcneFe1的EID组一一配对,每组生成唯一的
* feId、urmaId和devEid,组成一个UrmaDevInfo并命名后插入urmaList。
* 操作前保存回滚状态,任意一步失败时通过CreateAndInsertUrmaInfoRollback
* 恢复feId、urmaId并删除已创建的部分设备,保证可重入。
* @param serverIdx 服务器索引(从SMBIOS获取,用于生成devEid)
* @param nodeId 节点ID
* @param lcneFe0 第一个FE的信息(来自IOU 0的某一entity)
* @param lcneFe1 第二个FE的信息(来自IOU 1的对应entity)
* @param urmaList 目标URMA列表(容器侧urmaList或主机侧hostUrmaList)
* @return UbseResult UBSE_OK表示成功,UBSE_ERROR_AGAIN表示需重试
*/
UbseResult ConstructAndInsertUrmaBonding(
const uint32_t serverIdx, const std::string& nodeId, UbseMtiFeInfo& lcneFe0, UbseMtiFeInfo& lcneFe1,
std::map<std::string, UbseUrmaInfo, ubse::urma::UrmaNameCompare>& urmaList);
* @brief 遍历feInfos中配对的FE,逐一调用CreateAndInsertUrmaInfo完成bonding
* @details 当前约定每次从两个IOU区域各取一个FE组成bonding对(feInfos.size() == 2)。
* 对每对FE校验其EID组非空且未被使用后,调用CreateAndInsertUrmaInfo组建bonding。
* 若FE配置不足(无法为主机预留EID),接受feInfos为空的情况。
* @param nodeId 节点ID
* @param serverIdx 服务器索引
* @param feInfos 按IOU分组排序后的FE信息(已拆分/过滤为容器侧或主机侧)
* @param urmaList 目标URMA列表
* @return UbseResult UBSE_OK表示成功,UBSE_ERROR_AGAIN表示需重试
*/
UbseResult ProcessUrmaBondings(const std::string& nodeId, uint32_t serverIdx,
std::vector<std::vector<UbseMtiFeInfo>>& feInfos,
std::map<std::string, UbseUrmaInfo, ubse::urma::UrmaNameCompare>& urmaList);
* @brief 为单个节点推算URMA设备信息
* @details 以basedNodeId节点的nodeInfo为模板复制到目标节点(serverIdx对应),
* 重新生成nodeId,并为urmaList和hostUrmaList中的所有设备重新计算
* devEid、primaryEid和portEid(通过解析原EID中的feId并替换serverIdx部分)。
* 若isInferHostOnly为true则跳过容器侧urmaList的推算。
* @param isInferHostOnly 是否只推算主机侧bonding
* @param serverIdx 目标服务器索引
* @param baseNodeId 作为模板的基准节点ID
* @return UbseResult UBSE_OK表示成功,其他值表示失败
*/
UbseResult InferOneNodeUrmaDevInfo(bool isInferHostOnly, uint32_t serverIdx, const std::string& baseNodeId);
* @brief 生成节点内唯一的FE ID
* @details 基于原子变量globalFeId自增,保证线程安全
* @return uint16_t 新生成的唯一FE ID
*/
uint16_t GenerateUniqueFeId();
* @brief 生成节点内唯一的URMA设备ID
* @details 基于原子变量globalUrmaId自增,保证线程安全
* @return uint64_t 新生成的唯一URMA设备ID
*/
uint64_t GenerateUrmaDevId();
* @brief bonding操作失败时的回滚处理
* @details 将globalUrmaId和globalFeId恢复到操作前的值,并删除本次操作中
* 已创建的URMA设备条目,确保后续重试时状态正确。
* @param state 操作前保存的回滚状态快照
* @param urmaList 目标URMA列表,从中删除已创建的设备
*/
void ConstructAndInsertUrmaBondingRollback(
const UrmaDevRollbackState& state, std::map<std::string, UbseUrmaInfo, ubse::urma::UrmaNameCompare>& urmaList);
void PrintNodeInfo(const UbseUrmaNodeInfo& nodeInfo);
* @brief 获取本节点URMA设备信息的内部实现(不加锁)
* @details 在调用方已持有读锁的前提下,直接查找当前节点的urmaList中匹配名称的设备。
* 由GetLocalUrmaDevInfoByName加锁后调用。
* @param urmaName URMA设备名称
* @param urmaInfo 输出参数,返回匹配的设备信息
* @return UbseResult UBSE_OK表示找到,UBSE_URMACONTRL_ERROR_DEV_NOT_EXIST表示不存在
*/
UbseResult GetLocalUrmaDevInfoByNameInner(const std::string& urmaName, UbseUrmaInfo& urmaInfo);
* @brief 检查一对FE是否已在指定的urmaList中被使用(组成过bonding)
* @details 遍历urmaList中的所有设备,比较每个设备的两个eidGroup关联的feInfo
* 的四元组是否与fe0和fe1完全匹配。
* 注意:此方法基于传入的urmaList独立判断,而非全局状态,
* 因为CLOS组网下同一物理FE需要分别为容器侧和主机侧bonding。
* @param fe0 第一个FE信息
* @param fe1 第二个FE信息
* @param urmaList 待检查的URMA列表
* @return true 该FE对已被使用
* @return false 该FE对未被使用
*/
bool IsLcneFeUsed(const UbseMtiFeInfo& fe0, const UbseMtiFeInfo& fe1,
const std::map<std::string, UbseUrmaInfo, ubse::urma::UrmaNameCompare>& urmaList);
UbseResult InsertHostUrmaDevInner();
* @brief 基于通信bonding设备信息构建主机URMA设备
* @details 从通信bonding设备(comDev)的feList中提取ubpuId和entityId,
* 在已有的urmaList中查找匹配的feInfo,构建包含primaryEid、portEid
* 和feInfo的完整URMA设备信息。
* @param comDev 通信bonding的聚合设备信息(来自NodeComUrmaCollector)
* @param urmaList 已组建的URMA列表(用于查找匹配的feInfo)
* @param urmaDev 输出参数,构建完成的主机URMA设备信息
* @return UbseResult UBSE_OK表示成功,其他值表示失败
*/
UbseResult BuildHostUrmaDev(const ubse::urma::UbseUrmaUvsAggrDev& comDev,
const std::map<std::string, UbseUrmaInfo, ubse::urma::UrmaNameCompare>& urmaList,
UbseUrmaInfo& urmaDev);
* @brief 生成URMA设备名称
* @details 根据组网类型和FE拓扑类型决定命名规则:
* - CLOS全PFE场景且当前bonding对位于第16组EID(下标15)且与主机bonding同FE时,
* 固定命名为 "bonding_dev_96"(兼容此前版本,避免容器重建)
* - 其他情况则使用全局自增的urmaId生成 "bonding_dev_{urmaId}" 格式的名称
* @param nodeId 节点ID
* @param lcneFe0 第一个FE信息
* @param lcneFe1 第二个FE信息
* @param idx 当前bonding的EID组索引(从0开始)
* @return std::string 生成的URMA设备名称,失败时返回空字符串
*/
std::string GenerateUrmaDevName(const std::string& nodeId, const UbseMtiFeInfo& lcneFe0,
const UbseMtiFeInfo& lcneFe1, const size_t idx);
private:
utils::ReadWriteLock rwLock;
std::map<std::string, UbseUrmaNodeInfo> nodeInfos{};
std::atomic<uint16_t> globalFeId{0};
std::atomic<uint64_t> globalUrmaId{0};
FeTopoType feTopoType{FeTopoType::INVALID};
};
}
#endif