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