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