1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "node.hpp" 19 20 #include <boost/container/flat_map.hpp> 21 #include <variant> 22 23 namespace redfish 24 { 25 26 /** 27 * DBus types primitives for several generic DBus interfaces 28 * TODO(Pawel) consider move this to separate file into boost::dbus 29 */ 30 // Note, this is not a very useful Variant, but because it isn't used to get 31 // values, it should be as simple as possible 32 // TODO(ed) invent a nullvariant type 33 using VariantType = std::variant<bool, std::string, uint64_t>; 34 using ManagedObjectsType = std::vector<std::pair< 35 sdbusplus::message::object_path, 36 std::vector<std::pair<std::string, 37 std::vector<std::pair<std::string, VariantType>>>>>>; 38 39 using PropertiesType = boost::container::flat_map<std::string, VariantType>; 40 41 void getIntrusionByService(std::shared_ptr<AsyncResp> aResp, 42 const std::string &service, 43 const std::string &objPath) 44 { 45 BMCWEB_LOG_DEBUG << "Get intrusion status by service \n"; 46 47 crow::connections::systemBus->async_method_call( 48 [aResp{std::move(aResp)}](const boost::system::error_code ec, 49 const std::variant<std::string> &value) { 50 if (ec) 51 { 52 // do not add err msg in redfish response, becaues this is not 53 // mandatory property 54 BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n"; 55 return; 56 } 57 58 const std::string *status = std::get_if<std::string>(&value); 59 60 if (status == nullptr) 61 { 62 BMCWEB_LOG_ERROR << "intrusion status read error \n"; 63 return; 64 } 65 66 aResp->res.jsonValue["PhysicalSecurity"] = { 67 {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}}; 68 }, 69 service, objPath, "org.freedesktop.DBus.Properties", "Get", 70 "xyz.openbmc_project.Chassis.Intrusion", "Status"); 71 } 72 73 /** 74 * Retrieves physical security properties over dbus 75 */ 76 void getPhysicalSecurityData(std::shared_ptr<AsyncResp> aResp) 77 { 78 crow::connections::systemBus->async_method_call( 79 [aResp{std::move(aResp)}]( 80 const boost::system::error_code ec, 81 const std::vector<std::pair< 82 std::string, 83 std::vector<std::pair<std::string, std::vector<std::string>>>>> 84 &subtree) { 85 if (ec) 86 { 87 // do not add err msg in redfish response, becaues this is not 88 // mandatory property 89 BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec 90 << "\n"; 91 return; 92 } 93 // Iterate over all retrieved ObjectPaths. 94 for (const auto &object : subtree) 95 { 96 for (const auto &service : object.second) 97 { 98 getIntrusionByService(aResp, service.first, object.first); 99 return; 100 } 101 } 102 }, 103 "xyz.openbmc_project.ObjectMapper", 104 "/xyz/openbmc_project/object_mapper", 105 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 106 "/xyz/openbmc_project/Intrusion", int32_t(1), 107 std::array<const char *, 1>{"xyz.openbmc_project.Chassis.Intrusion"}); 108 } 109 110 /** 111 * ChassisCollection derived class for delivering Chassis Collection Schema 112 */ 113 class ChassisCollection : public Node 114 { 115 public: 116 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") 117 { 118 entityPrivileges = { 119 {boost::beast::http::verb::get, {{"Login"}}}, 120 {boost::beast::http::verb::head, {{"Login"}}}, 121 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 122 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 123 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 124 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 125 } 126 127 private: 128 /** 129 * Functions triggers appropriate requests on DBus 130 */ 131 void doGet(crow::Response &res, const crow::Request &req, 132 const std::vector<std::string> ¶ms) override 133 { 134 res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection"; 135 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; 136 res.jsonValue["@odata.context"] = 137 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 138 res.jsonValue["Name"] = "Chassis Collection"; 139 140 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS 141 // Assume one Chassis named "chassis" 142 res.jsonValue["Members@odata.count"] = 1; 143 res.jsonValue["Members"] = { 144 {{"@odata.id", "/redfish/v1/Chassis/chassis"}}}; 145 res.end(); 146 return; 147 #endif 148 const std::array<const char *, 3> interfaces = { 149 "xyz.openbmc_project.Inventory.Item.Board", 150 "xyz.openbmc_project.Inventory.Item.Chassis", 151 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 152 153 auto asyncResp = std::make_shared<AsyncResp>(res); 154 crow::connections::systemBus->async_method_call( 155 [asyncResp](const boost::system::error_code ec, 156 const std::vector<std::string> &chassisList) { 157 if (ec) 158 { 159 messages::internalError(asyncResp->res); 160 return; 161 } 162 nlohmann::json &chassisArray = 163 asyncResp->res.jsonValue["Members"]; 164 chassisArray = nlohmann::json::array(); 165 for (const std::string &objpath : chassisList) 166 { 167 std::size_t lastPos = objpath.rfind("/"); 168 if (lastPos == std::string::npos) 169 { 170 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 171 continue; 172 } 173 chassisArray.push_back( 174 {{"@odata.id", "/redfish/v1/Chassis/" + 175 objpath.substr(lastPos + 1)}}); 176 } 177 178 asyncResp->res.jsonValue["Members@odata.count"] = 179 chassisArray.size(); 180 }, 181 "xyz.openbmc_project.ObjectMapper", 182 "/xyz/openbmc_project/object_mapper", 183 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 184 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 185 } 186 }; 187 188 /** 189 * Chassis override class for delivering Chassis Schema 190 */ 191 class Chassis : public Node 192 { 193 public: 194 Chassis(CrowApp &app) : 195 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 196 { 197 entityPrivileges = { 198 {boost::beast::http::verb::get, {{"Login"}}}, 199 {boost::beast::http::verb::head, {{"Login"}}}, 200 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 201 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 202 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 203 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 204 } 205 206 private: 207 /** 208 * Functions triggers appropriate requests on DBus 209 */ 210 void doGet(crow::Response &res, const crow::Request &req, 211 const std::vector<std::string> ¶ms) override 212 { 213 const std::array<const char *, 3> interfaces = { 214 "xyz.openbmc_project.Inventory.Item.Board", 215 "xyz.openbmc_project.Inventory.Item.Chassis", 216 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 217 218 // Check if there is required param, truly entering this shall be 219 // impossible. 220 if (params.size() != 1) 221 { 222 messages::internalError(res); 223 res.end(); 224 return; 225 } 226 const std::string &chassisId = params[0]; 227 #ifdef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS 228 // In a one chassis system the only supported name is "chassis" 229 if (chassisId != "chassis") 230 { 231 messages::resourceNotFound(res, "#Chassis.v1_4_0.Chassis", 232 chassisId); 233 res.end(); 234 return; 235 } 236 #endif 237 238 res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 239 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 240 res.jsonValue["@odata.context"] = 241 "/redfish/v1/$metadata#Chassis.Chassis"; 242 res.jsonValue["Name"] = "Chassis Collection"; 243 res.jsonValue["ChassisType"] = "RackMount"; 244 res.jsonValue["PowerState"] = "On"; 245 246 auto asyncResp = std::make_shared<AsyncResp>(res); 247 crow::connections::systemBus->async_method_call( 248 [asyncResp, chassisId(std::string(chassisId))]( 249 const boost::system::error_code ec, 250 const std::vector<std::pair< 251 std::string, std::vector<std::pair< 252 std::string, std::vector<std::string>>>>> 253 &subtree) { 254 if (ec) 255 { 256 messages::internalError(asyncResp->res); 257 return; 258 } 259 // Iterate over all retrieved ObjectPaths. 260 for (const std::pair< 261 std::string, 262 std::vector< 263 std::pair<std::string, std::vector<std::string>>>> 264 &object : subtree) 265 { 266 const std::string &path = object.first; 267 const std::vector< 268 std::pair<std::string, std::vector<std::string>>> 269 &connectionNames = object.second; 270 271 // If only one chassis, just select the first one 272 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS 273 if (!boost::ends_with(path, chassisId)) 274 { 275 continue; 276 } 277 #endif 278 if (connectionNames.size() < 1) 279 { 280 BMCWEB_LOG_ERROR << "Only got " 281 << connectionNames.size() 282 << " Connection names"; 283 continue; 284 } 285 286 const std::string connectionName = connectionNames[0].first; 287 crow::connections::systemBus->async_method_call( 288 [asyncResp, chassisId(std::string(chassisId))]( 289 const boost::system::error_code ec, 290 const std::vector<std::pair< 291 std::string, VariantType>> &propertiesList) { 292 for (const std::pair<std::string, VariantType> 293 &property : propertiesList) 294 { 295 // Store DBus properties that are also Redfish 296 // properties with same name and a string value 297 const std::string &propertyName = 298 property.first; 299 if ((propertyName == "PartNumber") || 300 (propertyName == "SerialNumber") || 301 (propertyName == "Manufacturer") || 302 (propertyName == "Model")) 303 { 304 const std::string *value = 305 std::get_if<std::string>( 306 &property.second); 307 if (value != nullptr) 308 { 309 asyncResp->res.jsonValue[propertyName] = 310 *value; 311 } 312 } 313 } 314 asyncResp->res.jsonValue["Name"] = chassisId; 315 asyncResp->res.jsonValue["Id"] = chassisId; 316 asyncResp->res.jsonValue["Thermal"] = { 317 {"@odata.id", "/redfish/v1/Chassis/" + 318 chassisId + "/Thermal"}}; 319 // Power object 320 asyncResp->res.jsonValue["Power"] = { 321 {"@odata.id", "/redfish/v1/Chassis/" + 322 chassisId + "/Power"}}; 323 asyncResp->res.jsonValue["Status"] = { 324 {"Health", "OK"}, 325 {"State", "Enabled"}, 326 }; 327 328 asyncResp->res 329 .jsonValue["Links"]["ComputerSystems"] = { 330 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 331 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 332 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 333 }, 334 connectionName, path, "org.freedesktop.DBus.Properties", 335 "GetAll", 336 "xyz.openbmc_project.Inventory.Decorator.Asset"); 337 return; 338 } 339 340 // Couldn't find an object with that name. return an error 341 messages::resourceNotFound( 342 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); 343 }, 344 "xyz.openbmc_project.ObjectMapper", 345 "/xyz/openbmc_project/object_mapper", 346 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 347 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 348 349 getPhysicalSecurityData(asyncResp); 350 } 351 }; 352 } // namespace redfish 353