1 #pragma once 2 3 #include <app.hpp> 4 #include <boost/container/flat_set.hpp> 5 #include <dbus_singleton.hpp> 6 #include <dbus_utility.hpp> 7 #include <error_messages.hpp> 8 #include <registries/privilege_registry.hpp> 9 #include <sdbusplus/asio/property.hpp> 10 #include <utils/json_utils.hpp> 11 12 #include <optional> 13 #include <utility> 14 15 // TODO(ed) requestRoutesHypervisorSystems seems to have copy-pasted a 16 // lot of code, and has a number of methods that have name conflicts with the 17 // normal ethernet internfaces in ethernet.hpp. For the moment, we'll put 18 // hypervisor in a namespace to isolate it, but these methods eventually need 19 // deduplicated 20 namespace redfish::hypervisor 21 { 22 23 /** 24 * @brief Retrieves hypervisor state properties over dbus 25 * 26 * The hypervisor state object is optional so this function will only set the 27 * state variables if the object is found 28 * 29 * @param[in] aResp Shared pointer for completing asynchronous calls. 30 * 31 * @return None. 32 */ 33 inline void getHypervisorState(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 34 { 35 BMCWEB_LOG_DEBUG << "Get hypervisor state information."; 36 sdbusplus::asio::getProperty<std::string>( 37 *crow::connections::systemBus, "xyz.openbmc_project.State.Hypervisor", 38 "/xyz/openbmc_project/state/hypervisor0", 39 "xyz.openbmc_project.State.Host", "CurrentHostState", 40 [aResp](const boost::system::error_code ec, 41 const std::string& hostState) { 42 if (ec) 43 { 44 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 45 // This is an optional D-Bus object so just return if 46 // error occurs 47 return; 48 } 49 50 BMCWEB_LOG_DEBUG << "Hypervisor state: " << hostState; 51 // Verify Host State 52 if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") 53 { 54 aResp->res.jsonValue["PowerState"] = "On"; 55 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 56 } 57 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 58 "Quiesced") 59 { 60 aResp->res.jsonValue["PowerState"] = "On"; 61 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 62 } 63 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 64 "Standby") 65 { 66 aResp->res.jsonValue["PowerState"] = "On"; 67 aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; 68 } 69 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 70 "TransitioningToRunning") 71 { 72 aResp->res.jsonValue["PowerState"] = "PoweringOn"; 73 aResp->res.jsonValue["Status"]["State"] = "Starting"; 74 } 75 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 76 "TransitioningToOff") 77 { 78 aResp->res.jsonValue["PowerState"] = "PoweringOff"; 79 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 80 } 81 else if (hostState == 82 "xyz.openbmc_project.State.Host.HostState.Off") 83 { 84 aResp->res.jsonValue["PowerState"] = "Off"; 85 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 86 } 87 else 88 { 89 messages::internalError(aResp->res); 90 return; 91 } 92 }); 93 } 94 95 /** 96 * @brief Populate Actions if any are valid for hypervisor object 97 * 98 * The hypervisor state object is optional so this function will only set the 99 * Action if the object is found 100 * 101 * @param[in] aResp Shared pointer for completing asynchronous calls. 102 * 103 * @return None. 104 */ 105 inline void 106 getHypervisorActions(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 107 { 108 BMCWEB_LOG_DEBUG << "Get hypervisor actions."; 109 crow::connections::systemBus->async_method_call( 110 [aResp]( 111 const boost::system::error_code ec, 112 const std::vector<std::pair<std::string, std::vector<std::string>>>& 113 objInfo) { 114 if (ec) 115 { 116 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 117 // This is an optional D-Bus object so just return if 118 // error occurs 119 return; 120 } 121 122 if (objInfo.empty()) 123 { 124 // As noted above, this is an optional interface so just return 125 // if there is no instance found 126 return; 127 } 128 129 if (objInfo.size() > 1) 130 { 131 // More then one hypervisor object is not supported and is an 132 // error 133 messages::internalError(aResp->res); 134 return; 135 } 136 137 // Object present so system support limited ComputerSystem Action 138 aResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"] = { 139 {"target", 140 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"}, 141 {"@Redfish.ActionInfo", 142 "/redfish/v1/Systems/hypervisor/ResetActionInfo"}}; 143 }, 144 "xyz.openbmc_project.ObjectMapper", 145 "/xyz/openbmc_project/object_mapper", 146 "xyz.openbmc_project.ObjectMapper", "GetObject", 147 "/xyz/openbmc_project/state/hypervisor0", 148 std::array<const char*, 1>{"xyz.openbmc_project.State.Host"}); 149 } 150 151 inline bool extractHypervisorInterfaceData( 152 const std::string& ethIfaceId, 153 const dbus::utility::ManagedObjectType& dbusData, 154 EthernetInterfaceData& ethData, 155 boost::container::flat_set<IPv4AddressData>& ipv4Config) 156 { 157 bool idFound = false; 158 for (const auto& objpath : dbusData) 159 { 160 for (const auto& ifacePair : objpath.second) 161 { 162 if (objpath.first == 163 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) 164 { 165 idFound = true; 166 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 167 { 168 for (const auto& propertyPair : ifacePair.second) 169 { 170 if (propertyPair.first == "MACAddress") 171 { 172 const std::string* mac = 173 std::get_if<std::string>(&propertyPair.second); 174 if (mac != nullptr) 175 { 176 ethData.mac_address = *mac; 177 } 178 } 179 } 180 } 181 else if (ifacePair.first == 182 "xyz.openbmc_project.Network.EthernetInterface") 183 { 184 for (const auto& propertyPair : ifacePair.second) 185 { 186 if (propertyPair.first == "DHCPEnabled") 187 { 188 const std::string* dhcp = 189 std::get_if<std::string>(&propertyPair.second); 190 if (dhcp != nullptr) 191 { 192 ethData.DHCPEnabled = *dhcp; 193 break; // Interested on only "DHCPEnabled". 194 // Stop parsing since we got the 195 // "DHCPEnabled" value. 196 } 197 } 198 } 199 } 200 } 201 if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + 202 ethIfaceId + "/ipv4/addr0") 203 { 204 std::pair<boost::container::flat_set<IPv4AddressData>::iterator, 205 bool> 206 it = ipv4Config.insert(IPv4AddressData{}); 207 IPv4AddressData& ipv4Address = *it.first; 208 if (ifacePair.first == "xyz.openbmc_project.Object.Enable") 209 { 210 for (const auto& property : ifacePair.second) 211 { 212 if (property.first == "Enabled") 213 { 214 const bool* intfEnable = 215 std::get_if<bool>(&property.second); 216 if (intfEnable != nullptr) 217 { 218 ipv4Address.isActive = *intfEnable; 219 break; 220 } 221 } 222 } 223 } 224 if (ifacePair.first == "xyz.openbmc_project.Network.IP") 225 { 226 for (const auto& property : ifacePair.second) 227 { 228 if (property.first == "Address") 229 { 230 const std::string* address = 231 std::get_if<std::string>(&property.second); 232 if (address != nullptr) 233 { 234 ipv4Address.address = *address; 235 } 236 } 237 else if (property.first == "Origin") 238 { 239 const std::string* origin = 240 std::get_if<std::string>(&property.second); 241 if (origin != nullptr) 242 { 243 ipv4Address.origin = 244 translateAddressOriginDbusToRedfish(*origin, 245 true); 246 } 247 } 248 else if (property.first == "PrefixLength") 249 { 250 const uint8_t* mask = 251 std::get_if<uint8_t>(&property.second); 252 if (mask != nullptr) 253 { 254 // convert it to the string 255 ipv4Address.netmask = getNetmask(*mask); 256 } 257 } 258 else if (property.first == "Type" || 259 property.first == "Gateway") 260 { 261 // Type & Gateway is not used 262 continue; 263 } 264 else 265 { 266 BMCWEB_LOG_ERROR 267 << "Got extra property: " << property.first 268 << " on the " << objpath.first.str << " object"; 269 } 270 } 271 } 272 } 273 if (objpath.first == "/xyz/openbmc_project/network/hypervisor") 274 { 275 // System configuration shows up in the global namespace, so no 276 // need to check eth number 277 if (ifacePair.first == 278 "xyz.openbmc_project.Network.SystemConfiguration") 279 { 280 for (const auto& propertyPair : ifacePair.second) 281 { 282 if (propertyPair.first == "HostName") 283 { 284 const std::string* hostName = 285 std::get_if<std::string>(&propertyPair.second); 286 if (hostName != nullptr) 287 { 288 ethData.hostname = *hostName; 289 } 290 } 291 else if (propertyPair.first == "DefaultGateway") 292 { 293 const std::string* defaultGateway = 294 std::get_if<std::string>(&propertyPair.second); 295 if (defaultGateway != nullptr) 296 { 297 ethData.default_gateway = *defaultGateway; 298 } 299 } 300 } 301 } 302 } 303 } 304 } 305 return idFound; 306 } 307 /** 308 * Function that retrieves all properties for given Hypervisor Ethernet 309 * Interface Object from Settings Manager 310 * @param ethIfaceId Hypervisor ethernet interface id to query on DBus 311 * @param callback a function that shall be called to convert Dbus output 312 * into JSON 313 */ 314 template <typename CallbackFunc> 315 void getHypervisorIfaceData(const std::string& ethIfaceId, 316 CallbackFunc&& callback) 317 { 318 crow::connections::systemBus->async_method_call( 319 [ethIfaceId{std::string{ethIfaceId}}, 320 callback{std::forward<CallbackFunc>(callback)}]( 321 const boost::system::error_code error, 322 const dbus::utility::ManagedObjectType& resp) { 323 EthernetInterfaceData ethData{}; 324 boost::container::flat_set<IPv4AddressData> ipv4Data; 325 if (error) 326 { 327 callback(false, ethData, ipv4Data); 328 return; 329 } 330 331 bool found = extractHypervisorInterfaceData(ethIfaceId, resp, 332 ethData, ipv4Data); 333 if (!found) 334 { 335 BMCWEB_LOG_INFO << "Hypervisor Interface not found"; 336 } 337 callback(found, ethData, ipv4Data); 338 }, 339 "xyz.openbmc_project.Settings", "/", 340 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 341 } 342 343 /** 344 * @brief Sets the Hypervisor Interface IPAddress DBUS 345 * 346 * @param[in] aResp Shared pointer for generating response message. 347 * @param[in] ipv4Address Address from the incoming request 348 * @param[in] ethIfaceId Hypervisor Interface Id 349 * 350 * @return None. 351 */ 352 inline void 353 setHypervisorIPv4Address(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 354 const std::string& ethIfaceId, 355 const std::string& ipv4Address) 356 { 357 BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address 358 << " on Iface: " << ethIfaceId; 359 crow::connections::systemBus->async_method_call( 360 [aResp](const boost::system::error_code ec) { 361 if (ec) 362 { 363 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 364 return; 365 } 366 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set"; 367 }, 368 "xyz.openbmc_project.Settings", 369 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 370 "org.freedesktop.DBus.Properties", "Set", 371 "xyz.openbmc_project.Network.IP", "Address", 372 dbus::utility::DbusVariantType(ipv4Address)); 373 } 374 375 /** 376 * @brief Sets the Hypervisor Interface SubnetMask DBUS 377 * 378 * @param[in] aResp Shared pointer for generating response message. 379 * @param[in] subnet SubnetMask from the incoming request 380 * @param[in] ethIfaceId Hypervisor Interface Id 381 * 382 * @return None. 383 */ 384 inline void 385 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 386 const std::string& ethIfaceId, const uint8_t subnet) 387 { 388 BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet 389 << " on Iface: " << ethIfaceId; 390 391 crow::connections::systemBus->async_method_call( 392 [aResp](const boost::system::error_code ec) { 393 if (ec) 394 { 395 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 396 return; 397 } 398 BMCWEB_LOG_DEBUG << "SubnetMask is Set"; 399 }, 400 "xyz.openbmc_project.Settings", 401 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 402 "org.freedesktop.DBus.Properties", "Set", 403 "xyz.openbmc_project.Network.IP", "PrefixLength", 404 dbus::utility::DbusVariantType(subnet)); 405 } 406 407 /** 408 * @brief Sets the Hypervisor Interface Gateway DBUS 409 * 410 * @param[in] aResp Shared pointer for generating response message. 411 * @param[in] gateway Gateway from the incoming request 412 * @param[in] ethIfaceId Hypervisor Interface Id 413 * 414 * @return None. 415 */ 416 inline void 417 setHypervisorIPv4Gateway(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 418 const std::string& gateway) 419 { 420 BMCWEB_LOG_DEBUG 421 << "Setting the DefaultGateway to the last configured gateway"; 422 423 crow::connections::systemBus->async_method_call( 424 [aResp](const boost::system::error_code ec) { 425 if (ec) 426 { 427 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 428 return; 429 } 430 BMCWEB_LOG_DEBUG << "Default Gateway is Set"; 431 }, 432 "xyz.openbmc_project.Settings", 433 "/xyz/openbmc_project/network/hypervisor", 434 "org.freedesktop.DBus.Properties", "Set", 435 "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", 436 dbus::utility::DbusVariantType(gateway)); 437 } 438 439 /** 440 * @brief Creates a static IPv4 entry 441 * 442 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 443 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 444 * @param[in] gateway IPv4 address of this interfaces gateway 445 * @param[in] address IPv4 address to assign to this interface 446 * @param[io] asyncResp Response object that will be returned to client 447 * 448 * @return None 449 */ 450 inline void 451 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 452 const std::string& gateway, const std::string& address, 453 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 454 { 455 setHypervisorIPv4Address(asyncResp, ifaceId, address); 456 setHypervisorIPv4Gateway(asyncResp, gateway); 457 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 458 } 459 460 /** 461 * @brief Deletes given IPv4 interface 462 * 463 * @param[in] ifaceId Id of interface whose IP should be deleted 464 * @param[io] asyncResp Response object that will be returned to client 465 * 466 * @return None 467 */ 468 inline void 469 deleteHypervisorIPv4(const std::string& ifaceId, 470 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 471 { 472 std::string address = "0.0.0.0"; 473 std::string gateway = "0.0.0.0"; 474 const uint8_t prefixLength = 0; 475 setHypervisorIPv4Address(asyncResp, ifaceId, address); 476 setHypervisorIPv4Gateway(asyncResp, gateway); 477 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 478 } 479 480 inline void parseInterfaceData( 481 nlohmann::json& jsonResponse, const std::string& ifaceId, 482 const EthernetInterfaceData& ethData, 483 const boost::container::flat_set<IPv4AddressData>& ipv4Data) 484 { 485 jsonResponse["Id"] = ifaceId; 486 jsonResponse["@odata.id"] = 487 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + ifaceId; 488 jsonResponse["InterfaceEnabled"] = true; 489 jsonResponse["MACAddress"] = ethData.mac_address; 490 491 jsonResponse["HostName"] = ethData.hostname; 492 jsonResponse["DHCPv4"]["DHCPEnabled"] = 493 translateDHCPEnabledToBool(ethData.DHCPEnabled, true); 494 495 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 496 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 497 ipv4Array = nlohmann::json::array(); 498 ipv4StaticArray = nlohmann::json::array(); 499 for (const auto& ipv4Config : ipv4Data) 500 { 501 if (ipv4Config.isActive) 502 { 503 504 ipv4Array.push_back({{"AddressOrigin", ipv4Config.origin}, 505 {"SubnetMask", ipv4Config.netmask}, 506 {"Address", ipv4Config.address}, 507 {"Gateway", ethData.default_gateway}}); 508 if (ipv4Config.origin == "Static") 509 { 510 ipv4StaticArray.push_back( 511 {{"AddressOrigin", ipv4Config.origin}, 512 {"SubnetMask", ipv4Config.netmask}, 513 {"Address", ipv4Config.address}, 514 {"Gateway", ethData.default_gateway}}); 515 } 516 } 517 } 518 } 519 520 inline void setDHCPEnabled(const std::string& ifaceId, 521 const bool& ipv4DHCPEnabled, 522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 523 { 524 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 525 crow::connections::systemBus->async_method_call( 526 [asyncResp](const boost::system::error_code ec) { 527 if (ec) 528 { 529 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 530 messages::internalError(asyncResp->res); 531 return; 532 } 533 }, 534 "xyz.openbmc_project.Settings", 535 "/xyz/openbmc_project/network/hypervisor/" + ifaceId, 536 "org.freedesktop.DBus.Properties", "Set", 537 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", 538 dbus::utility::DbusVariantType{dhcp}); 539 540 // Set the IPv4 address origin to the DHCP / Static as per the new value 541 // of the DHCPEnabled property 542 std::string origin; 543 if (!ipv4DHCPEnabled) 544 { 545 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 546 } 547 else 548 { 549 // DHCPEnabled is set to true. Delete the current IPv4 settings 550 // to receive the new values from DHCP server. 551 deleteHypervisorIPv4(ifaceId, asyncResp); 552 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 553 } 554 crow::connections::systemBus->async_method_call( 555 [asyncResp](const boost::system::error_code ec) { 556 if (ec) 557 { 558 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 559 messages::internalError(asyncResp->res); 560 return; 561 } 562 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; 563 }, 564 "xyz.openbmc_project.Settings", 565 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 566 "org.freedesktop.DBus.Properties", "Set", 567 "xyz.openbmc_project.Network.IP", "Origin", 568 dbus::utility::DbusVariantType(origin)); 569 } 570 571 inline void handleHypervisorIPv4StaticPatch( 572 const std::string& ifaceId, const nlohmann::json& input, 573 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 574 { 575 if ((!input.is_array()) || input.empty()) 576 { 577 messages::propertyValueTypeError(asyncResp->res, input.dump(), 578 "IPv4StaticAddresses"); 579 return; 580 } 581 582 // Hypervisor considers the first IP address in the array list 583 // as the Hypervisor's virtual management interface supports single IPv4 584 // address 585 const nlohmann::json& thisJson = input[0]; 586 587 // For the error string 588 std::string pathString = "IPv4StaticAddresses/1"; 589 590 if (!thisJson.is_null() && !thisJson.empty()) 591 { 592 std::optional<std::string> address; 593 std::optional<std::string> subnetMask; 594 std::optional<std::string> gateway; 595 nlohmann::json thisJsonCopy = thisJson; 596 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 597 address, "SubnetMask", subnetMask, "Gateway", 598 gateway)) 599 { 600 messages::propertyValueFormatError( 601 asyncResp->res, 602 thisJson.dump(2, ' ', true, 603 nlohmann::json::error_handler_t::replace), 604 pathString); 605 return; 606 } 607 608 uint8_t prefixLength = 0; 609 bool errorInEntry = false; 610 if (address) 611 { 612 if (!ipv4VerifyIpAndGetBitcount(*address)) 613 { 614 messages::propertyValueFormatError(asyncResp->res, *address, 615 pathString + "/Address"); 616 errorInEntry = true; 617 } 618 } 619 else 620 { 621 messages::propertyMissing(asyncResp->res, pathString + "/Address"); 622 errorInEntry = true; 623 } 624 625 if (subnetMask) 626 { 627 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 628 { 629 messages::propertyValueFormatError(asyncResp->res, *subnetMask, 630 pathString + "/SubnetMask"); 631 errorInEntry = true; 632 } 633 } 634 else 635 { 636 messages::propertyMissing(asyncResp->res, 637 pathString + "/SubnetMask"); 638 errorInEntry = true; 639 } 640 641 if (gateway) 642 { 643 if (!ipv4VerifyIpAndGetBitcount(*gateway)) 644 { 645 messages::propertyValueFormatError(asyncResp->res, *gateway, 646 pathString + "/Gateway"); 647 errorInEntry = true; 648 } 649 } 650 else 651 { 652 messages::propertyMissing(asyncResp->res, pathString + "/Gateway"); 653 errorInEntry = true; 654 } 655 656 if (errorInEntry) 657 { 658 return; 659 } 660 661 BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId 662 << "," << *address; 663 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 664 asyncResp); 665 // Set the DHCPEnabled to false since the Static IPv4 is set 666 setDHCPEnabled(ifaceId, false, asyncResp); 667 } 668 else 669 { 670 if (thisJson.is_null()) 671 { 672 deleteHypervisorIPv4(ifaceId, asyncResp); 673 } 674 } 675 } 676 677 inline void 678 handleHostnamePatch(const std::string& hostName, 679 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 680 { 681 if (!isHostnameValid(hostName)) 682 { 683 messages::propertyValueFormatError(asyncResp->res, hostName, 684 "HostName"); 685 return; 686 } 687 688 asyncResp->res.jsonValue["HostName"] = hostName; 689 crow::connections::systemBus->async_method_call( 690 [asyncResp](const boost::system::error_code ec) { 691 if (ec) 692 { 693 messages::internalError(asyncResp->res); 694 } 695 }, 696 "xyz.openbmc_project.Settings", 697 "/xyz/openbmc_project/network/hypervisor", 698 "org.freedesktop.DBus.Properties", "Set", 699 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 700 dbus::utility::DbusVariantType(hostName)); 701 } 702 703 inline void 704 setIPv4InterfaceEnabled(const std::string& ifaceId, const bool& isActive, 705 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 706 { 707 crow::connections::systemBus->async_method_call( 708 [asyncResp](const boost::system::error_code ec) { 709 if (ec) 710 { 711 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 712 messages::internalError(asyncResp->res); 713 return; 714 } 715 }, 716 "xyz.openbmc_project.Settings", 717 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 718 "org.freedesktop.DBus.Properties", "Set", 719 "xyz.openbmc_project.Object.Enable", "Enabled", 720 dbus::utility::DbusVariantType(isActive)); 721 } 722 723 inline void requestRoutesHypervisorSystems(App& app) 724 { 725 /** 726 * Hypervisor Systems derived class for delivering Computer Systems Schema. 727 */ 728 729 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/") 730 .privileges(redfish::privileges::getComputerSystem) 731 .methods( 732 boost::beast::http::verb:: 733 get)([](const crow::Request&, 734 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 735 sdbusplus::asio::getProperty<std::string>( 736 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 737 "/xyz/openbmc_project/network/hypervisor", 738 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 739 [asyncResp](const boost::system::error_code ec, 740 const std::string& /*hostName*/) { 741 if (ec) 742 { 743 messages::resourceNotFound(asyncResp->res, "System", 744 "hypervisor"); 745 return; 746 } 747 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 748 749 asyncResp->res.jsonValue["@odata.type"] = 750 "#ComputerSystem.v1_6_0.ComputerSystem"; 751 asyncResp->res.jsonValue["@odata.id"] = 752 "/redfish/v1/Systems/hypervisor"; 753 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 754 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 755 asyncResp->res.jsonValue["Id"] = "hypervisor"; 756 asyncResp->res.jsonValue["SystemType"] = "OS"; 757 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 758 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 759 asyncResp->res.jsonValue["EthernetInterfaces"] = { 760 {"@odata.id", 761 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"}}; 762 getHypervisorState(asyncResp); 763 getHypervisorActions(asyncResp); 764 // TODO: Add "SystemType" : "hypervisor" 765 }); 766 }); 767 768 /** 769 * HypervisorInterfaceCollection class to handle the GET and PATCH on 770 * Hypervisor Interface 771 */ 772 773 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 774 .privileges(redfish::privileges::getEthernetInterfaceCollection) 775 .methods( 776 boost::beast::http::verb:: 777 get)([](const crow::Request&, 778 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 779 const std::array<const char*, 1> interfaces = { 780 "xyz.openbmc_project.Network.EthernetInterface"}; 781 782 crow::connections::systemBus->async_method_call( 783 [asyncResp](const boost::system::error_code error, 784 const dbus::utility::MapperGetSubTreePathsResponse& 785 ifaceList) { 786 if (error) 787 { 788 messages::resourceNotFound(asyncResp->res, "System", 789 "hypervisor"); 790 return; 791 } 792 asyncResp->res.jsonValue["@odata.type"] = 793 "#EthernetInterfaceCollection." 794 "EthernetInterfaceCollection"; 795 asyncResp->res.jsonValue["@odata.id"] = 796 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 797 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 798 "Interface Collection"; 799 asyncResp->res.jsonValue["Description"] = 800 "Collection of Virtual Management " 801 "Interfaces for the hypervisor"; 802 803 nlohmann::json& ifaceArray = 804 asyncResp->res.jsonValue["Members"]; 805 ifaceArray = nlohmann::json::array(); 806 for (const std::string& iface : ifaceList) 807 { 808 sdbusplus::message::object_path path(iface); 809 std::string name = path.filename(); 810 if (name.empty()) 811 { 812 continue; 813 } 814 815 ifaceArray.push_back( 816 {{"@odata.id", 817 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + 818 name}}); 819 } 820 asyncResp->res.jsonValue["Members@odata.count"] = 821 ifaceArray.size(); 822 }, 823 "xyz.openbmc_project.ObjectMapper", 824 "/xyz/openbmc_project/object_mapper", 825 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 826 "/xyz/openbmc_project/network/hypervisor", 0, interfaces); 827 }); 828 829 BMCWEB_ROUTE(app, 830 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 831 .privileges(redfish::privileges::getEthernetInterface) 832 .methods( 833 boost::beast::http::verb::get)([](const crow::Request&, 834 const std::shared_ptr< 835 bmcweb::AsyncResp>& asyncResp, 836 const std::string& id) { 837 getHypervisorIfaceData( 838 id, 839 [asyncResp, ifaceId{std::string(id)}]( 840 const bool& success, const EthernetInterfaceData& ethData, 841 const boost::container::flat_set<IPv4AddressData>& 842 ipv4Data) { 843 if (!success) 844 { 845 messages::resourceNotFound( 846 asyncResp->res, "EthernetInterface", ifaceId); 847 return; 848 } 849 asyncResp->res.jsonValue["@odata.type"] = 850 "#EthernetInterface.v1_5_1.EthernetInterface"; 851 asyncResp->res.jsonValue["Name"] = 852 "Hypervisor Ethernet Interface"; 853 asyncResp->res.jsonValue["Description"] = 854 "Hypervisor's Virtual Management Ethernet Interface"; 855 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, 856 ethData, ipv4Data); 857 }); 858 }); 859 860 BMCWEB_ROUTE(app, 861 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 862 .privileges(redfish::privileges::patchEthernetInterface) 863 .methods( 864 boost::beast::http::verb:: 865 patch)([](const crow::Request& req, 866 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 867 const std::string& ifaceId) { 868 std::optional<std::string> hostName; 869 std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; 870 std::optional<nlohmann::json> ipv4Addresses; 871 std::optional<nlohmann::json> dhcpv4; 872 std::optional<bool> ipv4DHCPEnabled; 873 874 if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", 875 hostName, "IPv4StaticAddresses", 876 ipv4StaticAddresses, "IPv4Addresses", 877 ipv4Addresses, "DHCPv4", dhcpv4)) 878 { 879 return; 880 } 881 882 if (ipv4Addresses) 883 { 884 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 885 return; 886 } 887 888 if (dhcpv4) 889 { 890 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 891 ipv4DHCPEnabled)) 892 { 893 return; 894 } 895 } 896 897 getHypervisorIfaceData( 898 ifaceId, 899 [asyncResp, ifaceId, hostName = std::move(hostName), 900 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 901 ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)]( 902 const bool& success, const EthernetInterfaceData& ethData, 903 const boost::container::flat_set<IPv4AddressData>&) { 904 if (!success) 905 { 906 messages::resourceNotFound( 907 asyncResp->res, "EthernetInterface", ifaceId); 908 return; 909 } 910 911 if (ipv4StaticAddresses) 912 { 913 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 914 if (ipv4Static.begin() == ipv4Static.end()) 915 { 916 messages::propertyValueTypeError( 917 asyncResp->res, 918 ipv4Static.dump( 919 2, ' ', true, 920 nlohmann::json::error_handler_t::replace), 921 "IPv4StaticAddresses"); 922 return; 923 } 924 925 // One and only one hypervisor instance supported 926 if (ipv4Static.size() != 1) 927 { 928 messages::propertyValueFormatError( 929 asyncResp->res, 930 ipv4Static.dump( 931 2, ' ', true, 932 nlohmann::json::error_handler_t::replace), 933 "IPv4StaticAddresses"); 934 return; 935 } 936 937 const nlohmann::json& ipv4Json = ipv4Static[0]; 938 // Check if the param is 'null'. If its null, it means 939 // that user wants to delete the IP address. Deleting 940 // the IP address is allowed only if its statically 941 // configured. Deleting the address originated from DHCP 942 // is not allowed. 943 if ((ipv4Json.is_null()) && 944 (translateDHCPEnabledToBool(ethData.DHCPEnabled, 945 true))) 946 { 947 BMCWEB_LOG_INFO 948 << "Ignoring the delete on ipv4StaticAddresses " 949 "as the interface is DHCP enabled"; 950 } 951 else 952 { 953 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 954 asyncResp); 955 } 956 } 957 958 if (hostName) 959 { 960 handleHostnamePatch(*hostName, asyncResp); 961 } 962 963 if (dhcpv4) 964 { 965 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 966 } 967 968 // Set this interface to disabled/inactive. This will be set 969 // to enabled/active by the pldm once the hypervisor 970 // consumes the updated settings from the user. 971 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 972 }); 973 asyncResp->res.result(boost::beast::http::status::accepted); 974 }); 975 976 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/ResetActionInfo/") 977 .privileges(redfish::privileges::getActionInfo) 978 .methods(boost::beast::http::verb::get)( 979 [](const crow::Request&, 980 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 981 // Only return action info if hypervisor D-Bus object present 982 crow::connections::systemBus->async_method_call( 983 [asyncResp]( 984 const boost::system::error_code ec, 985 const std::vector<std::pair< 986 std::string, std::vector<std::string>>>& objInfo) { 987 if (ec) 988 { 989 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 990 991 // No hypervisor objects found by mapper 992 if (ec.value() == boost::system::errc::io_error) 993 { 994 messages::resourceNotFound(asyncResp->res, 995 "hypervisor", 996 "ResetActionInfo"); 997 return; 998 } 999 1000 messages::internalError(asyncResp->res); 1001 return; 1002 } 1003 1004 // One and only one hypervisor instance supported 1005 if (objInfo.size() != 1) 1006 { 1007 messages::internalError(asyncResp->res); 1008 return; 1009 } 1010 1011 // The hypervisor object only support the ability to 1012 // turn On The system object Action should be utilized 1013 // for other operations 1014 asyncResp->res.jsonValue = { 1015 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"}, 1016 {"@odata.id", 1017 "/redfish/v1/Systems/hypervisor/ResetActionInfo"}, 1018 {"Name", "Reset Action Info"}, 1019 {"Id", "ResetActionInfo"}, 1020 {"Parameters", 1021 {{{"Name", "ResetType"}, 1022 {"Required", true}, 1023 {"DataType", "String"}, 1024 {"AllowableValues", {"On"}}}}}}; 1025 }, 1026 "xyz.openbmc_project.ObjectMapper", 1027 "/xyz/openbmc_project/object_mapper", 1028 "xyz.openbmc_project.ObjectMapper", "GetObject", 1029 "/xyz/openbmc_project/state/hypervisor0", 1030 std::array<const char*, 1>{ 1031 "xyz.openbmc_project.State.Host"}); 1032 }); 1033 1034 BMCWEB_ROUTE(app, 1035 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 1036 .privileges(redfish::privileges::postComputerSystem) 1037 .methods(boost::beast::http::verb::post)( 1038 [](const crow::Request& req, 1039 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1040 std::optional<std::string> resetType; 1041 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 1042 resetType)) 1043 { 1044 // readJson adds appropriate error to response 1045 return; 1046 } 1047 1048 if (!resetType) 1049 { 1050 messages::actionParameterMissing( 1051 asyncResp->res, "ComputerSystem.Reset", "ResetType"); 1052 return; 1053 } 1054 1055 // Hypervisor object only support On operation 1056 if (resetType != "On") 1057 { 1058 messages::propertyValueNotInList(asyncResp->res, *resetType, 1059 "ResetType"); 1060 return; 1061 } 1062 1063 std::string command = 1064 "xyz.openbmc_project.State.Host.Transition.On"; 1065 1066 crow::connections::systemBus->async_method_call( 1067 [asyncResp, resetType](const boost::system::error_code ec) { 1068 if (ec) 1069 { 1070 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1071 if (ec.value() == 1072 boost::asio::error::invalid_argument) 1073 { 1074 messages::actionParameterNotSupported( 1075 asyncResp->res, *resetType, "Reset"); 1076 return; 1077 } 1078 1079 if (ec.value() == 1080 boost::asio::error::host_unreachable) 1081 { 1082 messages::resourceNotFound(asyncResp->res, 1083 "Actions", "Reset"); 1084 return; 1085 } 1086 1087 messages::internalError(asyncResp->res); 1088 return; 1089 } 1090 messages::success(asyncResp->res); 1091 }, 1092 "xyz.openbmc_project.State.Hypervisor", 1093 "/xyz/openbmc_project/state/hypervisor0", 1094 "org.freedesktop.DBus.Properties", "Set", 1095 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1096 dbus::utility::DbusVariantType{std::move(command)}); 1097 }); 1098 } 1099 } // namespace redfish::hypervisor 1100