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