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