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