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 <app.hpp> 19 #include <boost/algorithm/string/predicate.hpp> 20 #include <boost/algorithm/string/split.hpp> 21 #include <boost/container/flat_map.hpp> 22 #include <boost/range/algorithm/replace_copy_if.hpp> 23 #include <dbus_singleton.hpp> 24 #include <registries/privilege_registry.hpp> 25 #include <utils/json_utils.hpp> 26 27 #include <cmath> 28 #include <utility> 29 #include <variant> 30 31 namespace redfish 32 { 33 34 using GetSubTreeType = std::vector< 35 std::pair<std::string, 36 std::vector<std::pair<std::string, std::vector<std::string>>>>>; 37 38 using SensorVariant = 39 std::variant<int64_t, double, uint32_t, bool, std::string>; 40 41 using ManagedObjectsVectorType = std::vector<std::pair< 42 sdbusplus::message::object_path, 43 boost::container::flat_map< 44 std::string, boost::container::flat_map<std::string, SensorVariant>>>>; 45 46 namespace sensors 47 { 48 namespace node 49 { 50 static constexpr std::string_view power = "Power"; 51 static constexpr std::string_view sensors = "Sensors"; 52 static constexpr std::string_view thermal = "Thermal"; 53 } // namespace node 54 55 namespace dbus 56 { 57 58 static const boost::container::flat_map<std::string_view, 59 std::vector<const char*>> 60 paths = {{node::power, 61 {"/xyz/openbmc_project/sensors/voltage", 62 "/xyz/openbmc_project/sensors/power"}}, 63 {node::sensors, 64 {"/xyz/openbmc_project/sensors/power", 65 "/xyz/openbmc_project/sensors/current", 66 "/xyz/openbmc_project/sensors/airflow", 67 "/xyz/openbmc_project/sensors/utilization"}}, 68 {node::thermal, 69 {"/xyz/openbmc_project/sensors/fan_tach", 70 "/xyz/openbmc_project/sensors/temperature", 71 "/xyz/openbmc_project/sensors/fan_pwm"}}}; 72 } // namespace dbus 73 74 inline const char* toReadingType(const std::string& sensorType) 75 { 76 if (sensorType == "voltage") 77 { 78 return "Voltage"; 79 } 80 if (sensorType == "power") 81 { 82 return "Power"; 83 } 84 if (sensorType == "current") 85 { 86 return "Current"; 87 } 88 if (sensorType == "fan_tach") 89 { 90 return "Rotational"; 91 } 92 if (sensorType == "temperature") 93 { 94 return "Temperature"; 95 } 96 if (sensorType == "fan_pwm" || sensorType == "utilization") 97 { 98 return "Percent"; 99 } 100 if (sensorType == "altitude") 101 { 102 return "Altitude"; 103 } 104 if (sensorType == "airflow") 105 { 106 return "AirFlow"; 107 } 108 if (sensorType == "energy") 109 { 110 return "EnergyJoules"; 111 } 112 return ""; 113 } 114 115 inline const char* toReadingUnits(const std::string& sensorType) 116 { 117 if (sensorType == "voltage") 118 { 119 return "V"; 120 } 121 if (sensorType == "power") 122 { 123 return "W"; 124 } 125 if (sensorType == "current") 126 { 127 return "A"; 128 } 129 if (sensorType == "fan_tach") 130 { 131 return "RPM"; 132 } 133 if (sensorType == "temperature") 134 { 135 return "Cel"; 136 } 137 if (sensorType == "fan_pwm" || sensorType == "utilization") 138 { 139 return "%"; 140 } 141 if (sensorType == "altitude") 142 { 143 return "m"; 144 } 145 if (sensorType == "airflow") 146 { 147 return "cft_i/min"; 148 } 149 if (sensorType == "energy") 150 { 151 return "J"; 152 } 153 return ""; 154 } 155 } // namespace sensors 156 157 /** 158 * SensorsAsyncResp 159 * Gathers data needed for response processing after async calls are done 160 */ 161 class SensorsAsyncResp 162 { 163 public: 164 using DataCompleteCb = std::function<void( 165 const boost::beast::http::status status, 166 const boost::container::flat_map<std::string, std::string>& uriToDbus)>; 167 168 struct SensorData 169 { 170 const std::string name; 171 std::string uri; 172 const std::string valueKey; 173 const std::string dbusPath; 174 }; 175 176 SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 177 const std::string& chassisIdIn, 178 const std::vector<const char*>& typesIn, 179 const std::string_view& subNode) : 180 asyncResp(asyncResp), 181 chassisId(chassisIdIn), types(typesIn), chassisSubNode(subNode) 182 {} 183 184 // Store extra data about sensor mapping and return it in callback 185 SensorsAsyncResp(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 186 const std::string& chassisIdIn, 187 const std::vector<const char*>& typesIn, 188 const std::string_view& subNode, 189 DataCompleteCb&& creationComplete) : 190 asyncResp(asyncResp), 191 chassisId(chassisIdIn), types(typesIn), 192 chassisSubNode(subNode), metadata{std::vector<SensorData>()}, 193 dataComplete{std::move(creationComplete)} 194 {} 195 196 ~SensorsAsyncResp() 197 { 198 if (asyncResp->res.result() == 199 boost::beast::http::status::internal_server_error) 200 { 201 // Reset the json object to clear out any data that made it in 202 // before the error happened todo(ed) handle error condition with 203 // proper code 204 asyncResp->res.jsonValue = nlohmann::json::object(); 205 } 206 207 if (dataComplete && metadata) 208 { 209 boost::container::flat_map<std::string, std::string> map; 210 if (asyncResp->res.result() == boost::beast::http::status::ok) 211 { 212 for (auto& sensor : *metadata) 213 { 214 map.insert(std::make_pair(sensor.uri + sensor.valueKey, 215 sensor.dbusPath)); 216 } 217 } 218 dataComplete(asyncResp->res.result(), map); 219 } 220 } 221 222 void addMetadata(const nlohmann::json& sensorObject, 223 const std::string& valueKey, const std::string& dbusPath) 224 { 225 if (metadata) 226 { 227 metadata->emplace_back(SensorData{sensorObject["Name"], 228 sensorObject["@odata.id"], 229 valueKey, dbusPath}); 230 } 231 } 232 233 void updateUri(const std::string& name, const std::string& uri) 234 { 235 if (metadata) 236 { 237 for (auto& sensor : *metadata) 238 { 239 if (sensor.name == name) 240 { 241 sensor.uri = uri; 242 } 243 } 244 } 245 } 246 247 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 248 const std::string chassisId; 249 const std::vector<const char*> types; 250 const std::string chassisSubNode; 251 252 private: 253 std::optional<std::vector<SensorData>> metadata; 254 DataCompleteCb dataComplete; 255 }; 256 257 /** 258 * Possible states for physical inventory leds 259 */ 260 enum class LedState 261 { 262 OFF, 263 ON, 264 BLINK, 265 UNKNOWN 266 }; 267 268 /** 269 * D-Bus inventory item associated with one or more sensors. 270 */ 271 class InventoryItem 272 { 273 public: 274 InventoryItem(const std::string& objPath) : 275 objectPath(objPath), name(), isPresent(true), isFunctional(true), 276 isPowerSupply(false), powerSupplyEfficiencyPercent(-1), manufacturer(), 277 model(), partNumber(), serialNumber(), sensors(), ledObjectPath(""), 278 ledState(LedState::UNKNOWN) 279 { 280 // Set inventory item name to last node of object path 281 sdbusplus::message::object_path path(objectPath); 282 name = path.filename(); 283 if (name.empty()) 284 { 285 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objectPath; 286 } 287 } 288 289 std::string objectPath; 290 std::string name; 291 bool isPresent; 292 bool isFunctional; 293 bool isPowerSupply; 294 int powerSupplyEfficiencyPercent; 295 std::string manufacturer; 296 std::string model; 297 std::string partNumber; 298 std::string serialNumber; 299 std::set<std::string> sensors; 300 std::string ledObjectPath; 301 LedState ledState; 302 }; 303 304 /** 305 * @brief Get objects with connection necessary for sensors 306 * @param SensorsAsyncResp Pointer to object holding response data 307 * @param sensorNames Sensors retrieved from chassis 308 * @param callback Callback for processing gathered connections 309 */ 310 template <typename Callback> 311 void getObjectsWithConnection( 312 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 313 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames, 314 Callback&& callback) 315 { 316 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter"; 317 const std::string path = "/xyz/openbmc_project/sensors"; 318 const std::array<std::string, 1> interfaces = { 319 "xyz.openbmc_project.Sensor.Value"}; 320 321 // Response handler for parsing objects subtree 322 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp, 323 sensorNames](const boost::system::error_code ec, 324 const GetSubTreeType& subtree) { 325 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter"; 326 if (ec) 327 { 328 messages::internalError(sensorsAsyncResp->asyncResp->res); 329 BMCWEB_LOG_ERROR 330 << "getObjectsWithConnection resp_handler: Dbus error " << ec; 331 return; 332 } 333 334 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; 335 336 // Make unique list of connections only for requested sensor types and 337 // found in the chassis 338 boost::container::flat_set<std::string> connections; 339 std::set<std::pair<std::string, std::string>> objectsWithConnection; 340 // Intrinsic to avoid malloc. Most systems will have < 8 sensor 341 // producers 342 connections.reserve(8); 343 344 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames->size(); 345 for (const std::string& tsensor : *sensorNames) 346 { 347 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; 348 } 349 350 for (const std::pair< 351 std::string, 352 std::vector<std::pair<std::string, std::vector<std::string>>>>& 353 object : subtree) 354 { 355 if (sensorNames->find(object.first) != sensorNames->end()) 356 { 357 for (const std::pair<std::string, std::vector<std::string>>& 358 objData : object.second) 359 { 360 BMCWEB_LOG_DEBUG << "Adding connection: " << objData.first; 361 connections.insert(objData.first); 362 objectsWithConnection.insert( 363 std::make_pair(object.first, objData.first)); 364 } 365 } 366 } 367 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; 368 callback(std::move(connections), std::move(objectsWithConnection)); 369 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit"; 370 }; 371 // Make call to ObjectMapper to find all sensors objects 372 crow::connections::systemBus->async_method_call( 373 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 374 "/xyz/openbmc_project/object_mapper", 375 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces); 376 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit"; 377 } 378 379 /** 380 * @brief Create connections necessary for sensors 381 * @param SensorsAsyncResp Pointer to object holding response data 382 * @param sensorNames Sensors retrieved from chassis 383 * @param callback Callback for processing gathered connections 384 */ 385 template <typename Callback> 386 void getConnections( 387 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 388 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames, 389 Callback&& callback) 390 { 391 auto objectsWithConnectionCb = 392 [callback](const boost::container::flat_set<std::string>& connections, 393 const std::set<std::pair<std::string, std::string>>& 394 /*objectsWithConnection*/) { callback(connections); }; 395 getObjectsWithConnection(sensorsAsyncResp, sensorNames, 396 std::move(objectsWithConnectionCb)); 397 } 398 399 /** 400 * @brief Shrinks the list of sensors for processing 401 * @param SensorsAysncResp The class holding the Redfish response 402 * @param allSensors A list of all the sensors associated to the 403 * chassis element (i.e. baseboard, front panel, etc...) 404 * @param activeSensors A list that is a reduction of the incoming 405 * allSensors list. Eliminate Thermal sensors when a Power request is 406 * made, and eliminate Power sensors when a Thermal request is made. 407 */ 408 inline void reduceSensorList( 409 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 410 const std::vector<std::string>* allSensors, 411 const std::shared_ptr<boost::container::flat_set<std::string>>& 412 activeSensors) 413 { 414 if (sensorsAsyncResp == nullptr) 415 { 416 return; 417 } 418 if ((allSensors == nullptr) || (activeSensors == nullptr)) 419 { 420 messages::resourceNotFound( 421 sensorsAsyncResp->asyncResp->res, sensorsAsyncResp->chassisSubNode, 422 sensorsAsyncResp->chassisSubNode == sensors::node::thermal 423 ? "Temperatures" 424 : "Voltages"); 425 426 return; 427 } 428 if (allSensors->empty()) 429 { 430 // Nothing to do, the activeSensors object is also empty 431 return; 432 } 433 434 for (const char* type : sensorsAsyncResp->types) 435 { 436 for (const std::string& sensor : *allSensors) 437 { 438 if (boost::starts_with(sensor, type)) 439 { 440 activeSensors->emplace(sensor); 441 } 442 } 443 } 444 } 445 446 /** 447 * @brief Retrieves valid chassis path 448 * @param asyncResp Pointer to object holding response data 449 * @param callback Callback for next step to get valid chassis path 450 */ 451 template <typename Callback> 452 void getValidChassisPath(const std::shared_ptr<SensorsAsyncResp>& asyncResp, 453 Callback&& callback) 454 { 455 BMCWEB_LOG_DEBUG << "checkChassisId enter"; 456 const std::array<const char*, 2> interfaces = { 457 "xyz.openbmc_project.Inventory.Item.Board", 458 "xyz.openbmc_project.Inventory.Item.Chassis"}; 459 460 auto respHandler = 461 [callback{std::move(callback)}, 462 asyncResp](const boost::system::error_code ec, 463 const std::vector<std::string>& chassisPaths) mutable { 464 BMCWEB_LOG_DEBUG << "getValidChassisPath respHandler enter"; 465 if (ec) 466 { 467 BMCWEB_LOG_ERROR 468 << "getValidChassisPath respHandler DBUS error: " << ec; 469 messages::internalError(asyncResp->asyncResp->res); 470 return; 471 } 472 473 std::optional<std::string> chassisPath; 474 std::string chassisName; 475 for (const std::string& chassis : chassisPaths) 476 { 477 sdbusplus::message::object_path path(chassis); 478 chassisName = path.filename(); 479 if (chassisName.empty()) 480 { 481 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis; 482 continue; 483 } 484 if (chassisName == asyncResp->chassisId) 485 { 486 chassisPath = chassis; 487 break; 488 } 489 } 490 callback(chassisPath); 491 }; 492 493 // Get the Chassis Collection 494 crow::connections::systemBus->async_method_call( 495 respHandler, "xyz.openbmc_project.ObjectMapper", 496 "/xyz/openbmc_project/object_mapper", 497 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 498 "/xyz/openbmc_project/inventory", 0, interfaces); 499 BMCWEB_LOG_DEBUG << "checkChassisId exit"; 500 } 501 502 /** 503 * @brief Retrieves requested chassis sensors and redundancy data from DBus . 504 * @param SensorsAsyncResp Pointer to object holding response data 505 * @param callback Callback for next step in gathered sensor processing 506 */ 507 template <typename Callback> 508 void getChassis(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 509 Callback&& callback) 510 { 511 BMCWEB_LOG_DEBUG << "getChassis enter"; 512 const std::array<const char*, 2> interfaces = { 513 "xyz.openbmc_project.Inventory.Item.Board", 514 "xyz.openbmc_project.Inventory.Item.Chassis"}; 515 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp]( 516 const boost::system::error_code ec, 517 const std::vector<std::string>& chassisPaths) { 518 BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; 519 if (ec) 520 { 521 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; 522 messages::internalError(sensorsAsyncResp->asyncResp->res); 523 return; 524 } 525 526 const std::string* chassisPath = nullptr; 527 std::string chassisName; 528 for (const std::string& chassis : chassisPaths) 529 { 530 sdbusplus::message::object_path path(chassis); 531 chassisName = path.filename(); 532 if (chassisName.empty()) 533 { 534 BMCWEB_LOG_ERROR << "Failed to find '/' in " << chassis; 535 continue; 536 } 537 if (chassisName == sensorsAsyncResp->chassisId) 538 { 539 chassisPath = &chassis; 540 break; 541 } 542 } 543 if (chassisPath == nullptr) 544 { 545 messages::resourceNotFound(sensorsAsyncResp->asyncResp->res, 546 "Chassis", sensorsAsyncResp->chassisId); 547 return; 548 } 549 550 const std::string& chassisSubNode = sensorsAsyncResp->chassisSubNode; 551 if (chassisSubNode == sensors::node::power) 552 { 553 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] = 554 "#Power.v1_5_2.Power"; 555 } 556 else if (chassisSubNode == sensors::node::thermal) 557 { 558 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] = 559 "#Thermal.v1_4_0.Thermal"; 560 sensorsAsyncResp->asyncResp->res.jsonValue["Fans"] = 561 nlohmann::json::array(); 562 sensorsAsyncResp->asyncResp->res.jsonValue["Temperatures"] = 563 nlohmann::json::array(); 564 } 565 else if (chassisSubNode == sensors::node::sensors) 566 { 567 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.type"] = 568 "#SensorCollection.SensorCollection"; 569 sensorsAsyncResp->asyncResp->res.jsonValue["Description"] = 570 "Collection of Sensors for this Chassis"; 571 sensorsAsyncResp->asyncResp->res.jsonValue["Members"] = 572 nlohmann::json::array(); 573 sensorsAsyncResp->asyncResp->res.jsonValue["Members@odata.count"] = 574 0; 575 } 576 577 if (chassisSubNode != sensors::node::sensors) 578 { 579 sensorsAsyncResp->asyncResp->res.jsonValue["Id"] = chassisSubNode; 580 } 581 582 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.id"] = 583 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + "/" + 584 chassisSubNode; 585 sensorsAsyncResp->asyncResp->res.jsonValue["Name"] = chassisSubNode; 586 // Get the list of all sensors for this Chassis element 587 std::string sensorPath = *chassisPath + "/all_sensors"; 588 crow::connections::systemBus->async_method_call( 589 [sensorsAsyncResp, callback{std::move(callback)}]( 590 const boost::system::error_code& e, 591 const std::variant<std::vector<std::string>>& 592 variantEndpoints) { 593 if (e) 594 { 595 if (e.value() != EBADR) 596 { 597 messages::internalError( 598 sensorsAsyncResp->asyncResp->res); 599 return; 600 } 601 } 602 const std::vector<std::string>* nodeSensorList = 603 std::get_if<std::vector<std::string>>(&(variantEndpoints)); 604 if (nodeSensorList == nullptr) 605 { 606 messages::resourceNotFound( 607 sensorsAsyncResp->asyncResp->res, 608 sensorsAsyncResp->chassisSubNode, 609 sensorsAsyncResp->chassisSubNode == 610 sensors::node::thermal 611 ? "Temperatures" 612 : sensorsAsyncResp->chassisSubNode == 613 sensors::node::power 614 ? "Voltages" 615 : "Sensors"); 616 return; 617 } 618 const std::shared_ptr<boost::container::flat_set<std::string>> 619 culledSensorList = std::make_shared< 620 boost::container::flat_set<std::string>>(); 621 reduceSensorList(sensorsAsyncResp, nodeSensorList, 622 culledSensorList); 623 callback(culledSensorList); 624 }, 625 "xyz.openbmc_project.ObjectMapper", sensorPath, 626 "org.freedesktop.DBus.Properties", "Get", 627 "xyz.openbmc_project.Association", "endpoints"); 628 }; 629 630 // Get the Chassis Collection 631 crow::connections::systemBus->async_method_call( 632 respHandler, "xyz.openbmc_project.ObjectMapper", 633 "/xyz/openbmc_project/object_mapper", 634 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 635 "/xyz/openbmc_project/inventory", 0, interfaces); 636 BMCWEB_LOG_DEBUG << "getChassis exit"; 637 } 638 639 /** 640 * @brief Finds all DBus object paths that implement ObjectManager. 641 * 642 * Creates a mapping from the associated connection name to the object path. 643 * 644 * Finds the object paths asynchronously. Invokes callback when information has 645 * been obtained. 646 * 647 * The callback must have the following signature: 648 * @code 649 * callback(std::shared_ptr<boost::container::flat_map<std::string, 650 * std::string>> objectMgrPaths) 651 * @endcode 652 * 653 * @param sensorsAsyncResp Pointer to object holding response data. 654 * @param callback Callback to invoke when object paths obtained. 655 */ 656 template <typename Callback> 657 void getObjectManagerPaths( 658 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 659 Callback&& callback) 660 { 661 BMCWEB_LOG_DEBUG << "getObjectManagerPaths enter"; 662 const std::array<std::string, 1> interfaces = { 663 "org.freedesktop.DBus.ObjectManager"}; 664 665 // Response handler for GetSubTree DBus method 666 auto respHandler = [callback{std::move(callback)}, 667 sensorsAsyncResp](const boost::system::error_code ec, 668 const GetSubTreeType& subtree) { 669 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler enter"; 670 if (ec) 671 { 672 messages::internalError(sensorsAsyncResp->asyncResp->res); 673 BMCWEB_LOG_ERROR << "getObjectManagerPaths respHandler: DBus error " 674 << ec; 675 return; 676 } 677 678 // Loop over returned object paths 679 std::shared_ptr<boost::container::flat_map<std::string, std::string>> 680 objectMgrPaths = std::make_shared< 681 boost::container::flat_map<std::string, std::string>>(); 682 for (const std::pair< 683 std::string, 684 std::vector<std::pair<std::string, std::vector<std::string>>>>& 685 object : subtree) 686 { 687 // Loop over connections for current object path 688 const std::string& objectPath = object.first; 689 for (const std::pair<std::string, std::vector<std::string>>& 690 objData : object.second) 691 { 692 // Add mapping from connection to object path 693 const std::string& connection = objData.first; 694 (*objectMgrPaths)[connection] = objectPath; 695 BMCWEB_LOG_DEBUG << "Added mapping " << connection << " -> " 696 << objectPath; 697 } 698 } 699 callback(objectMgrPaths); 700 BMCWEB_LOG_DEBUG << "getObjectManagerPaths respHandler exit"; 701 }; 702 703 // Query mapper for all DBus object paths that implement ObjectManager 704 crow::connections::systemBus->async_method_call( 705 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 706 "/xyz/openbmc_project/object_mapper", 707 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, interfaces); 708 BMCWEB_LOG_DEBUG << "getObjectManagerPaths exit"; 709 } 710 711 /** 712 * @brief Returns the Redfish State value for the specified inventory item. 713 * @param inventoryItem D-Bus inventory item associated with a sensor. 714 * @return State value for inventory item. 715 */ 716 inline std::string getState(const InventoryItem* inventoryItem) 717 { 718 if ((inventoryItem != nullptr) && !(inventoryItem->isPresent)) 719 { 720 return "Absent"; 721 } 722 723 return "Enabled"; 724 } 725 726 /** 727 * @brief Returns the Redfish Health value for the specified sensor. 728 * @param sensorJson Sensor JSON object. 729 * @param interfacesDict Map of all sensor interfaces. 730 * @param inventoryItem D-Bus inventory item associated with the sensor. Will 731 * be nullptr if no associated inventory item was found. 732 * @return Health value for sensor. 733 */ 734 inline std::string getHealth( 735 nlohmann::json& sensorJson, 736 const boost::container::flat_map< 737 std::string, boost::container::flat_map<std::string, SensorVariant>>& 738 interfacesDict, 739 const InventoryItem* inventoryItem) 740 { 741 // Get current health value (if any) in the sensor JSON object. Some JSON 742 // objects contain multiple sensors (such as PowerSupplies). We want to set 743 // the overall health to be the most severe of any of the sensors. 744 std::string currentHealth; 745 auto statusIt = sensorJson.find("Status"); 746 if (statusIt != sensorJson.end()) 747 { 748 auto healthIt = statusIt->find("Health"); 749 if (healthIt != statusIt->end()) 750 { 751 std::string* health = healthIt->get_ptr<std::string*>(); 752 if (health != nullptr) 753 { 754 currentHealth = *health; 755 } 756 } 757 } 758 759 // If current health in JSON object is already Critical, return that. This 760 // should override the sensor health, which might be less severe. 761 if (currentHealth == "Critical") 762 { 763 return "Critical"; 764 } 765 766 // Check if sensor has critical threshold alarm 767 auto criticalThresholdIt = 768 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Critical"); 769 if (criticalThresholdIt != interfacesDict.end()) 770 { 771 auto thresholdHighIt = 772 criticalThresholdIt->second.find("CriticalAlarmHigh"); 773 auto thresholdLowIt = 774 criticalThresholdIt->second.find("CriticalAlarmLow"); 775 if (thresholdHighIt != criticalThresholdIt->second.end()) 776 { 777 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second); 778 if (asserted == nullptr) 779 { 780 BMCWEB_LOG_ERROR << "Illegal sensor threshold"; 781 } 782 else if (*asserted) 783 { 784 return "Critical"; 785 } 786 } 787 if (thresholdLowIt != criticalThresholdIt->second.end()) 788 { 789 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second); 790 if (asserted == nullptr) 791 { 792 BMCWEB_LOG_ERROR << "Illegal sensor threshold"; 793 } 794 else if (*asserted) 795 { 796 return "Critical"; 797 } 798 } 799 } 800 801 // Check if associated inventory item is not functional 802 if ((inventoryItem != nullptr) && !(inventoryItem->isFunctional)) 803 { 804 return "Critical"; 805 } 806 807 // If current health in JSON object is already Warning, return that. This 808 // should override the sensor status, which might be less severe. 809 if (currentHealth == "Warning") 810 { 811 return "Warning"; 812 } 813 814 // Check if sensor has warning threshold alarm 815 auto warningThresholdIt = 816 interfacesDict.find("xyz.openbmc_project.Sensor.Threshold.Warning"); 817 if (warningThresholdIt != interfacesDict.end()) 818 { 819 auto thresholdHighIt = 820 warningThresholdIt->second.find("WarningAlarmHigh"); 821 auto thresholdLowIt = 822 warningThresholdIt->second.find("WarningAlarmLow"); 823 if (thresholdHighIt != warningThresholdIt->second.end()) 824 { 825 const bool* asserted = std::get_if<bool>(&thresholdHighIt->second); 826 if (asserted == nullptr) 827 { 828 BMCWEB_LOG_ERROR << "Illegal sensor threshold"; 829 } 830 else if (*asserted) 831 { 832 return "Warning"; 833 } 834 } 835 if (thresholdLowIt != warningThresholdIt->second.end()) 836 { 837 const bool* asserted = std::get_if<bool>(&thresholdLowIt->second); 838 if (asserted == nullptr) 839 { 840 BMCWEB_LOG_ERROR << "Illegal sensor threshold"; 841 } 842 else if (*asserted) 843 { 844 return "Warning"; 845 } 846 } 847 } 848 849 return "OK"; 850 } 851 852 inline void setLedState(nlohmann::json& sensorJson, 853 const InventoryItem* inventoryItem) 854 { 855 if (inventoryItem != nullptr && !inventoryItem->ledObjectPath.empty()) 856 { 857 switch (inventoryItem->ledState) 858 { 859 case LedState::OFF: 860 sensorJson["IndicatorLED"] = "Off"; 861 break; 862 case LedState::ON: 863 sensorJson["IndicatorLED"] = "Lit"; 864 break; 865 case LedState::BLINK: 866 sensorJson["IndicatorLED"] = "Blinking"; 867 break; 868 case LedState::UNKNOWN: 869 break; 870 } 871 } 872 } 873 874 /** 875 * @brief Builds a json sensor representation of a sensor. 876 * @param sensorName The name of the sensor to be built 877 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to 878 * build 879 * @param sensorsAsyncResp Sensor metadata 880 * @param interfacesDict A dictionary of the interfaces and properties of said 881 * interfaces to be built from 882 * @param sensor_json The json object to fill 883 * @param inventoryItem D-Bus inventory item associated with the sensor. Will 884 * be nullptr if no associated inventory item was found. 885 */ 886 inline void objectInterfacesToJson( 887 const std::string& sensorName, const std::string& sensorType, 888 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 889 const boost::container::flat_map< 890 std::string, boost::container::flat_map<std::string, SensorVariant>>& 891 interfacesDict, 892 nlohmann::json& sensorJson, InventoryItem* inventoryItem) 893 { 894 // We need a value interface before we can do anything with it 895 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); 896 if (valueIt == interfacesDict.end()) 897 { 898 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; 899 return; 900 } 901 902 // Assume values exist as is (10^0 == 1) if no scale exists 903 int64_t scaleMultiplier = 0; 904 905 auto scaleIt = valueIt->second.find("Scale"); 906 // If a scale exists, pull value as int64, and use the scaling. 907 if (scaleIt != valueIt->second.end()) 908 { 909 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second); 910 if (int64Value != nullptr) 911 { 912 scaleMultiplier = *int64Value; 913 } 914 } 915 916 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) 917 { 918 // For sensors in SensorCollection we set Id instead of MemberId, 919 // including power sensors. 920 sensorJson["Id"] = sensorName; 921 sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " "); 922 } 923 else if (sensorType != "power") 924 { 925 // Set MemberId and Name for non-power sensors. For PowerSupplies and 926 // PowerControl, those properties have more general values because 927 // multiple sensors can be stored in the same JSON object. 928 sensorJson["MemberId"] = sensorName; 929 sensorJson["Name"] = boost::replace_all_copy(sensorName, "_", " "); 930 } 931 932 sensorJson["Status"]["State"] = getState(inventoryItem); 933 sensorJson["Status"]["Health"] = 934 getHealth(sensorJson, interfacesDict, inventoryItem); 935 936 // Parameter to set to override the type we get from dbus, and force it to 937 // int, regardless of what is available. This is used for schemas like fan, 938 // that require integers, not floats. 939 bool forceToInt = false; 940 941 nlohmann::json::json_pointer unit("/Reading"); 942 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) 943 { 944 sensorJson["@odata.type"] = "#Sensor.v1_0_0.Sensor"; 945 946 const std::string& readingType = sensors::toReadingType(sensorType); 947 if (readingType.empty()) 948 { 949 BMCWEB_LOG_ERROR << "Redfish cannot map reading type for " 950 << sensorType; 951 } 952 else 953 { 954 sensorJson["ReadingType"] = readingType; 955 } 956 957 const std::string& readingUnits = sensors::toReadingUnits(sensorType); 958 if (readingUnits.empty()) 959 { 960 BMCWEB_LOG_ERROR << "Redfish cannot map reading unit for " 961 << sensorType; 962 } 963 else 964 { 965 sensorJson["ReadingUnits"] = readingUnits; 966 } 967 } 968 else if (sensorType == "temperature") 969 { 970 unit = "/ReadingCelsius"_json_pointer; 971 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Temperature"; 972 // TODO(ed) Documentation says that path should be type fan_tach, 973 // implementation seems to implement fan 974 } 975 else if (sensorType == "fan" || sensorType == "fan_tach") 976 { 977 unit = "/Reading"_json_pointer; 978 sensorJson["ReadingUnits"] = "RPM"; 979 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; 980 setLedState(sensorJson, inventoryItem); 981 forceToInt = true; 982 } 983 else if (sensorType == "fan_pwm") 984 { 985 unit = "/Reading"_json_pointer; 986 sensorJson["ReadingUnits"] = "Percent"; 987 sensorJson["@odata.type"] = "#Thermal.v1_3_0.Fan"; 988 setLedState(sensorJson, inventoryItem); 989 forceToInt = true; 990 } 991 else if (sensorType == "voltage") 992 { 993 unit = "/ReadingVolts"_json_pointer; 994 sensorJson["@odata.type"] = "#Power.v1_0_0.Voltage"; 995 } 996 else if (sensorType == "power") 997 { 998 std::string sensorNameLower = 999 boost::algorithm::to_lower_copy(sensorName); 1000 1001 if (!sensorName.compare("total_power")) 1002 { 1003 sensorJson["@odata.type"] = "#Power.v1_0_0.PowerControl"; 1004 // Put multiple "sensors" into a single PowerControl, so have 1005 // generic names for MemberId and Name. Follows Redfish mockup. 1006 sensorJson["MemberId"] = "0"; 1007 sensorJson["Name"] = "Chassis Power Control"; 1008 unit = "/PowerConsumedWatts"_json_pointer; 1009 } 1010 else if (sensorNameLower.find("input") != std::string::npos) 1011 { 1012 unit = "/PowerInputWatts"_json_pointer; 1013 } 1014 else 1015 { 1016 unit = "/PowerOutputWatts"_json_pointer; 1017 } 1018 } 1019 else 1020 { 1021 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; 1022 return; 1023 } 1024 // Map of dbus interface name, dbus property name and redfish property_name 1025 std::vector< 1026 std::tuple<const char*, const char*, nlohmann::json::json_pointer>> 1027 properties; 1028 properties.reserve(7); 1029 1030 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); 1031 1032 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) 1033 { 1034 properties.emplace_back( 1035 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningHigh", 1036 "/Thresholds/UpperCaution/Reading"_json_pointer); 1037 properties.emplace_back( 1038 "xyz.openbmc_project.Sensor.Threshold.Warning", "WarningLow", 1039 "/Thresholds/LowerCaution/Reading"_json_pointer); 1040 properties.emplace_back( 1041 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalHigh", 1042 "/Thresholds/UpperCritical/Reading"_json_pointer); 1043 properties.emplace_back( 1044 "xyz.openbmc_project.Sensor.Threshold.Critical", "CriticalLow", 1045 "/Thresholds/LowerCritical/Reading"_json_pointer); 1046 } 1047 else if (sensorType != "power") 1048 { 1049 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 1050 "WarningHigh", 1051 "/UpperThresholdNonCritical"_json_pointer); 1052 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 1053 "WarningLow", 1054 "/LowerThresholdNonCritical"_json_pointer); 1055 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 1056 "CriticalHigh", 1057 "/UpperThresholdCritical"_json_pointer); 1058 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 1059 "CriticalLow", 1060 "/LowerThresholdCritical"_json_pointer); 1061 } 1062 1063 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal 1064 1065 if (sensorsAsyncResp->chassisSubNode == sensors::node::sensors) 1066 { 1067 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 1068 "/ReadingRangeMin"_json_pointer); 1069 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 1070 "/ReadingRangeMax"_json_pointer); 1071 } 1072 else if (sensorType == "temperature") 1073 { 1074 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 1075 "/MinReadingRangeTemp"_json_pointer); 1076 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 1077 "/MaxReadingRangeTemp"_json_pointer); 1078 } 1079 else if (sensorType != "power") 1080 { 1081 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 1082 "/MinReadingRange"_json_pointer); 1083 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 1084 "/MaxReadingRange"_json_pointer); 1085 } 1086 1087 for (const std::tuple<const char*, const char*, 1088 nlohmann::json::json_pointer>& p : properties) 1089 { 1090 auto interfaceProperties = interfacesDict.find(std::get<0>(p)); 1091 if (interfaceProperties != interfacesDict.end()) 1092 { 1093 auto thisValueIt = interfaceProperties->second.find(std::get<1>(p)); 1094 if (thisValueIt != interfaceProperties->second.end()) 1095 { 1096 const SensorVariant& valueVariant = thisValueIt->second; 1097 1098 // The property we want to set may be nested json, so use 1099 // a json_pointer for easy indexing into the json structure. 1100 const nlohmann::json::json_pointer& key = std::get<2>(p); 1101 1102 // Attempt to pull the int64 directly 1103 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant); 1104 1105 const double* doubleValue = std::get_if<double>(&valueVariant); 1106 const uint32_t* uValue = std::get_if<uint32_t>(&valueVariant); 1107 double temp = 0.0; 1108 if (int64Value != nullptr) 1109 { 1110 temp = static_cast<double>(*int64Value); 1111 } 1112 else if (doubleValue != nullptr) 1113 { 1114 temp = *doubleValue; 1115 } 1116 else if (uValue != nullptr) 1117 { 1118 temp = *uValue; 1119 } 1120 else 1121 { 1122 BMCWEB_LOG_ERROR 1123 << "Got value interface that wasn't int or double"; 1124 continue; 1125 } 1126 temp = temp * std::pow(10, scaleMultiplier); 1127 if (forceToInt) 1128 { 1129 sensorJson[key] = static_cast<int64_t>(temp); 1130 } 1131 else 1132 { 1133 sensorJson[key] = temp; 1134 } 1135 } 1136 } 1137 } 1138 1139 sensorsAsyncResp->addMetadata(sensorJson, unit.to_string(), 1140 "/xyz/openbmc_project/sensors/" + sensorType + 1141 "/" + sensorName); 1142 1143 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; 1144 } 1145 1146 inline void populateFanRedundancy( 1147 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp) 1148 { 1149 crow::connections::systemBus->async_method_call( 1150 [sensorsAsyncResp](const boost::system::error_code ec, 1151 const GetSubTreeType& resp) { 1152 if (ec) 1153 { 1154 return; // don't have to have this interface 1155 } 1156 for (const std::pair<std::string, 1157 std::vector<std::pair< 1158 std::string, std::vector<std::string>>>>& 1159 pathPair : resp) 1160 { 1161 const std::string& path = pathPair.first; 1162 const std::vector< 1163 std::pair<std::string, std::vector<std::string>>>& objDict = 1164 pathPair.second; 1165 if (objDict.empty()) 1166 { 1167 continue; // this should be impossible 1168 } 1169 1170 const std::string& owner = objDict.begin()->first; 1171 crow::connections::systemBus->async_method_call( 1172 [path, owner, 1173 sensorsAsyncResp](const boost::system::error_code e, 1174 std::variant<std::vector<std::string>> 1175 variantEndpoints) { 1176 if (e) 1177 { 1178 return; // if they don't have an association we 1179 // can't tell what chassis is 1180 } 1181 // verify part of the right chassis 1182 auto endpoints = std::get_if<std::vector<std::string>>( 1183 &variantEndpoints); 1184 1185 if (endpoints == nullptr) 1186 { 1187 BMCWEB_LOG_ERROR << "Invalid association interface"; 1188 messages::internalError( 1189 sensorsAsyncResp->asyncResp->res); 1190 return; 1191 } 1192 1193 auto found = std::find_if( 1194 endpoints->begin(), endpoints->end(), 1195 [sensorsAsyncResp](const std::string& entry) { 1196 return entry.find( 1197 sensorsAsyncResp->chassisId) != 1198 std::string::npos; 1199 }); 1200 1201 if (found == endpoints->end()) 1202 { 1203 return; 1204 } 1205 crow::connections::systemBus->async_method_call( 1206 [path, sensorsAsyncResp]( 1207 const boost::system::error_code& err, 1208 const boost::container::flat_map< 1209 std::string, 1210 std::variant<uint8_t, 1211 std::vector<std::string>, 1212 std::string>>& ret) { 1213 if (err) 1214 { 1215 return; // don't have to have this 1216 // interface 1217 } 1218 auto findFailures = ret.find("AllowedFailures"); 1219 auto findCollection = ret.find("Collection"); 1220 auto findStatus = ret.find("Status"); 1221 1222 if (findFailures == ret.end() || 1223 findCollection == ret.end() || 1224 findStatus == ret.end()) 1225 { 1226 BMCWEB_LOG_ERROR 1227 << "Invalid redundancy interface"; 1228 messages::internalError( 1229 sensorsAsyncResp->asyncResp->res); 1230 return; 1231 } 1232 1233 auto allowedFailures = std::get_if<uint8_t>( 1234 &(findFailures->second)); 1235 auto collection = 1236 std::get_if<std::vector<std::string>>( 1237 &(findCollection->second)); 1238 auto status = std::get_if<std::string>( 1239 &(findStatus->second)); 1240 1241 if (allowedFailures == nullptr || 1242 collection == nullptr || status == nullptr) 1243 { 1244 1245 BMCWEB_LOG_ERROR 1246 << "Invalid redundancy interface " 1247 "types"; 1248 messages::internalError( 1249 sensorsAsyncResp->asyncResp->res); 1250 return; 1251 } 1252 sdbusplus::message::object_path objectPath( 1253 path); 1254 std::string name = objectPath.filename(); 1255 if (name.empty()) 1256 { 1257 // this should be impossible 1258 messages::internalError( 1259 sensorsAsyncResp->asyncResp->res); 1260 return; 1261 } 1262 std::replace(name.begin(), name.end(), '_', 1263 ' '); 1264 1265 std::string health; 1266 1267 if (boost::ends_with(*status, "Full")) 1268 { 1269 health = "OK"; 1270 } 1271 else if (boost::ends_with(*status, "Degraded")) 1272 { 1273 health = "Warning"; 1274 } 1275 else 1276 { 1277 health = "Critical"; 1278 } 1279 std::vector<nlohmann::json> redfishCollection; 1280 const auto& fanRedfish = 1281 sensorsAsyncResp->asyncResp->res 1282 .jsonValue["Fans"]; 1283 for (const std::string& item : *collection) 1284 { 1285 sdbusplus::message::object_path path(item); 1286 std::string itemName = path.filename(); 1287 if (itemName.empty()) 1288 { 1289 continue; 1290 } 1291 /* 1292 todo(ed): merge patch that fixes the names 1293 std::replace(itemName.begin(), 1294 itemName.end(), '_', ' ');*/ 1295 auto schemaItem = std::find_if( 1296 fanRedfish.begin(), fanRedfish.end(), 1297 [itemName](const nlohmann::json& fan) { 1298 return fan["MemberId"] == itemName; 1299 }); 1300 if (schemaItem != fanRedfish.end()) 1301 { 1302 redfishCollection.push_back( 1303 {{"@odata.id", 1304 (*schemaItem)["@odata.id"]}}); 1305 } 1306 else 1307 { 1308 BMCWEB_LOG_ERROR 1309 << "failed to find fan in schema"; 1310 messages::internalError( 1311 sensorsAsyncResp->asyncResp->res); 1312 return; 1313 } 1314 } 1315 1316 size_t minNumNeeded = 1317 collection->size() > 0 1318 ? collection->size() - *allowedFailures 1319 : 0; 1320 nlohmann::json& jResp = 1321 sensorsAsyncResp->asyncResp->res 1322 .jsonValue["Redundancy"]; 1323 jResp.push_back( 1324 {{"@odata.id", 1325 "/redfish/v1/Chassis/" + 1326 sensorsAsyncResp->chassisId + "/" + 1327 sensorsAsyncResp->chassisSubNode + 1328 "#/Redundancy/" + 1329 std::to_string(jResp.size())}, 1330 {"@odata.type", 1331 "#Redundancy.v1_3_2.Redundancy"}, 1332 {"MinNumNeeded", minNumNeeded}, 1333 {"MemberId", name}, 1334 {"Mode", "N+m"}, 1335 {"Name", name}, 1336 {"RedundancySet", redfishCollection}, 1337 {"Status", 1338 {{"Health", health}, 1339 {"State", "Enabled"}}}}); 1340 }, 1341 owner, path, "org.freedesktop.DBus.Properties", 1342 "GetAll", 1343 "xyz.openbmc_project.Control.FanRedundancy"); 1344 }, 1345 "xyz.openbmc_project.ObjectMapper", path + "/chassis", 1346 "org.freedesktop.DBus.Properties", "Get", 1347 "xyz.openbmc_project.Association", "endpoints"); 1348 } 1349 }, 1350 "xyz.openbmc_project.ObjectMapper", 1351 "/xyz/openbmc_project/object_mapper", 1352 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 1353 "/xyz/openbmc_project/control", 2, 1354 std::array<const char*, 1>{ 1355 "xyz.openbmc_project.Control.FanRedundancy"}); 1356 } 1357 1358 inline void 1359 sortJSONResponse(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp) 1360 { 1361 nlohmann::json& response = sensorsAsyncResp->asyncResp->res.jsonValue; 1362 std::array<std::string, 2> sensorHeaders{"Temperatures", "Fans"}; 1363 if (sensorsAsyncResp->chassisSubNode == sensors::node::power) 1364 { 1365 sensorHeaders = {"Voltages", "PowerSupplies"}; 1366 } 1367 for (const std::string& sensorGroup : sensorHeaders) 1368 { 1369 nlohmann::json::iterator entry = response.find(sensorGroup); 1370 if (entry != response.end()) 1371 { 1372 std::sort(entry->begin(), entry->end(), 1373 [](nlohmann::json& c1, nlohmann::json& c2) { 1374 return c1["Name"] < c2["Name"]; 1375 }); 1376 1377 // add the index counts to the end of each entry 1378 size_t count = 0; 1379 for (nlohmann::json& sensorJson : *entry) 1380 { 1381 nlohmann::json::iterator odata = sensorJson.find("@odata.id"); 1382 if (odata == sensorJson.end()) 1383 { 1384 continue; 1385 } 1386 std::string* value = odata->get_ptr<std::string*>(); 1387 if (value != nullptr) 1388 { 1389 *value += std::to_string(count); 1390 count++; 1391 sensorsAsyncResp->updateUri(sensorJson["Name"], *value); 1392 } 1393 } 1394 } 1395 } 1396 } 1397 1398 /** 1399 * @brief Finds the inventory item with the specified object path. 1400 * @param inventoryItems D-Bus inventory items associated with sensors. 1401 * @param invItemObjPath D-Bus object path of inventory item. 1402 * @return Inventory item within vector, or nullptr if no match found. 1403 */ 1404 inline InventoryItem* findInventoryItem( 1405 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems, 1406 const std::string& invItemObjPath) 1407 { 1408 for (InventoryItem& inventoryItem : *inventoryItems) 1409 { 1410 if (inventoryItem.objectPath == invItemObjPath) 1411 { 1412 return &inventoryItem; 1413 } 1414 } 1415 return nullptr; 1416 } 1417 1418 /** 1419 * @brief Finds the inventory item associated with the specified sensor. 1420 * @param inventoryItems D-Bus inventory items associated with sensors. 1421 * @param sensorObjPath D-Bus object path of sensor. 1422 * @return Inventory item within vector, or nullptr if no match found. 1423 */ 1424 inline InventoryItem* findInventoryItemForSensor( 1425 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems, 1426 const std::string& sensorObjPath) 1427 { 1428 for (InventoryItem& inventoryItem : *inventoryItems) 1429 { 1430 if (inventoryItem.sensors.count(sensorObjPath) > 0) 1431 { 1432 return &inventoryItem; 1433 } 1434 } 1435 return nullptr; 1436 } 1437 1438 /** 1439 * @brief Finds the inventory item associated with the specified led path. 1440 * @param inventoryItems D-Bus inventory items associated with sensors. 1441 * @param ledObjPath D-Bus object path of led. 1442 * @return Inventory item within vector, or nullptr if no match found. 1443 */ 1444 inline InventoryItem* 1445 findInventoryItemForLed(std::vector<InventoryItem>& inventoryItems, 1446 const std::string& ledObjPath) 1447 { 1448 for (InventoryItem& inventoryItem : inventoryItems) 1449 { 1450 if (inventoryItem.ledObjectPath == ledObjPath) 1451 { 1452 return &inventoryItem; 1453 } 1454 } 1455 return nullptr; 1456 } 1457 1458 /** 1459 * @brief Adds inventory item and associated sensor to specified vector. 1460 * 1461 * Adds a new InventoryItem to the vector if necessary. Searches for an 1462 * existing InventoryItem with the specified object path. If not found, one is 1463 * added to the vector. 1464 * 1465 * Next, the specified sensor is added to the set of sensors associated with the 1466 * InventoryItem. 1467 * 1468 * @param inventoryItems D-Bus inventory items associated with sensors. 1469 * @param invItemObjPath D-Bus object path of inventory item. 1470 * @param sensorObjPath D-Bus object path of sensor 1471 */ 1472 inline void addInventoryItem( 1473 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems, 1474 const std::string& invItemObjPath, const std::string& sensorObjPath) 1475 { 1476 // Look for inventory item in vector 1477 InventoryItem* inventoryItem = 1478 findInventoryItem(inventoryItems, invItemObjPath); 1479 1480 // If inventory item doesn't exist in vector, add it 1481 if (inventoryItem == nullptr) 1482 { 1483 inventoryItems->emplace_back(invItemObjPath); 1484 inventoryItem = &(inventoryItems->back()); 1485 } 1486 1487 // Add sensor to set of sensors associated with inventory item 1488 inventoryItem->sensors.emplace(sensorObjPath); 1489 } 1490 1491 /** 1492 * @brief Stores D-Bus data in the specified inventory item. 1493 * 1494 * Finds D-Bus data in the specified map of interfaces. Stores the data in the 1495 * specified InventoryItem. 1496 * 1497 * This data is later used to provide sensor property values in the JSON 1498 * response. 1499 * 1500 * @param inventoryItem Inventory item where data will be stored. 1501 * @param interfacesDict Map containing D-Bus interfaces and their properties 1502 * for the specified inventory item. 1503 */ 1504 inline void storeInventoryItemData( 1505 InventoryItem& inventoryItem, 1506 const boost::container::flat_map< 1507 std::string, boost::container::flat_map<std::string, SensorVariant>>& 1508 interfacesDict) 1509 { 1510 // Get properties from Inventory.Item interface 1511 auto interfaceIt = 1512 interfacesDict.find("xyz.openbmc_project.Inventory.Item"); 1513 if (interfaceIt != interfacesDict.end()) 1514 { 1515 auto propertyIt = interfaceIt->second.find("Present"); 1516 if (propertyIt != interfaceIt->second.end()) 1517 { 1518 const bool* value = std::get_if<bool>(&propertyIt->second); 1519 if (value != nullptr) 1520 { 1521 inventoryItem.isPresent = *value; 1522 } 1523 } 1524 } 1525 1526 // Check if Inventory.Item.PowerSupply interface is present 1527 interfaceIt = 1528 interfacesDict.find("xyz.openbmc_project.Inventory.Item.PowerSupply"); 1529 if (interfaceIt != interfacesDict.end()) 1530 { 1531 inventoryItem.isPowerSupply = true; 1532 } 1533 1534 // Get properties from Inventory.Decorator.Asset interface 1535 interfaceIt = 1536 interfacesDict.find("xyz.openbmc_project.Inventory.Decorator.Asset"); 1537 if (interfaceIt != interfacesDict.end()) 1538 { 1539 auto propertyIt = interfaceIt->second.find("Manufacturer"); 1540 if (propertyIt != interfaceIt->second.end()) 1541 { 1542 const std::string* value = 1543 std::get_if<std::string>(&propertyIt->second); 1544 if (value != nullptr) 1545 { 1546 inventoryItem.manufacturer = *value; 1547 } 1548 } 1549 1550 propertyIt = interfaceIt->second.find("Model"); 1551 if (propertyIt != interfaceIt->second.end()) 1552 { 1553 const std::string* value = 1554 std::get_if<std::string>(&propertyIt->second); 1555 if (value != nullptr) 1556 { 1557 inventoryItem.model = *value; 1558 } 1559 } 1560 1561 propertyIt = interfaceIt->second.find("PartNumber"); 1562 if (propertyIt != interfaceIt->second.end()) 1563 { 1564 const std::string* value = 1565 std::get_if<std::string>(&propertyIt->second); 1566 if (value != nullptr) 1567 { 1568 inventoryItem.partNumber = *value; 1569 } 1570 } 1571 1572 propertyIt = interfaceIt->second.find("SerialNumber"); 1573 if (propertyIt != interfaceIt->second.end()) 1574 { 1575 const std::string* value = 1576 std::get_if<std::string>(&propertyIt->second); 1577 if (value != nullptr) 1578 { 1579 inventoryItem.serialNumber = *value; 1580 } 1581 } 1582 } 1583 1584 // Get properties from State.Decorator.OperationalStatus interface 1585 interfaceIt = interfacesDict.find( 1586 "xyz.openbmc_project.State.Decorator.OperationalStatus"); 1587 if (interfaceIt != interfacesDict.end()) 1588 { 1589 auto propertyIt = interfaceIt->second.find("Functional"); 1590 if (propertyIt != interfaceIt->second.end()) 1591 { 1592 const bool* value = std::get_if<bool>(&propertyIt->second); 1593 if (value != nullptr) 1594 { 1595 inventoryItem.isFunctional = *value; 1596 } 1597 } 1598 } 1599 } 1600 1601 /** 1602 * @brief Gets D-Bus data for inventory items associated with sensors. 1603 * 1604 * Uses the specified connections (services) to obtain D-Bus data for inventory 1605 * items associated with sensors. Stores the resulting data in the 1606 * inventoryItems vector. 1607 * 1608 * This data is later used to provide sensor property values in the JSON 1609 * response. 1610 * 1611 * Finds the inventory item data asynchronously. Invokes callback when data has 1612 * been obtained. 1613 * 1614 * The callback must have the following signature: 1615 * @code 1616 * callback(void) 1617 * @endcode 1618 * 1619 * This function is called recursively, obtaining data asynchronously from one 1620 * connection in each call. This ensures the callback is not invoked until the 1621 * last asynchronous function has completed. 1622 * 1623 * @param sensorsAsyncResp Pointer to object holding response data. 1624 * @param inventoryItems D-Bus inventory items associated with sensors. 1625 * @param invConnections Connections that provide data for the inventory items. 1626 * @param objectMgrPaths Mappings from connection name to DBus object path that 1627 * implements ObjectManager. 1628 * @param callback Callback to invoke when inventory data has been obtained. 1629 * @param invConnectionsIndex Current index in invConnections. Only specified 1630 * in recursive calls to this function. 1631 */ 1632 template <typename Callback> 1633 static void getInventoryItemsData( 1634 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 1635 std::shared_ptr<std::vector<InventoryItem>> inventoryItems, 1636 std::shared_ptr<boost::container::flat_set<std::string>> invConnections, 1637 std::shared_ptr<boost::container::flat_map<std::string, std::string>> 1638 objectMgrPaths, 1639 Callback&& callback, size_t invConnectionsIndex = 0) 1640 { 1641 BMCWEB_LOG_DEBUG << "getInventoryItemsData enter"; 1642 1643 // If no more connections left, call callback 1644 if (invConnectionsIndex >= invConnections->size()) 1645 { 1646 callback(); 1647 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit"; 1648 return; 1649 } 1650 1651 // Get inventory item data from current connection 1652 auto it = invConnections->nth(invConnectionsIndex); 1653 if (it != invConnections->end()) 1654 { 1655 const std::string& invConnection = *it; 1656 1657 // Response handler for GetManagedObjects 1658 auto respHandler = [sensorsAsyncResp, inventoryItems, invConnections, 1659 objectMgrPaths, callback{std::move(callback)}, 1660 invConnectionsIndex]( 1661 const boost::system::error_code ec, 1662 ManagedObjectsVectorType& resp) { 1663 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler enter"; 1664 if (ec) 1665 { 1666 BMCWEB_LOG_ERROR 1667 << "getInventoryItemsData respHandler DBus error " << ec; 1668 messages::internalError(sensorsAsyncResp->asyncResp->res); 1669 return; 1670 } 1671 1672 // Loop through returned object paths 1673 for (const auto& objDictEntry : resp) 1674 { 1675 const std::string& objPath = 1676 static_cast<const std::string&>(objDictEntry.first); 1677 1678 // If this object path is one of the specified inventory items 1679 InventoryItem* inventoryItem = 1680 findInventoryItem(inventoryItems, objPath); 1681 if (inventoryItem != nullptr) 1682 { 1683 // Store inventory data in InventoryItem 1684 storeInventoryItemData(*inventoryItem, objDictEntry.second); 1685 } 1686 } 1687 1688 // Recurse to get inventory item data from next connection 1689 getInventoryItemsData(sensorsAsyncResp, inventoryItems, 1690 invConnections, objectMgrPaths, 1691 std::move(callback), invConnectionsIndex + 1); 1692 1693 BMCWEB_LOG_DEBUG << "getInventoryItemsData respHandler exit"; 1694 }; 1695 1696 // Find DBus object path that implements ObjectManager for the current 1697 // connection. If no mapping found, default to "/". 1698 auto iter = objectMgrPaths->find(invConnection); 1699 const std::string& objectMgrPath = 1700 (iter != objectMgrPaths->end()) ? iter->second : "/"; 1701 BMCWEB_LOG_DEBUG << "ObjectManager path for " << invConnection << " is " 1702 << objectMgrPath; 1703 1704 // Get all object paths and their interfaces for current connection 1705 crow::connections::systemBus->async_method_call( 1706 std::move(respHandler), invConnection, objectMgrPath, 1707 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1708 } 1709 1710 BMCWEB_LOG_DEBUG << "getInventoryItemsData exit"; 1711 } 1712 1713 /** 1714 * @brief Gets connections that provide D-Bus data for inventory items. 1715 * 1716 * Gets the D-Bus connections (services) that provide data for the inventory 1717 * items that are associated with sensors. 1718 * 1719 * Finds the connections asynchronously. Invokes callback when information has 1720 * been obtained. 1721 * 1722 * The callback must have the following signature: 1723 * @code 1724 * callback(std::shared_ptr<boost::container::flat_set<std::string>> 1725 * invConnections) 1726 * @endcode 1727 * 1728 * @param sensorsAsyncResp Pointer to object holding response data. 1729 * @param inventoryItems D-Bus inventory items associated with sensors. 1730 * @param callback Callback to invoke when connections have been obtained. 1731 */ 1732 template <typename Callback> 1733 static void getInventoryItemsConnections( 1734 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 1735 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems, 1736 Callback&& callback) 1737 { 1738 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections enter"; 1739 1740 const std::string path = "/xyz/openbmc_project/inventory"; 1741 const std::array<std::string, 4> interfaces = { 1742 "xyz.openbmc_project.Inventory.Item", 1743 "xyz.openbmc_project.Inventory.Item.PowerSupply", 1744 "xyz.openbmc_project.Inventory.Decorator.Asset", 1745 "xyz.openbmc_project.State.Decorator.OperationalStatus"}; 1746 1747 // Response handler for parsing output from GetSubTree 1748 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp, 1749 inventoryItems](const boost::system::error_code ec, 1750 const GetSubTreeType& subtree) { 1751 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler enter"; 1752 if (ec) 1753 { 1754 messages::internalError(sensorsAsyncResp->asyncResp->res); 1755 BMCWEB_LOG_ERROR 1756 << "getInventoryItemsConnections respHandler DBus error " << ec; 1757 return; 1758 } 1759 1760 // Make unique list of connections for desired inventory items 1761 std::shared_ptr<boost::container::flat_set<std::string>> 1762 invConnections = 1763 std::make_shared<boost::container::flat_set<std::string>>(); 1764 invConnections->reserve(8); 1765 1766 // Loop through objects from GetSubTree 1767 for (const std::pair< 1768 std::string, 1769 std::vector<std::pair<std::string, std::vector<std::string>>>>& 1770 object : subtree) 1771 { 1772 // Check if object path is one of the specified inventory items 1773 const std::string& objPath = object.first; 1774 if (findInventoryItem(inventoryItems, objPath) != nullptr) 1775 { 1776 // Store all connections to inventory item 1777 for (const std::pair<std::string, std::vector<std::string>>& 1778 objData : object.second) 1779 { 1780 const std::string& invConnection = objData.first; 1781 invConnections->insert(invConnection); 1782 } 1783 } 1784 } 1785 1786 callback(invConnections); 1787 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections respHandler exit"; 1788 }; 1789 1790 // Make call to ObjectMapper to find all inventory items 1791 crow::connections::systemBus->async_method_call( 1792 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 1793 "/xyz/openbmc_project/object_mapper", 1794 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces); 1795 BMCWEB_LOG_DEBUG << "getInventoryItemsConnections exit"; 1796 } 1797 1798 /** 1799 * @brief Gets associations from sensors to inventory items. 1800 * 1801 * Looks for ObjectMapper associations from the specified sensors to related 1802 * inventory items. Then finds the associations from those inventory items to 1803 * their LEDs, if any. 1804 * 1805 * Finds the inventory items asynchronously. Invokes callback when information 1806 * has been obtained. 1807 * 1808 * The callback must have the following signature: 1809 * @code 1810 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems) 1811 * @endcode 1812 * 1813 * @param sensorsAsyncResp Pointer to object holding response data. 1814 * @param sensorNames All sensors within the current chassis. 1815 * @param objectMgrPaths Mappings from connection name to DBus object path that 1816 * implements ObjectManager. 1817 * @param callback Callback to invoke when inventory items have been obtained. 1818 */ 1819 template <typename Callback> 1820 static void getInventoryItemAssociations( 1821 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 1822 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames, 1823 const std::shared_ptr<boost::container::flat_map<std::string, std::string>>& 1824 objectMgrPaths, 1825 Callback&& callback) 1826 { 1827 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations enter"; 1828 1829 // Response handler for GetManagedObjects 1830 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp, 1831 sensorNames](const boost::system::error_code ec, 1832 dbus::utility::ManagedObjectType& resp) { 1833 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler enter"; 1834 if (ec) 1835 { 1836 BMCWEB_LOG_ERROR 1837 << "getInventoryItemAssociations respHandler DBus error " << ec; 1838 messages::internalError(sensorsAsyncResp->asyncResp->res); 1839 return; 1840 } 1841 1842 // Create vector to hold list of inventory items 1843 std::shared_ptr<std::vector<InventoryItem>> inventoryItems = 1844 std::make_shared<std::vector<InventoryItem>>(); 1845 1846 // Loop through returned object paths 1847 std::string sensorAssocPath; 1848 sensorAssocPath.reserve(128); // avoid memory allocations 1849 for (const auto& objDictEntry : resp) 1850 { 1851 const std::string& objPath = 1852 static_cast<const std::string&>(objDictEntry.first); 1853 const boost::container::flat_map< 1854 std::string, boost::container::flat_map< 1855 std::string, dbus::utility::DbusVariantType>>& 1856 interfacesDict = objDictEntry.second; 1857 1858 // If path is inventory association for one of the specified sensors 1859 for (const std::string& sensorName : *sensorNames) 1860 { 1861 sensorAssocPath = sensorName; 1862 sensorAssocPath += "/inventory"; 1863 if (objPath == sensorAssocPath) 1864 { 1865 // Get Association interface for object path 1866 auto assocIt = 1867 interfacesDict.find("xyz.openbmc_project.Association"); 1868 if (assocIt != interfacesDict.end()) 1869 { 1870 // Get inventory item from end point 1871 auto endpointsIt = assocIt->second.find("endpoints"); 1872 if (endpointsIt != assocIt->second.end()) 1873 { 1874 const std::vector<std::string>* endpoints = 1875 std::get_if<std::vector<std::string>>( 1876 &endpointsIt->second); 1877 if ((endpoints != nullptr) && !endpoints->empty()) 1878 { 1879 // Add inventory item to vector 1880 const std::string& invItemPath = 1881 endpoints->front(); 1882 addInventoryItem(inventoryItems, invItemPath, 1883 sensorName); 1884 } 1885 } 1886 } 1887 break; 1888 } 1889 } 1890 } 1891 1892 // Now loop through the returned object paths again, this time to 1893 // find the leds associated with the inventory items we just found 1894 std::string inventoryAssocPath; 1895 inventoryAssocPath.reserve(128); // avoid memory allocations 1896 for (const auto& objDictEntry : resp) 1897 { 1898 const std::string& objPath = 1899 static_cast<const std::string&>(objDictEntry.first); 1900 const boost::container::flat_map< 1901 std::string, boost::container::flat_map< 1902 std::string, dbus::utility::DbusVariantType>>& 1903 interfacesDict = objDictEntry.second; 1904 1905 for (InventoryItem& inventoryItem : *inventoryItems) 1906 { 1907 inventoryAssocPath = inventoryItem.objectPath; 1908 inventoryAssocPath += "/leds"; 1909 if (objPath == inventoryAssocPath) 1910 { 1911 // Get Association interface for object path 1912 auto assocIt = 1913 interfacesDict.find("xyz.openbmc_project.Association"); 1914 if (assocIt != interfacesDict.end()) 1915 { 1916 // Get inventory item from end point 1917 auto endpointsIt = assocIt->second.find("endpoints"); 1918 if (endpointsIt != assocIt->second.end()) 1919 { 1920 const std::vector<std::string>* endpoints = 1921 std::get_if<std::vector<std::string>>( 1922 &endpointsIt->second); 1923 if ((endpoints != nullptr) && !endpoints->empty()) 1924 { 1925 // Store LED path in inventory item 1926 const std::string& ledPath = endpoints->front(); 1927 inventoryItem.ledObjectPath = ledPath; 1928 } 1929 } 1930 } 1931 break; 1932 } 1933 } 1934 } 1935 callback(inventoryItems); 1936 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations respHandler exit"; 1937 }; 1938 1939 // Find DBus object path that implements ObjectManager for ObjectMapper 1940 std::string connection = "xyz.openbmc_project.ObjectMapper"; 1941 auto iter = objectMgrPaths->find(connection); 1942 const std::string& objectMgrPath = 1943 (iter != objectMgrPaths->end()) ? iter->second : "/"; 1944 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is " 1945 << objectMgrPath; 1946 1947 // Call GetManagedObjects on the ObjectMapper to get all associations 1948 crow::connections::systemBus->async_method_call( 1949 std::move(respHandler), connection, objectMgrPath, 1950 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1951 1952 BMCWEB_LOG_DEBUG << "getInventoryItemAssociations exit"; 1953 } 1954 1955 /** 1956 * @brief Gets D-Bus data for inventory item leds associated with sensors. 1957 * 1958 * Uses the specified connections (services) to obtain D-Bus data for inventory 1959 * item leds associated with sensors. Stores the resulting data in the 1960 * inventoryItems vector. 1961 * 1962 * This data is later used to provide sensor property values in the JSON 1963 * response. 1964 * 1965 * Finds the inventory item led data asynchronously. Invokes callback when data 1966 * has been obtained. 1967 * 1968 * The callback must have the following signature: 1969 * @code 1970 * callback() 1971 * @endcode 1972 * 1973 * This function is called recursively, obtaining data asynchronously from one 1974 * connection in each call. This ensures the callback is not invoked until the 1975 * last asynchronous function has completed. 1976 * 1977 * @param sensorsAsyncResp Pointer to object holding response data. 1978 * @param inventoryItems D-Bus inventory items associated with sensors. 1979 * @param ledConnections Connections that provide data for the inventory leds. 1980 * @param callback Callback to invoke when inventory data has been obtained. 1981 * @param ledConnectionsIndex Current index in ledConnections. Only specified 1982 * in recursive calls to this function. 1983 */ 1984 template <typename Callback> 1985 void getInventoryLedData( 1986 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 1987 std::shared_ptr<std::vector<InventoryItem>> inventoryItems, 1988 std::shared_ptr<boost::container::flat_map<std::string, std::string>> 1989 ledConnections, 1990 Callback&& callback, size_t ledConnectionsIndex = 0) 1991 { 1992 BMCWEB_LOG_DEBUG << "getInventoryLedData enter"; 1993 1994 // If no more connections left, call callback 1995 if (ledConnectionsIndex >= ledConnections->size()) 1996 { 1997 callback(); 1998 BMCWEB_LOG_DEBUG << "getInventoryLedData exit"; 1999 return; 2000 } 2001 2002 // Get inventory item data from current connection 2003 auto it = ledConnections->nth(ledConnectionsIndex); 2004 if (it != ledConnections->end()) 2005 { 2006 const std::string& ledPath = (*it).first; 2007 const std::string& ledConnection = (*it).second; 2008 // Response handler for Get State property 2009 auto respHandler = 2010 [sensorsAsyncResp, inventoryItems, ledConnections, ledPath, 2011 callback{std::move(callback)}, 2012 ledConnectionsIndex](const boost::system::error_code ec, 2013 const std::variant<std::string>& ledState) { 2014 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler enter"; 2015 if (ec) 2016 { 2017 BMCWEB_LOG_ERROR 2018 << "getInventoryLedData respHandler DBus error " << ec; 2019 messages::internalError(sensorsAsyncResp->asyncResp->res); 2020 return; 2021 } 2022 2023 const std::string* state = std::get_if<std::string>(&ledState); 2024 if (state != nullptr) 2025 { 2026 BMCWEB_LOG_DEBUG << "Led state: " << *state; 2027 // Find inventory item with this LED object path 2028 InventoryItem* inventoryItem = 2029 findInventoryItemForLed(*inventoryItems, ledPath); 2030 if (inventoryItem != nullptr) 2031 { 2032 // Store LED state in InventoryItem 2033 if (boost::ends_with(*state, "On")) 2034 { 2035 inventoryItem->ledState = LedState::ON; 2036 } 2037 else if (boost::ends_with(*state, "Blink")) 2038 { 2039 inventoryItem->ledState = LedState::BLINK; 2040 } 2041 else if (boost::ends_with(*state, "Off")) 2042 { 2043 inventoryItem->ledState = LedState::OFF; 2044 } 2045 else 2046 { 2047 inventoryItem->ledState = LedState::UNKNOWN; 2048 } 2049 } 2050 } 2051 else 2052 { 2053 BMCWEB_LOG_DEBUG << "Failed to find State data for LED: " 2054 << ledPath; 2055 } 2056 2057 // Recurse to get LED data from next connection 2058 getInventoryLedData(sensorsAsyncResp, inventoryItems, 2059 ledConnections, std::move(callback), 2060 ledConnectionsIndex + 1); 2061 2062 BMCWEB_LOG_DEBUG << "getInventoryLedData respHandler exit"; 2063 }; 2064 2065 // Get the State property for the current LED 2066 crow::connections::systemBus->async_method_call( 2067 std::move(respHandler), ledConnection, ledPath, 2068 "org.freedesktop.DBus.Properties", "Get", 2069 "xyz.openbmc_project.Led.Physical", "State"); 2070 } 2071 2072 BMCWEB_LOG_DEBUG << "getInventoryLedData exit"; 2073 } 2074 2075 /** 2076 * @brief Gets LED data for LEDs associated with given inventory items. 2077 * 2078 * Gets the D-Bus connections (services) that provide LED data for the LEDs 2079 * associated with the specified inventory items. Then gets the LED data from 2080 * each connection and stores it in the inventory item. 2081 * 2082 * This data is later used to provide sensor property values in the JSON 2083 * response. 2084 * 2085 * Finds the LED data asynchronously. Invokes callback when information has 2086 * been obtained. 2087 * 2088 * The callback must have the following signature: 2089 * @code 2090 * callback() 2091 * @endcode 2092 * 2093 * @param sensorsAsyncResp Pointer to object holding response data. 2094 * @param inventoryItems D-Bus inventory items associated with sensors. 2095 * @param callback Callback to invoke when inventory items have been obtained. 2096 */ 2097 template <typename Callback> 2098 void getInventoryLeds( 2099 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 2100 std::shared_ptr<std::vector<InventoryItem>> inventoryItems, 2101 Callback&& callback) 2102 { 2103 BMCWEB_LOG_DEBUG << "getInventoryLeds enter"; 2104 2105 const std::string path = "/xyz/openbmc_project"; 2106 const std::array<std::string, 1> interfaces = { 2107 "xyz.openbmc_project.Led.Physical"}; 2108 2109 // Response handler for parsing output from GetSubTree 2110 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp, 2111 inventoryItems](const boost::system::error_code ec, 2112 const GetSubTreeType& subtree) { 2113 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler enter"; 2114 if (ec) 2115 { 2116 messages::internalError(sensorsAsyncResp->asyncResp->res); 2117 BMCWEB_LOG_ERROR << "getInventoryLeds respHandler DBus error " 2118 << ec; 2119 return; 2120 } 2121 2122 // Build map of LED object paths to connections 2123 std::shared_ptr<boost::container::flat_map<std::string, std::string>> 2124 ledConnections = std::make_shared< 2125 boost::container::flat_map<std::string, std::string>>(); 2126 2127 // Loop through objects from GetSubTree 2128 for (const std::pair< 2129 std::string, 2130 std::vector<std::pair<std::string, std::vector<std::string>>>>& 2131 object : subtree) 2132 { 2133 // Check if object path is LED for one of the specified inventory 2134 // items 2135 const std::string& ledPath = object.first; 2136 if (findInventoryItemForLed(*inventoryItems, ledPath) != nullptr) 2137 { 2138 // Add mapping from ledPath to connection 2139 const std::string& connection = object.second.begin()->first; 2140 (*ledConnections)[ledPath] = connection; 2141 BMCWEB_LOG_DEBUG << "Added mapping " << ledPath << " -> " 2142 << connection; 2143 } 2144 } 2145 2146 getInventoryLedData(sensorsAsyncResp, inventoryItems, ledConnections, 2147 std::move(callback)); 2148 BMCWEB_LOG_DEBUG << "getInventoryLeds respHandler exit"; 2149 }; 2150 // Make call to ObjectMapper to find all inventory items 2151 crow::connections::systemBus->async_method_call( 2152 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 2153 "/xyz/openbmc_project/object_mapper", 2154 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 0, interfaces); 2155 BMCWEB_LOG_DEBUG << "getInventoryLeds exit"; 2156 } 2157 2158 /** 2159 * @brief Gets D-Bus data for Power Supply Attributes such as EfficiencyPercent 2160 * 2161 * Uses the specified connections (services) (currently assumes just one) to 2162 * obtain D-Bus data for Power Supply Attributes. Stores the resulting data in 2163 * the inventoryItems vector. Only stores data in Power Supply inventoryItems. 2164 * 2165 * This data is later used to provide sensor property values in the JSON 2166 * response. 2167 * 2168 * Finds the Power Supply Attributes data asynchronously. Invokes callback 2169 * when data has been obtained. 2170 * 2171 * The callback must have the following signature: 2172 * @code 2173 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems) 2174 * @endcode 2175 * 2176 * @param sensorsAsyncResp Pointer to object holding response data. 2177 * @param inventoryItems D-Bus inventory items associated with sensors. 2178 * @param psAttributesConnections Connections that provide data for the Power 2179 * Supply Attributes 2180 * @param callback Callback to invoke when data has been obtained. 2181 */ 2182 template <typename Callback> 2183 void getPowerSupplyAttributesData( 2184 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 2185 std::shared_ptr<std::vector<InventoryItem>> inventoryItems, 2186 const boost::container::flat_map<std::string, std::string>& 2187 psAttributesConnections, 2188 Callback&& callback) 2189 { 2190 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData enter"; 2191 2192 if (psAttributesConnections.empty()) 2193 { 2194 BMCWEB_LOG_DEBUG << "Can't find PowerSupplyAttributes, no connections!"; 2195 callback(inventoryItems); 2196 return; 2197 } 2198 2199 // Assuming just one connection (service) for now 2200 auto it = psAttributesConnections.nth(0); 2201 2202 const std::string& psAttributesPath = (*it).first; 2203 const std::string& psAttributesConnection = (*it).second; 2204 2205 // Response handler for Get DeratingFactor property 2206 auto respHandler = [sensorsAsyncResp, inventoryItems, 2207 callback{std::move(callback)}]( 2208 const boost::system::error_code ec, 2209 const std::variant<uint32_t>& deratingFactor) { 2210 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler enter"; 2211 if (ec) 2212 { 2213 BMCWEB_LOG_ERROR 2214 << "getPowerSupplyAttributesData respHandler DBus error " << ec; 2215 messages::internalError(sensorsAsyncResp->asyncResp->res); 2216 return; 2217 } 2218 2219 const uint32_t* value = std::get_if<uint32_t>(&deratingFactor); 2220 if (value != nullptr) 2221 { 2222 BMCWEB_LOG_DEBUG << "PS EfficiencyPercent value: " << *value; 2223 // Store value in Power Supply Inventory Items 2224 for (InventoryItem& inventoryItem : *inventoryItems) 2225 { 2226 if (inventoryItem.isPowerSupply == true) 2227 { 2228 inventoryItem.powerSupplyEfficiencyPercent = 2229 static_cast<int>(*value); 2230 } 2231 } 2232 } 2233 else 2234 { 2235 BMCWEB_LOG_DEBUG 2236 << "Failed to find EfficiencyPercent value for PowerSupplies"; 2237 } 2238 2239 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData respHandler exit"; 2240 callback(inventoryItems); 2241 }; 2242 2243 // Get the DeratingFactor property for the PowerSupplyAttributes 2244 // Currently only property on the interface/only one we care about 2245 crow::connections::systemBus->async_method_call( 2246 std::move(respHandler), psAttributesConnection, psAttributesPath, 2247 "org.freedesktop.DBus.Properties", "Get", 2248 "xyz.openbmc_project.Control.PowerSupplyAttributes", "DeratingFactor"); 2249 2250 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributesData exit"; 2251 } 2252 2253 /** 2254 * @brief Gets the Power Supply Attributes such as EfficiencyPercent 2255 * 2256 * Gets the D-Bus connection (service) that provides Power Supply Attributes 2257 * data. Then gets the Power Supply Attributes data from the connection 2258 * (currently just assumes 1 connection) and stores the data in the inventory 2259 * item. 2260 * 2261 * This data is later used to provide sensor property values in the JSON 2262 * response. DeratingFactor on D-Bus is mapped to EfficiencyPercent on Redfish. 2263 * 2264 * Finds the Power Supply Attributes data asynchronously. Invokes callback 2265 * when information has been obtained. 2266 * 2267 * The callback must have the following signature: 2268 * @code 2269 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems) 2270 * @endcode 2271 * 2272 * @param sensorsAsyncResp Pointer to object holding response data. 2273 * @param inventoryItems D-Bus inventory items associated with sensors. 2274 * @param callback Callback to invoke when data has been obtained. 2275 */ 2276 template <typename Callback> 2277 void getPowerSupplyAttributes( 2278 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 2279 std::shared_ptr<std::vector<InventoryItem>> inventoryItems, 2280 Callback&& callback) 2281 { 2282 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes enter"; 2283 2284 // Only need the power supply attributes when the Power Schema 2285 if (sensorsAsyncResp->chassisSubNode != sensors::node::power) 2286 { 2287 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit since not Power"; 2288 callback(inventoryItems); 2289 return; 2290 } 2291 2292 const std::array<std::string, 1> interfaces = { 2293 "xyz.openbmc_project.Control.PowerSupplyAttributes"}; 2294 2295 // Response handler for parsing output from GetSubTree 2296 auto respHandler = [callback{std::move(callback)}, sensorsAsyncResp, 2297 inventoryItems](const boost::system::error_code ec, 2298 const GetSubTreeType& subtree) { 2299 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler enter"; 2300 if (ec) 2301 { 2302 messages::internalError(sensorsAsyncResp->asyncResp->res); 2303 BMCWEB_LOG_ERROR 2304 << "getPowerSupplyAttributes respHandler DBus error " << ec; 2305 return; 2306 } 2307 if (subtree.size() == 0) 2308 { 2309 BMCWEB_LOG_DEBUG << "Can't find Power Supply Attributes!"; 2310 callback(inventoryItems); 2311 return; 2312 } 2313 2314 // Currently we only support 1 power supply attribute, use this for 2315 // all the power supplies. Build map of object path to connection. 2316 // Assume just 1 connection and 1 path for now. 2317 boost::container::flat_map<std::string, std::string> 2318 psAttributesConnections; 2319 2320 if (subtree[0].first.empty() || subtree[0].second.empty()) 2321 { 2322 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!"; 2323 callback(inventoryItems); 2324 return; 2325 } 2326 2327 const std::string& psAttributesPath = subtree[0].first; 2328 const std::string& connection = subtree[0].second.begin()->first; 2329 2330 if (connection.empty()) 2331 { 2332 BMCWEB_LOG_DEBUG << "Power Supply Attributes mapper error!"; 2333 callback(inventoryItems); 2334 return; 2335 } 2336 2337 psAttributesConnections[psAttributesPath] = connection; 2338 BMCWEB_LOG_DEBUG << "Added mapping " << psAttributesPath << " -> " 2339 << connection; 2340 2341 getPowerSupplyAttributesData(sensorsAsyncResp, inventoryItems, 2342 psAttributesConnections, 2343 std::move(callback)); 2344 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes respHandler exit"; 2345 }; 2346 // Make call to ObjectMapper to find the PowerSupplyAttributes service 2347 crow::connections::systemBus->async_method_call( 2348 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 2349 "/xyz/openbmc_project/object_mapper", 2350 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 2351 "/xyz/openbmc_project", 0, interfaces); 2352 BMCWEB_LOG_DEBUG << "getPowerSupplyAttributes exit"; 2353 } 2354 2355 /** 2356 * @brief Gets inventory items associated with sensors. 2357 * 2358 * Finds the inventory items that are associated with the specified sensors. 2359 * Then gets D-Bus data for the inventory items, such as presence and VPD. 2360 * 2361 * This data is later used to provide sensor property values in the JSON 2362 * response. 2363 * 2364 * Finds the inventory items asynchronously. Invokes callback when the 2365 * inventory items have been obtained. 2366 * 2367 * The callback must have the following signature: 2368 * @code 2369 * callback(std::shared_ptr<std::vector<InventoryItem>> inventoryItems) 2370 * @endcode 2371 * 2372 * @param sensorsAsyncResp Pointer to object holding response data. 2373 * @param sensorNames All sensors within the current chassis. 2374 * @param objectMgrPaths Mappings from connection name to DBus object path that 2375 * implements ObjectManager. 2376 * @param callback Callback to invoke when inventory items have been obtained. 2377 */ 2378 template <typename Callback> 2379 static void getInventoryItems( 2380 std::shared_ptr<SensorsAsyncResp> sensorsAsyncResp, 2381 const std::shared_ptr<boost::container::flat_set<std::string>> sensorNames, 2382 std::shared_ptr<boost::container::flat_map<std::string, std::string>> 2383 objectMgrPaths, 2384 Callback&& callback) 2385 { 2386 BMCWEB_LOG_DEBUG << "getInventoryItems enter"; 2387 auto getInventoryItemAssociationsCb = 2388 [sensorsAsyncResp, objectMgrPaths, callback{std::move(callback)}]( 2389 std::shared_ptr<std::vector<InventoryItem>> inventoryItems) { 2390 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb enter"; 2391 auto getInventoryItemsConnectionsCb = 2392 [sensorsAsyncResp, inventoryItems, objectMgrPaths, 2393 callback{std::move(callback)}]( 2394 std::shared_ptr<boost::container::flat_set<std::string>> 2395 invConnections) { 2396 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb enter"; 2397 auto getInventoryItemsDataCb = 2398 [sensorsAsyncResp, inventoryItems, 2399 callback{std::move(callback)}]() { 2400 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb enter"; 2401 2402 auto getInventoryLedsCb = [sensorsAsyncResp, 2403 inventoryItems, 2404 callback{std::move( 2405 callback)}]() { 2406 BMCWEB_LOG_DEBUG << "getInventoryLedsCb enter"; 2407 // Find Power Supply Attributes and get the data 2408 getPowerSupplyAttributes(sensorsAsyncResp, 2409 inventoryItems, 2410 std::move(callback)); 2411 BMCWEB_LOG_DEBUG << "getInventoryLedsCb exit"; 2412 }; 2413 2414 // Find led connections and get the data 2415 getInventoryLeds(sensorsAsyncResp, inventoryItems, 2416 std::move(getInventoryLedsCb)); 2417 BMCWEB_LOG_DEBUG << "getInventoryItemsDataCb exit"; 2418 }; 2419 2420 // Get inventory item data from connections 2421 getInventoryItemsData(sensorsAsyncResp, inventoryItems, 2422 invConnections, objectMgrPaths, 2423 std::move(getInventoryItemsDataCb)); 2424 BMCWEB_LOG_DEBUG << "getInventoryItemsConnectionsCb exit"; 2425 }; 2426 2427 // Get connections that provide inventory item data 2428 getInventoryItemsConnections( 2429 sensorsAsyncResp, inventoryItems, 2430 std::move(getInventoryItemsConnectionsCb)); 2431 BMCWEB_LOG_DEBUG << "getInventoryItemAssociationsCb exit"; 2432 }; 2433 2434 // Get associations from sensors to inventory items 2435 getInventoryItemAssociations(sensorsAsyncResp, sensorNames, objectMgrPaths, 2436 std::move(getInventoryItemAssociationsCb)); 2437 BMCWEB_LOG_DEBUG << "getInventoryItems exit"; 2438 } 2439 2440 /** 2441 * @brief Returns JSON PowerSupply object for the specified inventory item. 2442 * 2443 * Searches for a JSON PowerSupply object that matches the specified inventory 2444 * item. If one is not found, a new PowerSupply object is added to the JSON 2445 * array. 2446 * 2447 * Multiple sensors are often associated with one power supply inventory item. 2448 * As a result, multiple sensor values are stored in one JSON PowerSupply 2449 * object. 2450 * 2451 * @param powerSupplyArray JSON array containing Redfish PowerSupply objects. 2452 * @param inventoryItem Inventory item for the power supply. 2453 * @param chassisId Chassis that contains the power supply. 2454 * @return JSON PowerSupply object for the specified inventory item. 2455 */ 2456 inline nlohmann::json& getPowerSupply(nlohmann::json& powerSupplyArray, 2457 const InventoryItem& inventoryItem, 2458 const std::string& chassisId) 2459 { 2460 // Check if matching PowerSupply object already exists in JSON array 2461 for (nlohmann::json& powerSupply : powerSupplyArray) 2462 { 2463 if (powerSupply["MemberId"] == inventoryItem.name) 2464 { 2465 return powerSupply; 2466 } 2467 } 2468 2469 // Add new PowerSupply object to JSON array 2470 powerSupplyArray.push_back({}); 2471 nlohmann::json& powerSupply = powerSupplyArray.back(); 2472 powerSupply["@odata.id"] = 2473 "/redfish/v1/Chassis/" + chassisId + "/Power#/PowerSupplies/"; 2474 powerSupply["MemberId"] = inventoryItem.name; 2475 powerSupply["Name"] = boost::replace_all_copy(inventoryItem.name, "_", " "); 2476 powerSupply["Manufacturer"] = inventoryItem.manufacturer; 2477 powerSupply["Model"] = inventoryItem.model; 2478 powerSupply["PartNumber"] = inventoryItem.partNumber; 2479 powerSupply["SerialNumber"] = inventoryItem.serialNumber; 2480 setLedState(powerSupply, &inventoryItem); 2481 2482 if (inventoryItem.powerSupplyEfficiencyPercent >= 0) 2483 { 2484 powerSupply["EfficiencyPercent"] = 2485 inventoryItem.powerSupplyEfficiencyPercent; 2486 } 2487 2488 powerSupply["Status"]["State"] = getState(&inventoryItem); 2489 const char* health = inventoryItem.isFunctional ? "OK" : "Critical"; 2490 powerSupply["Status"]["Health"] = health; 2491 2492 return powerSupply; 2493 } 2494 2495 /** 2496 * @brief Gets the values of the specified sensors. 2497 * 2498 * Stores the results as JSON in the SensorsAsyncResp. 2499 * 2500 * Gets the sensor values asynchronously. Stores the results later when the 2501 * information has been obtained. 2502 * 2503 * The sensorNames set contains all requested sensors for the current chassis. 2504 * 2505 * To minimize the number of DBus calls, the DBus method 2506 * org.freedesktop.DBus.ObjectManager.GetManagedObjects() is used to get the 2507 * values of all sensors provided by a connection (service). 2508 * 2509 * The connections set contains all the connections that provide sensor values. 2510 * 2511 * The objectMgrPaths map contains mappings from a connection name to the 2512 * corresponding DBus object path that implements ObjectManager. 2513 * 2514 * The InventoryItem vector contains D-Bus inventory items associated with the 2515 * sensors. Inventory item data is needed for some Redfish sensor properties. 2516 * 2517 * @param SensorsAsyncResp Pointer to object holding response data. 2518 * @param sensorNames All requested sensors within the current chassis. 2519 * @param connections Connections that provide sensor values. 2520 * @param objectMgrPaths Mappings from connection name to DBus object path that 2521 * implements ObjectManager. 2522 * @param inventoryItems Inventory items associated with the sensors. 2523 */ 2524 inline void getSensorData( 2525 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 2526 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames, 2527 const boost::container::flat_set<std::string>& connections, 2528 const std::shared_ptr<boost::container::flat_map<std::string, std::string>>& 2529 objectMgrPaths, 2530 const std::shared_ptr<std::vector<InventoryItem>>& inventoryItems) 2531 { 2532 BMCWEB_LOG_DEBUG << "getSensorData enter"; 2533 // Get managed objects from all services exposing sensors 2534 for (const std::string& connection : connections) 2535 { 2536 // Response handler to process managed objects 2537 auto getManagedObjectsCb = [sensorsAsyncResp, sensorNames, 2538 inventoryItems]( 2539 const boost::system::error_code ec, 2540 ManagedObjectsVectorType& resp) { 2541 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; 2542 if (ec) 2543 { 2544 BMCWEB_LOG_ERROR << "getManagedObjectsCb DBUS error: " << ec; 2545 messages::internalError(sensorsAsyncResp->asyncResp->res); 2546 return; 2547 } 2548 // Go through all objects and update response with sensor data 2549 for (const auto& objDictEntry : resp) 2550 { 2551 const std::string& objPath = 2552 static_cast<const std::string&>(objDictEntry.first); 2553 BMCWEB_LOG_DEBUG << "getManagedObjectsCb parsing object " 2554 << objPath; 2555 2556 std::vector<std::string> split; 2557 // Reserve space for 2558 // /xyz/openbmc_project/sensors/<name>/<subname> 2559 split.reserve(6); 2560 boost::algorithm::split(split, objPath, boost::is_any_of("/")); 2561 if (split.size() < 6) 2562 { 2563 BMCWEB_LOG_ERROR << "Got path that isn't long enough " 2564 << objPath; 2565 continue; 2566 } 2567 // These indexes aren't intuitive, as boost::split puts an empty 2568 // string at the beginning 2569 const std::string& sensorType = split[4]; 2570 const std::string& sensorName = split[5]; 2571 BMCWEB_LOG_DEBUG << "sensorName " << sensorName 2572 << " sensorType " << sensorType; 2573 if (sensorNames->find(objPath) == sensorNames->end()) 2574 { 2575 BMCWEB_LOG_ERROR << sensorName << " not in sensor list "; 2576 continue; 2577 } 2578 2579 // Find inventory item (if any) associated with sensor 2580 InventoryItem* inventoryItem = 2581 findInventoryItemForSensor(inventoryItems, objPath); 2582 2583 const std::string& sensorSchema = 2584 sensorsAsyncResp->chassisSubNode; 2585 2586 nlohmann::json* sensorJson = nullptr; 2587 2588 if (sensorSchema == sensors::node::sensors) 2589 { 2590 sensorsAsyncResp->asyncResp->res.jsonValue["@odata.id"] = 2591 "/redfish/v1/Chassis/" + sensorsAsyncResp->chassisId + 2592 "/" + sensorsAsyncResp->chassisSubNode + "/" + 2593 sensorName; 2594 sensorJson = &(sensorsAsyncResp->asyncResp->res.jsonValue); 2595 } 2596 else 2597 { 2598 std::string fieldName; 2599 if (sensorType == "temperature") 2600 { 2601 fieldName = "Temperatures"; 2602 } 2603 else if (sensorType == "fan" || sensorType == "fan_tach" || 2604 sensorType == "fan_pwm") 2605 { 2606 fieldName = "Fans"; 2607 } 2608 else if (sensorType == "voltage") 2609 { 2610 fieldName = "Voltages"; 2611 } 2612 else if (sensorType == "power") 2613 { 2614 if (!sensorName.compare("total_power")) 2615 { 2616 fieldName = "PowerControl"; 2617 } 2618 else if ((inventoryItem != nullptr) && 2619 (inventoryItem->isPowerSupply)) 2620 { 2621 fieldName = "PowerSupplies"; 2622 } 2623 else 2624 { 2625 // Other power sensors are in SensorCollection 2626 continue; 2627 } 2628 } 2629 else 2630 { 2631 BMCWEB_LOG_ERROR << "Unsure how to handle sensorType " 2632 << sensorType; 2633 continue; 2634 } 2635 2636 nlohmann::json& tempArray = 2637 sensorsAsyncResp->asyncResp->res.jsonValue[fieldName]; 2638 if (fieldName == "PowerControl") 2639 { 2640 if (tempArray.empty()) 2641 { 2642 // Put multiple "sensors" into a single 2643 // PowerControl. Follows MemberId naming and 2644 // naming in power.hpp. 2645 tempArray.push_back( 2646 {{"@odata.id", 2647 "/redfish/v1/Chassis/" + 2648 sensorsAsyncResp->chassisId + "/" + 2649 sensorsAsyncResp->chassisSubNode + "#/" + 2650 fieldName + "/0"}}); 2651 } 2652 sensorJson = &(tempArray.back()); 2653 } 2654 else if (fieldName == "PowerSupplies") 2655 { 2656 if (inventoryItem != nullptr) 2657 { 2658 sensorJson = 2659 &(getPowerSupply(tempArray, *inventoryItem, 2660 sensorsAsyncResp->chassisId)); 2661 } 2662 } 2663 else 2664 { 2665 tempArray.push_back( 2666 {{"@odata.id", 2667 "/redfish/v1/Chassis/" + 2668 sensorsAsyncResp->chassisId + "/" + 2669 sensorsAsyncResp->chassisSubNode + "#/" + 2670 fieldName + "/"}}); 2671 sensorJson = &(tempArray.back()); 2672 } 2673 } 2674 2675 if (sensorJson != nullptr) 2676 { 2677 objectInterfacesToJson( 2678 sensorName, sensorType, sensorsAsyncResp, 2679 objDictEntry.second, *sensorJson, inventoryItem); 2680 } 2681 } 2682 if (sensorsAsyncResp.use_count() == 1) 2683 { 2684 sortJSONResponse(sensorsAsyncResp); 2685 if (sensorsAsyncResp->chassisSubNode == sensors::node::thermal) 2686 { 2687 populateFanRedundancy(sensorsAsyncResp); 2688 } 2689 } 2690 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; 2691 }; 2692 2693 // Find DBus object path that implements ObjectManager for the current 2694 // connection. If no mapping found, default to "/". 2695 auto iter = objectMgrPaths->find(connection); 2696 const std::string& objectMgrPath = 2697 (iter != objectMgrPaths->end()) ? iter->second : "/"; 2698 BMCWEB_LOG_DEBUG << "ObjectManager path for " << connection << " is " 2699 << objectMgrPath; 2700 2701 crow::connections::systemBus->async_method_call( 2702 getManagedObjectsCb, connection, objectMgrPath, 2703 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 2704 } 2705 BMCWEB_LOG_DEBUG << "getSensorData exit"; 2706 } 2707 2708 inline void processSensorList( 2709 const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp, 2710 const std::shared_ptr<boost::container::flat_set<std::string>>& sensorNames) 2711 { 2712 auto getConnectionCb = 2713 [sensorsAsyncResp, sensorNames]( 2714 const boost::container::flat_set<std::string>& connections) { 2715 BMCWEB_LOG_DEBUG << "getConnectionCb enter"; 2716 auto getObjectManagerPathsCb = 2717 [sensorsAsyncResp, sensorNames, 2718 connections](const std::shared_ptr<boost::container::flat_map< 2719 std::string, std::string>>& objectMgrPaths) { 2720 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb enter"; 2721 auto getInventoryItemsCb = 2722 [sensorsAsyncResp, sensorNames, connections, 2723 objectMgrPaths]( 2724 const std::shared_ptr<std::vector<InventoryItem>>& 2725 inventoryItems) { 2726 BMCWEB_LOG_DEBUG << "getInventoryItemsCb enter"; 2727 // Get sensor data and store results in JSON 2728 getSensorData(sensorsAsyncResp, sensorNames, 2729 connections, objectMgrPaths, 2730 inventoryItems); 2731 BMCWEB_LOG_DEBUG << "getInventoryItemsCb exit"; 2732 }; 2733 2734 // Get inventory items associated with sensors 2735 getInventoryItems(sensorsAsyncResp, sensorNames, 2736 objectMgrPaths, 2737 std::move(getInventoryItemsCb)); 2738 2739 BMCWEB_LOG_DEBUG << "getObjectManagerPathsCb exit"; 2740 }; 2741 2742 // Get mapping from connection names to the DBus object 2743 // paths that implement the ObjectManager interface 2744 getObjectManagerPaths(sensorsAsyncResp, 2745 std::move(getObjectManagerPathsCb)); 2746 BMCWEB_LOG_DEBUG << "getConnectionCb exit"; 2747 }; 2748 2749 // Get set of connections that provide sensor values 2750 getConnections(sensorsAsyncResp, sensorNames, std::move(getConnectionCb)); 2751 } 2752 2753 /** 2754 * @brief Entry point for retrieving sensors data related to requested 2755 * chassis. 2756 * @param SensorsAsyncResp Pointer to object holding response data 2757 */ 2758 inline void 2759 getChassisData(const std::shared_ptr<SensorsAsyncResp>& sensorsAsyncResp) 2760 { 2761 BMCWEB_LOG_DEBUG << "getChassisData enter"; 2762 auto getChassisCb = 2763 [sensorsAsyncResp]( 2764 const std::shared_ptr<boost::container::flat_set<std::string>>& 2765 sensorNames) { 2766 BMCWEB_LOG_DEBUG << "getChassisCb enter"; 2767 processSensorList(sensorsAsyncResp, sensorNames); 2768 BMCWEB_LOG_DEBUG << "getChassisCb exit"; 2769 }; 2770 sensorsAsyncResp->asyncResp->res.jsonValue["Redundancy"] = 2771 nlohmann::json::array(); 2772 2773 // Get set of sensors in chassis 2774 getChassis(sensorsAsyncResp, std::move(getChassisCb)); 2775 BMCWEB_LOG_DEBUG << "getChassisData exit"; 2776 } 2777 2778 /** 2779 * @brief Find the requested sensorName in the list of all sensors supplied by 2780 * the chassis node 2781 * 2782 * @param sensorName The sensor name supplied in the PATCH request 2783 * @param sensorsList The list of sensors managed by the chassis node 2784 * @param sensorsModified The list of sensors that were found as a result of 2785 * repeated calls to this function 2786 */ 2787 inline bool findSensorNameUsingSensorPath( 2788 std::string_view sensorName, 2789 boost::container::flat_set<std::string>& sensorsList, 2790 boost::container::flat_set<std::string>& sensorsModified) 2791 { 2792 for (auto& chassisSensor : sensorsList) 2793 { 2794 sdbusplus::message::object_path path(chassisSensor); 2795 std::string thisSensorName = path.filename(); 2796 if (thisSensorName.empty()) 2797 { 2798 continue; 2799 } 2800 if (thisSensorName == sensorName) 2801 { 2802 sensorsModified.emplace(chassisSensor); 2803 return true; 2804 } 2805 } 2806 return false; 2807 } 2808 2809 /** 2810 * @brief Entry point for overriding sensor values of given sensor 2811 * 2812 * @param sensorAsyncResp response object 2813 * @param allCollections Collections extract from sensors' request patch info 2814 * @param chassisSubNode Chassis Node for which the query has to happen 2815 */ 2816 inline void setSensorsOverride( 2817 const std::shared_ptr<SensorsAsyncResp>& sensorAsyncResp, 2818 std::unordered_map<std::string, std::vector<nlohmann::json>>& 2819 allCollections) 2820 { 2821 BMCWEB_LOG_INFO << "setSensorsOverride for subNode" 2822 << sensorAsyncResp->chassisSubNode << "\n"; 2823 2824 const char* propertyValueName; 2825 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap; 2826 std::string memberId; 2827 double value; 2828 for (auto& collectionItems : allCollections) 2829 { 2830 if (collectionItems.first == "Temperatures") 2831 { 2832 propertyValueName = "ReadingCelsius"; 2833 } 2834 else if (collectionItems.first == "Fans") 2835 { 2836 propertyValueName = "Reading"; 2837 } 2838 else 2839 { 2840 propertyValueName = "ReadingVolts"; 2841 } 2842 for (auto& item : collectionItems.second) 2843 { 2844 if (!json_util::readJson(item, sensorAsyncResp->asyncResp->res, 2845 "MemberId", memberId, propertyValueName, 2846 value)) 2847 { 2848 return; 2849 } 2850 overrideMap.emplace(memberId, 2851 std::make_pair(value, collectionItems.first)); 2852 } 2853 } 2854 2855 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap]( 2856 const std::shared_ptr< 2857 boost::container::flat_set< 2858 std::string>>& sensorsList) { 2859 // Match sensor names in the PATCH request to those managed by the 2860 // chassis node 2861 const std::shared_ptr<boost::container::flat_set<std::string>> 2862 sensorNames = 2863 std::make_shared<boost::container::flat_set<std::string>>(); 2864 for (const auto& item : overrideMap) 2865 { 2866 const auto& sensor = item.first; 2867 if (!findSensorNameUsingSensorPath(sensor, *sensorsList, 2868 *sensorNames)) 2869 { 2870 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first; 2871 messages::resourceNotFound(sensorAsyncResp->asyncResp->res, 2872 item.second.second, item.first); 2873 return; 2874 } 2875 } 2876 // Get the connection to which the memberId belongs 2877 auto getObjectsWithConnectionCb = 2878 [sensorAsyncResp, overrideMap]( 2879 const boost::container::flat_set<std::string>& /*connections*/, 2880 const std::set<std::pair<std::string, std::string>>& 2881 objectsWithConnection) { 2882 if (objectsWithConnection.size() != overrideMap.size()) 2883 { 2884 BMCWEB_LOG_INFO 2885 << "Unable to find all objects with proper connection " 2886 << objectsWithConnection.size() << " requested " 2887 << overrideMap.size() << "\n"; 2888 messages::resourceNotFound( 2889 sensorAsyncResp->asyncResp->res, 2890 sensorAsyncResp->chassisSubNode == 2891 sensors::node::thermal 2892 ? "Temperatures" 2893 : "Voltages", 2894 "Count"); 2895 return; 2896 } 2897 for (const auto& item : objectsWithConnection) 2898 { 2899 sdbusplus::message::object_path path(item.first); 2900 std::string sensorName = path.filename(); 2901 if (sensorName.empty()) 2902 { 2903 messages::internalError( 2904 sensorAsyncResp->asyncResp->res); 2905 return; 2906 } 2907 2908 const auto& iterator = overrideMap.find(sensorName); 2909 if (iterator == overrideMap.end()) 2910 { 2911 BMCWEB_LOG_INFO << "Unable to find sensor object" 2912 << item.first << "\n"; 2913 messages::internalError( 2914 sensorAsyncResp->asyncResp->res); 2915 return; 2916 } 2917 crow::connections::systemBus->async_method_call( 2918 [sensorAsyncResp](const boost::system::error_code ec) { 2919 if (ec) 2920 { 2921 BMCWEB_LOG_DEBUG 2922 << "setOverrideValueStatus DBUS error: " 2923 << ec; 2924 messages::internalError( 2925 sensorAsyncResp->asyncResp->res); 2926 return; 2927 } 2928 }, 2929 item.second, item.first, 2930 "org.freedesktop.DBus.Properties", "Set", 2931 "xyz.openbmc_project.Sensor.Value", "Value", 2932 std::variant<double>(iterator->second.first)); 2933 } 2934 }; 2935 // Get object with connection for the given sensor name 2936 getObjectsWithConnection(sensorAsyncResp, sensorNames, 2937 std::move(getObjectsWithConnectionCb)); 2938 }; 2939 // get full sensor list for the given chassisId and cross verify the sensor. 2940 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb)); 2941 } 2942 2943 /** 2944 * @brief Retrieves mapping of Redfish URIs to sensor value property to D-Bus 2945 * path of the sensor. 2946 * 2947 * Function builds valid Redfish response for sensor query of given chassis and 2948 * node. It then builds metadata about Redfish<->D-Bus correlations and provides 2949 * it to caller in a callback. 2950 * 2951 * @param chassis Chassis for which retrieval should be performed 2952 * @param node Node (group) of sensors. See sensors::node for supported values 2953 * @param mapComplete Callback to be called with retrieval result 2954 */ 2955 inline void retrieveUriToDbusMap(const std::string& chassis, 2956 const std::string& node, 2957 SensorsAsyncResp::DataCompleteCb&& mapComplete) 2958 { 2959 auto pathIt = sensors::dbus::paths.find(node); 2960 if (pathIt == sensors::dbus::paths.end()) 2961 { 2962 BMCWEB_LOG_ERROR << "Wrong node provided : " << node; 2963 mapComplete(boost::beast::http::status::bad_request, {}); 2964 return; 2965 } 2966 2967 auto res = std::make_shared<crow::Response>(); 2968 auto asyncResp = std::make_shared<bmcweb::AsyncResp>(*res); 2969 auto callback = 2970 [res, asyncResp, mapCompleteCb{std::move(mapComplete)}]( 2971 const boost::beast::http::status status, 2972 const boost::container::flat_map<std::string, std::string>& 2973 uriToDbus) { mapCompleteCb(status, uriToDbus); }; 2974 2975 auto resp = std::make_shared<SensorsAsyncResp>( 2976 asyncResp, chassis, pathIt->second, node, std::move(callback)); 2977 getChassisData(resp); 2978 } 2979 2980 inline void requestRoutesSensorCollection(App& app) 2981 { 2982 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/") 2983 .privileges(redfish::privileges::getSensorCollection) 2984 .methods( 2985 boost::beast::http::verb::get)([](const crow::Request&, 2986 const std::shared_ptr< 2987 bmcweb::AsyncResp>& aResp, 2988 const std::string& chassisId) { 2989 BMCWEB_LOG_DEBUG << "SensorCollection doGet enter"; 2990 2991 std::shared_ptr<SensorsAsyncResp> asyncResp = 2992 std::make_shared<SensorsAsyncResp>( 2993 aResp, chassisId, 2994 sensors::dbus::paths.at(sensors::node::sensors), 2995 sensors::node::sensors); 2996 2997 auto getChassisCb = 2998 [asyncResp]( 2999 const std::shared_ptr< 3000 boost::container::flat_set<std::string>>& sensorNames) { 3001 BMCWEB_LOG_DEBUG << "getChassisCb enter"; 3002 3003 nlohmann::json& entriesArray = 3004 asyncResp->asyncResp->res.jsonValue["Members"]; 3005 for (auto& sensor : *sensorNames) 3006 { 3007 BMCWEB_LOG_DEBUG << "Adding sensor: " << sensor; 3008 3009 sdbusplus::message::object_path path(sensor); 3010 std::string sensorName = path.filename(); 3011 if (sensorName.empty()) 3012 { 3013 BMCWEB_LOG_ERROR << "Invalid sensor path: " 3014 << sensor; 3015 messages::internalError(asyncResp->asyncResp->res); 3016 return; 3017 } 3018 entriesArray.push_back( 3019 {{"@odata.id", "/redfish/v1/Chassis/" + 3020 asyncResp->chassisId + "/" + 3021 asyncResp->chassisSubNode + "/" + 3022 sensorName}}); 3023 } 3024 3025 asyncResp->asyncResp->res.jsonValue["Members@odata.count"] = 3026 entriesArray.size(); 3027 BMCWEB_LOG_DEBUG << "getChassisCb exit"; 3028 }; 3029 3030 // Get set of sensors in chassis 3031 getChassis(asyncResp, std::move(getChassisCb)); 3032 BMCWEB_LOG_DEBUG << "SensorCollection doGet exit"; 3033 }); 3034 } 3035 3036 inline void requestRoutesSensor(App& app) 3037 { 3038 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Sensors/<str>/") 3039 .privileges(redfish::privileges::getSensor) 3040 .methods( 3041 boost::beast::http::verb::get)([](const crow::Request&, 3042 const std::shared_ptr< 3043 bmcweb::AsyncResp>& aResp, 3044 const std::string& chassisId, 3045 const std::string& sensorName) { 3046 BMCWEB_LOG_DEBUG << "Sensor doGet enter"; 3047 std::shared_ptr<SensorsAsyncResp> asyncResp = 3048 std::make_shared<SensorsAsyncResp>(aResp, chassisId, 3049 std::vector<const char*>(), 3050 sensors::node::sensors); 3051 3052 const std::array<const char*, 1> interfaces = { 3053 "xyz.openbmc_project.Sensor.Value"}; 3054 3055 // Get a list of all of the sensors that implement Sensor.Value 3056 // and get the path and service name associated with the sensor 3057 crow::connections::systemBus->async_method_call( 3058 [asyncResp, sensorName](const boost::system::error_code ec, 3059 const GetSubTreeType& subtree) { 3060 BMCWEB_LOG_DEBUG << "respHandler1 enter"; 3061 if (ec) 3062 { 3063 messages::internalError(asyncResp->asyncResp->res); 3064 BMCWEB_LOG_ERROR 3065 << "Sensor getSensorPaths resp_handler: " 3066 << "Dbus error " << ec; 3067 return; 3068 } 3069 3070 GetSubTreeType::const_iterator it = std::find_if( 3071 subtree.begin(), subtree.end(), 3072 [sensorName]( 3073 const std::pair< 3074 std::string, 3075 std::vector<std::pair< 3076 std::string, std::vector<std::string>>>>& 3077 object) { 3078 sdbusplus::message::object_path path(object.first); 3079 std::string name = path.filename(); 3080 if (name.empty()) 3081 { 3082 BMCWEB_LOG_ERROR << "Invalid sensor path: " 3083 << object.first; 3084 return false; 3085 } 3086 3087 return name == sensorName; 3088 }); 3089 3090 if (it == subtree.end()) 3091 { 3092 BMCWEB_LOG_ERROR << "Could not find path for sensor: " 3093 << sensorName; 3094 messages::resourceNotFound(asyncResp->asyncResp->res, 3095 "Sensor", sensorName); 3096 return; 3097 } 3098 std::string_view sensorPath = (*it).first; 3099 BMCWEB_LOG_DEBUG << "Found sensor path for sensor '" 3100 << sensorName << "': " << sensorPath; 3101 3102 const std::shared_ptr< 3103 boost::container::flat_set<std::string>> 3104 sensorList = std::make_shared< 3105 boost::container::flat_set<std::string>>(); 3106 3107 sensorList->emplace(sensorPath); 3108 processSensorList(asyncResp, sensorList); 3109 BMCWEB_LOG_DEBUG << "respHandler1 exit"; 3110 }, 3111 "xyz.openbmc_project.ObjectMapper", 3112 "/xyz/openbmc_project/object_mapper", 3113 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 3114 "/xyz/openbmc_project/sensors", 2, interfaces); 3115 }); 3116 } 3117 3118 } // namespace redfish 3119