[OpenBMC] 快速上手OpenBMC的Redfish
最近好像很多公司都在开始做OpenBMC,真的好夯,所以今天来聊聊OpenBMC的Redfish怎么快速上手, 如果对redfish没有很熟的话,可以先看"认识Redfish"里面讲解的概念10分钟认识下一代数据中心基础设施管理标准 - 红鱼(Redfish)_yeiris的博客-CSDN博客这几年BMC领域中最受瞩目的两件事情就是Redfish的出现和OpenBMC的崛起,所以今天我想用10分钟
最近好像很多公司都在开始做OpenBMC,真的好夯,所以今天来聊聊OpenBMC的Redfish怎么快速上手, 如果对redfish没有很熟的话,可以先看"认识Redfish"里面讲解的概念
Code 在哪里?
Redfish的Code 是放在bmcweb这一包里面
GitHub - openbmc/bmcweb: A do everything Redfish, KVM, GUI, and DBus webserver for OpenBMChttps://github.com/openbmc/bmcweb目前OpenBMC的Web打算采用前后端分离的方式,后端是Redfish,前端是webui-vue,所以我猜bmcweb这个名字是这样来的
如何开始? 该先看哪只程式码?
RedfishService
在src\webserver_main.cpp 中将RedfishServices加到router
RedfishServices (redfish-core\include\redfish.hpp)里面有很多sub router
這些subrouter定義的程式碼都在"redfish-core\lib\"資料夾底下,以requestAccountServiceRoutes為例,定義在account_service.hpp中
使用者权限 (privileges)
根据Redfish.md里面的描述,User Role有三种可以选择 Administrator, Operator 和 ReadOnly
这边还是以AccountService的redfish::privileges::getAccountService为范例,在redfish-core\include\registries\privilege_registry.hpp可看到
// AccountService
const static auto& getAccountService = privilegeSetLogin;
其中privilegeSetLogin 是Login 權限,所以Administrator, Operator 和 ReadOnly User都可以GET /redfish/v1/AccountService/
namespace redfish::privileges
{
// clang-format off
const std::array<Privileges, 1> privilegeSetLogin = {{
{"Login"}
}};
const std::array<Privileges, 1> privilegeSetConfigureComponents = {{
{"ConfigureComponents"}
}};
const std::array<Privileges, 1> privilegeSetConfigureUsers = {{
{"ConfigureUsers"}
}};
const std::array<Privileges, 1> privilegeSetConfigureManager = {{
{"ConfigureManager"}
}};
const std::array<Privileges, 2> privilegeSetConfigureManagerOrConfigureComponents = {{
{"ConfigureManager"},
{"ConfigureComponents"}
}};
const std::array<Privileges, 2> privilegeSetConfigureManagerOrConfigureSelf = {{
{"ConfigureManager"},
{"ConfigureSelf"}
}};
const std::array<Privileges, 3> privilegeSetConfigureManagerOrConfigureUsersOrConfigureSelf = {{
{"ConfigureManager"},
{"ConfigureUsers"},
{"ConfigureSelf"}
}};
const std::array<Privileges, 2> privilegeSetLoginOrNoAuth = {{
{"Login"},
{}
}};
redfish-core\lib
这个资料夹中存放了所有support 的redfish resource的程式码,可以先看自己要研究的API,例如常見的ComputerServices
//redfish-core\lib\systems.hpp
BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/")
.privileges(redfish::privileges::getComputerSystem)
.methods(
boost::beast::http::verb::
get)([](const crow::Request&,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {}
资料操作皆来自Dbus
什么是Dbus
OpenBMC提供了介绍影片,我觉得讲得很完整 sdbusplus & phosphor-dbus-interface - YouTube
OpenBMC是由很多个daemon所组成的,像是ipmid, fwupd, fscd等,dbus可以想像成他们的共享资料夹,所以当我们要修改资料或是拿取资料的时候,就去dbus相对位置拿取就可以了,底下是我在维基看到的图片,从图片中可以感受到有了Dbus之后,Daemon间的沟通变得简单有条理
Dbus的组成有三个元素:
Service - A daemon attached to the dbus and providing objects.
root@romulus:~# busctl
xyz.openbmc_project.State.BMC
Object Paths – A tree, like a file system, of paths to objects.(对象路径的树,如文件系统)
root@romulus:~# busctl tree xyz.openbmc_project.State.BMC
└─/xyz
└─/xyz/openbmc_project
└─/xyz/openbmc_project/state
└─/xyz/openbmc_project/state/bmc0
Interface – The ‘class’ of an object. Objects support multiple inheritance.(对象的“class”。 对象支持多重继承)
- Property – Store values. Some can be written to (存储值。有些可以改写)
- Method – Make method calls.(进行方法调用)
- Signal – Inform other process about an event. (将事件通知其他进程)
root@romulus:~# busctl introspect xyz.openbmc_project.State.BMC /xyz/openbmc_project/state/bmc0
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
xyz.openbmc_project.State.BMC interface - - -
.CurrentBMCState property s "xyz.openbmc_project.State.BMC.BMCState… emits-change writable
.LastRebootTime property t 1619401752000 emits-change writable
.RequestedBMCTransition property s "xyz.openbmc_project.State.BMC.Transiti… emits-change writable
所以如果Redfish 想要取得BMC的LastRebootTime,那他就要下get-property指令
get-property
SERVICE
OBJECT
INTERFACE
PROPERTY
...
// 这个指令是下在bmc console 中的
busctl get-property xyz.openbmc_project.State.BMC /xyz/openbmc_project/state/bmc0 xyz.openbmc_project.State.BMC LastRebootTime
那在程式码实作就是如下,可以看到他在get-property 的function中分别带入service, object ,interface和property
//redfish-core\lib\managers.hpp
inline void
managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
{
BMCWEB_LOG_DEBUG << "Getting Manager Last Reset Time";
sdbusplus::asio::getProperty<uint64_t>(
*crow::connections::systemBus, "xyz.openbmc_project.State.BMC",
"/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC",
"LastRebootTime",
[aResp](const boost::system::error_code ec,
const uint64_t lastResetTime) {
if (ec)
{
BMCWEB_LOG_DEBUG << "D-BUS response error " << ec;
return;
}
// LastRebootTime is epoch time, in milliseconds
// https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
uint64_t lastResetTimeStamp = lastResetTime / 1000;
// Convert to ISO 8601 standard
aResp->res.jsonValue["LastResetTime"] =
crow::utility::getDateTimeUint(lastResetTimeStamp);
});
}
回调函数(Callback function)
回调函数(callback function)是指能藉由参数(argument)通往另一个函数的函数。例如底下范例
//sdbusplus/include/sdbusplus/asio/connection.hpp
void async_method_call(MessageHandler&& handler, const std::string& service,
const std::string& objpath,
const std::string& interf, const std::string& method,
const InputArgs&... a)
在 async_method_call 中,先注册了MessageHandler&& handler 这个回调函数,因此在Dbus 的回覆回传时,就会执行我们注册的MessageHandler&& handler
前面有提到Redfish所有资料都是来自于Dbus,因此我们必须在Dbus回复回传的时候,才能Response StatusCode,所以我们在Redfish中可以看到大量Callback function的用法
例如AccountService/Accounts的PATCH Method,在Dbus回覆之后,便执行注册的callback function
//redfish-core\lib\account_service.hpp
BMCWEB_ROUTE(app, "/redfish/v1/AccountService/Accounts/<str>/")
.privileges({{"ConfigureUsers"}, {"ConfigureSelf"}})
.methods(boost::beast::http::verb::patch)(
[](const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& username) -> void {
//verify value, 先省略掉
crow::connections::systemBus->async_method_call(
//callback function start
[asyncResp, username, password(std::move(password)),
roleId(std::move(roleId)), enabled,
newUser{std::string(*newUserName)},
locked](const boost::system::error_code ec,
sdbusplus::message::message& m) {
if (ec)
{
userErrorMessageHandler(m.get_error(), asyncResp,
newUser, username);
return;
}
updateUserProperties(asyncResp, newUser, password,
enabled, roleId, locked);
}
//callback function end
,
"xyz.openbmc_project.User.Manager",
"/xyz/openbmc_project/user",
"xyz.openbmc_project.User.Manager", "RenameUser", username,
*newUserName);
});
那如果有很多Dbus method需要依序呼叫的时候,就会发现有巢状回调函数的出现,这样可以执行完一个method后再call 下一个mthod
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec,
sdbusplus::message::message& m) {
if (ec)
{
return;
}
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec,
sdbusplus::message::message& m) {
if (ec)
{
return;
}
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec,
sdbusplus::message::message& m) {
if (ec)
{
return;
}
updateUserProperties(asyncResp, newUser, password,
enabled, roleId, locked);
},
"Service 3",
"object path3",
"Interface", "method", value, value'
);
},
"Service 2",
"object path2",
"Interface", "method", value, value'
);
},
"Service 1",
"object path",
"Interface", "method", value, value'
);
如果掌握以上几点再去看bmcweb的code,会发现其实也没这么复杂
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)