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