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