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