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