1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "node.hpp" 19 20 #include <boost/algorithm/string/replace.hpp> 21 #include <dbus_utility.hpp> 22 23 namespace redfish 24 { 25 26 /** 27 * ManagerActionsReset class supports handle POST method for Reset action. 28 * The class retrieves and sends data directly to dbus. 29 */ 30 class ManagerActionsReset : public Node 31 { 32 public: 33 ManagerActionsReset(CrowApp& app) : 34 Node(app, "/redfish/v1/Managers/bmc/Actions/Manager.Reset/") 35 { 36 entityPrivileges = { 37 {boost::beast::http::verb::get, {{"Login"}}}, 38 {boost::beast::http::verb::head, {{"Login"}}}, 39 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 40 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 41 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 42 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 43 } 44 45 private: 46 /** 47 * Function handles GET method request. 48 * ManagerActionReset supports for POST method, 49 * it is not required to retrieve more information in GET. 50 */ 51 void doGet(crow::Response& res, const crow::Request& req, 52 const std::vector<std::string>& params) override 53 { 54 res.jsonValue = Node::json; 55 res.end(); 56 } 57 58 /** 59 * Function handles POST method request. 60 * Analyzes POST body message before sends Reset request data to dbus. 61 * OpenBMC allows for ResetType is GracefulRestart only. 62 */ 63 void doPost(crow::Response& res, const crow::Request& req, 64 const std::vector<std::string>& params) override 65 { 66 std::string resetType; 67 68 if (!json_util::readJson(req, res, "ResetType", resetType)) 69 { 70 return; 71 } 72 73 if (resetType != "GracefulRestart") 74 { 75 res.result(boost::beast::http::status::bad_request); 76 messages::actionParameterNotSupported(res, resetType, "ResetType"); 77 BMCWEB_LOG_ERROR << "Request incorrect action parameter: " 78 << resetType; 79 res.end(); 80 return; 81 } 82 doBMCGracefulRestart(res, req, params); 83 } 84 85 /** 86 * Function transceives data with dbus directly. 87 * All BMC state properties will be retrieved before sending reset request. 88 */ 89 void doBMCGracefulRestart(crow::Response& res, const crow::Request& req, 90 const std::vector<std::string>& params) 91 { 92 const char* processName = "xyz.openbmc_project.State.BMC"; 93 const char* objectPath = "/xyz/openbmc_project/state/bmc0"; 94 const char* interfaceName = "xyz.openbmc_project.State.BMC"; 95 const std::string& propertyValue = 96 "xyz.openbmc_project.State.BMC.Transition.Reboot"; 97 const char* destProperty = "RequestedBMCTransition"; 98 99 // Create the D-Bus variant for D-Bus call. 100 VariantType dbusPropertyValue(propertyValue); 101 102 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 103 104 crow::connections::systemBus->async_method_call( 105 [asyncResp](const boost::system::error_code ec) { 106 // Use "Set" method to set the property value. 107 if (ec) 108 { 109 BMCWEB_LOG_ERROR << "[Set] Bad D-Bus request error: " << ec; 110 messages::internalError(asyncResp->res); 111 return; 112 } 113 114 messages::success(asyncResp->res); 115 }, 116 processName, objectPath, "org.freedesktop.DBus.Properties", "Set", 117 interfaceName, destProperty, dbusPropertyValue); 118 } 119 }; 120 121 static constexpr const char* objectManagerIface = 122 "org.freedesktop.DBus.ObjectManager"; 123 static constexpr const char* pidConfigurationIface = 124 "xyz.openbmc_project.Configuration.Pid"; 125 static constexpr const char* pidZoneConfigurationIface = 126 "xyz.openbmc_project.Configuration.Pid.Zone"; 127 128 static void asyncPopulatePid(const std::string& connection, 129 const std::string& path, 130 std::shared_ptr<AsyncResp> asyncResp) 131 { 132 133 crow::connections::systemBus->async_method_call( 134 [asyncResp](const boost::system::error_code ec, 135 const dbus::utility::ManagedObjectType& managedObj) { 136 if (ec) 137 { 138 BMCWEB_LOG_ERROR << ec; 139 asyncResp->res.jsonValue.clear(); 140 messages::internalError(asyncResp->res); 141 return; 142 } 143 nlohmann::json& configRoot = 144 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["Fan"]; 145 nlohmann::json& fans = configRoot["FanControllers"]; 146 fans["@odata.type"] = "#OemManager.FanControllers"; 147 fans["@odata.context"] = 148 "/redfish/v1/$metadata#OemManager.FanControllers"; 149 fans["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc/" 150 "Fan/FanControllers"; 151 152 nlohmann::json& pids = configRoot["PidControllers"]; 153 pids["@odata.type"] = "#OemManager.PidControllers"; 154 pids["@odata.context"] = 155 "/redfish/v1/$metadata#OemManager.PidControllers"; 156 pids["@odata.id"] = 157 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/PidControllers"; 158 159 nlohmann::json& zones = configRoot["FanZones"]; 160 zones["@odata.id"] = 161 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan/FanZones"; 162 zones["@odata.type"] = "#OemManager.FanZones"; 163 zones["@odata.context"] = 164 "/redfish/v1/$metadata#OemManager.FanZones"; 165 configRoot["@odata.id"] = 166 "/redfish/v1/Managers/bmc#/Oem/OpenBmc/Fan"; 167 configRoot["@odata.type"] = "#OemManager.Fan"; 168 configRoot["@odata.context"] = 169 "/redfish/v1/$metadata#OemManager.Fan"; 170 171 bool propertyError = false; 172 for (const auto& pathPair : managedObj) 173 { 174 for (const auto& intfPair : pathPair.second) 175 { 176 if (intfPair.first != pidConfigurationIface && 177 intfPair.first != pidZoneConfigurationIface) 178 { 179 continue; 180 } 181 auto findName = intfPair.second.find("Name"); 182 if (findName == intfPair.second.end()) 183 { 184 BMCWEB_LOG_ERROR << "Pid Field missing Name"; 185 messages::internalError(asyncResp->res); 186 return; 187 } 188 const std::string* namePtr = 189 mapbox::getPtr<const std::string>(findName->second); 190 if (namePtr == nullptr) 191 { 192 BMCWEB_LOG_ERROR << "Pid Name Field illegal"; 193 return; 194 } 195 196 std::string name = *namePtr; 197 dbus::utility::escapePathForDbus(name); 198 if (intfPair.first == pidZoneConfigurationIface) 199 { 200 std::string chassis; 201 if (!dbus::utility::getNthStringFromPath( 202 pathPair.first.str, 5, chassis)) 203 { 204 chassis = "#IllegalValue"; 205 } 206 nlohmann::json& zone = zones[name]; 207 zone["Chassis"] = { 208 {"@odata.id", "/redfish/v1/Chassis/" + chassis}}; 209 zone["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/" 210 "OpenBmc/Fan/FanZones/" + 211 name; 212 zone["@odata.type"] = "#OemManager.FanZone"; 213 zone["@odata.context"] = 214 "/redfish/v1/$metadata#OemManager.FanZone"; 215 } 216 217 for (const auto& propertyPair : intfPair.second) 218 { 219 if (propertyPair.first == "Type" || 220 propertyPair.first == "Class" || 221 propertyPair.first == "Name") 222 { 223 continue; 224 } 225 226 // zones 227 if (intfPair.first == pidZoneConfigurationIface) 228 { 229 const double* ptr = mapbox::getPtr<const double>( 230 propertyPair.second); 231 if (ptr == nullptr) 232 { 233 BMCWEB_LOG_ERROR << "Field Illegal " 234 << propertyPair.first; 235 messages::internalError(asyncResp->res); 236 return; 237 } 238 zones[name][propertyPair.first] = *ptr; 239 } 240 241 // pid and fans are off the same configuration 242 if (intfPair.first == pidConfigurationIface) 243 { 244 const std::string* classPtr = nullptr; 245 auto findClass = intfPair.second.find("Class"); 246 if (findClass != intfPair.second.end()) 247 { 248 classPtr = mapbox::getPtr<const std::string>( 249 findClass->second); 250 } 251 if (classPtr == nullptr) 252 { 253 BMCWEB_LOG_ERROR << "Pid Class Field illegal"; 254 messages::internalError(asyncResp->res); 255 return; 256 } 257 bool isFan = *classPtr == "fan"; 258 nlohmann::json& element = 259 isFan ? fans[name] : pids[name]; 260 if (isFan) 261 { 262 element["@odata.id"] = 263 "/redfish/v1/Managers/bmc#/Oem/" 264 "OpenBmc/Fan/FanControllers/" + 265 std::string(name); 266 element["@odata.type"] = 267 "#OemManager.FanController"; 268 269 element["@odata.context"] = 270 "/redfish/v1/" 271 "$metadata#OemManager.FanController"; 272 } 273 else 274 { 275 element["@odata.id"] = 276 "/redfish/v1/Managers/bmc#/Oem/" 277 "OpenBmc/Fan/PidControllers/" + 278 std::string(name); 279 element["@odata.type"] = 280 "#OemManager.PidController"; 281 element["@odata.context"] = 282 "/redfish/v1/$metadata" 283 "#OemManager.PidController"; 284 } 285 286 if (propertyPair.first == "Zones") 287 { 288 const std::vector<std::string>* inputs = 289 mapbox::getPtr< 290 const std::vector<std::string>>( 291 propertyPair.second); 292 293 if (inputs == nullptr) 294 { 295 BMCWEB_LOG_ERROR 296 << "Zones Pid Field Illegal"; 297 messages::internalError(asyncResp->res); 298 return; 299 } 300 auto& data = element[propertyPair.first]; 301 data = nlohmann::json::array(); 302 for (std::string itemCopy : *inputs) 303 { 304 dbus::utility::escapePathForDbus(itemCopy); 305 data.push_back( 306 {{"@odata.id", 307 "/redfish/v1/Managers/bmc#/Oem/" 308 "OpenBmc/Fan/FanZones/" + 309 itemCopy}}); 310 } 311 } 312 // todo(james): may never happen, but this 313 // assumes configuration data referenced in the 314 // PID config is provided by the same daemon, we 315 // could add another loop to cover all cases, 316 // but I'm okay kicking this can down the road a 317 // bit 318 319 else if (propertyPair.first == "Inputs" || 320 propertyPair.first == "Outputs") 321 { 322 auto& data = element[propertyPair.first]; 323 const std::vector<std::string>* inputs = 324 mapbox::getPtr< 325 const std::vector<std::string>>( 326 propertyPair.second); 327 328 if (inputs == nullptr) 329 { 330 BMCWEB_LOG_ERROR << "Field Illegal " 331 << propertyPair.first; 332 messages::internalError(asyncResp->res); 333 return; 334 } 335 data = *inputs; 336 } // doubles 337 else if (propertyPair.first == 338 "FFGainCoefficient" || 339 propertyPair.first == "FFOffCoefficient" || 340 propertyPair.first == "ICoefficient" || 341 propertyPair.first == "ILimitMax" || 342 propertyPair.first == "ILimitMin" || 343 propertyPair.first == "OutLimitMax" || 344 propertyPair.first == "OutLimitMin" || 345 propertyPair.first == "PCoefficient" || 346 propertyPair.first == "SlewNeg" || 347 propertyPair.first == "SlewPos") 348 { 349 const double* ptr = 350 mapbox::getPtr<const double>( 351 propertyPair.second); 352 if (ptr == nullptr) 353 { 354 BMCWEB_LOG_ERROR << "Field Illegal " 355 << propertyPair.first; 356 messages::internalError(asyncResp->res); 357 return; 358 } 359 element[propertyPair.first] = *ptr; 360 } 361 } 362 } 363 } 364 } 365 }, 366 connection, path, objectManagerIface, "GetManagedObjects"); 367 } 368 369 enum class CreatePIDRet 370 { 371 fail, 372 del, 373 patch 374 }; 375 376 static CreatePIDRet createPidInterface( 377 const std::shared_ptr<AsyncResp>& response, const std::string& type, 378 const nlohmann::json& record, const std::string& path, 379 const dbus::utility::ManagedObjectType& managedObj, bool createNewObject, 380 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>& 381 output, 382 std::string& chassis) 383 { 384 385 if (type == "PidControllers" || type == "FanControllers") 386 { 387 if (createNewObject) 388 { 389 output["Class"] = type == "PidControllers" ? std::string("temp") 390 : std::string("fan"); 391 output["Type"] = std::string("Pid"); 392 } 393 else if (record == nullptr) 394 { 395 // delete interface 396 crow::connections::systemBus->async_method_call( 397 [response, 398 path{std::string(path)}](const boost::system::error_code ec) { 399 if (ec) 400 { 401 BMCWEB_LOG_ERROR << "Error patching " << path << ": " 402 << ec; 403 messages::internalError(response->res); 404 } 405 }, 406 "xyz.openbmc_project.EntityManager", path, 407 pidConfigurationIface, "Delete"); 408 return CreatePIDRet::del; 409 } 410 411 for (auto& field : record.items()) 412 { 413 if (field.key() == "Zones") 414 { 415 if (!field.value().is_array()) 416 { 417 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 418 messages::propertyValueFormatError( 419 response->res, field.value(), field.key()); 420 return CreatePIDRet::fail; 421 } 422 std::vector<std::string> inputs; 423 for (const auto& odata : field.value().items()) 424 { 425 for (const auto& value : odata.value().items()) 426 { 427 const std::string* path = 428 value.value().get_ptr<const std::string*>(); 429 if (path == nullptr) 430 { 431 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 432 messages::propertyValueFormatError( 433 response->res, field.value().dump(), 434 field.key()); 435 return CreatePIDRet::fail; 436 } 437 std::string input; 438 if (!dbus::utility::getNthStringFromPath(*path, 4, 439 input)) 440 { 441 BMCWEB_LOG_ERROR << "Got invalid path " << *path; 442 messages::propertyValueFormatError( 443 response->res, field.value().dump(), 444 field.key()); 445 return CreatePIDRet::fail; 446 } 447 boost::replace_all(input, "_", " "); 448 inputs.emplace_back(std::move(input)); 449 } 450 } 451 output["Zones"] = std::move(inputs); 452 } 453 else if (field.key() == "Inputs" || field.key() == "Outputs") 454 { 455 if (!field.value().is_array()) 456 { 457 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 458 messages::propertyValueFormatError( 459 response->res, field.value().dump(), field.key()); 460 return CreatePIDRet::fail; 461 } 462 std::vector<std::string> inputs; 463 for (const auto& value : field.value().items()) 464 { 465 const std::string* sensor = 466 value.value().get_ptr<const std::string*>(); 467 468 if (sensor == nullptr) 469 { 470 BMCWEB_LOG_ERROR << "Illegal Type " 471 << field.value().dump(); 472 messages::propertyValueFormatError( 473 response->res, field.value().dump(), field.key()); 474 return CreatePIDRet::fail; 475 } 476 477 std::string input = 478 boost::replace_all_copy(*sensor, "_", " "); 479 inputs.push_back(std::move(input)); 480 // try to find the sensor in the 481 // configuration 482 if (chassis.empty()) 483 { 484 std::find_if( 485 managedObj.begin(), managedObj.end(), 486 [&chassis, sensor](const auto& obj) { 487 if (boost::algorithm::ends_with(obj.first.str, 488 *sensor)) 489 { 490 return dbus::utility::getNthStringFromPath( 491 obj.first.str, 5, chassis); 492 } 493 return false; 494 }); 495 } 496 } 497 output[field.key()] = inputs; 498 } 499 500 // doubles 501 else if (field.key() == "FFGainCoefficient" || 502 field.key() == "FFOffCoefficient" || 503 field.key() == "ICoefficient" || 504 field.key() == "ILimitMax" || field.key() == "ILimitMin" || 505 field.key() == "OutLimitMax" || 506 field.key() == "OutLimitMin" || 507 field.key() == "PCoefficient" || 508 field.key() == "SetPoint" || field.key() == "SlewNeg" || 509 field.key() == "SlewPos") 510 { 511 const double* ptr = field.value().get_ptr<const double*>(); 512 if (ptr == nullptr) 513 { 514 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 515 messages::propertyValueFormatError( 516 response->res, field.value().dump(), field.key()); 517 return CreatePIDRet::fail; 518 } 519 output[field.key()] = *ptr; 520 } 521 522 else 523 { 524 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 525 messages::propertyUnknown(response->res, field.key()); 526 return CreatePIDRet::fail; 527 } 528 } 529 } 530 else if (type == "FanZones") 531 { 532 if (!createNewObject && record == nullptr) 533 { 534 // delete interface 535 crow::connections::systemBus->async_method_call( 536 [response, 537 path{std::string(path)}](const boost::system::error_code ec) { 538 if (ec) 539 { 540 BMCWEB_LOG_ERROR << "Error patching " << path << ": " 541 << ec; 542 messages::internalError(response->res); 543 } 544 }, 545 "xyz.openbmc_project.EntityManager", path, 546 pidZoneConfigurationIface, "Delete"); 547 return CreatePIDRet::del; 548 } 549 output["Type"] = std::string("Pid.Zone"); 550 551 for (auto& field : record.items()) 552 { 553 if (field.key() == "Chassis") 554 { 555 const std::string* chassisId = nullptr; 556 for (const auto& id : field.value().items()) 557 { 558 if (id.key() != "@odata.id") 559 { 560 BMCWEB_LOG_ERROR << "Illegal Type " << id.key(); 561 messages::propertyUnknown(response->res, field.key()); 562 return CreatePIDRet::fail; 563 } 564 chassisId = id.value().get_ptr<const std::string*>(); 565 if (chassisId == nullptr) 566 { 567 messages::createFailedMissingReqProperties( 568 response->res, field.key()); 569 return CreatePIDRet::fail; 570 } 571 } 572 573 // /refish/v1/chassis/chassis_name/ 574 if (!dbus::utility::getNthStringFromPath(*chassisId, 3, 575 chassis)) 576 { 577 BMCWEB_LOG_ERROR << "Got invalid path " << *chassisId; 578 messages::invalidObject(response->res, *chassisId); 579 return CreatePIDRet::fail; 580 } 581 } 582 else if (field.key() == "FailSafePercent" || 583 field.key() == "MinThermalRpm") 584 { 585 const double* ptr = field.value().get_ptr<const double*>(); 586 if (ptr == nullptr) 587 { 588 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 589 messages::propertyValueFormatError( 590 response->res, field.value().dump(), field.key()); 591 return CreatePIDRet::fail; 592 } 593 output[field.key()] = *ptr; 594 } 595 else 596 { 597 BMCWEB_LOG_ERROR << "Illegal Type " << field.key(); 598 messages::propertyUnknown(response->res, field.key()); 599 return CreatePIDRet::fail; 600 } 601 } 602 } 603 else 604 { 605 BMCWEB_LOG_ERROR << "Illegal Type " << type; 606 messages::propertyUnknown(response->res, type); 607 return CreatePIDRet::fail; 608 } 609 return CreatePIDRet::patch; 610 } 611 612 class Manager : public Node 613 { 614 public: 615 Manager(CrowApp& app) : Node(app, "/redfish/v1/Managers/bmc/") 616 { 617 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc"; 618 Node::json["@odata.type"] = "#Manager.v1_3_0.Manager"; 619 Node::json["@odata.context"] = "/redfish/v1/$metadata#Manager.Manager"; 620 Node::json["Id"] = "bmc"; 621 Node::json["Name"] = "OpenBmc Manager"; 622 Node::json["Description"] = "Baseboard Management Controller"; 623 Node::json["PowerState"] = "On"; 624 Node::json["ManagerType"] = "BMC"; 625 Node::json["UUID"] = 626 app.template getMiddleware<crow::persistent_data::Middleware>() 627 .systemUuid; 628 Node::json["Model"] = "OpenBmc"; // TODO(ed), get model 629 Node::json["EthernetInterfaces"] = { 630 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces"}}; 631 632 entityPrivileges = { 633 {boost::beast::http::verb::get, {{"Login"}}}, 634 {boost::beast::http::verb::head, {{"Login"}}}, 635 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 636 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 637 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 638 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 639 640 // default oem data 641 nlohmann::json& oem = Node::json["Oem"]; 642 nlohmann::json& oemOpenbmc = oem["OpenBmc"]; 643 oem["@odata.type"] = "#OemManager.Oem"; 644 oem["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem"; 645 oem["@odata.context"] = "/redfish/v1/$metadata#OemManager.Oem"; 646 oemOpenbmc["@odata.type"] = "#OemManager.OpenBmc"; 647 oemOpenbmc["@odata.id"] = "/redfish/v1/Managers/bmc#/Oem/OpenBmc"; 648 oemOpenbmc["@odata.context"] = 649 "/redfish/v1/$metadata#OemManager.OpenBmc"; 650 } 651 652 private: 653 void getPidValues(std::shared_ptr<AsyncResp> asyncResp) 654 { 655 crow::connections::systemBus->async_method_call( 656 [asyncResp](const boost::system::error_code ec, 657 const crow::openbmc_mapper::GetSubTreeType& subtree) { 658 if (ec) 659 { 660 BMCWEB_LOG_ERROR << ec; 661 messages::internalError(asyncResp->res); 662 return; 663 } 664 665 // create map of <connection, path to objMgr>> 666 boost::container::flat_map<std::string, std::string> 667 objectMgrPaths; 668 boost::container::flat_set<std::string> calledConnections; 669 for (const auto& pathGroup : subtree) 670 { 671 for (const auto& connectionGroup : pathGroup.second) 672 { 673 auto findConnection = 674 calledConnections.find(connectionGroup.first); 675 if (findConnection != calledConnections.end()) 676 { 677 break; 678 } 679 for (const std::string& interface : 680 connectionGroup.second) 681 { 682 if (interface == objectManagerIface) 683 { 684 objectMgrPaths[connectionGroup.first] = 685 pathGroup.first; 686 } 687 // this list is alphabetical, so we 688 // should have found the objMgr by now 689 if (interface == pidConfigurationIface || 690 interface == pidZoneConfigurationIface) 691 { 692 auto findObjMgr = 693 objectMgrPaths.find(connectionGroup.first); 694 if (findObjMgr == objectMgrPaths.end()) 695 { 696 BMCWEB_LOG_DEBUG << connectionGroup.first 697 << "Has no Object Manager"; 698 continue; 699 } 700 701 calledConnections.insert(connectionGroup.first); 702 703 asyncPopulatePid(findObjMgr->first, 704 findObjMgr->second, asyncResp); 705 break; 706 } 707 } 708 } 709 } 710 }, 711 "xyz.openbmc_project.ObjectMapper", 712 "/xyz/openbmc_project/object_mapper", 713 "xyz.openbmc_project.ObjectMapper", "GetSubTree", "/", 0, 714 std::array<const char*, 3>{pidConfigurationIface, 715 pidZoneConfigurationIface, 716 objectManagerIface}); 717 } 718 719 void doGet(crow::Response& res, const crow::Request& req, 720 const std::vector<std::string>& params) override 721 { 722 // Update Actions object. 723 nlohmann::json& manager_reset = Node::json["Actions"]["#Manager.Reset"]; 724 manager_reset["target"] = 725 "/redfish/v1/Managers/bmc/Actions/Manager.Reset"; 726 manager_reset["ResetType@Redfish.AllowableValues"] = { 727 "GracefulRestart"}; 728 729 Node::json["DateTime"] = getDateTime(); 730 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 731 asyncResp->res.jsonValue = Node::json; 732 733 crow::connections::systemBus->async_method_call( 734 [asyncResp](const boost::system::error_code ec, 735 const dbus::utility::ManagedObjectType& resp) { 736 if (ec) 737 { 738 BMCWEB_LOG_ERROR << "Error while getting Software Version"; 739 messages::internalError(asyncResp->res); 740 return; 741 } 742 743 for (auto& objpath : resp) 744 { 745 for (auto& interface : objpath.second) 746 { 747 // If interface is xyz.openbmc_project.Software.Version, 748 // this is what we're looking for. 749 if (interface.first == 750 "xyz.openbmc_project.Software.Version") 751 { 752 // Cut out everyting until last "/", ... 753 const std::string& iface_id = objpath.first; 754 for (auto& property : interface.second) 755 { 756 if (property.first == "Version") 757 { 758 const std::string* value = 759 mapbox::getPtr<const std::string>( 760 property.second); 761 if (value == nullptr) 762 { 763 continue; 764 } 765 asyncResp->res 766 .jsonValue["FirmwareVersion"] = *value; 767 } 768 } 769 } 770 } 771 } 772 }, 773 "xyz.openbmc_project.Software.BMC.Updater", 774 "/xyz/openbmc_project/software", 775 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 776 getPidValues(asyncResp); 777 } 778 void setPidValues(std::shared_ptr<AsyncResp> response, 779 const nlohmann::json& data) 780 { 781 // todo(james): might make sense to do a mapper call here if this 782 // interface gets more traction 783 crow::connections::systemBus->async_method_call( 784 [response, 785 data](const boost::system::error_code ec, 786 const dbus::utility::ManagedObjectType& managedObj) { 787 if (ec) 788 { 789 BMCWEB_LOG_ERROR << "Error communicating to Entity Manager"; 790 messages::internalError(response->res); 791 return; 792 } 793 for (const auto& type : data.items()) 794 { 795 if (!type.value().is_object()) 796 { 797 BMCWEB_LOG_ERROR << "Illegal Type " << type.key(); 798 messages::propertyValueFormatError( 799 response->res, type.value(), type.key()); 800 return; 801 } 802 for (const auto& record : type.value().items()) 803 { 804 const std::string& name = record.key(); 805 auto pathItr = 806 std::find_if(managedObj.begin(), managedObj.end(), 807 [&name](const auto& obj) { 808 return boost::algorithm::ends_with( 809 obj.first.str, name); 810 }); 811 boost::container::flat_map< 812 std::string, dbus::utility::DbusVariantType> 813 output; 814 815 output.reserve(16); // The pid interface length 816 817 // determines if we're patching entity-manager or 818 // creating a new object 819 bool createNewObject = (pathItr == managedObj.end()); 820 if (type.key() == "PidControllers" || 821 type.key() == "FanControllers") 822 { 823 if (!createNewObject && 824 pathItr->second.find(pidConfigurationIface) == 825 pathItr->second.end()) 826 { 827 createNewObject = true; 828 } 829 } 830 else if (!createNewObject && 831 pathItr->second.find( 832 pidZoneConfigurationIface) == 833 pathItr->second.end()) 834 { 835 createNewObject = true; 836 } 837 output["Name"] = 838 boost::replace_all_copy(name, "_", " "); 839 840 std::string chassis; 841 CreatePIDRet ret = createPidInterface( 842 response, type.key(), record.value(), 843 pathItr->first.str, managedObj, createNewObject, 844 output, chassis); 845 if (ret == CreatePIDRet::fail) 846 { 847 return; 848 } 849 else if (ret == CreatePIDRet::del) 850 { 851 continue; 852 } 853 854 if (!createNewObject) 855 { 856 for (const auto& property : output) 857 { 858 const char* iface = 859 type.key() == "FanZones" 860 ? pidZoneConfigurationIface 861 : pidConfigurationIface; 862 crow::connections::systemBus->async_method_call( 863 [response, 864 propertyName{std::string(property.first)}]( 865 const boost::system::error_code ec) { 866 if (ec) 867 { 868 BMCWEB_LOG_ERROR 869 << "Error patching " 870 << propertyName << ": " << ec; 871 messages::internalError( 872 response->res); 873 } 874 }, 875 "xyz.openbmc_project.EntityManager", 876 pathItr->first.str, 877 "org.freedesktop.DBus.Properties", "Set", 878 std::string(iface), property.first, 879 property.second); 880 } 881 } 882 else 883 { 884 if (chassis.empty()) 885 { 886 BMCWEB_LOG_ERROR 887 << "Failed to get chassis from config"; 888 messages::invalidObject(response->res, name); 889 return; 890 } 891 892 bool foundChassis = false; 893 for (const auto& obj : managedObj) 894 { 895 if (boost::algorithm::ends_with(obj.first.str, 896 chassis)) 897 { 898 chassis = obj.first.str; 899 foundChassis = true; 900 break; 901 } 902 } 903 if (!foundChassis) 904 { 905 BMCWEB_LOG_ERROR 906 << "Failed to find chassis on dbus"; 907 messages::resourceMissingAtURI( 908 response->res, 909 "/redfish/v1/Chassis/" + chassis); 910 return; 911 } 912 913 crow::connections::systemBus->async_method_call( 914 [response](const boost::system::error_code ec) { 915 if (ec) 916 { 917 BMCWEB_LOG_ERROR 918 << "Error Adding Pid Object " << ec; 919 messages::internalError(response->res); 920 } 921 }, 922 "xyz.openbmc_project.EntityManager", chassis, 923 "xyz.openbmc_project.AddObject", "AddObject", 924 output); 925 } 926 } 927 } 928 }, 929 "xyz.openbmc_project.EntityManager", "/", objectManagerIface, 930 "GetManagedObjects"); 931 } 932 933 void doPatch(crow::Response& res, const crow::Request& req, 934 const std::vector<std::string>& params) override 935 { 936 nlohmann::json patch; 937 if (!json_util::processJsonFromRequest(res, req, patch)) 938 { 939 return; 940 } 941 std::shared_ptr<AsyncResp> response = std::make_shared<AsyncResp>(res); 942 for (const auto& topLevel : patch.items()) 943 { 944 if (topLevel.key() == "Oem") 945 { 946 if (!topLevel.value().is_object()) 947 { 948 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key(); 949 messages::propertyValueFormatError( 950 response->res, topLevel.key(), "OemManager.Oem"); 951 return; 952 } 953 } 954 else 955 { 956 BMCWEB_LOG_ERROR << "Bad Patch " << topLevel.key(); 957 messages::propertyUnknown(response->res, topLevel.key()); 958 return; 959 } 960 for (const auto& oemLevel : topLevel.value().items()) 961 { 962 if (oemLevel.key() == "OpenBmc") 963 { 964 if (!oemLevel.value().is_object()) 965 { 966 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key(); 967 messages::propertyValueFormatError( 968 response->res, topLevel.key(), 969 "OemManager.OpenBmc"); 970 return; 971 } 972 for (const auto& typeLevel : oemLevel.value().items()) 973 { 974 975 if (typeLevel.key() == "Fan") 976 { 977 if (!typeLevel.value().is_object()) 978 { 979 BMCWEB_LOG_ERROR << "Bad Patch " 980 << typeLevel.key(); 981 messages::propertyValueFormatError( 982 response->res, typeLevel.value().dump(), 983 typeLevel.key()); 984 return; 985 } 986 setPidValues(response, 987 std::move(typeLevel.value())); 988 } 989 else 990 { 991 BMCWEB_LOG_ERROR << "Bad Patch " << typeLevel.key(); 992 messages::propertyUnknown(response->res, 993 typeLevel.key()); 994 return; 995 } 996 } 997 } 998 else 999 { 1000 BMCWEB_LOG_ERROR << "Bad Patch " << oemLevel.key(); 1001 messages::propertyUnknown(response->res, oemLevel.key()); 1002 return; 1003 } 1004 } 1005 } 1006 } 1007 1008 std::string getDateTime() const 1009 { 1010 std::array<char, 128> dateTime; 1011 std::string redfishDateTime("0000-00-00T00:00:00Z00:00"); 1012 std::time_t time = std::time(nullptr); 1013 1014 if (std::strftime(dateTime.begin(), dateTime.size(), "%FT%T%z", 1015 std::localtime(&time))) 1016 { 1017 // insert the colon required by the ISO 8601 standard 1018 redfishDateTime = std::string(dateTime.data()); 1019 redfishDateTime.insert(redfishDateTime.end() - 2, ':'); 1020 } 1021 1022 return redfishDateTime; 1023 } 1024 }; 1025 1026 class ManagerCollection : public Node 1027 { 1028 public: 1029 ManagerCollection(CrowApp& app) : Node(app, "/redfish/v1/Managers/") 1030 { 1031 Node::json["@odata.id"] = "/redfish/v1/Managers"; 1032 Node::json["@odata.type"] = "#ManagerCollection.ManagerCollection"; 1033 Node::json["@odata.context"] = 1034 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; 1035 Node::json["Name"] = "Manager Collection"; 1036 Node::json["Members@odata.count"] = 1; 1037 Node::json["Members"] = {{{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 1038 1039 entityPrivileges = { 1040 {boost::beast::http::verb::get, {{"Login"}}}, 1041 {boost::beast::http::verb::head, {{"Login"}}}, 1042 {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1043 {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1044 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1045 {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1046 } 1047 1048 private: 1049 void doGet(crow::Response& res, const crow::Request& req, 1050 const std::vector<std::string>& params) override 1051 { 1052 // Collections don't include the static data added by SubRoute 1053 // because it has a duplicate entry for members 1054 res.jsonValue["@odata.id"] = "/redfish/v1/Managers"; 1055 res.jsonValue["@odata.type"] = "#ManagerCollection.ManagerCollection"; 1056 res.jsonValue["@odata.context"] = 1057 "/redfish/v1/$metadata#ManagerCollection.ManagerCollection"; 1058 res.jsonValue["Name"] = "Manager Collection"; 1059 res.jsonValue["Members@odata.count"] = 1; 1060 res.jsonValue["Members"] = { 1061 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 1062 res.end(); 1063 } 1064 }; 1065 } // namespace redfish 1066