// SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright OpenBMC Authors #pragma once #include "app.hpp" #include "async_resp.hpp" #include "dbus_singleton.hpp" #include "dbus_utility.hpp" #include "error_messages.hpp" #include "generated/enums/resource.hpp" #include "http_request.hpp" #include "http_response.hpp" #include "logging.hpp" #include "query.hpp" #include "registries/privilege_registry.hpp" #include "utils/assembly_utils.hpp" #include "utils/asset_utils.hpp" #include "utils/chassis_utils.hpp" #include "utils/dbus_utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace redfish { /** * @brief Get Location code for the given assembly. * @param[in] asyncResp - Shared pointer for asynchronous calls. * @param[in] serviceName - Service in which the assembly is hosted. * @param[in] assembly - Assembly object. * @param[in] assemblyJsonPtr - json-keyname on the assembly list output. * @return None. */ inline void getAssemblyLocationCode( const std::shared_ptr& asyncResp, const std::string& serviceName, const std::string& assembly, const nlohmann::json::json_pointer& assemblyJsonPtr) { sdbusplus::asio::getProperty( *crow::connections::systemBus, serviceName, assembly, "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", [asyncResp, assembly, assemblyJsonPtr]( const boost::system::error_code& ec, const std::string& value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error: {} for assembly {}", ec.value(), assembly); messages::internalError(asyncResp->res); } return; } asyncResp->res.jsonValue[assemblyJsonPtr]["Location"] ["PartLocation"]["ServiceLabel"] = value; }); } inline void getAssemblyState( const std::shared_ptr& asyncResp, const auto& serviceName, const auto& assembly, const nlohmann::json::json_pointer& assemblyJsonPtr) { asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["State"] = resource::State::Enabled; dbus::utility::getProperty( serviceName, assembly, "xyz.openbmc_project.Inventory.Item", "Present", [asyncResp, assemblyJsonPtr, assembly](const boost::system::error_code& ec, const bool value) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error: {}", ec.value()); messages::internalError(asyncResp->res); } return; } if (!value) { asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["State"] = resource::State::Absent; } }); } void getAssemblyHealth(const std::shared_ptr& asyncResp, const auto& serviceName, const auto& assembly, const nlohmann::json::json_pointer& assemblyJsonPtr) { asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["Health"] = resource::Health::OK; dbus::utility::getProperty( serviceName, assembly, "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", [asyncResp, assemblyJsonPtr](const boost::system::error_code& ec, bool functional) { if (ec) { if (ec.value() != EBADR) { BMCWEB_LOG_ERROR("DBUS response error {}", ec.value()); messages::internalError(asyncResp->res); } return; } if (!functional) { asyncResp->res.jsonValue[assemblyJsonPtr]["Status"]["Health"] = resource::Health::Critical; } }); } inline void afterGetDbusObject( const std::shared_ptr& asyncResp, const std::string& assembly, const nlohmann::json::json_pointer& assemblyJsonPtr, const boost::system::error_code& ec, const dbus::utility::MapperGetObject& object) { if (ec) { BMCWEB_LOG_ERROR("DBUS response error : {} for assembly {}", ec.value(), assembly); messages::internalError(asyncResp->res); return; } for (const auto& [serviceName, interfaceList] : object) { for (const auto& interface : interfaceList) { if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset") { asset_utils::getAssetInfo(asyncResp, serviceName, assembly, assemblyJsonPtr, true, false); } else if (interface == "xyz.openbmc_project.Inventory.Decorator.LocationCode") { getAssemblyLocationCode(asyncResp, serviceName, assembly, assemblyJsonPtr); } else if (interface == "xyz.openbmc_project.Inventory.Item") { getAssemblyState(asyncResp, serviceName, assembly, assemblyJsonPtr); } else if (interface == "xyz.openbmc_project.State.Decorator.OperationalStatus") { getAssemblyHealth(asyncResp, serviceName, assembly, assemblyJsonPtr); } } } } /** * @brief Get properties for the assemblies associated to given chassis * @param[in] asyncResp - Shared pointer for asynchronous calls. * @param[in] chassisId - Chassis the assemblies are associated with. * @param[in] assemblies - list of all the assemblies associated with the * chassis. * @return None. */ inline void getAssemblyProperties( const std::shared_ptr& asyncResp, const std::string& chassisId, const std::vector& assemblies) { BMCWEB_LOG_DEBUG("Get properties for assembly associated"); std::size_t assemblyIndex = 0; for (const std::string& assembly : assemblies) { nlohmann::json::object_t item; item["@odata.type"] = "#Assembly.v1_5_1.AssemblyData"; item["@odata.id"] = boost::urls::format( "/redfish/v1/Chassis/{}/Assembly#/Assemblies/{}", chassisId, std::to_string(assemblyIndex)); item["MemberId"] = std::to_string(assemblyIndex); item["Name"] = sdbusplus::message::object_path(assembly).filename(); asyncResp->res.jsonValue["Assemblies"].emplace_back(item); nlohmann::json::json_pointer assemblyJsonPtr( "/Assemblies/" + std::to_string(assemblyIndex)); dbus::utility::getDbusObject( assembly, assemblyInterfaces, std::bind_front(afterGetDbusObject, asyncResp, assembly, assemblyJsonPtr)); nlohmann::json& assemblyArray = asyncResp->res.jsonValue["Assemblies"]; asyncResp->res.jsonValue["Assemblies@odata.count"] = assemblyArray.size(); assemblyIndex++; } } inline void afterHandleChassisAssemblyGet( const std::shared_ptr& asyncResp, const std::string& chassisID, const boost::system::error_code& ec, const std::vector& assemblyList) { if (ec) { BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); asyncResp->res.jsonValue["@odata.type"] = "#Assembly.v1_5_1.Assembly"; asyncResp->res.jsonValue["@odata.id"] = boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisID); asyncResp->res.jsonValue["Name"] = "Assembly Collection"; asyncResp->res.jsonValue["Id"] = "Assembly"; asyncResp->res.jsonValue["Assemblies"] = nlohmann::json::array(); asyncResp->res.jsonValue["Assemblies@odata.count"] = 0; if (!assemblyList.empty()) { getAssemblyProperties(asyncResp, chassisID, assemblyList); } } /** * @param[in] asyncResp - Shared pointer for asynchronous calls. * @param[in] chassisID - Chassis to which the assemblies are * associated. * * @return None. */ inline void handleChassisAssemblyGet( App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisID) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } BMCWEB_LOG_DEBUG("Get chassis Assembly"); assembly_utils::getChassisAssembly( asyncResp, chassisID, std::bind_front(afterHandleChassisAssemblyGet, asyncResp, chassisID)); } inline void handleChassisAssemblyHead( crow::App& app, const crow::Request& req, const std::shared_ptr& asyncResp, const std::string& chassisID) { if (!redfish::setUpRedfishRoute(app, req, asyncResp)) { return; } assembly_utils::getChassisAssembly( asyncResp, chassisID, [asyncResp, chassisID](const boost::system::error_code& ec, const std::vector& /*assemblyList*/) { if (ec) { BMCWEB_LOG_WARNING("Chassis {} not found", chassisID); messages::resourceNotFound(asyncResp->res, "Chassis", chassisID); return; } asyncResp->res.addHeader( boost::beast::http::field::link, "; rel=describedby"); }); } inline void requestRoutesAssembly(App& app) { BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") .privileges(redfish::privileges::headAssembly) .methods(boost::beast::http::verb::head)( std::bind_front(handleChassisAssemblyHead, std::ref(app))); BMCWEB_ROUTE(app, "/redfish/v1/Chassis//Assembly/") .privileges(redfish::privileges::getAssembly) .methods(boost::beast::http::verb::get)( std::bind_front(handleChassisAssemblyGet, std::ref(app))); } } // namespace redfish