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 constexpr const char* dbusSensorPrefix = "/xyz/openbmc_project/sensors/"; 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 = std::variant<int64_t, double>; 38 39 using ManagedObjectsVectorType = std::vector<std::pair< 40 sdbusplus::message::object_path, 41 boost::container::flat_map< 42 std::string, boost::container::flat_map<std::string, SensorVariant>>>>; 43 44 /** 45 * SensorsAsyncResp 46 * Gathers data needed for response processing after async calls are done 47 */ 48 class SensorsAsyncResp 49 { 50 public: 51 SensorsAsyncResp(crow::Response& response, const std::string& chassisId, 52 const std::initializer_list<const char*> types, 53 const std::string& subNode) : 54 res(response), 55 chassisId(chassisId), types(types), chassisSubNode(subNode) 56 { 57 res.jsonValue["@odata.id"] = 58 "/redfish/v1/Chassis/" + chassisId + "/" + subNode; 59 } 60 61 ~SensorsAsyncResp() 62 { 63 if (res.result() == boost::beast::http::status::internal_server_error) 64 { 65 // Reset the json object to clear out any data that made it in 66 // before the error happened todo(ed) handle error condition with 67 // proper code 68 res.jsonValue = nlohmann::json::object(); 69 } 70 res.end(); 71 } 72 73 crow::Response& res; 74 std::string chassisId{}; 75 const std::vector<const char*> types; 76 std::string chassisSubNode{}; 77 }; 78 79 /** 80 * @brief Get objects with connection necessary for sensors 81 * @param SensorsAsyncResp Pointer to object holding response data 82 * @param sensorNames Sensors retrieved from chassis 83 * @param callback Callback for processing gathered connections 84 */ 85 template <typename Callback> 86 void getObjectsWithConnection( 87 std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, 88 const boost::container::flat_set<std::string>& sensorNames, 89 Callback&& callback) 90 { 91 BMCWEB_LOG_DEBUG << "getObjectsWithConnection enter"; 92 const std::string path = "/xyz/openbmc_project/sensors"; 93 const std::array<std::string, 1> interfaces = { 94 "xyz.openbmc_project.Sensor.Value"}; 95 96 // Response handler for parsing objects subtree 97 auto respHandler = [callback{std::move(callback)}, SensorsAsyncResp, 98 sensorNames](const boost::system::error_code ec, 99 const GetSubTreeType& subtree) { 100 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler enter"; 101 if (ec) 102 { 103 messages::internalError(SensorsAsyncResp->res); 104 BMCWEB_LOG_ERROR 105 << "getObjectsWithConnection resp_handler: Dbus error " << ec; 106 return; 107 } 108 109 BMCWEB_LOG_DEBUG << "Found " << subtree.size() << " subtrees"; 110 111 // Make unique list of connections only for requested sensor types and 112 // found in the chassis 113 boost::container::flat_set<std::string> connections; 114 std::set<std::pair<std::string, std::string>> objectsWithConnection; 115 // Intrinsic to avoid malloc. Most systems will have < 8 sensor 116 // producers 117 connections.reserve(8); 118 119 BMCWEB_LOG_DEBUG << "sensorNames list count: " << sensorNames.size(); 120 for (const std::string& tsensor : sensorNames) 121 { 122 BMCWEB_LOG_DEBUG << "Sensor to find: " << tsensor; 123 } 124 125 for (const std::pair< 126 std::string, 127 std::vector<std::pair<std::string, std::vector<std::string>>>>& 128 object : subtree) 129 { 130 for (const char* type : SensorsAsyncResp->types) 131 { 132 if (boost::starts_with(object.first, type)) 133 { 134 auto lastPos = object.first.rfind('/'); 135 if (lastPos != std::string::npos) 136 { 137 std::string sensorName = 138 object.first.substr(lastPos + 1); 139 140 if (sensorNames.find(sensorName) != sensorNames.end()) 141 { 142 // For each Connection name 143 for (const std::pair<std::string, 144 std::vector<std::string>>& 145 objData : object.second) 146 { 147 BMCWEB_LOG_DEBUG << "Adding connection: " 148 << objData.first; 149 connections.insert(objData.first); 150 objectsWithConnection.insert(std::make_pair( 151 object.first, objData.first)); 152 } 153 } 154 } 155 break; 156 } 157 } 158 } 159 BMCWEB_LOG_DEBUG << "Found " << connections.size() << " connections"; 160 callback(std::move(connections), std::move(objectsWithConnection)); 161 BMCWEB_LOG_DEBUG << "getObjectsWithConnection resp_handler exit"; 162 }; 163 // Make call to ObjectMapper to find all sensors objects 164 crow::connections::systemBus->async_method_call( 165 std::move(respHandler), "xyz.openbmc_project.ObjectMapper", 166 "/xyz/openbmc_project/object_mapper", 167 "xyz.openbmc_project.ObjectMapper", "GetSubTree", path, 2, interfaces); 168 BMCWEB_LOG_DEBUG << "getObjectsWithConnection exit"; 169 } 170 171 /** 172 * @brief Create connections necessary for sensors 173 * @param SensorsAsyncResp Pointer to object holding response data 174 * @param sensorNames Sensors retrieved from chassis 175 * @param callback Callback for processing gathered connections 176 */ 177 template <typename Callback> 178 void getConnections(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, 179 const boost::container::flat_set<std::string>& sensorNames, 180 Callback&& callback) 181 { 182 auto objectsWithConnectionCb = 183 [callback](const boost::container::flat_set<std::string>& connections, 184 const std::set<std::pair<std::string, std::string>>& 185 objectsWithConnection) { 186 callback(std::move(connections)); 187 }; 188 getObjectsWithConnection(SensorsAsyncResp, sensorNames, 189 std::move(objectsWithConnectionCb)); 190 } 191 192 /** 193 * @brief Retrieves requested chassis sensors and redundancy data from DBus . 194 * @param SensorsAsyncResp Pointer to object holding response data 195 * @param callback Callback for next step in gathered sensor processing 196 */ 197 template <typename Callback> 198 void getChassis(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp, 199 Callback&& callback) 200 { 201 BMCWEB_LOG_DEBUG << "getChassis enter"; 202 // Process response from EntityManager and extract chassis data 203 auto respHandler = [callback{std::move(callback)}, 204 SensorsAsyncResp](const boost::system::error_code ec, 205 ManagedObjectsVectorType& resp) { 206 BMCWEB_LOG_DEBUG << "getChassis respHandler enter"; 207 if (ec) 208 { 209 BMCWEB_LOG_ERROR << "getChassis respHandler DBUS error: " << ec; 210 messages::internalError(SensorsAsyncResp->res); 211 return; 212 } 213 boost::container::flat_set<std::string> sensorNames; 214 215 // SensorsAsyncResp->chassisId 216 bool foundChassis = false; 217 std::vector<std::string> split; 218 // Reserve space for 219 // /xyz/openbmc_project/inventory/<name>/<subname> + 3 subnames 220 split.reserve(8); 221 222 for (const auto& objDictEntry : resp) 223 { 224 const std::string& objectPath = 225 static_cast<const std::string&>(objDictEntry.first); 226 boost::algorithm::split(split, objectPath, boost::is_any_of("/")); 227 if (split.size() < 2) 228 { 229 BMCWEB_LOG_ERROR << "Got path that isn't long enough " 230 << objectPath; 231 split.clear(); 232 continue; 233 } 234 const std::string& sensorName = split.end()[-1]; 235 const std::string& chassisName = split.end()[-2]; 236 237 if (chassisName != SensorsAsyncResp->chassisId) 238 { 239 split.clear(); 240 continue; 241 } 242 BMCWEB_LOG_DEBUG << "New sensor: " << sensorName; 243 foundChassis = true; 244 sensorNames.emplace(sensorName); 245 split.clear(); 246 }; 247 BMCWEB_LOG_DEBUG << "Found " << sensorNames.size() << " Sensor names"; 248 249 if (!foundChassis) 250 { 251 BMCWEB_LOG_INFO << "Unable to find chassis named " 252 << SensorsAsyncResp->chassisId; 253 messages::resourceNotFound(SensorsAsyncResp->res, "Chassis", 254 SensorsAsyncResp->chassisId); 255 } 256 else 257 { 258 callback(sensorNames); 259 } 260 BMCWEB_LOG_DEBUG << "getChassis respHandler exit"; 261 }; 262 263 // Make call to EntityManager to find all chassis objects 264 crow::connections::systemBus->async_method_call( 265 respHandler, "xyz.openbmc_project.EntityManager", "/", 266 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 267 BMCWEB_LOG_DEBUG << "getChassis exit"; 268 } 269 270 /** 271 * @brief Builds a json sensor representation of a sensor. 272 * @param sensorName The name of the sensor to be built 273 * @param sensorType The type (temperature, fan_tach, etc) of the sensor to 274 * build 275 * @param interfacesDict A dictionary of the interfaces and properties of said 276 * interfaces to be built from 277 * @param sensor_json The json object to fill 278 */ 279 void objectInterfacesToJson( 280 const std::string& sensorName, const std::string& sensorType, 281 const boost::container::flat_map< 282 std::string, boost::container::flat_map<std::string, SensorVariant>>& 283 interfacesDict, 284 nlohmann::json& sensor_json) 285 { 286 // We need a value interface before we can do anything with it 287 auto valueIt = interfacesDict.find("xyz.openbmc_project.Sensor.Value"); 288 if (valueIt == interfacesDict.end()) 289 { 290 BMCWEB_LOG_ERROR << "Sensor doesn't have a value interface"; 291 return; 292 } 293 294 // Assume values exist as is (10^0 == 1) if no scale exists 295 int64_t scaleMultiplier = 0; 296 297 auto scaleIt = valueIt->second.find("Scale"); 298 // If a scale exists, pull value as int64, and use the scaling. 299 if (scaleIt != valueIt->second.end()) 300 { 301 const int64_t* int64Value = std::get_if<int64_t>(&scaleIt->second); 302 if (int64Value != nullptr) 303 { 304 scaleMultiplier = *int64Value; 305 } 306 } 307 308 sensor_json["MemberId"] = sensorName; 309 sensor_json["Name"] = sensorName; 310 sensor_json["Status"]["State"] = "Enabled"; 311 sensor_json["Status"]["Health"] = "OK"; 312 313 // Parameter to set to override the type we get from dbus, and force it to 314 // int, regardless of what is available. This is used for schemas like fan, 315 // that require integers, not floats. 316 bool forceToInt = false; 317 318 const char* unit = "Reading"; 319 if (sensorType == "temperature") 320 { 321 unit = "ReadingCelsius"; 322 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Temperature"; 323 // TODO(ed) Documentation says that path should be type fan_tach, 324 // implementation seems to implement fan 325 } 326 else if (sensorType == "fan" || sensorType == "fan_tach") 327 { 328 unit = "Reading"; 329 sensor_json["ReadingUnits"] = "RPM"; 330 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; 331 forceToInt = true; 332 } 333 else if (sensorType == "fan_pwm") 334 { 335 unit = "Reading"; 336 sensor_json["ReadingUnits"] = "Percent"; 337 sensor_json["@odata.type"] = "#Thermal.v1_3_0.Fan"; 338 forceToInt = true; 339 } 340 else if (sensorType == "voltage") 341 { 342 unit = "ReadingVolts"; 343 sensor_json["@odata.type"] = "#Power.v1_0_0.Voltage"; 344 } 345 else if (sensorType == "power") 346 { 347 unit = "LastPowerOutputWatts"; 348 } 349 else 350 { 351 BMCWEB_LOG_ERROR << "Redfish cannot map object type for " << sensorName; 352 return; 353 } 354 // Map of dbus interface name, dbus property name and redfish property_name 355 std::vector<std::tuple<const char*, const char*, const char*>> properties; 356 properties.reserve(7); 357 358 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "Value", unit); 359 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 360 "WarningHigh", "UpperThresholdNonCritical"); 361 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Warning", 362 "WarningLow", "LowerThresholdNonCritical"); 363 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 364 "CriticalHigh", "UpperThresholdCritical"); 365 properties.emplace_back("xyz.openbmc_project.Sensor.Threshold.Critical", 366 "CriticalLow", "LowerThresholdCritical"); 367 368 // TODO Need to get UpperThresholdFatal and LowerThresholdFatal 369 370 if (sensorType == "temperature") 371 { 372 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 373 "MinReadingRangeTemp"); 374 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 375 "MaxReadingRangeTemp"); 376 } 377 else 378 { 379 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MinValue", 380 "MinReadingRange"); 381 properties.emplace_back("xyz.openbmc_project.Sensor.Value", "MaxValue", 382 "MaxReadingRange"); 383 } 384 385 for (const std::tuple<const char*, const char*, const char*>& p : 386 properties) 387 { 388 auto interfaceProperties = interfacesDict.find(std::get<0>(p)); 389 if (interfaceProperties != interfacesDict.end()) 390 { 391 auto valueIt = interfaceProperties->second.find(std::get<1>(p)); 392 if (valueIt != interfaceProperties->second.end()) 393 { 394 const SensorVariant& valueVariant = valueIt->second; 395 nlohmann::json& valueIt = sensor_json[std::get<2>(p)]; 396 // Attempt to pull the int64 directly 397 const int64_t* int64Value = std::get_if<int64_t>(&valueVariant); 398 399 const double* doubleValue = std::get_if<double>(&valueVariant); 400 double temp = 0.0; 401 if (int64Value != nullptr) 402 { 403 temp = *int64Value; 404 } 405 else if (doubleValue != nullptr) 406 { 407 temp = *doubleValue; 408 } 409 else 410 { 411 BMCWEB_LOG_ERROR 412 << "Got value interface that wasn't int or double"; 413 continue; 414 } 415 temp = temp * std::pow(10, scaleMultiplier); 416 if (forceToInt) 417 { 418 valueIt = static_cast<int64_t>(temp); 419 } 420 else 421 { 422 valueIt = temp; 423 } 424 } 425 } 426 } 427 BMCWEB_LOG_DEBUG << "Added sensor " << sensorName; 428 } 429 430 /** 431 * @brief Entry point for retrieving sensors data related to requested 432 * chassis. 433 * @param SensorsAsyncResp Pointer to object holding response data 434 */ 435 void getChassisData(std::shared_ptr<SensorsAsyncResp> SensorsAsyncResp) 436 { 437 BMCWEB_LOG_DEBUG << "getChassisData enter"; 438 auto getChassisCb = [&, SensorsAsyncResp]( 439 boost::container::flat_set<std::string>& 440 sensorNames) { 441 BMCWEB_LOG_DEBUG << "getChassisCb enter"; 442 auto getConnectionCb = 443 [&, SensorsAsyncResp, sensorNames]( 444 const boost::container::flat_set<std::string>& connections) { 445 BMCWEB_LOG_DEBUG << "getConnectionCb enter"; 446 // Get managed objects from all services exposing sensors 447 for (const std::string& connection : connections) 448 { 449 // Response handler to process managed objects 450 auto getManagedObjectsCb = 451 [&, SensorsAsyncResp, 452 sensorNames](const boost::system::error_code ec, 453 ManagedObjectsVectorType& resp) { 454 BMCWEB_LOG_DEBUG << "getManagedObjectsCb enter"; 455 if (ec) 456 { 457 BMCWEB_LOG_ERROR 458 << "getManagedObjectsCb DBUS error: " << ec; 459 messages::internalError(SensorsAsyncResp->res); 460 return; 461 } 462 // Go through all objects and update response with 463 // sensor data 464 for (const auto& objDictEntry : resp) 465 { 466 const std::string& objPath = 467 static_cast<const std::string&>( 468 objDictEntry.first); 469 BMCWEB_LOG_DEBUG 470 << "getManagedObjectsCb parsing object " 471 << objPath; 472 473 std::vector<std::string> split; 474 // Reserve space for 475 // /xyz/openbmc_project/sensors/<name>/<subname> 476 split.reserve(6); 477 boost::algorithm::split(split, objPath, 478 boost::is_any_of("/")); 479 if (split.size() < 6) 480 { 481 BMCWEB_LOG_ERROR 482 << "Got path that isn't long enough " 483 << objPath; 484 continue; 485 } 486 // These indexes aren't intuitive, as 487 // boost::split puts an empty string at the 488 // beggining 489 const std::string& sensorType = split[4]; 490 const std::string& sensorName = split[5]; 491 BMCWEB_LOG_DEBUG << "sensorName " << sensorName 492 << " sensorType " 493 << sensorType; 494 if (sensorNames.find(sensorName) == 495 sensorNames.end()) 496 { 497 BMCWEB_LOG_ERROR << sensorName 498 << " not in sensor list "; 499 continue; 500 } 501 502 const char* fieldName = nullptr; 503 if (sensorType == "temperature") 504 { 505 fieldName = "Temperatures"; 506 } 507 else if (sensorType == "fan" || 508 sensorType == "fan_tach" || 509 sensorType == "fan_pwm") 510 { 511 fieldName = "Fans"; 512 } 513 else if (sensorType == "voltage") 514 { 515 fieldName = "Voltages"; 516 } 517 else if (sensorType == "current") 518 { 519 fieldName = "PowerSupply"; 520 } 521 else if (sensorType == "power") 522 { 523 fieldName = "PowerSupply"; 524 } 525 else 526 { 527 BMCWEB_LOG_ERROR 528 << "Unsure how to handle sensorType " 529 << sensorType; 530 continue; 531 } 532 533 nlohmann::json& tempArray = 534 SensorsAsyncResp->res.jsonValue[fieldName]; 535 536 tempArray.push_back( 537 {{"@odata.id", 538 "/redfish/v1/Chassis/" + 539 SensorsAsyncResp->chassisId + "/" + 540 SensorsAsyncResp->chassisSubNode + 541 "#/" + fieldName + "/" + 542 std::to_string(tempArray.size())}}); 543 nlohmann::json& sensorJson = tempArray.back(); 544 545 objectInterfacesToJson(sensorName, sensorType, 546 objDictEntry.second, 547 sensorJson); 548 } 549 BMCWEB_LOG_DEBUG << "getManagedObjectsCb exit"; 550 }; 551 crow::connections::systemBus->async_method_call( 552 getManagedObjectsCb, connection, "/", 553 "org.freedesktop.DBus.ObjectManager", 554 "GetManagedObjects"); 555 }; 556 BMCWEB_LOG_DEBUG << "getConnectionCb exit"; 557 }; 558 // get connections and then pass it to get sensors 559 getConnections(SensorsAsyncResp, sensorNames, 560 std::move(getConnectionCb)); 561 BMCWEB_LOG_DEBUG << "getChassisCb exit"; 562 }; 563 564 // get chassis information related to sensors 565 getChassis(SensorsAsyncResp, std::move(getChassisCb)); 566 BMCWEB_LOG_DEBUG << "getChassisData exit"; 567 }; 568 569 /** 570 * @brief Entry point for overriding sensor values of given sensor 571 * 572 * @param res response object 573 * @param req request object 574 * @param params parameter passed for CRUD 575 * @param typeList TypeList of sensors for the resource queried 576 * @param chassisSubNode Chassis Node for which the query has to happen 577 */ 578 void setSensorOverride(crow::Response& res, const crow::Request& req, 579 const std::vector<std::string>& params, 580 const std::initializer_list<const char*> typeList, 581 const std::string& chassisSubNode) 582 { 583 584 // TODO: Need to figure out dynamic way to restrict patch (Set Sensor 585 // override) based on another d-bus announcement to be more generic. 586 if (params.size() != 1) 587 { 588 messages::internalError(res); 589 res.end(); 590 return; 591 } 592 593 std::unordered_map<std::string, std::vector<nlohmann::json>> allCollections; 594 std::optional<std::vector<nlohmann::json>> temperatureCollections; 595 std::optional<std::vector<nlohmann::json>> fanCollections; 596 std::vector<nlohmann::json> voltageCollections; 597 BMCWEB_LOG_INFO << "setSensorOverride for subNode" << chassisSubNode 598 << "\n"; 599 600 if (chassisSubNode == "Thermal") 601 { 602 if (!json_util::readJson(req, res, "Temperatures", 603 temperatureCollections, "Fans", 604 fanCollections)) 605 { 606 return; 607 } 608 if (!temperatureCollections && !fanCollections) 609 { 610 messages::resourceNotFound(res, "Thermal", 611 "Temperatures / Voltages"); 612 res.end(); 613 return; 614 } 615 if (temperatureCollections) 616 { 617 allCollections.emplace("Temperatures", 618 *std::move(temperatureCollections)); 619 } 620 if (fanCollections) 621 { 622 allCollections.emplace("Fans", *std::move(fanCollections)); 623 } 624 } 625 else if (chassisSubNode == "Power") 626 { 627 if (!json_util::readJson(req, res, "Voltages", voltageCollections)) 628 { 629 return; 630 } 631 allCollections.emplace("Voltages", std::move(voltageCollections)); 632 } 633 else 634 { 635 res.result(boost::beast::http::status::not_found); 636 res.end(); 637 return; 638 } 639 640 const char* propertyValueName; 641 std::unordered_map<std::string, std::pair<double, std::string>> overrideMap; 642 std::string memberId; 643 double value; 644 for (auto& collectionItems : allCollections) 645 { 646 if (collectionItems.first == "Temperatures") 647 { 648 propertyValueName = "ReadingCelsius"; 649 } 650 else if (collectionItems.first == "Fans") 651 { 652 propertyValueName = "Reading"; 653 } 654 else 655 { 656 propertyValueName = "ReadingVolts"; 657 } 658 for (auto& item : collectionItems.second) 659 { 660 if (!json_util::readJson(item, res, "MemberId", memberId, 661 propertyValueName, value)) 662 { 663 return; 664 } 665 overrideMap.emplace(memberId, 666 std::make_pair(value, collectionItems.first)); 667 } 668 } 669 const std::string& chassisName = params[0]; 670 auto sensorAsyncResp = std::make_shared<SensorsAsyncResp>( 671 res, chassisName, typeList, chassisSubNode); 672 // first check for valid chassis id & sensor in requested chassis. 673 auto getChassisSensorListCb = [sensorAsyncResp, overrideMap]( 674 const boost::container::flat_set< 675 std::string>& sensorLists) { 676 boost::container::flat_set<std::string> sensorNames; 677 for (const auto& item : overrideMap) 678 { 679 const auto& sensor = item.first; 680 if (sensorLists.find(item.first) == sensorLists.end()) 681 { 682 BMCWEB_LOG_INFO << "Unable to find memberId " << item.first; 683 messages::resourceNotFound(sensorAsyncResp->res, 684 item.second.second, item.first); 685 return; 686 } 687 sensorNames.emplace(sensor); 688 } 689 // Get the connection to which the memberId belongs 690 auto getObjectsWithConnectionCb = 691 [sensorAsyncResp, overrideMap]( 692 const boost::container::flat_set<std::string>& connections, 693 const std::set<std::pair<std::string, std::string>>& 694 objectsWithConnection) { 695 if (objectsWithConnection.size() != overrideMap.size()) 696 { 697 BMCWEB_LOG_INFO 698 << "Unable to find all objects with proper connection " 699 << objectsWithConnection.size() << " requested " 700 << overrideMap.size() << "\n"; 701 messages::resourceNotFound( 702 sensorAsyncResp->res, 703 sensorAsyncResp->chassisSubNode == "Thermal" 704 ? "Temperatures" 705 : "Voltages", 706 "Count"); 707 return; 708 } 709 for (const auto& item : objectsWithConnection) 710 { 711 712 auto lastPos = item.first.rfind('/'); 713 if (lastPos == std::string::npos) 714 { 715 messages::internalError(sensorAsyncResp->res); 716 return; 717 } 718 std::string sensorName = item.first.substr(lastPos + 1); 719 720 const auto& iterator = overrideMap.find(sensorName); 721 if (iterator == overrideMap.end()) 722 { 723 BMCWEB_LOG_INFO << "Unable to find sensor object" 724 << item.first << "\n"; 725 messages::internalError(sensorAsyncResp->res); 726 return; 727 } 728 crow::connections::systemBus->async_method_call( 729 [sensorAsyncResp](const boost::system::error_code ec) { 730 if (ec) 731 { 732 BMCWEB_LOG_DEBUG 733 << "setOverrideValueStatus DBUS error: " 734 << ec; 735 messages::internalError(sensorAsyncResp->res); 736 return; 737 } 738 }, 739 item.second, item.first, 740 "org.freedesktop.DBus.Properties", "Set", 741 "xyz.openbmc_project.Sensor.Value", "Value", 742 sdbusplus::message::variant<double>( 743 iterator->second.first)); 744 } 745 }; 746 // Get object with connection for the given sensor name 747 getObjectsWithConnection(sensorAsyncResp, sensorNames, 748 std::move(getObjectsWithConnectionCb)); 749 }; 750 // get full sensor list for the given chassisId and cross verify the sensor. 751 getChassis(sensorAsyncResp, std::move(getChassisSensorListCb)); 752 } 753 754 } // namespace redfish 755