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 <error_messages.hpp> 8 #include <registries/privilege_registry.hpp> 9 #include <utils/json_utils.hpp> 10 11 #include <optional> 12 #include <utility> 13 #include <variant> 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 std::variant<std::string>& 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." 62 "Quiesced") 63 { 64 aResp->res.jsonValue["PowerState"] = "On"; 65 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 66 } 67 else if (*s == "xyz.openbmc_project.State.Host.HostState." 68 "Standby") 69 { 70 aResp->res.jsonValue["PowerState"] = "On"; 71 aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; 72 } 73 else if (*s == "xyz.openbmc_project.State.Host.HostState." 74 "TransitioningToRunning") 75 { 76 aResp->res.jsonValue["PowerState"] = "PoweringOn"; 77 aResp->res.jsonValue["Status"]["State"] = "Starting"; 78 } 79 else if (*s == "xyz.openbmc_project.State.Host.HostState." 80 "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 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::move(callback)}](const boost::system::error_code error, 321 const GetManagedObjects& resp) { 322 EthernetInterfaceData ethData{}; 323 boost::container::flat_set<IPv4AddressData> ipv4Data; 324 if (error) 325 { 326 callback(false, ethData, ipv4Data); 327 return; 328 } 329 330 bool found = extractHypervisorInterfaceData(ethIfaceId, resp, 331 ethData, ipv4Data); 332 if (!found) 333 { 334 BMCWEB_LOG_INFO << "Hypervisor Interface not found"; 335 } 336 callback(found, ethData, ipv4Data); 337 }, 338 "xyz.openbmc_project.Settings", "/", 339 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 340 } 341 342 /** 343 * @brief Sets the Hypervisor Interface IPAddress DBUS 344 * 345 * @param[in] aResp Shared pointer for generating response message. 346 * @param[in] ipv4Address Address from the incoming request 347 * @param[in] ethIfaceId Hypervisor Interface Id 348 * 349 * @return None. 350 */ 351 inline void 352 setHypervisorIPv4Address(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 353 const std::string& ethIfaceId, 354 const std::string& ipv4Address) 355 { 356 BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address 357 << " on Iface: " << ethIfaceId; 358 crow::connections::systemBus->async_method_call( 359 [aResp](const boost::system::error_code ec) { 360 if (ec) 361 { 362 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 363 return; 364 } 365 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set"; 366 }, 367 "xyz.openbmc_project.Settings", 368 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 369 "org.freedesktop.DBus.Properties", "Set", 370 "xyz.openbmc_project.Network.IP", "Address", 371 std::variant<std::string>(ipv4Address)); 372 } 373 374 /** 375 * @brief Sets the Hypervisor Interface SubnetMask DBUS 376 * 377 * @param[in] aResp Shared pointer for generating response message. 378 * @param[in] subnet SubnetMask from the incoming request 379 * @param[in] ethIfaceId Hypervisor Interface Id 380 * 381 * @return None. 382 */ 383 inline void 384 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 385 const std::string& ethIfaceId, const uint8_t subnet) 386 { 387 BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet 388 << " on Iface: " << ethIfaceId; 389 390 crow::connections::systemBus->async_method_call( 391 [aResp](const boost::system::error_code ec) { 392 if (ec) 393 { 394 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 395 return; 396 } 397 BMCWEB_LOG_DEBUG << "SubnetMask is Set"; 398 }, 399 "xyz.openbmc_project.Settings", 400 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 401 "org.freedesktop.DBus.Properties", "Set", 402 "xyz.openbmc_project.Network.IP", "PrefixLength", 403 std::variant<uint8_t>(subnet)); 404 } 405 406 /** 407 * @brief Sets the Hypervisor Interface Gateway DBUS 408 * 409 * @param[in] aResp Shared pointer for generating response message. 410 * @param[in] gateway Gateway from the incoming request 411 * @param[in] ethIfaceId Hypervisor Interface Id 412 * 413 * @return None. 414 */ 415 inline void 416 setHypervisorIPv4Gateway(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 417 const std::string& gateway) 418 { 419 BMCWEB_LOG_DEBUG 420 << "Setting the DefaultGateway to the last configured gateway"; 421 422 crow::connections::systemBus->async_method_call( 423 [aResp](const boost::system::error_code ec) { 424 if (ec) 425 { 426 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 427 return; 428 } 429 BMCWEB_LOG_DEBUG << "Default Gateway is Set"; 430 }, 431 "xyz.openbmc_project.Settings", 432 "/xyz/openbmc_project/network/hypervisor", 433 "org.freedesktop.DBus.Properties", "Set", 434 "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", 435 std::variant<std::string>(gateway)); 436 } 437 438 /** 439 * @brief Creates a static IPv4 entry 440 * 441 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 442 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 443 * @param[in] gateway IPv4 address of this interfaces gateway 444 * @param[in] address IPv4 address to assign to this interface 445 * @param[io] asyncResp Response object that will be returned to client 446 * 447 * @return None 448 */ 449 inline void 450 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 451 const std::string& gateway, const std::string& address, 452 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 453 { 454 setHypervisorIPv4Address(asyncResp, ifaceId, address); 455 setHypervisorIPv4Gateway(asyncResp, gateway); 456 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 457 } 458 459 /** 460 * @brief Deletes given IPv4 interface 461 * 462 * @param[in] ifaceId Id of interface whose IP should be deleted 463 * @param[io] asyncResp Response object that will be returned to client 464 * 465 * @return None 466 */ 467 inline void 468 deleteHypervisorIPv4(const std::string& ifaceId, 469 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 470 { 471 std::string address = "0.0.0.0"; 472 std::string gateway = "0.0.0.0"; 473 const uint8_t prefixLength = 0; 474 setHypervisorIPv4Address(asyncResp, ifaceId, address); 475 setHypervisorIPv4Gateway(asyncResp, gateway); 476 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 477 } 478 479 inline void parseInterfaceData( 480 nlohmann::json& jsonResponse, const std::string& ifaceId, 481 const EthernetInterfaceData& ethData, 482 const boost::container::flat_set<IPv4AddressData>& ipv4Data) 483 { 484 jsonResponse["Id"] = ifaceId; 485 jsonResponse["@odata.id"] = 486 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + ifaceId; 487 jsonResponse["InterfaceEnabled"] = true; 488 jsonResponse["MACAddress"] = ethData.mac_address; 489 490 jsonResponse["HostName"] = ethData.hostname; 491 jsonResponse["DHCPv4"]["DHCPEnabled"] = 492 translateDHCPEnabledToBool(ethData.DHCPEnabled, true); 493 494 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 495 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 496 ipv4Array = nlohmann::json::array(); 497 ipv4StaticArray = nlohmann::json::array(); 498 for (auto& ipv4Config : ipv4Data) 499 { 500 if (ipv4Config.isActive) 501 { 502 503 ipv4Array.push_back({{"AddressOrigin", ipv4Config.origin}, 504 {"SubnetMask", ipv4Config.netmask}, 505 {"Address", ipv4Config.address}, 506 {"Gateway", ethData.default_gateway}}); 507 if (ipv4Config.origin == "Static") 508 { 509 ipv4StaticArray.push_back( 510 {{"AddressOrigin", ipv4Config.origin}, 511 {"SubnetMask", ipv4Config.netmask}, 512 {"Address", ipv4Config.address}, 513 {"Gateway", ethData.default_gateway}}); 514 } 515 } 516 } 517 } 518 519 inline void setDHCPEnabled(const std::string& ifaceId, 520 const bool& ipv4DHCPEnabled, 521 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 522 { 523 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 524 crow::connections::systemBus->async_method_call( 525 [asyncResp](const boost::system::error_code ec) { 526 if (ec) 527 { 528 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 529 messages::internalError(asyncResp->res); 530 return; 531 } 532 }, 533 "xyz.openbmc_project.Settings", 534 "/xyz/openbmc_project/network/hypervisor/" + ifaceId, 535 "org.freedesktop.DBus.Properties", "Set", 536 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", 537 std::variant<std::string>{dhcp}); 538 539 // Set the IPv4 address origin to the DHCP / Static as per the new value 540 // of the DHCPEnabled property 541 std::string origin; 542 if (ipv4DHCPEnabled == false) 543 { 544 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 545 } 546 else 547 { 548 // DHCPEnabled is set to true. Delete the current IPv4 settings 549 // to receive the new values from DHCP server. 550 deleteHypervisorIPv4(ifaceId, asyncResp); 551 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 552 } 553 crow::connections::systemBus->async_method_call( 554 [asyncResp](const boost::system::error_code ec) { 555 if (ec) 556 { 557 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 558 messages::internalError(asyncResp->res); 559 return; 560 } 561 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; 562 }, 563 "xyz.openbmc_project.Settings", 564 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 565 "org.freedesktop.DBus.Properties", "Set", 566 "xyz.openbmc_project.Network.IP", "Origin", 567 std::variant<std::string>(origin)); 568 } 569 570 inline void handleHypervisorIPv4StaticPatch( 571 const std::string& ifaceId, const nlohmann::json& input, 572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 573 { 574 if ((!input.is_array()) || input.empty()) 575 { 576 messages::propertyValueTypeError(asyncResp->res, input.dump(), 577 "IPv4StaticAddresses"); 578 return; 579 } 580 581 // Hypervisor considers the first IP address in the array list 582 // as the Hypervisor's virtual management interface supports single IPv4 583 // address 584 const nlohmann::json& thisJson = input[0]; 585 586 // For the error string 587 std::string pathString = "IPv4StaticAddresses/1"; 588 589 if (!thisJson.is_null() && !thisJson.empty()) 590 { 591 std::optional<std::string> address; 592 std::optional<std::string> subnetMask; 593 std::optional<std::string> gateway; 594 nlohmann::json thisJsonCopy = thisJson; 595 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 596 address, "SubnetMask", subnetMask, "Gateway", 597 gateway)) 598 { 599 messages::propertyValueFormatError( 600 asyncResp->res, 601 thisJson.dump(2, ' ', true, 602 nlohmann::json::error_handler_t::replace), 603 pathString); 604 return; 605 } 606 607 uint8_t prefixLength = 0; 608 bool errorInEntry = false; 609 if (address) 610 { 611 if (!ipv4VerifyIpAndGetBitcount(*address)) 612 { 613 messages::propertyValueFormatError(asyncResp->res, *address, 614 pathString + "/Address"); 615 errorInEntry = true; 616 } 617 } 618 else 619 { 620 messages::propertyMissing(asyncResp->res, pathString + "/Address"); 621 errorInEntry = true; 622 } 623 624 if (subnetMask) 625 { 626 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 627 { 628 messages::propertyValueFormatError(asyncResp->res, *subnetMask, 629 pathString + "/SubnetMask"); 630 errorInEntry = true; 631 } 632 } 633 else 634 { 635 messages::propertyMissing(asyncResp->res, 636 pathString + "/SubnetMask"); 637 errorInEntry = true; 638 } 639 640 if (gateway) 641 { 642 if (!ipv4VerifyIpAndGetBitcount(*gateway)) 643 { 644 messages::propertyValueFormatError(asyncResp->res, *gateway, 645 pathString + "/Gateway"); 646 errorInEntry = true; 647 } 648 } 649 else 650 { 651 messages::propertyMissing(asyncResp->res, pathString + "/Gateway"); 652 errorInEntry = true; 653 } 654 655 if (errorInEntry) 656 { 657 return; 658 } 659 660 BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId 661 << "," << *address; 662 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 663 asyncResp); 664 // Set the DHCPEnabled to false since the Static IPv4 is set 665 setDHCPEnabled(ifaceId, false, asyncResp); 666 } 667 else 668 { 669 if (thisJson.is_null()) 670 { 671 deleteHypervisorIPv4(ifaceId, asyncResp); 672 } 673 } 674 } 675 676 inline void 677 handleHostnamePatch(const std::string& hostName, 678 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 679 { 680 if (!isHostnameValid(hostName)) 681 { 682 messages::propertyValueFormatError(asyncResp->res, hostName, 683 "HostName"); 684 return; 685 } 686 687 asyncResp->res.jsonValue["HostName"] = hostName; 688 crow::connections::systemBus->async_method_call( 689 [asyncResp](const boost::system::error_code ec) { 690 if (ec) 691 { 692 messages::internalError(asyncResp->res); 693 } 694 }, 695 "xyz.openbmc_project.Settings", 696 "/xyz/openbmc_project/network/hypervisor", 697 "org.freedesktop.DBus.Properties", "Set", 698 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 699 std::variant<std::string>(hostName)); 700 } 701 702 inline void 703 setIPv4InterfaceEnabled(const std::string& ifaceId, const bool& isActive, 704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 705 { 706 crow::connections::systemBus->async_method_call( 707 [asyncResp](const boost::system::error_code ec) { 708 if (ec) 709 { 710 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 711 messages::internalError(asyncResp->res); 712 return; 713 } 714 }, 715 "xyz.openbmc_project.Settings", 716 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 717 "org.freedesktop.DBus.Properties", "Set", 718 "xyz.openbmc_project.Object.Enable", "Enabled", 719 std::variant<bool>(isActive)); 720 } 721 722 inline void requestRoutesHypervisorSystems(App& app) 723 { 724 /** 725 * Hypervisor Systems derived class for delivering Computer Systems Schema. 726 */ 727 728 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/") 729 .privileges(redfish::privileges::getComputerSystem) 730 .methods(boost::beast::http::verb::get)( 731 [](const crow::Request&, 732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 733 crow::connections::systemBus->async_method_call( 734 [asyncResp](const boost::system::error_code ec, 735 const std::variant<std::string>& /*hostName*/) { 736 if (ec) 737 { 738 messages::resourceNotFound(asyncResp->res, "System", 739 "hypervisor"); 740 return; 741 } 742 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 743 744 asyncResp->res.jsonValue["@odata.type"] = 745 "#ComputerSystem.v1_6_0.ComputerSystem"; 746 asyncResp->res.jsonValue["@odata.id"] = 747 "/redfish/v1/Systems/hypervisor"; 748 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 749 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 750 asyncResp->res.jsonValue["Id"] = "hypervisor"; 751 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 752 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 753 asyncResp->res.jsonValue["EthernetInterfaces"] = { 754 {"@odata.id", "/redfish/v1/Systems/hypervisor/" 755 "EthernetInterfaces"}}; 756 getHypervisorState(asyncResp); 757 getHypervisorActions(asyncResp); 758 // TODO: Add "SystemType" : "hypervisor" 759 }, 760 "xyz.openbmc_project.Settings", 761 "/xyz/openbmc_project/network/hypervisor", 762 "org.freedesktop.DBus.Properties", "Get", 763 "xyz.openbmc_project.Network.SystemConfiguration", 764 "HostName"); 765 }); 766 767 /** 768 * HypervisorInterfaceCollection class to handle the GET and PATCH on 769 * Hypervisor Interface 770 */ 771 772 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 773 .privileges(redfish::privileges::getEthernetInterfaceCollection) 774 .methods(boost::beast::http::verb::get)( 775 [](const crow::Request&, 776 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 777 const std::array<const char*, 1> interfaces = { 778 "xyz.openbmc_project.Network.EthernetInterface"}; 779 780 crow::connections::systemBus->async_method_call( 781 [asyncResp](const boost::system::error_code error, 782 const std::vector<std::string>& ifaceList) { 783 if (error) 784 { 785 messages::resourceNotFound(asyncResp->res, "System", 786 "hypervisor"); 787 return; 788 } 789 asyncResp->res.jsonValue["@odata.type"] = 790 "#EthernetInterfaceCollection." 791 "EthernetInterfaceCollection"; 792 asyncResp->res.jsonValue["@odata.id"] = 793 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 794 asyncResp->res.jsonValue["Name"] = 795 "Hypervisor Ethernet " 796 "Interface Collection"; 797 asyncResp->res.jsonValue["Description"] = 798 "Collection of Virtual Management " 799 "Interfaces for the hypervisor"; 800 801 nlohmann::json& ifaceArray = 802 asyncResp->res.jsonValue["Members"]; 803 ifaceArray = nlohmann::json::array(); 804 for (const std::string& iface : ifaceList) 805 { 806 sdbusplus::message::object_path path(iface); 807 std::string name = path.filename(); 808 if (name.empty()) 809 { 810 continue; 811 } 812 813 ifaceArray.push_back( 814 {{"@odata.id", "/redfish/v1/Systems/hypervisor/" 815 "EthernetInterfaces/" + 816 name}}); 817 } 818 asyncResp->res.jsonValue["Members@odata.count"] = 819 ifaceArray.size(); 820 }, 821 "xyz.openbmc_project.ObjectMapper", 822 "/xyz/openbmc_project/object_mapper", 823 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 824 "/xyz/openbmc_project/network/hypervisor", 0, interfaces); 825 }); 826 827 BMCWEB_ROUTE(app, 828 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 829 .privileges(redfish::privileges::getEthernetInterface) 830 .methods( 831 boost::beast::http::verb::get)([](const crow::Request&, 832 const std::shared_ptr< 833 bmcweb::AsyncResp>& asyncResp, 834 const std::string& id) { 835 getHypervisorIfaceData( 836 id, 837 [asyncResp, ifaceId{std::string(id)}]( 838 const bool& success, const EthernetInterfaceData& ethData, 839 const boost::container::flat_set<IPv4AddressData>& 840 ipv4Data) { 841 if (!success) 842 { 843 messages::resourceNotFound( 844 asyncResp->res, "EthernetInterface", ifaceId); 845 return; 846 } 847 asyncResp->res.jsonValue["@odata.type"] = 848 "#EthernetInterface.v1_5_1.EthernetInterface"; 849 asyncResp->res.jsonValue["Name"] = 850 "Hypervisor Ethernet Interface"; 851 asyncResp->res.jsonValue["Description"] = 852 "Hypervisor's Virtual Management Ethernet Interface"; 853 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, 854 ethData, ipv4Data); 855 }); 856 }); 857 858 BMCWEB_ROUTE(app, 859 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 860 .privileges(redfish::privileges::patchEthernetInterface) 861 .methods( 862 boost::beast::http::verb:: 863 patch)([](const crow::Request& req, 864 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 865 const std::string& ifaceId) { 866 std::optional<std::string> hostName; 867 std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; 868 std::optional<nlohmann::json> ipv4Addresses; 869 std::optional<nlohmann::json> dhcpv4; 870 std::optional<bool> ipv4DHCPEnabled; 871 872 if (!json_util::readJson(req, asyncResp->res, "HostName", hostName, 873 "IPv4StaticAddresses", ipv4StaticAddresses, 874 "IPv4Addresses", ipv4Addresses, "DHCPv4", 875 dhcpv4)) 876 { 877 return; 878 } 879 880 if (ipv4Addresses) 881 { 882 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 883 return; 884 } 885 886 if (dhcpv4) 887 { 888 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 889 ipv4DHCPEnabled)) 890 { 891 return; 892 } 893 } 894 895 getHypervisorIfaceData( 896 ifaceId, 897 [asyncResp, ifaceId, hostName = std::move(hostName), 898 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 899 ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)]( 900 const bool& success, const EthernetInterfaceData& ethData, 901 const boost::container::flat_set<IPv4AddressData>&) { 902 if (!success) 903 { 904 messages::resourceNotFound( 905 asyncResp->res, "EthernetInterface", ifaceId); 906 return; 907 } 908 909 if (ipv4StaticAddresses) 910 { 911 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 912 if (ipv4Static.begin() == ipv4Static.end()) 913 { 914 messages::propertyValueTypeError( 915 asyncResp->res, 916 ipv4Static.dump( 917 2, ' ', true, 918 nlohmann::json::error_handler_t::replace), 919 "IPv4StaticAddresses"); 920 return; 921 } 922 923 // One and only one hypervisor instance supported 924 if (ipv4Static.size() != 1) 925 { 926 messages::propertyValueFormatError( 927 asyncResp->res, 928 ipv4Static.dump( 929 2, ' ', true, 930 nlohmann::json::error_handler_t::replace), 931 "IPv4StaticAddresses"); 932 return; 933 } 934 935 const nlohmann::json& ipv4Json = ipv4Static[0]; 936 // Check if the param is 'null'. If its null, it means 937 // that user wants to delete the IP address. Deleting 938 // the IP address is allowed only if its statically 939 // configured. Deleting the address originated from DHCP 940 // is not allowed. 941 if ((ipv4Json.is_null()) && 942 (translateDHCPEnabledToBool(ethData.DHCPEnabled, 943 true))) 944 { 945 BMCWEB_LOG_INFO 946 << "Ignoring the delete on ipv4StaticAddresses " 947 "as the interface is DHCP enabled"; 948 } 949 else 950 { 951 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 952 asyncResp); 953 } 954 } 955 956 if (hostName) 957 { 958 handleHostnamePatch(*hostName, asyncResp); 959 } 960 961 if (dhcpv4) 962 { 963 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 964 } 965 966 // Set this interface to disabled/inactive. This will be set 967 // to enabled/active by the pldm once the hypervisor 968 // consumes the updated settings from the user. 969 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 970 }); 971 asyncResp->res.result(boost::beast::http::status::accepted); 972 }); 973 974 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/ResetActionInfo/") 975 .privileges(redfish::privileges::getActionInfo) 976 .methods(boost::beast::http::verb::get)( 977 [](const crow::Request&, 978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 979 // Only return action info if hypervisor D-Bus object present 980 crow::connections::systemBus->async_method_call( 981 [asyncResp]( 982 const boost::system::error_code ec, 983 const std::vector<std::pair< 984 std::string, std::vector<std::string>>>& objInfo) { 985 if (ec) 986 { 987 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 988 989 // No hypervisor objects found by mapper 990 if (ec.value() == boost::system::errc::io_error) 991 { 992 messages::resourceNotFound(asyncResp->res, 993 "hypervisor", 994 "ResetActionInfo"); 995 return; 996 } 997 998 messages::internalError(asyncResp->res); 999 return; 1000 } 1001 1002 // One and only one hypervisor instance supported 1003 if (objInfo.size() != 1) 1004 { 1005 messages::internalError(asyncResp->res); 1006 return; 1007 } 1008 1009 // The hypervisor object only support the ability to 1010 // turn On The system object Action should be utilized 1011 // for other operations 1012 asyncResp->res.jsonValue = { 1013 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"}, 1014 {"@odata.id", 1015 "/redfish/v1/Systems/hypervisor/ResetActionInfo"}, 1016 {"Name", "Reset Action Info"}, 1017 {"Id", "ResetActionInfo"}, 1018 {"Parameters", 1019 {{{"Name", "ResetType"}, 1020 {"Required", true}, 1021 {"DataType", "String"}, 1022 {"AllowableValues", {"On"}}}}}}; 1023 }, 1024 "xyz.openbmc_project.ObjectMapper", 1025 "/xyz/openbmc_project/object_mapper", 1026 "xyz.openbmc_project.ObjectMapper", "GetObject", 1027 "/xyz/openbmc_project/state/hypervisor0", 1028 std::array<const char*, 1>{ 1029 "xyz.openbmc_project.State.Host"}); 1030 }); 1031 1032 BMCWEB_ROUTE(app, 1033 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 1034 .privileges(redfish::privileges::postComputerSystem) 1035 .methods(boost::beast::http::verb::post)( 1036 [](const crow::Request& req, 1037 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1038 std::optional<std::string> resetType; 1039 if (!json_util::readJson(req, asyncResp->res, "ResetType", 1040 resetType)) 1041 { 1042 // readJson adds appropriate error to response 1043 return; 1044 } 1045 1046 if (!resetType) 1047 { 1048 messages::actionParameterMissing( 1049 asyncResp->res, "ComputerSystem.Reset", "ResetType"); 1050 return; 1051 } 1052 1053 // Hypervisor object only support On operation 1054 if (resetType != "On") 1055 { 1056 messages::propertyValueNotInList(asyncResp->res, *resetType, 1057 "ResetType"); 1058 return; 1059 } 1060 1061 std::string command = 1062 "xyz.openbmc_project.State.Host.Transition.On"; 1063 1064 crow::connections::systemBus->async_method_call( 1065 [asyncResp, resetType](const boost::system::error_code ec) { 1066 if (ec) 1067 { 1068 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1069 if (ec.value() == 1070 boost::asio::error::invalid_argument) 1071 { 1072 messages::actionParameterNotSupported( 1073 asyncResp->res, *resetType, "Reset"); 1074 return; 1075 } 1076 1077 if (ec.value() == 1078 boost::asio::error::host_unreachable) 1079 { 1080 messages::resourceNotFound(asyncResp->res, 1081 "Actions", "Reset"); 1082 return; 1083 } 1084 1085 messages::internalError(asyncResp->res); 1086 return; 1087 } 1088 messages::success(asyncResp->res); 1089 }, 1090 "xyz.openbmc_project.State.Hypervisor", 1091 "/xyz/openbmc_project/state/hypervisor0", 1092 "org.freedesktop.DBus.Properties", "Set", 1093 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1094 std::variant<std::string>{std::move(command)}); 1095 }); 1096 } 1097 } // namespace redfish::hypervisor 1098