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