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["SystemType"] = "OS"; 752 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 753 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 754 asyncResp->res.jsonValue["EthernetInterfaces"] = { 755 {"@odata.id", "/redfish/v1/Systems/hypervisor/" 756 "EthernetInterfaces"}}; 757 getHypervisorState(asyncResp); 758 getHypervisorActions(asyncResp); 759 // TODO: Add "SystemType" : "hypervisor" 760 }, 761 "xyz.openbmc_project.Settings", 762 "/xyz/openbmc_project/network/hypervisor", 763 "org.freedesktop.DBus.Properties", "Get", 764 "xyz.openbmc_project.Network.SystemConfiguration", 765 "HostName"); 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(boost::beast::http::verb::get)( 776 [](const crow::Request&, 777 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 778 const std::array<const char*, 1> interfaces = { 779 "xyz.openbmc_project.Network.EthernetInterface"}; 780 781 crow::connections::systemBus->async_method_call( 782 [asyncResp](const boost::system::error_code error, 783 const std::vector<std::string>& ifaceList) { 784 if (error) 785 { 786 messages::resourceNotFound(asyncResp->res, "System", 787 "hypervisor"); 788 return; 789 } 790 asyncResp->res.jsonValue["@odata.type"] = 791 "#EthernetInterfaceCollection." 792 "EthernetInterfaceCollection"; 793 asyncResp->res.jsonValue["@odata.id"] = 794 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 795 asyncResp->res.jsonValue["Name"] = 796 "Hypervisor Ethernet " 797 "Interface Collection"; 798 asyncResp->res.jsonValue["Description"] = 799 "Collection of Virtual Management " 800 "Interfaces for the hypervisor"; 801 802 nlohmann::json& ifaceArray = 803 asyncResp->res.jsonValue["Members"]; 804 ifaceArray = nlohmann::json::array(); 805 for (const std::string& iface : ifaceList) 806 { 807 sdbusplus::message::object_path path(iface); 808 std::string name = path.filename(); 809 if (name.empty()) 810 { 811 continue; 812 } 813 814 ifaceArray.push_back( 815 {{"@odata.id", "/redfish/v1/Systems/hypervisor/" 816 "EthernetInterfaces/" + 817 name}}); 818 } 819 asyncResp->res.jsonValue["Members@odata.count"] = 820 ifaceArray.size(); 821 }, 822 "xyz.openbmc_project.ObjectMapper", 823 "/xyz/openbmc_project/object_mapper", 824 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 825 "/xyz/openbmc_project/network/hypervisor", 0, interfaces); 826 }); 827 828 BMCWEB_ROUTE(app, 829 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 830 .privileges(redfish::privileges::getEthernetInterface) 831 .methods( 832 boost::beast::http::verb::get)([](const crow::Request&, 833 const std::shared_ptr< 834 bmcweb::AsyncResp>& asyncResp, 835 const std::string& id) { 836 getHypervisorIfaceData( 837 id, 838 [asyncResp, ifaceId{std::string(id)}]( 839 const bool& success, const EthernetInterfaceData& ethData, 840 const boost::container::flat_set<IPv4AddressData>& 841 ipv4Data) { 842 if (!success) 843 { 844 messages::resourceNotFound( 845 asyncResp->res, "EthernetInterface", ifaceId); 846 return; 847 } 848 asyncResp->res.jsonValue["@odata.type"] = 849 "#EthernetInterface.v1_5_1.EthernetInterface"; 850 asyncResp->res.jsonValue["Name"] = 851 "Hypervisor Ethernet Interface"; 852 asyncResp->res.jsonValue["Description"] = 853 "Hypervisor's Virtual Management Ethernet Interface"; 854 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, 855 ethData, ipv4Data); 856 }); 857 }); 858 859 BMCWEB_ROUTE(app, 860 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 861 .privileges(redfish::privileges::patchEthernetInterface) 862 .methods( 863 boost::beast::http::verb:: 864 patch)([](const crow::Request& req, 865 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 866 const std::string& ifaceId) { 867 std::optional<std::string> hostName; 868 std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; 869 std::optional<nlohmann::json> ipv4Addresses; 870 std::optional<nlohmann::json> dhcpv4; 871 std::optional<bool> ipv4DHCPEnabled; 872 873 if (!json_util::readJson(req, asyncResp->res, "HostName", hostName, 874 "IPv4StaticAddresses", ipv4StaticAddresses, 875 "IPv4Addresses", ipv4Addresses, "DHCPv4", 876 dhcpv4)) 877 { 878 return; 879 } 880 881 if (ipv4Addresses) 882 { 883 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 884 return; 885 } 886 887 if (dhcpv4) 888 { 889 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 890 ipv4DHCPEnabled)) 891 { 892 return; 893 } 894 } 895 896 getHypervisorIfaceData( 897 ifaceId, 898 [asyncResp, ifaceId, hostName = std::move(hostName), 899 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 900 ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)]( 901 const bool& success, const EthernetInterfaceData& ethData, 902 const boost::container::flat_set<IPv4AddressData>&) { 903 if (!success) 904 { 905 messages::resourceNotFound( 906 asyncResp->res, "EthernetInterface", ifaceId); 907 return; 908 } 909 910 if (ipv4StaticAddresses) 911 { 912 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 913 if (ipv4Static.begin() == ipv4Static.end()) 914 { 915 messages::propertyValueTypeError( 916 asyncResp->res, 917 ipv4Static.dump( 918 2, ' ', true, 919 nlohmann::json::error_handler_t::replace), 920 "IPv4StaticAddresses"); 921 return; 922 } 923 924 // One and only one hypervisor instance supported 925 if (ipv4Static.size() != 1) 926 { 927 messages::propertyValueFormatError( 928 asyncResp->res, 929 ipv4Static.dump( 930 2, ' ', true, 931 nlohmann::json::error_handler_t::replace), 932 "IPv4StaticAddresses"); 933 return; 934 } 935 936 const nlohmann::json& ipv4Json = ipv4Static[0]; 937 // Check if the param is 'null'. If its null, it means 938 // that user wants to delete the IP address. Deleting 939 // the IP address is allowed only if its statically 940 // configured. Deleting the address originated from DHCP 941 // is not allowed. 942 if ((ipv4Json.is_null()) && 943 (translateDHCPEnabledToBool(ethData.DHCPEnabled, 944 true))) 945 { 946 BMCWEB_LOG_INFO 947 << "Ignoring the delete on ipv4StaticAddresses " 948 "as the interface is DHCP enabled"; 949 } 950 else 951 { 952 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 953 asyncResp); 954 } 955 } 956 957 if (hostName) 958 { 959 handleHostnamePatch(*hostName, asyncResp); 960 } 961 962 if (dhcpv4) 963 { 964 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 965 } 966 967 // Set this interface to disabled/inactive. This will be set 968 // to enabled/active by the pldm once the hypervisor 969 // consumes the updated settings from the user. 970 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 971 }); 972 asyncResp->res.result(boost::beast::http::status::accepted); 973 }); 974 975 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/ResetActionInfo/") 976 .privileges(redfish::privileges::getActionInfo) 977 .methods(boost::beast::http::verb::get)( 978 [](const crow::Request&, 979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 980 // Only return action info if hypervisor D-Bus object present 981 crow::connections::systemBus->async_method_call( 982 [asyncResp]( 983 const boost::system::error_code ec, 984 const std::vector<std::pair< 985 std::string, std::vector<std::string>>>& objInfo) { 986 if (ec) 987 { 988 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 989 990 // No hypervisor objects found by mapper 991 if (ec.value() == boost::system::errc::io_error) 992 { 993 messages::resourceNotFound(asyncResp->res, 994 "hypervisor", 995 "ResetActionInfo"); 996 return; 997 } 998 999 messages::internalError(asyncResp->res); 1000 return; 1001 } 1002 1003 // One and only one hypervisor instance supported 1004 if (objInfo.size() != 1) 1005 { 1006 messages::internalError(asyncResp->res); 1007 return; 1008 } 1009 1010 // The hypervisor object only support the ability to 1011 // turn On The system object Action should be utilized 1012 // for other operations 1013 asyncResp->res.jsonValue = { 1014 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"}, 1015 {"@odata.id", 1016 "/redfish/v1/Systems/hypervisor/ResetActionInfo"}, 1017 {"Name", "Reset Action Info"}, 1018 {"Id", "ResetActionInfo"}, 1019 {"Parameters", 1020 {{{"Name", "ResetType"}, 1021 {"Required", true}, 1022 {"DataType", "String"}, 1023 {"AllowableValues", {"On"}}}}}}; 1024 }, 1025 "xyz.openbmc_project.ObjectMapper", 1026 "/xyz/openbmc_project/object_mapper", 1027 "xyz.openbmc_project.ObjectMapper", "GetObject", 1028 "/xyz/openbmc_project/state/hypervisor0", 1029 std::array<const char*, 1>{ 1030 "xyz.openbmc_project.State.Host"}); 1031 }); 1032 1033 BMCWEB_ROUTE(app, 1034 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 1035 .privileges(redfish::privileges::postComputerSystem) 1036 .methods(boost::beast::http::verb::post)( 1037 [](const crow::Request& req, 1038 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 1039 std::optional<std::string> resetType; 1040 if (!json_util::readJson(req, asyncResp->res, "ResetType", 1041 resetType)) 1042 { 1043 // readJson adds appropriate error to response 1044 return; 1045 } 1046 1047 if (!resetType) 1048 { 1049 messages::actionParameterMissing( 1050 asyncResp->res, "ComputerSystem.Reset", "ResetType"); 1051 return; 1052 } 1053 1054 // Hypervisor object only support On operation 1055 if (resetType != "On") 1056 { 1057 messages::propertyValueNotInList(asyncResp->res, *resetType, 1058 "ResetType"); 1059 return; 1060 } 1061 1062 std::string command = 1063 "xyz.openbmc_project.State.Host.Transition.On"; 1064 1065 crow::connections::systemBus->async_method_call( 1066 [asyncResp, resetType](const boost::system::error_code ec) { 1067 if (ec) 1068 { 1069 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1070 if (ec.value() == 1071 boost::asio::error::invalid_argument) 1072 { 1073 messages::actionParameterNotSupported( 1074 asyncResp->res, *resetType, "Reset"); 1075 return; 1076 } 1077 1078 if (ec.value() == 1079 boost::asio::error::host_unreachable) 1080 { 1081 messages::resourceNotFound(asyncResp->res, 1082 "Actions", "Reset"); 1083 return; 1084 } 1085 1086 messages::internalError(asyncResp->res); 1087 return; 1088 } 1089 messages::success(asyncResp->res); 1090 }, 1091 "xyz.openbmc_project.State.Hypervisor", 1092 "/xyz/openbmc_project/state/hypervisor0", 1093 "org.freedesktop.DBus.Properties", "Set", 1094 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1095 std::variant<std::string>{std::move(command)}); 1096 }); 1097 } 1098 } // namespace redfish::hypervisor 1099