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