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