/* // Copyright (c) 2018 Intel Corporation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. */ #pragma once #include "node.hpp" #include #include namespace redfish { /** * @brief Retrieves chassis state properties over dbus * * @param[in] aResp - Shared pointer for completing asynchronous calls. * * @return None. */ void getChassisState(std::shared_ptr aResp) { crow::connections::systemBus->async_method_call( [aResp{std::move(aResp)}]( const boost::system::error_code ec, const std::variant &chassisState) { if (ec) { BMCWEB_LOG_DEBUG << "DBUS response error " << ec; messages::internalError(aResp->res); return; } const std::string *s = std::get_if(&chassisState); BMCWEB_LOG_DEBUG << "Chassis state: " << *s; if (s != nullptr) { // Verify Chassis State if (*s == "xyz.openbmc_project.State.Chassis.PowerState.On") { aResp->res.jsonValue["PowerState"] = "On"; aResp->res.jsonValue["Status"]["State"] = "Enabled"; } else if (*s == "xyz.openbmc_project.State.Chassis.PowerState.Off") { aResp->res.jsonValue["PowerState"] = "Off"; aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; } } }, "xyz.openbmc_project.State.Chassis", "/xyz/openbmc_project/state/chassis0", "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); } /** * DBus types primitives for several generic DBus interfaces * TODO(Pawel) consider move this to separate file into boost::dbus */ // Note, this is not a very useful Variant, but because it isn't used to get // values, it should be as simple as possible // TODO(ed) invent a nullvariant type using VariantType = std::variant; using ManagedObjectsType = std::vector>>>>>; using PropertiesType = boost::container::flat_map; void getIntrusionByService(std::shared_ptr aResp, const std::string &service, const std::string &objPath) { BMCWEB_LOG_DEBUG << "Get intrusion status by service \n"; crow::connections::systemBus->async_method_call( [aResp{std::move(aResp)}](const boost::system::error_code ec, const std::variant &value) { if (ec) { // do not add err msg in redfish response, becaues this is not // mandatory property BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n"; return; } const std::string *status = std::get_if(&value); if (status == nullptr) { BMCWEB_LOG_ERROR << "intrusion status read error \n"; return; } aResp->res.jsonValue["PhysicalSecurity"] = { {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}}; }, service, objPath, "org.freedesktop.DBus.Properties", "Get", "xyz.openbmc_project.Chassis.Intrusion", "Status"); } /** * Retrieves physical security properties over dbus */ void getPhysicalSecurityData(std::shared_ptr aResp) { crow::connections::systemBus->async_method_call( [aResp{std::move(aResp)}]( const boost::system::error_code ec, const std::vector>>>> &subtree) { if (ec) { // do not add err msg in redfish response, becaues this is not // mandatory property BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec << "\n"; return; } // Iterate over all retrieved ObjectPaths. for (const auto &object : subtree) { for (const auto &service : object.second) { getIntrusionByService(aResp, service.first, object.first); return; } } }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/xyz/openbmc_project/Intrusion", int32_t(1), std::array{"xyz.openbmc_project.Chassis.Intrusion"}); } /** * ChassisCollection derived class for delivering Chassis Collection Schema */ class ChassisCollection : public Node { public: ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { entityPrivileges = { {boost::beast::http::verb::get, {{"Login"}}}, {boost::beast::http::verb::head, {{"Login"}}}, {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } private: /** * Functions triggers appropriate requests on DBus */ void doGet(crow::Response &res, const crow::Request &req, const std::vector ¶ms) override { res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection"; res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; res.jsonValue["@odata.context"] = "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; res.jsonValue["Name"] = "Chassis Collection"; const std::array interfaces = { "xyz.openbmc_project.Inventory.Item.Board", "xyz.openbmc_project.Inventory.Item.Chassis", "xyz.openbmc_project.Inventory.Item.PowerSupply"}; auto asyncResp = std::make_shared(res); crow::connections::systemBus->async_method_call( [asyncResp](const boost::system::error_code ec, const std::vector &chassisList) { if (ec) { messages::internalError(asyncResp->res); return; } nlohmann::json &chassisArray = asyncResp->res.jsonValue["Members"]; chassisArray = nlohmann::json::array(); for (const std::string &objpath : chassisList) { std::size_t lastPos = objpath.rfind("/"); if (lastPos == std::string::npos) { BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; continue; } chassisArray.push_back( {{"@odata.id", "/redfish/v1/Chassis/" + objpath.substr(lastPos + 1)}}); } asyncResp->res.jsonValue["Members@odata.count"] = chassisArray.size(); }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/xyz/openbmc_project/inventory", int32_t(0), interfaces); } }; /** * Chassis override class for delivering Chassis Schema */ class Chassis : public Node { public: Chassis(CrowApp &app) : Node(app, "/redfish/v1/Chassis//", std::string()) { entityPrivileges = { {boost::beast::http::verb::get, {{"Login"}}}, {boost::beast::http::verb::head, {{"Login"}}}, {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; } private: /** * Functions triggers appropriate requests on DBus */ void doGet(crow::Response &res, const crow::Request &req, const std::vector ¶ms) override { const std::array interfaces = { "xyz.openbmc_project.Inventory.Item.Board", "xyz.openbmc_project.Inventory.Item.Chassis", "xyz.openbmc_project.Inventory.Item.PowerSupply"}; // Check if there is required param, truly entering this shall be // impossible. if (params.size() != 1) { messages::internalError(res); res.end(); return; } const std::string &chassisId = params[0]; auto asyncResp = std::make_shared(res); crow::connections::systemBus->async_method_call( [asyncResp, chassisId(std::string(chassisId))]( const boost::system::error_code ec, const std::vector>>>> &subtree) { if (ec) { messages::internalError(asyncResp->res); return; } // Iterate over all retrieved ObjectPaths. for (const std::pair< std::string, std::vector< std::pair>>> &object : subtree) { const std::string &path = object.first; const std::vector< std::pair>> &connectionNames = object.second; if (!boost::ends_with(path, chassisId)) { continue; } if (connectionNames.size() < 1) { BMCWEB_LOG_ERROR << "Only got " << connectionNames.size() << " Connection names"; continue; } asyncResp->res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis"; asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; asyncResp->res.jsonValue["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; asyncResp->res.jsonValue["Name"] = "Chassis Collection"; asyncResp->res.jsonValue["ChassisType"] = "RackMount"; const std::string &connectionName = connectionNames[0].first; crow::connections::systemBus->async_method_call( [asyncResp, chassisId(std::string(chassisId))]( const boost::system::error_code ec, const std::vector> &propertiesList) { for (const std::pair &property : propertiesList) { // Store DBus properties that are also Redfish // properties with same name and a string value const std::string &propertyName = property.first; if ((propertyName == "PartNumber") || (propertyName == "SerialNumber") || (propertyName == "Manufacturer") || (propertyName == "Model")) { const std::string *value = std::get_if( &property.second); if (value != nullptr) { asyncResp->res.jsonValue[propertyName] = *value; } } } asyncResp->res.jsonValue["Name"] = chassisId; asyncResp->res.jsonValue["Id"] = chassisId; asyncResp->res.jsonValue["Thermal"] = { {"@odata.id", "/redfish/v1/Chassis/" + chassisId + "/Thermal"}}; // Power object asyncResp->res.jsonValue["Power"] = { {"@odata.id", "/redfish/v1/Chassis/" + chassisId + "/Power"}}; asyncResp->res.jsonValue["Status"] = { {"Health", "OK"}, {"State", "Enabled"}, }; asyncResp->res .jsonValue["Links"]["ComputerSystems"] = { {{"@odata.id", "/redfish/v1/Systems/system"}}}; asyncResp->res.jsonValue["Links"]["ManagedBy"] = { {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; getChassisState(asyncResp); }, connectionName, path, "org.freedesktop.DBus.Properties", "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); return; } // Couldn't find an object with that name. return an error messages::resourceNotFound( asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); }, "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/xyz/openbmc_project/inventory", int32_t(0), interfaces); getPhysicalSecurityData(asyncResp); } }; } // namespace redfish