* Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. 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.
*/
#include "ubse_node_controller_agent.h"
#include <unistd.h>
#include <condition_variable>
#include <mutex>
#include "ubse_common_def.h"
#include "ubse_election.h"
#include "ubse_error.h"
#include "ubse_event.h"
#include "ubse_logger.h"
#include "ubse_node_com_urma_collector.h"
#include "ubse_node_controller_collector.h"
#include "ubse_serial_util.h"
#include "ubse_smbios.h"
#include "ubse_timer.h"
const uint32_t UBSE_NODE_COLLECT_RETRY_INTERVAL = 2;
const uint32_t UBSE_NODE_REPORT_INTERVAL = 2;
constexpr int UBSE_RPC_TIMEOUT_MS = 60000;
UBSE_DEFINE_THIS_MODULE("ubse");
namespace ubse::nodeController {
using namespace ubse::context;
using namespace ubse::election;
using namespace ubse::log;
using namespace ubse::event;
using namespace ubse::timer;
using namespace ubse::common::def;
using namespace ubse::com;
using namespace ubse::serial;
using namespace ubse::task_executor;
using namespace ubse::adapter_plugins::smbios;
constexpr UbseResult UBSE_ERROR_TIMEOUT = 0x80000001;
const std::string UBSE_NODE_AGENT_REPORT_TIMER = "UbseNodeReport";
std::string UBSE_TOPOLOGY_CHANGE_EVENT = UBSE_EVENT_TOPOLOGY_CHANGE;
UbseResult RegAgentMsgHandler()
{
const ubse::com::UbseComEndpoint collectEndpoint = {
static_cast<uint16_t>(UbseModuleCode::NODE_CONTROLLER),
static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_COLLECT)};
const ubse::com::UbseComEndpoint nodeChangeEndpoint = {
static_cast<uint16_t>(UbseModuleCode::NODE_CONTROLLER),
static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_NODE_CHANGE)};
auto ret = UbseRegRpcService(collectEndpoint, CollectNodeInfoHandler);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "Register collect endpoint failed";
return ret;
}
ret = UbseRegRpcService(nodeChangeEndpoint, nodeChangeHandler);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "Register join endpoint failed";
return ret;
}
UBSE_LOG_INFO << "Agent message handler registered successfully";
return UBSE_OK;
}
UbseResult UbseNodeControllerAgent::Initialize()
{
UBSE_LOG_INFO << "UbseNodeControllerAgent init";
auto ret = RegAgentMsgHandler();
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "Register agent message handler failed, " << FormatRetCode(ret);
return ret;
}
taskExecutor_ = UbseTaskExecutor::Create("UbseNodeAgent", NO_1, NO_1024);
if (taskExecutor_ == nullptr || !taskExecutor_->Start()) {
UBSE_LOG_ERROR << "Create agent task executor failed";
return UBSE_ERROR_NULLPTR;
}
UBSE_LOG_INFO << "UbseNodeControllerAgent initialized successfully";
return UBSE_OK;
}
void CollectBaseInfo(UbseNodeInfo& info)
{
while (!g_globalStop.load()) {
auto ret = CollectNodeBaseInfo(info);
if (ret == UBSE_OK) {
return;
}
UBSE_LOG_ERROR << "collect node base info failed, " << FormatRetCode(ret);
sleep(UBSE_NODE_COLLECT_RETRY_INTERVAL);
}
}
void CollectTopology(UbseNodeInfo& info)
{
while (!g_globalStop.load()) {
auto ret = CollectNodeTopology(info);
if (ret == UBSE_OK) {
return;
}
UBSE_LOG_ERROR << "collect node topology failed, " << FormatRetCode(ret);
sleep(UBSE_NODE_COLLECT_RETRY_INTERVAL);
}
}
UbseNodeInfo UbseNodeInfoCollect()
{
UbseNodeInfo info{};
auto ret = CollectNodeBaseInfo(info);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "nodeId=" << UbseNodeController::GetInstance().GetCurrentNodeId()
<< "collect base info failed, " << FormatRetCode(ret) << ", will return last collect info";
return UbseNodeController::GetInstance().GetCurNode();
}
ret = CollectNodeTopology(info);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "nodeId=" << UbseNodeController::GetInstance().GetCurrentNodeId()
<< "collect topology info failed, " << FormatRetCode(ret) << ", will return last collect info";
return UbseNodeController::GetInstance().GetCurNode();
}
ret = CollectSysSentryState(info);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "nodeId=" << UbseNodeController::GetInstance().GetCurrentNodeId()
<< "collect sentry info failed, " << FormatRetCode(ret) << ", will return last collect info";
return UbseNodeController::GetInstance().GetCurNode();
}
ret = CollectObmmKernelState(info);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "nodeId=" << UbseNodeController::GetInstance().GetCurrentNodeId()
<< "collect obmm kernel info failed, " << FormatRetCode(ret)
<< ", will return last collect info";
return UbseNodeController::GetInstance().GetCurNode();
}
UbseNodeController::GetInstance().UpdateNodeInfo(info.nodeId, info);
UbseNodeController::GetInstance().UpdateDevDirConnectInfo();
return info;
}
UbseResult UbseNodeInfoReport()
{
UbseNodeInfo info = UbseNodeInfoCollect();
UbseRoleInfo masterInfo{};
auto ret = UbseGetMasterInfo(masterInfo);
if (ret != UBSE_OK) {
UBSE_LOG_WARN << "ubse get master node failed, skip report.";
return ret;
}
if (masterInfo.nodeId == UbseNodeController::GetInstance().GetCurrentNodeId()) {
return UBSE_OK;
}
return UbseNodeReportNodeInfo(masterInfo.nodeId, info);
}
UbseResult UbseNodeControllerAgent::UbseNodeInfoReportTimerHandler()
{
taskExecutor_->Execute([]() { UbseNodeInfoReport(); });
return UBSE_OK;
}
void UbseNodeControllerAgent::StartExec()
{
while (!g_globalStop.load()) {
auto ret = SetUrmaUvs(true);
if (ret == UBSE_OK) {
UBSE_LOG_INFO << "set urma uvs successfully";
break;
}
UBSE_LOG_ERROR << "set urma uvs_set_topo_info failed, will retry 3s later";
sleep(NO_3);
}
UbseNodeInfo info{};
CollectBaseInfo(info);
CollectTopology(info);
UbseNodeController::GetInstance().SetCurrentNodeId(info.nodeId);
UbseNodeController::GetInstance().UpdateNodeInfo(info.nodeId, info);
UbseNodeController::GetInstance().UpdateDevDirConnectInfo();
UbseNodeController::GetInstance().UpdateNodeInfoLocalState(UbseNodeLocalState::UBSE_NODE_READY);
if (!UbseSmbios::GetInstance().IsClosType()) {
UbseTimerHandlerRegister(
UBSE_NODE_AGENT_REPORT_TIMER, [this]() -> UbseResult { return UbseNodeInfoReportTimerHandler(); },
UBSE_NODE_REPORT_INTERVAL);
}
UbseSubEvent(UBSE_TOPOLOGY_CHANGE_EVENT, UbseNodeInfoLcneNotifyHandler);
}
UbseResult UbseNodeControllerAgent::Start()
{
taskExecutor_->Execute([this]() -> void { StartExec(); });
return UBSE_OK;
}
UbseResult UbseNodeControllerAgent::UbseNodeInfoLcneNotifyHandler(std::string&, std::string& eventMsg)
{
UBSE_LOG_INFO << "lcne change, start to collect";
UbseNodeInfo info = UbseNodeInfoCollect();
info.eventMessage = eventMsg;
UbseNodeController::GetInstance().UpdateNodeInfo(info.nodeId, info);
UbseNodeController::GetInstance().UpdateDevDirConnectInfo();
UbseRoleInfo masterInfo{};
auto ret = UbseGetMasterInfo(masterInfo);
if (ret != UBSE_OK) {
UBSE_LOG_INFO << "ubse get master node failed, skip report.";
return ret;
}
return LcneChangeReportNodeInfo(masterInfo.nodeId, info);
}
void UbseNodeControllerAgent::UnInitialize() {}
void UbseNodeControllerAgent::Stop()
{
UBSE_LOG_INFO << "ubse node agent start to stop executor.";
if (taskExecutor_ != nullptr) {
taskExecutor_->Stop();
}
UBSE_LOG_INFO << "ubse node agent start to stop report timer.";
UbseTimerHandlerUnregister(UBSE_NODE_AGENT_REPORT_TIMER);
UbseUnSubEvent(UBSE_TOPOLOGY_CHANGE_EVENT, UbseNodeInfoLcneNotifyHandler);
UBSE_LOG_INFO << "ubse node agent stopped.";
}
static UbseResult SafeSerializeUbseNode(const UbseNodeInfo& info, UbseByteBuffer& buffer)
{
uint8_t* data = nullptr;
size_t size = 0;
auto ret = SerializeUbseNode(info, data, size);
if (ret != UBSE_OK) {
if (data != nullptr) {
SafeDeleteArray(data, size);
}
return ret;
}
buffer = {data, size, [size](uint8_t* p) noexcept {
SafeDeleteArray(p, size);
}};
return UBSE_OK;
}
UbseResult UbseNodeReportNodeInfo(const std::string& nodeId, const UbseNodeInfo& info)
{
const ubse::com::UbseComEndpoint endpoint{
.moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::NODE_CONTROLLER),
.serviceId = static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_REPORT),
.address = nodeId,
};
struct SyncData {
UbseResult reportRet = UBSE_OK;
bool callbackCalled = false;
std::mutex mtx;
std::condition_variable cv;
};
auto syncData = std::make_shared<SyncData>();
UbseByteBuffer reqBuffer;
auto ret = SafeSerializeUbseNode(info, reqBuffer);
if (ret != UBSE_OK) {
return ret;
}
ret = UbseRpcSend(endpoint, reqBuffer, nullptr,
[syncData, nodeId](void* ctx, const UbseByteBuffer& respData, uint32_t resCode) -> void {
if (resCode != UBSE_OK) {
UBSE_LOG_ERROR << "report node to nodeId=" << nodeId << " failed, "
<< FormatRetCode(resCode);
syncData->reportRet = resCode;
}
{
std::lock_guard<std::mutex> lock(syncData->mtx);
syncData->callbackCalled = true;
}
syncData->cv.notify_one();
});
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "send report nodeId=" << nodeId << " msg failed, " << FormatRetCode(ret);
return ret;
}
{
std::unique_lock<std::mutex> lock(syncData->mtx);
auto timeout = std::chrono::milliseconds(UBSE_RPC_TIMEOUT_MS);
if (!syncData->cv.wait_for(lock, timeout, [syncData] { return syncData->callbackCalled; })) {
UBSE_LOG_ERROR << "report node to " << nodeId << " timeout after " << UBSE_RPC_TIMEOUT_MS << "ms";
return UBSE_ERROR_TIMEOUT;
}
}
return syncData->reportRet;
}
UbseResult LcneChangeReportNodeInfo(const std::string& nodeId, const UbseNodeInfo& info)
{
const ubse::com::UbseComEndpoint endpoint{
.moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::NODE_CONTROLLER),
.serviceId = static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_LCNE_CHANGE_REPORT_TOPOLOGY),
.address = nodeId,
};
struct SyncData {
UbseResult reportRet = UBSE_OK;
bool callbackCalled = false;
std::mutex mtx;
std::condition_variable cv;
};
auto syncData = std::make_shared<SyncData>();
UbseByteBuffer reqBuffer;
auto ret = SafeSerializeUbseNode(info, reqBuffer);
if (ret != UBSE_OK) {
return ret;
}
ret = UbseRpcSend(endpoint, reqBuffer, nullptr,
[syncData, nodeId](void* ctx, const UbseByteBuffer& respData, uint32_t resCode) -> void {
if (resCode != UBSE_OK) {
UBSE_LOG_ERROR << "lcne, report node to nodeId=" << nodeId << " failed, "
<< FormatRetCode(resCode);
syncData->reportRet = resCode;
}
{
std::lock_guard<std::mutex> lock(syncData->mtx);
syncData->callbackCalled = true;
}
syncData->cv.notify_one();
});
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "lcne change, send report nodeId=" << nodeId << " msg failed, " << FormatRetCode(ret);
return ret;
}
{
std::unique_lock<std::mutex> lock(syncData->mtx);
auto timeout = std::chrono::milliseconds(UBSE_RPC_TIMEOUT_MS);
if (!syncData->cv.wait_for(lock, timeout, [syncData] { return syncData->callbackCalled; })) {
UBSE_LOG_ERROR << "lcne change report to " << nodeId << " timeout after " << UBSE_RPC_TIMEOUT_MS << "ms";
return UBSE_ERROR_TIMEOUT;
}
}
return syncData->reportRet;
}
static UbseResult CreateErrorResponse(UbseResult errorCode, UbseByteBuffer& resp)
{
uint8_t* errorBuffer = new (std::nothrow) uint8_t[4];
if (errorBuffer != nullptr) {
*reinterpret_cast<uint32_t*>(errorBuffer) =
static_cast<uint32_t>(errorCode);
resp = {errorBuffer, 4, [](uint8_t* p) noexcept {
delete[] p;
}};
return errorCode;
} else {
resp = {nullptr, 0, nullptr};
return UBSE_ERROR_NULLPTR;
}
}
UbseResult CollectNodeInfoHandler(const UbseByteBuffer& req, UbseByteBuffer& resp)
{
UbseNodeInfo info = UbseNodeController::GetInstance().GetCurNode();
uint8_t* buffer = nullptr;
size_t size = 0;
auto ret = SerializeUbseNode(info, buffer, size);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "SerializeUbseNode failed: " << FormatRetCode(ret);
if (buffer != nullptr) {
SafeDeleteArray(buffer, size);
}
return CreateErrorResponse(ret, resp);
}
resp = {buffer, size, [size](uint8_t* p) noexcept {
SafeDeleteArray(p, size);
}};
return ret;
}
static void GetAllNodeInfoFromRemoteRespHandler(const std::string& nodeId, const UbseByteBuffer& respData,
uint32_t resCode, std::vector<UbseNodeInfo>& infos, UbseResult& getRet)
{
if (resCode != UBSE_OK) {
UBSE_LOG_ERROR << "get all node info failed, " << FormatRetCode(resCode);
getRet = resCode;
return;
}
if (respData.data == nullptr || respData.len == 0) {
UBSE_LOG_ERROR << "get all node resp null";
getRet = UBSE_ERROR_NULLPTR;
return;
}
getRet = DeSerializeUbseNodeList(infos, respData.data, respData.len);
if (getRet != UBSE_OK) {
UBSE_LOG_ERROR << "get all node deserialize failed, " << FormatRetCode(getRet);
}
}
UbseResult nodeChangeHandler(const UbseByteBuffer& req, UbseByteBuffer& resp)
{
UBSE_LOG_INFO << "Receive node change req from master";
uint8_t* buffer = nullptr;
size_t size = 0;
resp = {buffer, size, [size](uint8_t* p) noexcept {
SafeDeleteArray(p, size);
}};
UbseDeSerialization inStream(req.data, req.len);
std::string node;
std::string action;
inStream >> node >> action;
if (!inStream.Check()) {
UBSE_LOG_ERROR << "Failed to deserialize node join response";
return CreateErrorResponse(UBSE_ERROR, resp);
}
auto ret = SetUrmaUvs(false);
if (ret != UBSE_OK) {
UBSE_LOG_WARN << "SetUrmaUvs failed: " << FormatRetCode(ret);
}
if (action == UBSE_EVENT_NODE_TOPO_LINK_CHANGE || action == UBSE_EVENT_NODE_JOIN) {
ret = PubNodeUrmaChange(node, action);
}
return CreateErrorResponse(ret, resp);
}
UbseResult GetAllNodeInfoFromRemote(const std::string& nodeId, std::vector<UbseNodeInfo>& infos)
{
const ubse::com::UbseComEndpoint endpoint{
.moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::NODE_CONTROLLER),
.serviceId = static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_ALL_NODE),
.address = nodeId,
};
UbseResult getRet = UBSE_OK;
bool callbackCalled = false;
std::mutex mtx;
std::condition_variable cv;
uint8_t* buffer = nullptr;
size_t size = 0;
auto ret = SerializeUbseNodeList(std::vector<UbseNodeInfo>{}, buffer, size);
if (ret != UBSE_OK) {
if (buffer != nullptr) {
SafeDeleteArray(buffer, size);
}
return ret;
}
UbseByteBuffer reqBuffer{buffer, size, [size](uint8_t* p) noexcept {
SafeDeleteArray(p, size);
}};
ret = UbseRpcSend(endpoint, reqBuffer, nullptr,
[&infos, &getRet, &callbackCalled, &mtx, &cv, nodeId](void* ctx, const UbseByteBuffer& respData,
uint32_t resCode) -> void {
GetAllNodeInfoFromRemoteRespHandler(nodeId, respData, resCode, infos, getRet);
{
std::lock_guard<std::mutex> lock(mtx);
callbackCalled = true;
}
cv.notify_one();
});
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "send get all node msg failed, " << FormatRetCode(ret);
return ret;
}
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&callbackCalled] { return callbackCalled; });
}
return getRet;
}
static void HandleGetDirConnectInfoCallback(const std::string& nodeId, const UbseByteBuffer& respData, uint32_t resCode,
std::map<std::string, PhysicalLink>& devDirConnectInfoRemote,
UbseResult& getRet, bool& callbackCalled, std::mutex& mtx,
std::condition_variable& cv)
{
if (resCode != UBSE_OK) {
UBSE_LOG_ERROR << "get all node info failed, " << FormatRetCode(resCode);
getRet = resCode;
} else if (respData.data == nullptr || respData.len == 0) {
UBSE_LOG_ERROR << "get all node resp null";
getRet = UBSE_ERROR_NULLPTR;
} else {
getRet = DeSerializeDevDirConnectInfo(devDirConnectInfoRemote, respData.data, respData.len);
if (getRet != UBSE_OK) {
UBSE_LOG_ERROR << "get devDirConnectInfo deserialize failed, " << FormatRetCode(getRet);
}
}
{
std::lock_guard<std::mutex> lock(mtx);
callbackCalled = true;
}
cv.notify_one();
}
UbseResult UbseGetDirConnectInfoFromRemote(const std::string& nodeId,
std::map<std::string, PhysicalLink>& devDirConnectInfoRemote)
{
const ubse::com::UbseComEndpoint endpoint{
.moduleId = static_cast<uint16_t>(ubse::com::UbseModuleCode::NODE_CONTROLLER),
.serviceId = static_cast<uint32_t>(UbseNodeControllerOpCode::NODE_CONTROLLER_GET_DEV_CONNECT),
.address = nodeId,
};
UbseResult getRet = UBSE_OK;
bool callbackCalled = false;
std::mutex mtx;
std::condition_variable cv;
uint8_t* buffer = new (std::nothrow) uint8_t[1];
if (buffer == nullptr) {
UBSE_LOG_ERROR << "Memory allocation failed.";
return UBSE_ERROR_NULLPTR;
}
size_t size = 1;
UbseByteBuffer reqBuffer{buffer, size, [size](uint8_t* p) noexcept {
SafeDeleteArray(p, size);
}};
auto ret = UbseRpcSend(endpoint, reqBuffer, nullptr,
[&devDirConnectInfoRemote, &getRet, &callbackCalled, &mtx, &cv,
nodeId](void* ctx, const UbseByteBuffer& respData, uint32_t resCode) -> void {
HandleGetDirConnectInfoCallback(nodeId, respData, resCode, devDirConnectInfoRemote,
getRet, callbackCalled, mtx, cv);
});
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "send get all node msg failed, " << FormatRetCode(ret);
return ret;
}
{
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&callbackCalled] { return callbackCalled; });
}
return getRet;
}
UbseResult FillLinkAndBondingFM(bool isBeforeElection, std::vector<PhysicalLink>& links)
{
if (isBeforeElection) {
auto ret = UbseNodeComUrmaCollector::GetInstance().GetCurNodeTopo(links);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "get cur node topo failed";
return ret;
}
ret = UbseNodeComUrmaCollector::GetInstance().FillComUrmaInfo();
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "fill com urma info failed";
return ret;
}
UBSE_LOG_INFO << "fill cur node topo and urma device success, set to urma uvs first time";
} else {
std::map<std::string, PhysicalLink> connectInfo = UbseNodeController::GetInstance().UbseGetDirConnectInfo();
if (connectInfo.empty()) {
UBSE_LOG_WARN << "get cur node link size = 0";
}
for (const auto& entry : connectInfo) {
links.push_back(entry.second);
}
UBSE_LOG_INFO << "get cur node topo success, update urma uvs";
}
return UBSE_OK;
}
UbseResult FillLinkAndBondingClos(bool isBeforeElection = false)
{
if (!isBeforeElection) {
return UBSE_OK;
}
auto ret = UbseNodeComUrmaCollector::GetInstance().FillComUrmaInfoClos();
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "fill com urma info failed";
}
return ret;
}
UbseResult SetUrmaUvs(bool isBeforeElection = false)
{
std::vector<PhysicalLink> links;
if (UbseSmbios::GetInstance().IsClosType()) {
auto ret = FillLinkAndBondingClos(isBeforeElection);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "fill links and bonding clos failed";
return ret;
}
} else {
auto ret = FillLinkAndBondingFM(isBeforeElection, links);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "fill links and bonding failed";
return ret;
}
}
auto ret = UbseNodeComUrmaCollector::GetInstance().SetComUrma(links, isBeforeElection);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "set to urma uvs failed, " << FormatRetCode(ret);
}
return ret;
}
UbseResult PubNodeUrmaChange(std::string& nodeId, std::string action)
{
if (action != UBSE_EVENT_NODE_TOPO_LINK_CHANGE && action != UBSE_EVENT_NODE_JOIN) {
UBSE_LOG_ERROR << "PubEvent " << action << " is not supported.";
return UBSE_ERROR;
}
UBSE_LOG_INFO << "PubEvent " << action << " to urma when change";
auto ret = UbsePubEvent(action, nodeId);
if (ret != UBSE_OK) {
UBSE_LOG_ERROR << "Failed to PubEvent" << action << ", nodeId = " << nodeId << "," << FormatRetCode(ret);
return ret;
}
return UBSE_OK;
}
}