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