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