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 "health.hpp" 19 #include "led.hpp" 20 #include "node.hpp" 21 22 #include <boost/container/flat_map.hpp> 23 #include <variant> 24 25 namespace redfish 26 { 27 28 /** 29 * @brief Retrieves chassis state properties over dbus 30 * 31 * @param[in] aResp - Shared pointer for completing asynchronous calls. 32 * 33 * @return None. 34 */ 35 void getChassisState(std::shared_ptr<AsyncResp> aResp) 36 { 37 crow::connections::systemBus->async_method_call( 38 [aResp{std::move(aResp)}]( 39 const boost::system::error_code ec, 40 const std::variant<std::string> &chassisState) { 41 if (ec) 42 { 43 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 44 messages::internalError(aResp->res); 45 return; 46 } 47 48 const std::string *s = std::get_if<std::string>(&chassisState); 49 BMCWEB_LOG_DEBUG << "Chassis state: " << *s; 50 if (s != nullptr) 51 { 52 // Verify Chassis State 53 if (*s == "xyz.openbmc_project.State.Chassis.PowerState.On") 54 { 55 aResp->res.jsonValue["PowerState"] = "On"; 56 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 57 } 58 else if (*s == 59 "xyz.openbmc_project.State.Chassis.PowerState.Off") 60 { 61 aResp->res.jsonValue["PowerState"] = "Off"; 62 aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; 63 } 64 } 65 }, 66 "xyz.openbmc_project.State.Chassis", 67 "/xyz/openbmc_project/state/chassis0", 68 "org.freedesktop.DBus.Properties", "Get", 69 "xyz.openbmc_project.State.Chassis", "CurrentPowerState"); 70 } 71 72 /** 73 * DBus types primitives for several generic DBus interfaces 74 * TODO(Pawel) consider move this to separate file into boost::dbus 75 */ 76 // Note, this is not a very useful Variant, but because it isn't used to get 77 // values, it should be as simple as possible 78 // TODO(ed) invent a nullvariant type 79 using VariantType = std::variant<bool, std::string, uint64_t, uint32_t>; 80 using ManagedObjectsType = std::vector<std::pair< 81 sdbusplus::message::object_path, 82 std::vector<std::pair<std::string, 83 std::vector<std::pair<std::string, VariantType>>>>>>; 84 85 using PropertiesType = boost::container::flat_map<std::string, VariantType>; 86 87 void getIntrusionByService(std::shared_ptr<AsyncResp> aResp, 88 const std::string &service, 89 const std::string &objPath) 90 { 91 BMCWEB_LOG_DEBUG << "Get intrusion status by service \n"; 92 93 crow::connections::systemBus->async_method_call( 94 [aResp{std::move(aResp)}](const boost::system::error_code ec, 95 const std::variant<std::string> &value) { 96 if (ec) 97 { 98 // do not add err msg in redfish response, becaues this is not 99 // mandatory property 100 BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n"; 101 return; 102 } 103 104 const std::string *status = std::get_if<std::string>(&value); 105 106 if (status == nullptr) 107 { 108 BMCWEB_LOG_ERROR << "intrusion status read error \n"; 109 return; 110 } 111 112 aResp->res.jsonValue["PhysicalSecurity"] = { 113 {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}}; 114 }, 115 service, objPath, "org.freedesktop.DBus.Properties", "Get", 116 "xyz.openbmc_project.Chassis.Intrusion", "Status"); 117 } 118 119 /** 120 * Retrieves physical security properties over dbus 121 */ 122 void getPhysicalSecurityData(std::shared_ptr<AsyncResp> aResp) 123 { 124 crow::connections::systemBus->async_method_call( 125 [aResp{std::move(aResp)}]( 126 const boost::system::error_code ec, 127 const std::vector<std::pair< 128 std::string, 129 std::vector<std::pair<std::string, std::vector<std::string>>>>> 130 &subtree) { 131 if (ec) 132 { 133 // do not add err msg in redfish response, becaues this is not 134 // mandatory property 135 BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec 136 << "\n"; 137 return; 138 } 139 // Iterate over all retrieved ObjectPaths. 140 for (const auto &object : subtree) 141 { 142 for (const auto &service : object.second) 143 { 144 getIntrusionByService(aResp, service.first, object.first); 145 return; 146 } 147 } 148 }, 149 "xyz.openbmc_project.ObjectMapper", 150 "/xyz/openbmc_project/object_mapper", 151 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 152 "/xyz/openbmc_project/Intrusion", 1, 153 std::array<const char *, 1>{"xyz.openbmc_project.Chassis.Intrusion"}); 154 } 155 156 /** 157 * ChassisCollection derived class for delivering Chassis Collection Schema 158 */ 159 class ChassisCollection : public Node 160 { 161 public: 162 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") 163 { 164 entityPrivileges = { 165 {boost::beast::http::verb::get, {{"Login"}}}, 166 {boost::beast::http::verb::head, {{"Login"}}}, 167 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 168 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 169 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 170 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 171 } 172 173 private: 174 /** 175 * Functions triggers appropriate requests on DBus 176 */ 177 void doGet(crow::Response &res, const crow::Request &req, 178 const std::vector<std::string> ¶ms) override 179 { 180 res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection"; 181 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; 182 res.jsonValue["Name"] = "Chassis Collection"; 183 184 const std::array<const char *, 2> interfaces = { 185 "xyz.openbmc_project.Inventory.Item.Board", 186 "xyz.openbmc_project.Inventory.Item.Chassis"}; 187 188 auto asyncResp = std::make_shared<AsyncResp>(res); 189 crow::connections::systemBus->async_method_call( 190 [asyncResp](const boost::system::error_code ec, 191 const std::vector<std::string> &chassisList) { 192 if (ec) 193 { 194 messages::internalError(asyncResp->res); 195 return; 196 } 197 nlohmann::json &chassisArray = 198 asyncResp->res.jsonValue["Members"]; 199 chassisArray = nlohmann::json::array(); 200 for (const std::string &objpath : chassisList) 201 { 202 std::size_t lastPos = objpath.rfind("/"); 203 if (lastPos == std::string::npos) 204 { 205 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 206 continue; 207 } 208 chassisArray.push_back( 209 {{"@odata.id", "/redfish/v1/Chassis/" + 210 objpath.substr(lastPos + 1)}}); 211 } 212 213 asyncResp->res.jsonValue["Members@odata.count"] = 214 chassisArray.size(); 215 }, 216 "xyz.openbmc_project.ObjectMapper", 217 "/xyz/openbmc_project/object_mapper", 218 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 219 "/xyz/openbmc_project/inventory", 0, interfaces); 220 } 221 }; 222 223 /** 224 * Chassis override class for delivering Chassis Schema 225 */ 226 class Chassis : public Node 227 { 228 public: 229 Chassis(CrowApp &app) : 230 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 231 { 232 entityPrivileges = { 233 {boost::beast::http::verb::get, {{"Login"}}}, 234 {boost::beast::http::verb::head, {{"Login"}}}, 235 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 236 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 237 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 238 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 239 } 240 241 private: 242 /** 243 * Functions triggers appropriate requests on DBus 244 */ 245 void doGet(crow::Response &res, const crow::Request &req, 246 const std::vector<std::string> ¶ms) override 247 { 248 const std::array<const char *, 2> interfaces = { 249 "xyz.openbmc_project.Inventory.Item.Board", 250 "xyz.openbmc_project.Inventory.Item.Chassis"}; 251 252 // Check if there is required param, truly entering this shall be 253 // impossible. 254 if (params.size() != 1) 255 { 256 messages::internalError(res); 257 res.end(); 258 return; 259 } 260 const std::string &chassisId = params[0]; 261 262 auto asyncResp = std::make_shared<AsyncResp>(res); 263 crow::connections::systemBus->async_method_call( 264 [asyncResp, chassisId(std::string(chassisId))]( 265 const boost::system::error_code ec, 266 const crow::openbmc_mapper::GetSubTreeType &subtree) { 267 if (ec) 268 { 269 messages::internalError(asyncResp->res); 270 return; 271 } 272 // Iterate over all retrieved ObjectPaths. 273 for (const std::pair< 274 std::string, 275 std::vector< 276 std::pair<std::string, std::vector<std::string>>>> 277 &object : subtree) 278 { 279 const std::string &path = object.first; 280 const std::vector< 281 std::pair<std::string, std::vector<std::string>>> 282 &connectionNames = object.second; 283 284 if (!boost::ends_with(path, chassisId)) 285 { 286 continue; 287 } 288 289 auto health = std::make_shared<HealthPopulate>(asyncResp); 290 291 crow::connections::systemBus->async_method_call( 292 [health](const boost::system::error_code ec, 293 std::variant<std::vector<std::string>> &resp) { 294 if (ec) 295 { 296 return; // no sensors = no failures 297 } 298 std::vector<std::string> *data = 299 std::get_if<std::vector<std::string>>(&resp); 300 if (data == nullptr) 301 { 302 return; 303 } 304 health->inventory = std::move(*data); 305 }, 306 "xyz.openbmc_project.ObjectMapper", 307 path + "/all_sensors", 308 "org.freedesktop.DBus.Properties", "Get", 309 "xyz.openbmc_project.Association", "endpoints"); 310 311 health->populate(); 312 313 if (connectionNames.size() < 1) 314 { 315 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 316 continue; 317 } 318 319 asyncResp->res.jsonValue["@odata.type"] = 320 "#Chassis.v1_10_0.Chassis"; 321 asyncResp->res.jsonValue["@odata.id"] = 322 "/redfish/v1/Chassis/" + chassisId; 323 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 324 asyncResp->res.jsonValue["ChassisType"] = "RackMount"; 325 asyncResp->res.jsonValue["PCIeDevices"] = { 326 {"@odata.id", 327 "/redfish/v1/Systems/system/PCIeDevices"}}; 328 329 const std::string &connectionName = 330 connectionNames[0].first; 331 332 const std::vector<std::string> &interfaces = 333 connectionNames[0].second; 334 const std::array<const char *, 2> hasIndicatorLed = { 335 "xyz.openbmc_project.Inventory.Item.Panel", 336 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 337 338 for (const char *interface : hasIndicatorLed) 339 { 340 if (std::find(interfaces.begin(), interfaces.end(), 341 interface) != interfaces.end()) 342 { 343 getIndicatorLedState(asyncResp); 344 break; 345 } 346 } 347 348 crow::connections::systemBus->async_method_call( 349 [asyncResp, chassisId(std::string(chassisId))]( 350 const boost::system::error_code ec, 351 const std::vector<std::pair< 352 std::string, VariantType>> &propertiesList) { 353 for (const std::pair<std::string, VariantType> 354 &property : propertiesList) 355 { 356 // Store DBus properties that are also Redfish 357 // properties with same name and a string value 358 const std::string &propertyName = 359 property.first; 360 if ((propertyName == "PartNumber") || 361 (propertyName == "SerialNumber") || 362 (propertyName == "Manufacturer") || 363 (propertyName == "Model")) 364 { 365 const std::string *value = 366 std::get_if<std::string>( 367 &property.second); 368 if (value != nullptr) 369 { 370 asyncResp->res.jsonValue[propertyName] = 371 *value; 372 } 373 } 374 } 375 asyncResp->res.jsonValue["Name"] = chassisId; 376 asyncResp->res.jsonValue["Id"] = chassisId; 377 asyncResp->res.jsonValue["Thermal"] = { 378 {"@odata.id", "/redfish/v1/Chassis/" + 379 chassisId + "/Thermal"}}; 380 // Power object 381 asyncResp->res.jsonValue["Power"] = { 382 {"@odata.id", "/redfish/v1/Chassis/" + 383 chassisId + "/Power"}}; 384 // SensorCollection 385 asyncResp->res.jsonValue["Sensors"] = { 386 {"@odata.id", "/redfish/v1/Chassis/" + 387 chassisId + "/Sensors"}}; 388 asyncResp->res.jsonValue["Status"] = { 389 {"State", "Enabled"}, 390 }; 391 392 asyncResp->res 393 .jsonValue["Links"]["ComputerSystems"] = { 394 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 395 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 396 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 397 getChassisState(asyncResp); 398 }, 399 connectionName, path, "org.freedesktop.DBus.Properties", 400 "GetAll", 401 "xyz.openbmc_project.Inventory.Decorator.Asset"); 402 return; 403 } 404 405 // Couldn't find an object with that name. return an error 406 messages::resourceNotFound( 407 asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId); 408 }, 409 "xyz.openbmc_project.ObjectMapper", 410 "/xyz/openbmc_project/object_mapper", 411 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 412 "/xyz/openbmc_project/inventory", 0, interfaces); 413 414 getPhysicalSecurityData(asyncResp); 415 } 416 417 void doPatch(crow::Response &res, const crow::Request &req, 418 const std::vector<std::string> ¶ms) override 419 { 420 std::optional<std::string> indicatorLed; 421 auto asyncResp = std::make_shared<AsyncResp>(res); 422 423 if (params.size() != 1) 424 { 425 return; 426 } 427 428 if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed)) 429 { 430 return; 431 } 432 433 if (!indicatorLed) 434 { 435 return; // delete this when we support more patch properties 436 } 437 438 const std::array<const char *, 2> interfaces = { 439 "xyz.openbmc_project.Inventory.Item.Board", 440 "xyz.openbmc_project.Inventory.Item.Chassis"}; 441 442 const std::string &chassisId = params[0]; 443 444 crow::connections::systemBus->async_method_call( 445 [asyncResp, chassisId, indicatorLed]( 446 const boost::system::error_code ec, 447 const crow::openbmc_mapper::GetSubTreeType &subtree) { 448 if (ec) 449 { 450 messages::internalError(asyncResp->res); 451 return; 452 } 453 454 // Iterate over all retrieved ObjectPaths. 455 for (const std::pair< 456 std::string, 457 std::vector< 458 std::pair<std::string, std::vector<std::string>>>> 459 &object : subtree) 460 { 461 const std::string &path = object.first; 462 const std::vector< 463 std::pair<std::string, std::vector<std::string>>> 464 &connectionNames = object.second; 465 466 if (!boost::ends_with(path, chassisId)) 467 { 468 continue; 469 } 470 471 if (connectionNames.size() < 1) 472 { 473 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 474 continue; 475 } 476 477 const std::vector<std::string> &interfaces = 478 connectionNames[0].second; 479 480 if (indicatorLed) 481 { 482 const std::array<const char *, 2> hasIndicatorLed = { 483 "xyz.openbmc_project.Inventory.Item.Panel", 484 "xyz.openbmc_project.Inventory.Item.Board." 485 "Motherboard"}; 486 bool indicatorChassis = false; 487 for (const char *interface : hasIndicatorLed) 488 { 489 if (std::find(interfaces.begin(), interfaces.end(), 490 interface) != interfaces.end()) 491 { 492 indicatorChassis = true; 493 break; 494 } 495 } 496 if (indicatorChassis) 497 { 498 setIndicatorLedState(asyncResp, 499 std::move(*indicatorLed)); 500 } 501 else 502 { 503 messages::propertyUnknown(asyncResp->res, 504 "IndicatorLED"); 505 } 506 } 507 return; 508 } 509 510 messages::resourceNotFound( 511 asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId); 512 }, 513 "xyz.openbmc_project.ObjectMapper", 514 "/xyz/openbmc_project/object_mapper", 515 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 516 "/xyz/openbmc_project/inventory", 0, interfaces); 517 } 518 }; 519 } // namespace redfish 520