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