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["@odata.context"] = 183 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 184 res.jsonValue["Name"] = "Chassis Collection"; 185 186 const std::array<const char *, 2> interfaces = { 187 "xyz.openbmc_project.Inventory.Item.Board", 188 "xyz.openbmc_project.Inventory.Item.Chassis"}; 189 190 auto asyncResp = std::make_shared<AsyncResp>(res); 191 crow::connections::systemBus->async_method_call( 192 [asyncResp](const boost::system::error_code ec, 193 const std::vector<std::string> &chassisList) { 194 if (ec) 195 { 196 messages::internalError(asyncResp->res); 197 return; 198 } 199 nlohmann::json &chassisArray = 200 asyncResp->res.jsonValue["Members"]; 201 chassisArray = nlohmann::json::array(); 202 for (const std::string &objpath : chassisList) 203 { 204 std::size_t lastPos = objpath.rfind("/"); 205 if (lastPos == std::string::npos) 206 { 207 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 208 continue; 209 } 210 chassisArray.push_back( 211 {{"@odata.id", "/redfish/v1/Chassis/" + 212 objpath.substr(lastPos + 1)}}); 213 } 214 215 asyncResp->res.jsonValue["Members@odata.count"] = 216 chassisArray.size(); 217 }, 218 "xyz.openbmc_project.ObjectMapper", 219 "/xyz/openbmc_project/object_mapper", 220 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 221 "/xyz/openbmc_project/inventory", 0, interfaces); 222 } 223 }; 224 225 /** 226 * Chassis override class for delivering Chassis Schema 227 */ 228 class Chassis : public Node 229 { 230 public: 231 Chassis(CrowApp &app) : 232 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 233 { 234 entityPrivileges = { 235 {boost::beast::http::verb::get, {{"Login"}}}, 236 {boost::beast::http::verb::head, {{"Login"}}}, 237 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 238 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 239 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 240 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 241 } 242 243 private: 244 /** 245 * Functions triggers appropriate requests on DBus 246 */ 247 void doGet(crow::Response &res, const crow::Request &req, 248 const std::vector<std::string> ¶ms) override 249 { 250 const std::array<const char *, 2> interfaces = { 251 "xyz.openbmc_project.Inventory.Item.Board", 252 "xyz.openbmc_project.Inventory.Item.Chassis"}; 253 254 // Check if there is required param, truly entering this shall be 255 // impossible. 256 if (params.size() != 1) 257 { 258 messages::internalError(res); 259 res.end(); 260 return; 261 } 262 const std::string &chassisId = params[0]; 263 264 auto asyncResp = std::make_shared<AsyncResp>(res); 265 crow::connections::systemBus->async_method_call( 266 [asyncResp, chassisId(std::string(chassisId))]( 267 const boost::system::error_code ec, 268 const crow::openbmc_mapper::GetSubTreeType &subtree) { 269 if (ec) 270 { 271 messages::internalError(asyncResp->res); 272 return; 273 } 274 // Iterate over all retrieved ObjectPaths. 275 for (const std::pair< 276 std::string, 277 std::vector< 278 std::pair<std::string, std::vector<std::string>>>> 279 &object : subtree) 280 { 281 const std::string &path = object.first; 282 const std::vector< 283 std::pair<std::string, std::vector<std::string>>> 284 &connectionNames = object.second; 285 286 if (!boost::ends_with(path, chassisId)) 287 { 288 continue; 289 } 290 291 auto health = std::make_shared<HealthPopulate>(asyncResp); 292 293 crow::connections::systemBus->async_method_call( 294 [health](const boost::system::error_code ec, 295 std::variant<std::vector<std::string>> &resp) { 296 if (ec) 297 { 298 return; // no sensors = no failures 299 } 300 std::vector<std::string> *data = 301 std::get_if<std::vector<std::string>>(&resp); 302 if (data == nullptr) 303 { 304 return; 305 } 306 health->inventory = std::move(*data); 307 }, 308 "xyz.openbmc_project.ObjectMapper", 309 path + "/all_sensors", 310 "org.freedesktop.DBus.Properties", "Get", 311 "xyz.openbmc_project.Association", "endpoints"); 312 313 health->populate(); 314 315 if (connectionNames.size() < 1) 316 { 317 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 318 continue; 319 } 320 321 asyncResp->res.jsonValue["@odata.type"] = 322 "#Chassis.v1_10_0.Chassis"; 323 asyncResp->res.jsonValue["@odata.id"] = 324 "/redfish/v1/Chassis/" + chassisId; 325 asyncResp->res.jsonValue["@odata.context"] = 326 "/redfish/v1/$metadata#Chassis.Chassis"; 327 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 328 asyncResp->res.jsonValue["ChassisType"] = "RackMount"; 329 asyncResp->res.jsonValue["PCIeDevices"] = { 330 {"@odata.id", 331 "/redfish/v1/Systems/system/PCIeDevices"}}; 332 333 const std::string &connectionName = 334 connectionNames[0].first; 335 336 const std::vector<std::string> &interfaces = 337 connectionNames[0].second; 338 const std::array<const char *, 2> hasIndicatorLed = { 339 "xyz.openbmc_project.Inventory.Item.Panel", 340 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 341 342 for (const char *interface : hasIndicatorLed) 343 { 344 if (std::find(interfaces.begin(), interfaces.end(), 345 interface) != interfaces.end()) 346 { 347 getIndicatorLedState(asyncResp); 348 break; 349 } 350 } 351 352 crow::connections::systemBus->async_method_call( 353 [asyncResp, chassisId(std::string(chassisId))]( 354 const boost::system::error_code ec, 355 const std::vector<std::pair< 356 std::string, VariantType>> &propertiesList) { 357 for (const std::pair<std::string, VariantType> 358 &property : propertiesList) 359 { 360 // Store DBus properties that are also Redfish 361 // properties with same name and a string value 362 const std::string &propertyName = 363 property.first; 364 if ((propertyName == "PartNumber") || 365 (propertyName == "SerialNumber") || 366 (propertyName == "Manufacturer") || 367 (propertyName == "Model")) 368 { 369 const std::string *value = 370 std::get_if<std::string>( 371 &property.second); 372 if (value != nullptr) 373 { 374 asyncResp->res.jsonValue[propertyName] = 375 *value; 376 } 377 } 378 } 379 asyncResp->res.jsonValue["Name"] = chassisId; 380 asyncResp->res.jsonValue["Id"] = chassisId; 381 asyncResp->res.jsonValue["Thermal"] = { 382 {"@odata.id", "/redfish/v1/Chassis/" + 383 chassisId + "/Thermal"}}; 384 // Power object 385 asyncResp->res.jsonValue["Power"] = { 386 {"@odata.id", "/redfish/v1/Chassis/" + 387 chassisId + "/Power"}}; 388 // SensorCollection 389 asyncResp->res.jsonValue["Sensors"] = { 390 {"@odata.id", "/redfish/v1/Chassis/" + 391 chassisId + "/Sensors"}}; 392 asyncResp->res.jsonValue["Status"] = { 393 {"State", "Enabled"}, 394 }; 395 396 asyncResp->res 397 .jsonValue["Links"]["ComputerSystems"] = { 398 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 399 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 400 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 401 getChassisState(asyncResp); 402 }, 403 connectionName, path, "org.freedesktop.DBus.Properties", 404 "GetAll", 405 "xyz.openbmc_project.Inventory.Decorator.Asset"); 406 return; 407 } 408 409 // Couldn't find an object with that name. return an error 410 messages::resourceNotFound( 411 asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId); 412 }, 413 "xyz.openbmc_project.ObjectMapper", 414 "/xyz/openbmc_project/object_mapper", 415 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 416 "/xyz/openbmc_project/inventory", 0, interfaces); 417 418 getPhysicalSecurityData(asyncResp); 419 } 420 421 void doPatch(crow::Response &res, const crow::Request &req, 422 const std::vector<std::string> ¶ms) override 423 { 424 std::optional<std::string> indicatorLed; 425 auto asyncResp = std::make_shared<AsyncResp>(res); 426 427 if (params.size() != 1) 428 { 429 return; 430 } 431 432 if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed)) 433 { 434 return; 435 } 436 437 if (!indicatorLed) 438 { 439 return; // delete this when we support more patch properties 440 } 441 442 const std::array<const char *, 2> interfaces = { 443 "xyz.openbmc_project.Inventory.Item.Board", 444 "xyz.openbmc_project.Inventory.Item.Chassis"}; 445 446 const std::string &chassisId = params[0]; 447 448 crow::connections::systemBus->async_method_call( 449 [asyncResp, chassisId, indicatorLed]( 450 const boost::system::error_code ec, 451 const crow::openbmc_mapper::GetSubTreeType &subtree) { 452 if (ec) 453 { 454 messages::internalError(asyncResp->res); 455 return; 456 } 457 458 // Iterate over all retrieved ObjectPaths. 459 for (const std::pair< 460 std::string, 461 std::vector< 462 std::pair<std::string, std::vector<std::string>>>> 463 &object : subtree) 464 { 465 const std::string &path = object.first; 466 const std::vector< 467 std::pair<std::string, std::vector<std::string>>> 468 &connectionNames = object.second; 469 470 if (!boost::ends_with(path, chassisId)) 471 { 472 continue; 473 } 474 475 if (connectionNames.size() < 1) 476 { 477 BMCWEB_LOG_ERROR << "Got 0 Connection names"; 478 continue; 479 } 480 481 const std::vector<std::string> &interfaces = 482 connectionNames[0].second; 483 484 if (indicatorLed) 485 { 486 const std::array<const char *, 2> hasIndicatorLed = { 487 "xyz.openbmc_project.Inventory.Item.Panel", 488 "xyz.openbmc_project.Inventory.Item.Board." 489 "Motherboard"}; 490 bool indicatorChassis = false; 491 for (const char *interface : hasIndicatorLed) 492 { 493 if (std::find(interfaces.begin(), interfaces.end(), 494 interface) != interfaces.end()) 495 { 496 indicatorChassis = true; 497 break; 498 } 499 } 500 if (indicatorChassis) 501 { 502 setIndicatorLedState(asyncResp, 503 std::move(*indicatorLed)); 504 } 505 else 506 { 507 messages::propertyUnknown(asyncResp->res, 508 "IndicatorLED"); 509 } 510 } 511 return; 512 } 513 514 messages::resourceNotFound( 515 asyncResp->res, "#Chassis.v1_10_0.Chassis", chassisId); 516 }, 517 "xyz.openbmc_project.ObjectMapper", 518 "/xyz/openbmc_project/object_mapper", 519 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 520 "/xyz/openbmc_project/inventory", 0, interfaces); 521 } 522 }; 523 } // namespace redfish 524