1a7210020SGeorge Liu #pragma once 2a7210020SGeorge Liu 3a7210020SGeorge Liu #include "app.hpp" 4a7210020SGeorge Liu #include "dbus_utility.hpp" 5a7210020SGeorge Liu #include "query.hpp" 6a7210020SGeorge Liu #include "registries/privilege_registry.hpp" 7a7210020SGeorge Liu #include "utils/chassis_utils.hpp" 8a7210020SGeorge Liu 9*34dfcb94SGeorge Liu #include <boost/system/error_code.hpp> 10ef4c65b7SEd Tanous #include <boost/url/format.hpp> 11ef4c65b7SEd Tanous 12a7210020SGeorge Liu #include <memory> 13a7210020SGeorge Liu #include <optional> 14a7210020SGeorge Liu #include <string> 15a7210020SGeorge Liu 16a7210020SGeorge Liu namespace redfish 17a7210020SGeorge Liu { 18a7210020SGeorge Liu 1900ef5dc6SGeorge Liu inline void 2000ef5dc6SGeorge Liu updatePowerSupplyList(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2100ef5dc6SGeorge Liu const std::string& chassisId, 2200ef5dc6SGeorge Liu const std::string& powerSupplyPath) 23a7210020SGeorge Liu { 2400ef5dc6SGeorge Liu std::string powerSupplyName = 2500ef5dc6SGeorge Liu sdbusplus::message::object_path(powerSupplyPath).filename(); 2600ef5dc6SGeorge Liu if (powerSupplyName.empty()) 2700ef5dc6SGeorge Liu { 2800ef5dc6SGeorge Liu return; 2900ef5dc6SGeorge Liu } 3000ef5dc6SGeorge Liu 3100ef5dc6SGeorge Liu nlohmann::json item = nlohmann::json::object(); 3200ef5dc6SGeorge Liu item["@odata.id"] = boost::urls::format( 3300ef5dc6SGeorge Liu "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 3400ef5dc6SGeorge Liu powerSupplyName); 3500ef5dc6SGeorge Liu 3600ef5dc6SGeorge Liu nlohmann::json& powerSupplyList = asyncResp->res.jsonValue["Members"]; 3700ef5dc6SGeorge Liu powerSupplyList.emplace_back(std::move(item)); 3800ef5dc6SGeorge Liu asyncResp->res.jsonValue["Members@odata.count"] = powerSupplyList.size(); 39a7210020SGeorge Liu } 40a7210020SGeorge Liu 41a7210020SGeorge Liu inline void 42a7210020SGeorge Liu doPowerSupplyCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 43a7210020SGeorge Liu const std::string& chassisId, 44a7210020SGeorge Liu const std::optional<std::string>& validChassisPath) 45a7210020SGeorge Liu { 46a7210020SGeorge Liu if (!validChassisPath) 47a7210020SGeorge Liu { 48a7210020SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 49a7210020SGeorge Liu return; 50a7210020SGeorge Liu } 51a7210020SGeorge Liu 52a7210020SGeorge Liu asyncResp->res.addHeader( 53a7210020SGeorge Liu boost::beast::http::field::link, 54a7210020SGeorge Liu "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 55a7210020SGeorge Liu asyncResp->res.jsonValue["@odata.type"] = 56a7210020SGeorge Liu "#PowerSupplyCollection.PowerSupplyCollection"; 57a7210020SGeorge Liu asyncResp->res.jsonValue["Name"] = "Power Supply Collection"; 58ef4c65b7SEd Tanous asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 59ef4c65b7SEd Tanous "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies", chassisId); 60a7210020SGeorge Liu asyncResp->res.jsonValue["Description"] = 61a7210020SGeorge Liu "The collection of PowerSupply resource instances."; 627a2bb2c9SGeorge Liu asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 637a2bb2c9SGeorge Liu asyncResp->res.jsonValue["Members@odata.count"] = 0; 64a7210020SGeorge Liu 65a7210020SGeorge Liu std::string powerPath = *validChassisPath + "/powered_by"; 66a7210020SGeorge Liu dbus::utility::getAssociationEndPoints( 67a7210020SGeorge Liu powerPath, [asyncResp, chassisId]( 68a7210020SGeorge Liu const boost::system::error_code& ec, 69a7210020SGeorge Liu const dbus::utility::MapperEndPoints& endpoints) { 70a7210020SGeorge Liu if (ec) 71a7210020SGeorge Liu { 72a7210020SGeorge Liu if (ec.value() != EBADR) 73a7210020SGeorge Liu { 74a7210020SGeorge Liu BMCWEB_LOG_ERROR << "DBUS response error" << ec.value(); 75a7210020SGeorge Liu messages::internalError(asyncResp->res); 76a7210020SGeorge Liu } 77a7210020SGeorge Liu return; 78a7210020SGeorge Liu } 79a7210020SGeorge Liu 80a7210020SGeorge Liu for (const auto& endpoint : endpoints) 81a7210020SGeorge Liu { 82a7210020SGeorge Liu updatePowerSupplyList(asyncResp, chassisId, endpoint); 83a7210020SGeorge Liu } 84a7210020SGeorge Liu }); 85a7210020SGeorge Liu } 86a7210020SGeorge Liu 87a7210020SGeorge Liu inline void handlePowerSupplyCollectionHead( 88a7210020SGeorge Liu App& app, const crow::Request& req, 89a7210020SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 90a7210020SGeorge Liu const std::string& chassisId) 91a7210020SGeorge Liu { 92a7210020SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 93a7210020SGeorge Liu { 94a7210020SGeorge Liu return; 95a7210020SGeorge Liu } 96a7210020SGeorge Liu 97a7210020SGeorge Liu redfish::chassis_utils::getValidChassisPath( 98a7210020SGeorge Liu asyncResp, chassisId, 99a7210020SGeorge Liu [asyncResp, 100a7210020SGeorge Liu chassisId](const std::optional<std::string>& validChassisPath) { 101a7210020SGeorge Liu if (!validChassisPath) 102a7210020SGeorge Liu { 103a7210020SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 104a7210020SGeorge Liu return; 105a7210020SGeorge Liu } 106a7210020SGeorge Liu asyncResp->res.addHeader( 107a7210020SGeorge Liu boost::beast::http::field::link, 108a7210020SGeorge Liu "</redfish/v1/JsonSchemas/PowerSupplyCollection/PowerSupplyCollection.json>; rel=describedby"); 109a7210020SGeorge Liu }); 110a7210020SGeorge Liu } 111a7210020SGeorge Liu 112a7210020SGeorge Liu inline void handlePowerSupplyCollectionGet( 113a7210020SGeorge Liu App& app, const crow::Request& req, 114a7210020SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 115a7210020SGeorge Liu const std::string& chassisId) 116a7210020SGeorge Liu { 117a7210020SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 118a7210020SGeorge Liu { 119a7210020SGeorge Liu return; 120a7210020SGeorge Liu } 121a7210020SGeorge Liu 122a7210020SGeorge Liu redfish::chassis_utils::getValidChassisPath( 123a7210020SGeorge Liu asyncResp, chassisId, 124a7210020SGeorge Liu std::bind_front(doPowerSupplyCollection, asyncResp, chassisId)); 125a7210020SGeorge Liu } 126a7210020SGeorge Liu 127a7210020SGeorge Liu inline void requestRoutesPowerSupplyCollection(App& app) 128a7210020SGeorge Liu { 129a7210020SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 130a7210020SGeorge Liu .privileges(redfish::privileges::headPowerSupplyCollection) 131a7210020SGeorge Liu .methods(boost::beast::http::verb::head)( 132a7210020SGeorge Liu std::bind_front(handlePowerSupplyCollectionHead, std::ref(app))); 133a7210020SGeorge Liu 134a7210020SGeorge Liu BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/") 135a7210020SGeorge Liu .privileges(redfish::privileges::getPowerSupplyCollection) 136a7210020SGeorge Liu .methods(boost::beast::http::verb::get)( 137a7210020SGeorge Liu std::bind_front(handlePowerSupplyCollectionGet, std::ref(app))); 138a7210020SGeorge Liu } 139a7210020SGeorge Liu 14000ef5dc6SGeorge Liu inline bool checkPowerSupplyId(const std::string& powerSupplyPath, 14100ef5dc6SGeorge Liu const std::string& powerSupplyId) 14200ef5dc6SGeorge Liu { 14300ef5dc6SGeorge Liu std::string powerSupplyName = 14400ef5dc6SGeorge Liu sdbusplus::message::object_path(powerSupplyPath).filename(); 14500ef5dc6SGeorge Liu 14600ef5dc6SGeorge Liu return !(powerSupplyName.empty() || powerSupplyName != powerSupplyId); 14700ef5dc6SGeorge Liu } 14800ef5dc6SGeorge Liu 149*34dfcb94SGeorge Liu inline void getValidPowerSupplyPath( 150*34dfcb94SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 151*34dfcb94SGeorge Liu const std::string& validChassisPath, const std::string& powerSupplyId, 152*34dfcb94SGeorge Liu std::function<void(const std::string& powerSupplyPath)>&& callback) 15300ef5dc6SGeorge Liu { 15400ef5dc6SGeorge Liu std::string powerPath = validChassisPath + "/powered_by"; 15500ef5dc6SGeorge Liu dbus::utility::getAssociationEndPoints( 15600ef5dc6SGeorge Liu powerPath, [asyncResp, powerSupplyId, callback{std::move(callback)}]( 15700ef5dc6SGeorge Liu const boost::system::error_code& ec, 15800ef5dc6SGeorge Liu const dbus::utility::MapperEndPoints& endpoints) { 15900ef5dc6SGeorge Liu if (ec) 16000ef5dc6SGeorge Liu { 16100ef5dc6SGeorge Liu if (ec.value() != EBADR) 16200ef5dc6SGeorge Liu { 16300ef5dc6SGeorge Liu BMCWEB_LOG_ERROR 16400ef5dc6SGeorge Liu << "DBUS response error for getAssociationEndPoints" 16500ef5dc6SGeorge Liu << ec.value(); 16600ef5dc6SGeorge Liu messages::internalError(asyncResp->res); 16700ef5dc6SGeorge Liu return; 16800ef5dc6SGeorge Liu } 16900ef5dc6SGeorge Liu messages::resourceNotFound(asyncResp->res, "PowerSupplies", 17000ef5dc6SGeorge Liu powerSupplyId); 17100ef5dc6SGeorge Liu return; 17200ef5dc6SGeorge Liu } 17300ef5dc6SGeorge Liu 17400ef5dc6SGeorge Liu for (const auto& endpoint : endpoints) 17500ef5dc6SGeorge Liu { 17600ef5dc6SGeorge Liu if (checkPowerSupplyId(endpoint, powerSupplyId)) 17700ef5dc6SGeorge Liu { 178*34dfcb94SGeorge Liu callback(endpoint); 17900ef5dc6SGeorge Liu return; 18000ef5dc6SGeorge Liu } 18100ef5dc6SGeorge Liu } 18200ef5dc6SGeorge Liu 18300ef5dc6SGeorge Liu if (!endpoints.empty()) 18400ef5dc6SGeorge Liu { 18500ef5dc6SGeorge Liu BMCWEB_LOG_WARNING << "Power supply not found: " 18600ef5dc6SGeorge Liu << powerSupplyId; 18700ef5dc6SGeorge Liu messages::resourceNotFound(asyncResp->res, "PowerSupplies", 18800ef5dc6SGeorge Liu powerSupplyId); 18900ef5dc6SGeorge Liu return; 19000ef5dc6SGeorge Liu } 19100ef5dc6SGeorge Liu }); 19200ef5dc6SGeorge Liu } 19300ef5dc6SGeorge Liu 19400ef5dc6SGeorge Liu inline void 195*34dfcb94SGeorge Liu getPowerSupplyState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 196*34dfcb94SGeorge Liu const std::string& service, const std::string& path) 197*34dfcb94SGeorge Liu { 198*34dfcb94SGeorge Liu sdbusplus::asio::getProperty<bool>( 199*34dfcb94SGeorge Liu *crow::connections::systemBus, service, path, 200*34dfcb94SGeorge Liu "xyz.openbmc_project.Inventory.Item", "Present", 201*34dfcb94SGeorge Liu [asyncResp](const boost::system::error_code& ec, const bool value) { 202*34dfcb94SGeorge Liu if (ec) 203*34dfcb94SGeorge Liu { 204*34dfcb94SGeorge Liu if (ec.value() != EBADR) 205*34dfcb94SGeorge Liu { 206*34dfcb94SGeorge Liu BMCWEB_LOG_ERROR << "DBUS response error for State " 207*34dfcb94SGeorge Liu << ec.value(); 208*34dfcb94SGeorge Liu messages::internalError(asyncResp->res); 209*34dfcb94SGeorge Liu } 210*34dfcb94SGeorge Liu return; 211*34dfcb94SGeorge Liu } 212*34dfcb94SGeorge Liu 213*34dfcb94SGeorge Liu if (!value) 214*34dfcb94SGeorge Liu { 215*34dfcb94SGeorge Liu asyncResp->res.jsonValue["Status"]["State"] = "Absent"; 216*34dfcb94SGeorge Liu } 217*34dfcb94SGeorge Liu }); 218*34dfcb94SGeorge Liu } 219*34dfcb94SGeorge Liu 220*34dfcb94SGeorge Liu inline void 221*34dfcb94SGeorge Liu getPowerSupplyHealth(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 222*34dfcb94SGeorge Liu const std::string& service, const std::string& path) 223*34dfcb94SGeorge Liu { 224*34dfcb94SGeorge Liu sdbusplus::asio::getProperty<bool>( 225*34dfcb94SGeorge Liu *crow::connections::systemBus, service, path, 226*34dfcb94SGeorge Liu "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 227*34dfcb94SGeorge Liu [asyncResp](const boost::system::error_code& ec, const bool value) { 228*34dfcb94SGeorge Liu if (ec) 229*34dfcb94SGeorge Liu { 230*34dfcb94SGeorge Liu if (ec.value() != EBADR) 231*34dfcb94SGeorge Liu { 232*34dfcb94SGeorge Liu BMCWEB_LOG_ERROR << "DBUS response error for Health " 233*34dfcb94SGeorge Liu << ec.value(); 234*34dfcb94SGeorge Liu messages::internalError(asyncResp->res); 235*34dfcb94SGeorge Liu } 236*34dfcb94SGeorge Liu return; 237*34dfcb94SGeorge Liu } 238*34dfcb94SGeorge Liu 239*34dfcb94SGeorge Liu if (!value) 240*34dfcb94SGeorge Liu { 241*34dfcb94SGeorge Liu asyncResp->res.jsonValue["Status"]["Health"] = "Critical"; 242*34dfcb94SGeorge Liu } 243*34dfcb94SGeorge Liu }); 244*34dfcb94SGeorge Liu } 245*34dfcb94SGeorge Liu 246*34dfcb94SGeorge Liu inline void 24700ef5dc6SGeorge Liu doPowerSupplyGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 24800ef5dc6SGeorge Liu const std::string& chassisId, 24900ef5dc6SGeorge Liu const std::string& powerSupplyId, 25000ef5dc6SGeorge Liu const std::optional<std::string>& validChassisPath) 25100ef5dc6SGeorge Liu { 25200ef5dc6SGeorge Liu if (!validChassisPath) 25300ef5dc6SGeorge Liu { 25400ef5dc6SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 25500ef5dc6SGeorge Liu return; 25600ef5dc6SGeorge Liu } 25700ef5dc6SGeorge Liu 25800ef5dc6SGeorge Liu // Get the correct Path and Service that match the input parameters 25900ef5dc6SGeorge Liu getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 260*34dfcb94SGeorge Liu [asyncResp, chassisId, powerSupplyId]( 261*34dfcb94SGeorge Liu const std::string& powerSupplyPath) { 26200ef5dc6SGeorge Liu asyncResp->res.addHeader( 26300ef5dc6SGeorge Liu boost::beast::http::field::link, 26400ef5dc6SGeorge Liu "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 26500ef5dc6SGeorge Liu asyncResp->res.jsonValue["@odata.type"] = 26600ef5dc6SGeorge Liu "#PowerSupply.v1_5_0.PowerSupply"; 26700ef5dc6SGeorge Liu asyncResp->res.jsonValue["Name"] = "Power Supply"; 26800ef5dc6SGeorge Liu asyncResp->res.jsonValue["Id"] = powerSupplyId; 26900ef5dc6SGeorge Liu asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 27000ef5dc6SGeorge Liu "/redfish/v1/Chassis/{}/PowerSubsystem/PowerSupplies/{}", chassisId, 27100ef5dc6SGeorge Liu powerSupplyId); 272*34dfcb94SGeorge Liu 273*34dfcb94SGeorge Liu asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 274*34dfcb94SGeorge Liu asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 275*34dfcb94SGeorge Liu 276*34dfcb94SGeorge Liu constexpr std::array<std::string_view, 1> interfaces = { 277*34dfcb94SGeorge Liu "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 278*34dfcb94SGeorge Liu dbus::utility::getDbusObject( 279*34dfcb94SGeorge Liu powerSupplyPath, interfaces, 280*34dfcb94SGeorge Liu [asyncResp, 281*34dfcb94SGeorge Liu powerSupplyPath](const boost::system::error_code& ec, 282*34dfcb94SGeorge Liu const dbus::utility::MapperGetObject& object) { 283*34dfcb94SGeorge Liu if (ec || object.empty()) 284*34dfcb94SGeorge Liu { 285*34dfcb94SGeorge Liu messages::internalError(asyncResp->res); 286*34dfcb94SGeorge Liu return; 287*34dfcb94SGeorge Liu } 288*34dfcb94SGeorge Liu 289*34dfcb94SGeorge Liu getPowerSupplyState(asyncResp, object.begin()->first, 290*34dfcb94SGeorge Liu powerSupplyPath); 291*34dfcb94SGeorge Liu getPowerSupplyHealth(asyncResp, object.begin()->first, 292*34dfcb94SGeorge Liu powerSupplyPath); 293*34dfcb94SGeorge Liu }); 29400ef5dc6SGeorge Liu }); 29500ef5dc6SGeorge Liu } 29600ef5dc6SGeorge Liu 29700ef5dc6SGeorge Liu inline void 29800ef5dc6SGeorge Liu handlePowerSupplyHead(App& app, const crow::Request& req, 29900ef5dc6SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 30000ef5dc6SGeorge Liu const std::string& chassisId, 30100ef5dc6SGeorge Liu const std::string& powerSupplyId) 30200ef5dc6SGeorge Liu { 30300ef5dc6SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 30400ef5dc6SGeorge Liu { 30500ef5dc6SGeorge Liu return; 30600ef5dc6SGeorge Liu } 30700ef5dc6SGeorge Liu 30800ef5dc6SGeorge Liu redfish::chassis_utils::getValidChassisPath( 30900ef5dc6SGeorge Liu asyncResp, chassisId, 31000ef5dc6SGeorge Liu [asyncResp, chassisId, 31100ef5dc6SGeorge Liu powerSupplyId](const std::optional<std::string>& validChassisPath) { 31200ef5dc6SGeorge Liu if (!validChassisPath) 31300ef5dc6SGeorge Liu { 31400ef5dc6SGeorge Liu messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 31500ef5dc6SGeorge Liu return; 31600ef5dc6SGeorge Liu } 31700ef5dc6SGeorge Liu 31800ef5dc6SGeorge Liu // Get the correct Path and Service that match the input parameters 31900ef5dc6SGeorge Liu getValidPowerSupplyPath(asyncResp, *validChassisPath, powerSupplyId, 320*34dfcb94SGeorge Liu [asyncResp](const std::string&) { 32100ef5dc6SGeorge Liu asyncResp->res.addHeader( 32200ef5dc6SGeorge Liu boost::beast::http::field::link, 32300ef5dc6SGeorge Liu "</redfish/v1/JsonSchemas/PowerSupply/PowerSupply.json>; rel=describedby"); 32400ef5dc6SGeorge Liu }); 32500ef5dc6SGeorge Liu }); 32600ef5dc6SGeorge Liu } 32700ef5dc6SGeorge Liu 32800ef5dc6SGeorge Liu inline void 32900ef5dc6SGeorge Liu handlePowerSupplyGet(App& app, const crow::Request& req, 33000ef5dc6SGeorge Liu const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 33100ef5dc6SGeorge Liu const std::string& chassisId, 33200ef5dc6SGeorge Liu const std::string& powerSupplyId) 33300ef5dc6SGeorge Liu { 33400ef5dc6SGeorge Liu if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 33500ef5dc6SGeorge Liu { 33600ef5dc6SGeorge Liu return; 33700ef5dc6SGeorge Liu } 33800ef5dc6SGeorge Liu 33900ef5dc6SGeorge Liu redfish::chassis_utils::getValidChassisPath( 34000ef5dc6SGeorge Liu asyncResp, chassisId, 34100ef5dc6SGeorge Liu std::bind_front(doPowerSupplyGet, asyncResp, chassisId, powerSupplyId)); 34200ef5dc6SGeorge Liu } 34300ef5dc6SGeorge Liu 34400ef5dc6SGeorge Liu inline void requestRoutesPowerSupply(App& app) 34500ef5dc6SGeorge Liu { 34600ef5dc6SGeorge Liu BMCWEB_ROUTE( 34700ef5dc6SGeorge Liu app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 34800ef5dc6SGeorge Liu .privileges(redfish::privileges::headPowerSupply) 34900ef5dc6SGeorge Liu .methods(boost::beast::http::verb::head)( 35000ef5dc6SGeorge Liu std::bind_front(handlePowerSupplyHead, std::ref(app))); 35100ef5dc6SGeorge Liu 35200ef5dc6SGeorge Liu BMCWEB_ROUTE( 35300ef5dc6SGeorge Liu app, "/redfish/v1/Chassis/<str>/PowerSubsystem/PowerSupplies/<str>/") 35400ef5dc6SGeorge Liu .privileges(redfish::privileges::getPowerSupply) 35500ef5dc6SGeorge Liu .methods(boost::beast::http::verb::get)( 35600ef5dc6SGeorge Liu std::bind_front(handlePowerSupplyGet, std::ref(app))); 35700ef5dc6SGeorge Liu } 35800ef5dc6SGeorge Liu 359a7210020SGeorge Liu } // namespace redfish 360