1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include <boost/container/flat_map.hpp> 19 #include <boost/container/flat_set.hpp> 20 #include <dbus_singleton.hpp> 21 #include <error_messages.hpp> 22 #include <node.hpp> 23 #include <optional> 24 #include <utils/json_utils.hpp> 25 #include <variant> 26 27 namespace redfish 28 { 29 30 /** 31 * DBus types primitives for several generic DBus interfaces 32 * TODO(Pawel) consider move this to separate file into boost::dbus 33 */ 34 using PropertiesMapType = boost::container::flat_map< 35 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t, 36 int32_t, uint32_t, int64_t, uint64_t, double>>; 37 38 using GetManagedObjects = std::vector<std::pair< 39 sdbusplus::message::object_path, 40 std::vector<std::pair< 41 std::string, 42 boost::container::flat_map< 43 std::string, sdbusplus::message::variant< 44 std::string, bool, uint8_t, int16_t, uint16_t, 45 int32_t, uint32_t, int64_t, uint64_t, double, 46 std::vector<std::string>>>>>>>; 47 48 enum class LinkType 49 { 50 Local, 51 Global 52 }; 53 54 /** 55 * Structure for keeping IPv4 data required by Redfish 56 */ 57 struct IPv4AddressData 58 { 59 std::string id; 60 std::string address; 61 std::string domain; 62 std::string gateway; 63 std::string netmask; 64 std::string origin; 65 LinkType linktype; 66 67 bool operator<(const IPv4AddressData &obj) const 68 { 69 return id < obj.id; 70 } 71 }; 72 73 /** 74 * Structure for keeping basic single Ethernet Interface information 75 * available from DBus 76 */ 77 struct EthernetInterfaceData 78 { 79 uint32_t speed; 80 bool auto_neg; 81 std::string hostname; 82 std::string default_gateway; 83 std::string mac_address; 84 std::optional<uint32_t> vlan_id; 85 std::vector<std::string> nameservers; 86 }; 87 88 // Helper function that changes bits netmask notation (i.e. /24) 89 // into full dot notation 90 inline std::string getNetmask(unsigned int bits) 91 { 92 uint32_t value = 0xffffffff << (32 - bits); 93 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 94 std::to_string((value >> 16) & 0xff) + "." + 95 std::to_string((value >> 8) & 0xff) + "." + 96 std::to_string(value & 0xff); 97 return netmask; 98 } 99 100 inline std::string 101 translateAddressOriginDbusToRedfish(const std::string &inputOrigin, 102 bool isIPv4) 103 { 104 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static") 105 { 106 return "Static"; 107 } 108 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal") 109 { 110 if (isIPv4) 111 { 112 return "IPv4LinkLocal"; 113 } 114 else 115 { 116 return "LinkLocal"; 117 } 118 } 119 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP") 120 { 121 if (isIPv4) 122 { 123 return "DHCP"; 124 } 125 else 126 { 127 return "DHCPv6"; 128 } 129 } 130 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC") 131 { 132 return "SLAAC"; 133 } 134 return ""; 135 } 136 137 inline std::string 138 translateAddressOriginRedfishToDbus(const std::string &inputOrigin) 139 { 140 if (inputOrigin == "Static") 141 { 142 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 143 } 144 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6") 145 { 146 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 147 } 148 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal") 149 { 150 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal"; 151 } 152 if (inputOrigin == "SLAAC") 153 { 154 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC"; 155 } 156 return ""; 157 } 158 159 inline void extractEthernetInterfaceData(const std::string ðiface_id, 160 const GetManagedObjects &dbus_data, 161 EthernetInterfaceData ðData) 162 { 163 for (const auto &objpath : dbus_data) 164 { 165 for (const auto &ifacePair : objpath.second) 166 { 167 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id) 168 { 169 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 170 { 171 for (const auto &propertyPair : ifacePair.second) 172 { 173 if (propertyPair.first == "MACAddress") 174 { 175 const std::string *mac = 176 std::get_if<std::string>(&propertyPair.second); 177 if (mac != nullptr) 178 { 179 ethData.mac_address = *mac; 180 } 181 } 182 } 183 } 184 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN") 185 { 186 for (const auto &propertyPair : ifacePair.second) 187 { 188 if (propertyPair.first == "Id") 189 { 190 const uint32_t *id = 191 std::get_if<uint32_t>(&propertyPair.second); 192 if (id != nullptr) 193 { 194 ethData.vlan_id = *id; 195 } 196 } 197 } 198 } 199 else if (ifacePair.first == 200 "xyz.openbmc_project.Network.EthernetInterface") 201 { 202 for (const auto &propertyPair : ifacePair.second) 203 { 204 if (propertyPair.first == "AutoNeg") 205 { 206 const bool *auto_neg = 207 std::get_if<bool>(&propertyPair.second); 208 if (auto_neg != nullptr) 209 { 210 ethData.auto_neg = *auto_neg; 211 } 212 } 213 else if (propertyPair.first == "Speed") 214 { 215 const uint32_t *speed = 216 std::get_if<uint32_t>(&propertyPair.second); 217 if (speed != nullptr) 218 { 219 ethData.speed = *speed; 220 } 221 } 222 else if (propertyPair.first == "NameServers") 223 { 224 const std::vector<std::string> *nameservers = 225 sdbusplus::message::variant_ns::get_if< 226 std::vector<std::string>>( 227 &propertyPair.second); 228 if (nameservers != nullptr) 229 { 230 ethData.nameservers = std::move(*nameservers); 231 } 232 } 233 } 234 } 235 } 236 // System configuration shows up in the global namespace, so no need 237 // to check eth number 238 if (ifacePair.first == 239 "xyz.openbmc_project.Network.SystemConfiguration") 240 { 241 for (const auto &propertyPair : ifacePair.second) 242 { 243 if (propertyPair.first == "HostName") 244 { 245 const std::string *hostname = 246 sdbusplus::message::variant_ns::get_if<std::string>( 247 &propertyPair.second); 248 if (hostname != nullptr) 249 { 250 ethData.hostname = *hostname; 251 } 252 } 253 else if (propertyPair.first == "DefaultGateway") 254 { 255 const std::string *defaultGateway = 256 sdbusplus::message::variant_ns::get_if<std::string>( 257 &propertyPair.second); 258 if (defaultGateway != nullptr) 259 { 260 ethData.default_gateway = *defaultGateway; 261 } 262 } 263 } 264 } 265 } 266 } 267 } 268 269 // Helper function that extracts data for single ethernet ipv4 address 270 inline void 271 extractIPData(const std::string ðiface_id, 272 const GetManagedObjects &dbus_data, 273 boost::container::flat_set<IPv4AddressData> &ipv4_config) 274 { 275 const std::string ipv4PathStart = 276 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/"; 277 278 // Since there might be several IPv4 configurations aligned with 279 // single ethernet interface, loop over all of them 280 for (const auto &objpath : dbus_data) 281 { 282 // Check if proper pattern for object path appears 283 if (boost::starts_with(objpath.first.str, ipv4PathStart)) 284 { 285 for (auto &interface : objpath.second) 286 { 287 if (interface.first == "xyz.openbmc_project.Network.IP") 288 { 289 // Instance IPv4AddressData structure, and set as 290 // appropriate 291 std::pair< 292 boost::container::flat_set<IPv4AddressData>::iterator, 293 bool> 294 it = ipv4_config.insert( 295 {objpath.first.str.substr(ipv4PathStart.size())}); 296 IPv4AddressData &ipv4_address = *it.first; 297 for (auto &property : interface.second) 298 { 299 if (property.first == "Address") 300 { 301 const std::string *address = 302 std::get_if<std::string>(&property.second); 303 if (address != nullptr) 304 { 305 ipv4_address.address = *address; 306 } 307 } 308 else if (property.first == "Gateway") 309 { 310 const std::string *gateway = 311 std::get_if<std::string>(&property.second); 312 if (gateway != nullptr) 313 { 314 ipv4_address.gateway = *gateway; 315 } 316 } 317 else if (property.first == "Origin") 318 { 319 const std::string *origin = 320 std::get_if<std::string>(&property.second); 321 if (origin != nullptr) 322 { 323 ipv4_address.origin = 324 translateAddressOriginDbusToRedfish(*origin, 325 true); 326 } 327 } 328 else if (property.first == "PrefixLength") 329 { 330 const uint8_t *mask = 331 std::get_if<uint8_t>(&property.second); 332 if (mask != nullptr) 333 { 334 // convert it to the string 335 ipv4_address.netmask = getNetmask(*mask); 336 } 337 } 338 else 339 { 340 BMCWEB_LOG_ERROR 341 << "Got extra property: " << property.first 342 << " on the " << objpath.first.str << " object"; 343 } 344 } 345 // Check if given address is local, or global 346 ipv4_address.linktype = 347 boost::starts_with(ipv4_address.address, "169.254.") 348 ? LinkType::Global 349 : LinkType::Local; 350 } 351 } 352 } 353 } 354 } 355 356 /** 357 * @brief Sets given Id on the given VLAN interface through D-Bus 358 * 359 * @param[in] ifaceId Id of VLAN interface that should be modified 360 * @param[in] inputVlanId New ID of the VLAN 361 * @param[in] callback Function that will be called after the operation 362 * 363 * @return None. 364 */ 365 template <typename CallbackFunc> 366 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId, 367 CallbackFunc &&callback) 368 { 369 crow::connections::systemBus->async_method_call( 370 callback, "xyz.openbmc_project.Network", 371 std::string("/xyz/openbmc_project/network/") + ifaceId, 372 "org.freedesktop.DBus.Properties", "Set", 373 "xyz.openbmc_project.Network.VLAN", "Id", 374 std::variant<uint32_t>(inputVlanId)); 375 } 376 377 /** 378 * @brief Helper function that verifies IP address to check if it is in 379 * proper format. If bits pointer is provided, also calculates active 380 * bit count for Subnet Mask. 381 * 382 * @param[in] ip IP that will be verified 383 * @param[out] bits Calculated mask in bits notation 384 * 385 * @return true in case of success, false otherwise 386 */ 387 inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip, 388 uint8_t *bits = nullptr) 389 { 390 std::vector<std::string> bytesInMask; 391 392 boost::split(bytesInMask, ip, boost::is_any_of(".")); 393 394 static const constexpr int ipV4AddressSectionsCount = 4; 395 if (bytesInMask.size() != ipV4AddressSectionsCount) 396 { 397 return false; 398 } 399 400 if (bits != nullptr) 401 { 402 *bits = 0; 403 } 404 405 char *endPtr; 406 long previousValue = 255; 407 bool firstZeroInByteHit; 408 for (const std::string &byte : bytesInMask) 409 { 410 if (byte.empty()) 411 { 412 return false; 413 } 414 415 // Use strtol instead of stroi to avoid exceptions 416 long value = std::strtol(byte.c_str(), &endPtr, 10); 417 418 // endPtr should point to the end of the string, otherwise given string 419 // is not 100% number 420 if (*endPtr != '\0') 421 { 422 return false; 423 } 424 425 // Value should be contained in byte 426 if (value < 0 || value > 255) 427 { 428 return false; 429 } 430 431 if (bits != nullptr) 432 { 433 // Mask has to be continuous between bytes 434 if (previousValue != 255 && value != 0) 435 { 436 return false; 437 } 438 439 // Mask has to be continuous inside bytes 440 firstZeroInByteHit = false; 441 442 // Count bits 443 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) 444 { 445 if (value & (1 << bitIdx)) 446 { 447 if (firstZeroInByteHit) 448 { 449 // Continuity not preserved 450 return false; 451 } 452 else 453 { 454 (*bits)++; 455 } 456 } 457 else 458 { 459 firstZeroInByteHit = true; 460 } 461 } 462 } 463 464 previousValue = value; 465 } 466 467 return true; 468 } 469 470 /** 471 * @brief Changes IPv4 address type property (Address, Gateway) 472 * 473 * @param[in] ifaceId Id of interface whose IP should be modified 474 * @param[in] ipIdx Index of IP in input array that should be modified 475 * @param[in] ipHash DBus Hash id of modified IP 476 * @param[in] name Name of field in JSON representation 477 * @param[in] newValue New value that should be written 478 * @param[io] asyncResp Response object that will be returned to client 479 * 480 * @return true if give IP is valid and has been sent do D-Bus, false 481 * otherwise 482 */ 483 inline void changeIPv4AddressProperty( 484 const std::string &ifaceId, int ipIdx, const std::string &ipHash, 485 const std::string &name, const std::string &newValue, 486 const std::shared_ptr<AsyncResp> asyncResp) 487 { 488 auto callback = [asyncResp, ipIdx, name{std::string(name)}, 489 newValue{std::move(newValue)}]( 490 const boost::system::error_code ec) { 491 if (ec) 492 { 493 messages::internalError(asyncResp->res); 494 } 495 else 496 { 497 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue; 498 } 499 }; 500 501 crow::connections::systemBus->async_method_call( 502 std::move(callback), "xyz.openbmc_project.Network", 503 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 504 "org.freedesktop.DBus.Properties", "Set", 505 "xyz.openbmc_project.Network.IP", name, 506 std::variant<std::string>(newValue)); 507 } 508 509 /** 510 * @brief Changes IPv4 address origin property 511 * 512 * @param[in] ifaceId Id of interface whose IP should be modified 513 * @param[in] ipIdx Index of IP in input array that should be 514 * modified 515 * @param[in] ipHash DBus Hash id of modified IP 516 * @param[in] newValue New value in Redfish format 517 * @param[in] newValueDbus New value in D-Bus format 518 * @param[io] asyncResp Response object that will be returned to client 519 * 520 * @return true if give IP is valid and has been sent do D-Bus, false 521 * otherwise 522 */ 523 inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx, 524 const std::string &ipHash, 525 const std::string &newValue, 526 const std::string &newValueDbus, 527 const std::shared_ptr<AsyncResp> asyncResp) 528 { 529 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}]( 530 const boost::system::error_code ec) { 531 if (ec) 532 { 533 messages::internalError(asyncResp->res); 534 } 535 else 536 { 537 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = 538 newValue; 539 } 540 }; 541 542 crow::connections::systemBus->async_method_call( 543 std::move(callback), "xyz.openbmc_project.Network", 544 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 545 "org.freedesktop.DBus.Properties", "Set", 546 "xyz.openbmc_project.Network.IP", "Origin", 547 std::variant<std::string>(newValueDbus)); 548 } 549 550 /** 551 * @brief Modifies SubnetMask for given IP 552 * 553 * @param[in] ifaceId Id of interface whose IP should be modified 554 * @param[in] ipIdx Index of IP in input array that should be 555 * modified 556 * @param[in] ipHash DBus Hash id of modified IP 557 * @param[in] newValueStr Mask in dot notation as string 558 * @param[in] newValue Mask as PrefixLength in bitcount 559 * @param[io] asyncResp Response object that will be returned to client 560 * 561 * @return None 562 */ 563 inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx, 564 const std::string &ipHash, 565 const std::string &newValueStr, 566 uint8_t &newValue, 567 std::shared_ptr<AsyncResp> asyncResp) 568 { 569 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}]( 570 const boost::system::error_code ec) { 571 if (ec) 572 { 573 messages::internalError(asyncResp->res); 574 } 575 else 576 { 577 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = 578 newValueStr; 579 } 580 }; 581 582 crow::connections::systemBus->async_method_call( 583 std::move(callback), "xyz.openbmc_project.Network", 584 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 585 "org.freedesktop.DBus.Properties", "Set", 586 "xyz.openbmc_project.Network.IP", "PrefixLength", 587 std::variant<uint8_t>(newValue)); 588 } 589 590 /** 591 * @brief Deletes given IPv4 592 * 593 * @param[in] ifaceId Id of interface whose IP should be deleted 594 * @param[in] ipIdx Index of IP in input array that should be deleted 595 * @param[in] ipHash DBus Hash id of IP that should be deleted 596 * @param[io] asyncResp Response object that will be returned to client 597 * 598 * @return None 599 */ 600 inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, 601 unsigned int ipIdx, 602 const std::shared_ptr<AsyncResp> asyncResp) 603 { 604 crow::connections::systemBus->async_method_call( 605 [ipIdx, asyncResp](const boost::system::error_code ec) { 606 if (ec) 607 { 608 messages::internalError(asyncResp->res); 609 } 610 else 611 { 612 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; 613 } 614 }, 615 "xyz.openbmc_project.Network", 616 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 617 "xyz.openbmc_project.Object.Delete", "Delete"); 618 } 619 620 /** 621 * @brief Creates IPv4 with given data 622 * 623 * @param[in] ifaceId Id of interface whose IP should be deleted 624 * @param[in] ipIdx Index of IP in input array that should be deleted 625 * @param[in] ipHash DBus Hash id of IP that should be deleted 626 * @param[io] asyncResp Response object that will be returned to client 627 * 628 * @return None 629 */ 630 inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx, 631 uint8_t subnetMask, const std::string &gateway, 632 const std::string &address, 633 std::shared_ptr<AsyncResp> asyncResp) 634 { 635 auto createIpHandler = [ipIdx, 636 asyncResp](const boost::system::error_code ec) { 637 if (ec) 638 { 639 messages::internalError(asyncResp->res); 640 } 641 }; 642 643 crow::connections::systemBus->async_method_call( 644 std::move(createIpHandler), "xyz.openbmc_project.Network", 645 "/xyz/openbmc_project/network/" + ifaceId, 646 "xyz.openbmc_project.Network.IP.Create", "IP", 647 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, 648 gateway); 649 } 650 651 /** 652 * Function that retrieves all properties for given Ethernet Interface 653 * Object 654 * from EntityManager Network Manager 655 * @param ethiface_id a eth interface id to query on DBus 656 * @param callback a function that shall be called to convert Dbus output 657 * into JSON 658 */ 659 template <typename CallbackFunc> 660 void getEthernetIfaceData(const std::string ðiface_id, 661 CallbackFunc &&callback) 662 { 663 crow::connections::systemBus->async_method_call( 664 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}]( 665 const boost::system::error_code error_code, 666 const GetManagedObjects &resp) { 667 EthernetInterfaceData ethData{}; 668 boost::container::flat_set<IPv4AddressData> ipv4Data; 669 670 if (error_code) 671 { 672 callback(false, ethData, ipv4Data); 673 return; 674 } 675 676 extractEthernetInterfaceData(ethiface_id, resp, ethData); 677 extractIPData(ethiface_id, resp, ipv4Data); 678 679 // Fix global GW 680 for (IPv4AddressData &ipv4 : ipv4Data) 681 { 682 if ((ipv4.linktype == LinkType::Global) && 683 (ipv4.gateway == "0.0.0.0")) 684 { 685 ipv4.gateway = ethData.default_gateway; 686 } 687 } 688 689 // Finally make a callback with usefull data 690 callback(true, ethData, ipv4Data); 691 }, 692 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 693 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 694 }; 695 696 /** 697 * Function that retrieves all Ethernet Interfaces available through Network 698 * Manager 699 * @param callback a function that shall be called to convert Dbus output 700 * into JSON. 701 */ 702 template <typename CallbackFunc> 703 void getEthernetIfaceList(CallbackFunc &&callback) 704 { 705 crow::connections::systemBus->async_method_call( 706 [callback{std::move(callback)}]( 707 const boost::system::error_code error_code, 708 GetManagedObjects &resp) { 709 // Callback requires vector<string> to retrieve all available 710 // ethernet interfaces 711 std::vector<std::string> iface_list; 712 iface_list.reserve(resp.size()); 713 if (error_code) 714 { 715 callback(false, iface_list); 716 return; 717 } 718 719 // Iterate over all retrieved ObjectPaths. 720 for (const auto &objpath : resp) 721 { 722 // And all interfaces available for certain ObjectPath. 723 for (const auto &interface : objpath.second) 724 { 725 // If interface is 726 // xyz.openbmc_project.Network.EthernetInterface, this is 727 // what we're looking for. 728 if (interface.first == 729 "xyz.openbmc_project.Network.EthernetInterface") 730 { 731 // Cut out everyting until last "/", ... 732 const std::string &iface_id = objpath.first.str; 733 std::size_t last_pos = iface_id.rfind("/"); 734 if (last_pos != std::string::npos) 735 { 736 // and put it into output vector. 737 iface_list.emplace_back( 738 iface_id.substr(last_pos + 1)); 739 } 740 } 741 } 742 } 743 // Finally make a callback with useful data 744 callback(true, iface_list); 745 }, 746 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 747 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 748 }; 749 750 /** 751 * EthernetCollection derived class for delivering Ethernet Collection Schema 752 */ 753 class EthernetCollection : public Node 754 { 755 public: 756 template <typename CrowApp> 757 EthernetCollection(CrowApp &app) : 758 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/") 759 { 760 entityPrivileges = { 761 {boost::beast::http::verb::get, {{"Login"}}}, 762 {boost::beast::http::verb::head, {{"Login"}}}, 763 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 764 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 765 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 766 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 767 } 768 769 private: 770 /** 771 * Functions triggers appropriate requests on DBus 772 */ 773 void doGet(crow::Response &res, const crow::Request &req, 774 const std::vector<std::string> ¶ms) override 775 { 776 res.jsonValue["@odata.type"] = 777 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 778 res.jsonValue["@odata.context"] = 779 "/redfish/v1/" 780 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 781 res.jsonValue["@odata.id"] = 782 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 783 res.jsonValue["Name"] = "Ethernet Network Interface Collection"; 784 res.jsonValue["Description"] = 785 "Collection of EthernetInterfaces for this Manager"; 786 787 // Get eth interface list, and call the below callback for JSON 788 // preparation 789 getEthernetIfaceList( 790 [&res](const bool &success, 791 const std::vector<std::string> &iface_list) { 792 if (!success) 793 { 794 messages::internalError(res); 795 res.end(); 796 return; 797 } 798 799 nlohmann::json &iface_array = res.jsonValue["Members"]; 800 iface_array = nlohmann::json::array(); 801 for (const std::string &iface_item : iface_list) 802 { 803 iface_array.push_back( 804 {{"@odata.id", 805 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 806 iface_item}}); 807 } 808 809 res.jsonValue["Members@odata.count"] = iface_array.size(); 810 res.jsonValue["@odata.id"] = 811 "/redfish/v1/Managers/bmc/EthernetInterfaces"; 812 res.end(); 813 }); 814 } 815 }; 816 817 /** 818 * EthernetInterface derived class for delivering Ethernet Schema 819 */ 820 class EthernetInterface : public Node 821 { 822 public: 823 /* 824 * Default Constructor 825 */ 826 template <typename CrowApp> 827 EthernetInterface(CrowApp &app) : 828 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/", 829 std::string()) 830 { 831 entityPrivileges = { 832 {boost::beast::http::verb::get, {{"Login"}}}, 833 {boost::beast::http::verb::head, {{"Login"}}}, 834 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 835 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 836 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 837 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 838 } 839 840 // TODO(kkowalsk) Find a suitable class/namespace for this 841 static void handleVlanPatch(const std::string &ifaceId, bool vlanEnable, 842 uint64_t vlanId, 843 const EthernetInterfaceData ðData, 844 const std::shared_ptr<AsyncResp> asyncResp) 845 { 846 if (!ethData.vlan_id) 847 { 848 // This interface is not a VLAN. Cannot do anything with it 849 // TODO(kkowalsk) Change this message 850 messages::propertyNotWritable(asyncResp->res, "VLANEnable"); 851 852 return; 853 } 854 855 // VLAN is configured on the interface 856 if (vlanEnable == true) 857 { 858 // Change VLAN Id 859 asyncResp->res.jsonValue["VLANId"] = vlanId; 860 auto callback = [asyncResp](const boost::system::error_code ec) { 861 if (ec) 862 { 863 messages::internalError(asyncResp->res); 864 } 865 else 866 { 867 asyncResp->res.jsonValue["VLANEnable"] = true; 868 } 869 }; 870 crow::connections::systemBus->async_method_call( 871 std::move(callback), "xyz.openbmc_project.Network", 872 "/xyz/openbmc_project/network/" + ifaceId, 873 "org.freedesktop.DBus.Properties", "Set", 874 "xyz.openbmc_project.Network.VLAN", "Id", 875 std::variant<uint32_t>(vlanId)); 876 } 877 else 878 { 879 auto callback = [asyncResp](const boost::system::error_code ec) { 880 if (ec) 881 { 882 messages::internalError(asyncResp->res); 883 return; 884 } 885 asyncResp->res.jsonValue["VLANEnable"] = false; 886 }; 887 888 crow::connections::systemBus->async_method_call( 889 std::move(callback), "xyz.openbmc_project.Network", 890 "/xyz/openbmc_project/network/" + ifaceId, 891 "xyz.openbmc_project.Object.Delete", "Delete"); 892 } 893 } 894 895 private: 896 void handleHostnamePatch(const std::string &hostname, 897 const std::shared_ptr<AsyncResp> asyncResp) 898 { 899 asyncResp->res.jsonValue["HostName"] = hostname; 900 crow::connections::systemBus->async_method_call( 901 [asyncResp](const boost::system::error_code ec) { 902 if (ec) 903 { 904 messages::internalError(asyncResp->res); 905 } 906 }, 907 "xyz.openbmc_project.Network", 908 "/xyz/openbmc_project/network/config", 909 "org.freedesktop.DBus.Properties", "Set", 910 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 911 std::variant<std::string>(hostname)); 912 } 913 914 void handleIPv4Patch( 915 const std::string &ifaceId, const nlohmann::json &input, 916 const boost::container::flat_set<IPv4AddressData> &ipv4Data, 917 const std::shared_ptr<AsyncResp> asyncResp) 918 { 919 if (!input.is_array()) 920 { 921 messages::propertyValueTypeError(asyncResp->res, input.dump(), 922 "IPv4Addresses"); 923 return; 924 } 925 926 // According to Redfish PATCH definition, size must be at least equal 927 if (input.size() < ipv4Data.size()) 928 { 929 messages::propertyValueFormatError(asyncResp->res, input.dump(), 930 "IPv4Addresses"); 931 return; 932 } 933 934 int entryIdx = 0; 935 boost::container::flat_set<IPv4AddressData>::const_iterator thisData = 936 ipv4Data.begin(); 937 for (const nlohmann::json &thisJson : input) 938 { 939 std::string pathString = 940 "IPv4Addresses/" + std::to_string(entryIdx); 941 // Check that entry is not of some unexpected type 942 if (!thisJson.is_object() && !thisJson.is_null()) 943 { 944 messages::propertyValueTypeError(asyncResp->res, 945 thisJson.dump(), 946 pathString + "/IPv4Address"); 947 948 continue; 949 } 950 951 nlohmann::json::const_iterator addressFieldIt = 952 thisJson.find("Address"); 953 const std::string *addressField = nullptr; 954 if (addressFieldIt != thisJson.end()) 955 { 956 addressField = addressFieldIt->get_ptr<const std::string *>(); 957 if (addressField == nullptr) 958 { 959 messages::propertyValueFormatError(asyncResp->res, 960 addressFieldIt->dump(), 961 pathString + "/Address"); 962 continue; 963 } 964 else 965 { 966 if (!ipv4VerifyIpAndGetBitcount(*addressField)) 967 { 968 messages::propertyValueFormatError( 969 asyncResp->res, *addressField, 970 pathString + "/Address"); 971 continue; 972 } 973 } 974 } 975 976 std::optional<uint8_t> prefixLength; 977 const std::string *subnetField = nullptr; 978 nlohmann::json::const_iterator subnetFieldIt = 979 thisJson.find("SubnetMask"); 980 if (subnetFieldIt != thisJson.end()) 981 { 982 subnetField = subnetFieldIt->get_ptr<const std::string *>(); 983 if (subnetField == nullptr) 984 { 985 messages::propertyValueFormatError( 986 asyncResp->res, *subnetField, 987 pathString + "/SubnetMask"); 988 continue; 989 } 990 else 991 { 992 prefixLength = 0; 993 if (!ipv4VerifyIpAndGetBitcount(*subnetField, 994 &*prefixLength)) 995 { 996 messages::propertyValueFormatError( 997 asyncResp->res, *subnetField, 998 pathString + "/SubnetMask"); 999 continue; 1000 } 1001 } 1002 } 1003 1004 std::string addressOriginInDBusFormat; 1005 const std::string *addressOriginField = nullptr; 1006 nlohmann::json::const_iterator addressOriginFieldIt = 1007 thisJson.find("AddressOrigin"); 1008 if (addressOriginFieldIt != thisJson.end()) 1009 { 1010 const std::string *addressOriginField = 1011 addressOriginFieldIt->get_ptr<const std::string *>(); 1012 if (addressOriginField == nullptr) 1013 { 1014 messages::propertyValueFormatError( 1015 asyncResp->res, *addressOriginField, 1016 pathString + "/AddressOrigin"); 1017 continue; 1018 } 1019 else 1020 { 1021 // Get Address origin in proper format 1022 addressOriginInDBusFormat = 1023 translateAddressOriginRedfishToDbus( 1024 *addressOriginField); 1025 if (addressOriginInDBusFormat.empty()) 1026 { 1027 messages::propertyValueNotInList( 1028 asyncResp->res, *addressOriginField, 1029 pathString + "/AddressOrigin"); 1030 continue; 1031 } 1032 } 1033 } 1034 1035 nlohmann::json::const_iterator gatewayFieldIt = 1036 thisJson.find("Gateway"); 1037 const std::string *gatewayField = nullptr; 1038 if (gatewayFieldIt != thisJson.end()) 1039 { 1040 const std::string *gatewayField = 1041 gatewayFieldIt->get_ptr<const std::string *>(); 1042 if (gatewayField == nullptr || 1043 !ipv4VerifyIpAndGetBitcount(*gatewayField)) 1044 { 1045 messages::propertyValueFormatError( 1046 asyncResp->res, *gatewayField, pathString + "/Gateway"); 1047 continue; 1048 } 1049 } 1050 1051 // if a vlan already exists, modify the existing 1052 if (thisData != ipv4Data.end()) 1053 { 1054 // Existing object that should be modified/deleted/remain 1055 // unchanged 1056 if (thisJson.is_null()) 1057 { 1058 auto callback = [entryIdx{std::to_string(entryIdx)}, 1059 asyncResp]( 1060 const boost::system::error_code ec) { 1061 if (ec) 1062 { 1063 messages::internalError(asyncResp->res); 1064 return; 1065 } 1066 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = 1067 nullptr; 1068 }; 1069 crow::connections::systemBus->async_method_call( 1070 std::move(callback), "xyz.openbmc_project.Network", 1071 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + 1072 thisData->id, 1073 "xyz.openbmc_project.Object.Delete", "Delete"); 1074 } 1075 else if (thisJson.is_object()) 1076 { 1077 // Apply changes 1078 if (addressField != nullptr) 1079 { 1080 auto callback = 1081 [asyncResp, entryIdx, 1082 addressField{std::string(*addressField)}]( 1083 const boost::system::error_code ec) { 1084 if (ec) 1085 { 1086 messages::internalError(asyncResp->res); 1087 return; 1088 } 1089 asyncResp->res 1090 .jsonValue["IPv4Addresses"][std::to_string( 1091 entryIdx)]["Address"] = addressField; 1092 }; 1093 1094 crow::connections::systemBus->async_method_call( 1095 std::move(callback), "xyz.openbmc_project.Network", 1096 "/xyz/openbmc_project/network/" + ifaceId + 1097 "/ipv4/" + thisData->id, 1098 "org.freedesktop.DBus.Properties", "Set", 1099 "xyz.openbmc_project.Network.IP", "Address", 1100 std::variant<std::string>(*addressField)); 1101 } 1102 1103 if (prefixLength && subnetField != nullptr) 1104 { 1105 changeIPv4SubnetMaskProperty(ifaceId, entryIdx, 1106 thisData->id, *subnetField, 1107 *prefixLength, asyncResp); 1108 } 1109 1110 if (!addressOriginInDBusFormat.empty() && 1111 addressOriginField != nullptr) 1112 { 1113 changeIPv4Origin(ifaceId, entryIdx, thisData->id, 1114 *addressOriginField, 1115 addressOriginInDBusFormat, asyncResp); 1116 } 1117 1118 if (gatewayField != nullptr) 1119 { 1120 auto callback = 1121 [asyncResp, entryIdx, 1122 gatewayField{std::string(*gatewayField)}]( 1123 const boost::system::error_code ec) { 1124 if (ec) 1125 { 1126 messages::internalError(asyncResp->res); 1127 return; 1128 } 1129 asyncResp->res 1130 .jsonValue["IPv4Addresses"][std::to_string( 1131 entryIdx)]["Gateway"] = 1132 std::move(gatewayField); 1133 }; 1134 1135 crow::connections::systemBus->async_method_call( 1136 std::move(callback), "xyz.openbmc_project.Network", 1137 "/xyz/openbmc_project/network/" + ifaceId + 1138 "/ipv4/" + thisData->id, 1139 "org.freedesktop.DBus.Properties", "Set", 1140 "xyz.openbmc_project.Network.IP", "Gateway", 1141 std::variant<std::string>(*gatewayField)); 1142 } 1143 } 1144 thisData++; 1145 } 1146 else 1147 { 1148 // Create IPv4 with provided data 1149 if (gatewayField == nullptr) 1150 { 1151 messages::propertyMissing(asyncResp->res, 1152 pathString + "/Gateway"); 1153 continue; 1154 } 1155 1156 if (addressField == nullptr) 1157 { 1158 messages::propertyMissing(asyncResp->res, 1159 pathString + "/Address"); 1160 continue; 1161 } 1162 1163 if (!prefixLength) 1164 { 1165 messages::propertyMissing(asyncResp->res, 1166 pathString + "/SubnetMask"); 1167 continue; 1168 } 1169 1170 createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField, 1171 *addressField, asyncResp); 1172 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson; 1173 } 1174 entryIdx++; 1175 } 1176 } 1177 1178 void parseInterfaceData( 1179 nlohmann::json &json_response, const std::string &iface_id, 1180 const EthernetInterfaceData ðData, 1181 const boost::container::flat_set<IPv4AddressData> &ipv4Data) 1182 { 1183 json_response["Id"] = iface_id; 1184 json_response["@odata.id"] = 1185 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id; 1186 json_response["InterfaceEnabled"] = true; 1187 if (ethData.speed == 0) 1188 { 1189 json_response["LinkStatus"] = "NoLink"; 1190 json_response["Status"] = { 1191 {"Health", "OK"}, 1192 {"State", "Disabled"}, 1193 }; 1194 } 1195 else 1196 { 1197 json_response["LinkStatus"] = "LinkUp"; 1198 json_response["Status"] = { 1199 {"Health", "OK"}, 1200 {"State", "Enabled"}, 1201 }; 1202 } 1203 json_response["SpeedMbps"] = ethData.speed; 1204 json_response["MACAddress"] = ethData.mac_address; 1205 if (!ethData.hostname.empty()) 1206 { 1207 json_response["HostName"] = ethData.hostname; 1208 } 1209 1210 nlohmann::json &vlanObj = json_response["VLAN"]; 1211 if (ethData.vlan_id) 1212 { 1213 vlanObj["VLANEnable"] = true; 1214 vlanObj["VLANId"] = *ethData.vlan_id; 1215 } 1216 else 1217 { 1218 vlanObj["VLANEnable"] = false; 1219 vlanObj["VLANId"] = 0; 1220 } 1221 json_response["NameServers"] = ethData.nameservers; 1222 1223 if (ipv4Data.size() > 0) 1224 { 1225 nlohmann::json &ipv4_array = json_response["IPv4Addresses"]; 1226 ipv4_array = nlohmann::json::array(); 1227 for (auto &ipv4_config : ipv4Data) 1228 { 1229 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin}, 1230 {"SubnetMask", ipv4_config.netmask}, 1231 {"Address", ipv4_config.address}, 1232 {"Gateway", ipv4_config.gateway}}); 1233 } 1234 } 1235 } 1236 1237 /** 1238 * Functions triggers appropriate requests on DBus 1239 */ 1240 void doGet(crow::Response &res, const crow::Request &req, 1241 const std::vector<std::string> ¶ms) override 1242 { 1243 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1244 if (params.size() != 1) 1245 { 1246 messages::internalError(asyncResp->res); 1247 return; 1248 } 1249 1250 getEthernetIfaceData( 1251 params[0], 1252 [this, asyncResp, iface_id{std::string(params[0])}]( 1253 const bool &success, const EthernetInterfaceData ðData, 1254 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1255 if (!success) 1256 { 1257 // TODO(Pawel)consider distinguish between non existing 1258 // object, and other errors 1259 messages::resourceNotFound(asyncResp->res, 1260 "EthernetInterface", iface_id); 1261 return; 1262 } 1263 asyncResp->res.jsonValue["@odata.type"] = 1264 "#EthernetInterface.v1_2_0.EthernetInterface"; 1265 asyncResp->res.jsonValue["@odata.context"] = 1266 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 1267 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface"; 1268 asyncResp->res.jsonValue["Description"] = 1269 "Management Network Interface"; 1270 1271 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, 1272 ipv4Data); 1273 }); 1274 } 1275 1276 void doPatch(crow::Response &res, const crow::Request &req, 1277 const std::vector<std::string> ¶ms) override 1278 { 1279 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1280 if (params.size() != 1) 1281 { 1282 messages::internalError(asyncResp->res); 1283 return; 1284 } 1285 1286 const std::string &iface_id = params[0]; 1287 1288 std::optional<nlohmann::json> vlan; 1289 std::optional<std::string> hostname; 1290 std::optional<nlohmann::json> ipv4Addresses; 1291 std::optional<nlohmann::json> ipv6Addresses; 1292 1293 if (!json_util::readJson(req, res, "VLAN", vlan, "HostName", hostname, 1294 "IPv4Addresses", ipv4Addresses, 1295 "IPv6Addresses", ipv6Addresses)) 1296 { 1297 return; 1298 } 1299 std::optional<uint64_t> vlanId = 0; 1300 std::optional<bool> vlanEnable = false; 1301 if (vlan) 1302 { 1303 if (!json_util::readJson(*vlan, res, "VLANEnable", vlanEnable, 1304 "VLANId", vlanId)) 1305 { 1306 return; 1307 } 1308 // Need both vlanId and vlanEnable to service this request 1309 if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable)) 1310 { 1311 if (vlanId) 1312 { 1313 messages::propertyMissing(asyncResp->res, "VLANEnable"); 1314 } 1315 else 1316 { 1317 messages::propertyMissing(asyncResp->res, "VLANId"); 1318 } 1319 1320 return; 1321 } 1322 } 1323 1324 // Get single eth interface data, and call the below callback for JSON 1325 // preparation 1326 getEthernetIfaceData( 1327 iface_id, 1328 [this, asyncResp, iface_id, vlanId, vlanEnable, 1329 hostname = std::move(hostname), 1330 ipv4Addresses = std::move(ipv4Addresses), 1331 ipv6Addresses = std::move(ipv6Addresses)]( 1332 const bool &success, const EthernetInterfaceData ðData, 1333 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1334 if (!success) 1335 { 1336 // ... otherwise return error 1337 // TODO(Pawel)consider distinguish between non existing 1338 // object, and other errors 1339 messages::resourceNotFound( 1340 asyncResp->res, "VLAN Network Interface", iface_id); 1341 return; 1342 } 1343 1344 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData, 1345 ipv4Data); 1346 1347 if (vlanId && vlanEnable) 1348 { 1349 handleVlanPatch(iface_id, *vlanId, *vlanEnable, ethData, 1350 asyncResp); 1351 } 1352 1353 if (hostname) 1354 { 1355 handleHostnamePatch(*hostname, asyncResp); 1356 } 1357 1358 if (ipv4Addresses) 1359 { 1360 handleIPv4Patch(iface_id, *ipv4Addresses, ipv4Data, 1361 asyncResp); 1362 } 1363 1364 if (ipv6Addresses) 1365 { 1366 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet 1367 messages::propertyNotWritable(asyncResp->res, 1368 "IPv6Addresses"); 1369 } 1370 }); 1371 } 1372 }; 1373 1374 /** 1375 * VlanNetworkInterface derived class for delivering VLANNetworkInterface 1376 * Schema 1377 */ 1378 class VlanNetworkInterface : public Node 1379 { 1380 public: 1381 /* 1382 * Default Constructor 1383 */ 1384 template <typename CrowApp> 1385 VlanNetworkInterface(CrowApp &app) : 1386 Node(app, 1387 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>", 1388 std::string(), std::string()) 1389 { 1390 entityPrivileges = { 1391 {boost::beast::http::verb::get, {{"Login"}}}, 1392 {boost::beast::http::verb::head, {{"Login"}}}, 1393 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1394 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1395 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1396 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1397 } 1398 1399 private: 1400 void parseInterfaceData( 1401 nlohmann::json &json_response, const std::string &parent_iface_id, 1402 const std::string &iface_id, const EthernetInterfaceData ðData, 1403 const boost::container::flat_set<IPv4AddressData> &ipv4Data) 1404 { 1405 // Fill out obvious data... 1406 json_response["Id"] = iface_id; 1407 json_response["@odata.id"] = 1408 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id + 1409 "/VLANs/" + iface_id; 1410 1411 json_response["VLANEnable"] = true; 1412 if (ethData.vlan_id) 1413 { 1414 json_response["VLANId"] = *ethData.vlan_id; 1415 } 1416 } 1417 1418 bool verifyNames(crow::Response &res, const std::string &parent, 1419 const std::string &iface) 1420 { 1421 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1422 if (!boost::starts_with(iface, parent + "_")) 1423 { 1424 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface", 1425 iface); 1426 return false; 1427 } 1428 else 1429 { 1430 return true; 1431 } 1432 } 1433 1434 /** 1435 * Functions triggers appropriate requests on DBus 1436 */ 1437 void doGet(crow::Response &res, const crow::Request &req, 1438 const std::vector<std::string> ¶ms) override 1439 { 1440 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1441 // TODO(Pawel) this shall be parameterized call (two params) to get 1442 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1443 // Check if there is required param, truly entering this shall be 1444 // impossible. 1445 if (params.size() != 2) 1446 { 1447 messages::internalError(res); 1448 res.end(); 1449 return; 1450 } 1451 1452 const std::string &parent_iface_id = params[0]; 1453 const std::string &iface_id = params[1]; 1454 res.jsonValue["@odata.type"] = 1455 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; 1456 res.jsonValue["@odata.context"] = 1457 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; 1458 res.jsonValue["Name"] = "VLAN Network Interface"; 1459 1460 if (!verifyNames(res, parent_iface_id, iface_id)) 1461 { 1462 return; 1463 } 1464 1465 // Get single eth interface data, and call the below callback for JSON 1466 // preparation 1467 getEthernetIfaceData( 1468 iface_id, 1469 [this, asyncResp, parent_iface_id, iface_id]( 1470 const bool &success, const EthernetInterfaceData ðData, 1471 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1472 if (success && ethData.vlan_id) 1473 { 1474 parseInterfaceData(asyncResp->res.jsonValue, 1475 parent_iface_id, iface_id, ethData, 1476 ipv4Data); 1477 } 1478 else 1479 { 1480 // ... otherwise return error 1481 // TODO(Pawel)consider distinguish between non existing 1482 // object, and other errors 1483 messages::resourceNotFound( 1484 asyncResp->res, "VLAN Network Interface", iface_id); 1485 } 1486 }); 1487 } 1488 1489 void doPatch(crow::Response &res, const crow::Request &req, 1490 const std::vector<std::string> ¶ms) override 1491 { 1492 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1493 if (params.size() != 2) 1494 { 1495 messages::internalError(asyncResp->res); 1496 return; 1497 } 1498 1499 const std::string &parentIfaceId = params[0]; 1500 const std::string &ifaceId = params[1]; 1501 1502 if (!verifyNames(res, parentIfaceId, ifaceId)) 1503 { 1504 return; 1505 } 1506 1507 bool vlanEnable = false; 1508 uint64_t vlanId = 0; 1509 1510 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId", 1511 vlanId)) 1512 { 1513 return; 1514 } 1515 1516 // Get single eth interface data, and call the below callback for JSON 1517 // preparation 1518 getEthernetIfaceData( 1519 ifaceId, 1520 [this, asyncResp, parentIfaceId, ifaceId, vlanEnable, vlanId]( 1521 const bool &success, const EthernetInterfaceData ðData, 1522 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1523 if (!success) 1524 { 1525 // TODO(Pawel)consider distinguish between non existing 1526 // object, and other errors 1527 messages::resourceNotFound( 1528 asyncResp->res, "VLAN Network Interface", ifaceId); 1529 1530 return; 1531 } 1532 1533 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, 1534 ifaceId, ethData, ipv4Data); 1535 1536 EthernetInterface::handleVlanPatch(ifaceId, vlanId, vlanEnable, 1537 ethData, asyncResp); 1538 }); 1539 } 1540 1541 void doDelete(crow::Response &res, const crow::Request &req, 1542 const std::vector<std::string> ¶ms) override 1543 { 1544 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1545 if (params.size() != 2) 1546 { 1547 messages::internalError(asyncResp->res); 1548 return; 1549 } 1550 1551 const std::string &parentIfaceId = params[0]; 1552 const std::string &ifaceId = params[1]; 1553 1554 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId)) 1555 { 1556 return; 1557 } 1558 1559 // Get single eth interface data, and call the below callback for JSON 1560 // preparation 1561 getEthernetIfaceData( 1562 ifaceId, 1563 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)}, 1564 ifaceId{std::string(ifaceId)}]( 1565 const bool &success, const EthernetInterfaceData ðData, 1566 const boost::container::flat_set<IPv4AddressData> &ipv4Data) { 1567 if (success && ethData.vlan_id) 1568 { 1569 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId, 1570 ifaceId, ethData, ipv4Data); 1571 1572 auto callback = 1573 [asyncResp](const boost::system::error_code ec) { 1574 if (ec) 1575 { 1576 messages::internalError(asyncResp->res); 1577 } 1578 }; 1579 crow::connections::systemBus->async_method_call( 1580 std::move(callback), "xyz.openbmc_project.Network", 1581 std::string("/xyz/openbmc_project/network/") + ifaceId, 1582 "xyz.openbmc_project.Object.Delete", "Delete"); 1583 } 1584 else 1585 { 1586 // ... otherwise return error 1587 // TODO(Pawel)consider distinguish between non existing 1588 // object, and other errors 1589 messages::resourceNotFound( 1590 asyncResp->res, "VLAN Network Interface", ifaceId); 1591 } 1592 }); 1593 } 1594 }; 1595 1596 /** 1597 * VlanNetworkInterfaceCollection derived class for delivering 1598 * VLANNetworkInterface Collection Schema 1599 */ 1600 class VlanNetworkInterfaceCollection : public Node 1601 { 1602 public: 1603 template <typename CrowApp> 1604 VlanNetworkInterfaceCollection(CrowApp &app) : 1605 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/", 1606 std::string()) 1607 { 1608 entityPrivileges = { 1609 {boost::beast::http::verb::get, {{"Login"}}}, 1610 {boost::beast::http::verb::head, {{"Login"}}}, 1611 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1612 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1613 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1614 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1615 } 1616 1617 private: 1618 /** 1619 * Functions triggers appropriate requests on DBus 1620 */ 1621 void doGet(crow::Response &res, const crow::Request &req, 1622 const std::vector<std::string> ¶ms) override 1623 { 1624 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1625 if (params.size() != 1) 1626 { 1627 // This means there is a problem with the router 1628 messages::internalError(asyncResp->res); 1629 return; 1630 } 1631 1632 const std::string &rootInterfaceName = params[0]; 1633 1634 // Get eth interface list, and call the below callback for JSON 1635 // preparation 1636 getEthernetIfaceList( 1637 [this, asyncResp, 1638 rootInterfaceName{std::string(rootInterfaceName)}]( 1639 const bool &success, 1640 const std::vector<std::string> &iface_list) { 1641 if (!success) 1642 { 1643 messages::internalError(asyncResp->res); 1644 return; 1645 } 1646 asyncResp->res.jsonValue["@odata.type"] = 1647 "#VLanNetworkInterfaceCollection." 1648 "VLanNetworkInterfaceCollection"; 1649 asyncResp->res.jsonValue["@odata.context"] = 1650 "/redfish/v1/$metadata" 1651 "#VLanNetworkInterfaceCollection." 1652 "VLanNetworkInterfaceCollection"; 1653 asyncResp->res.jsonValue["Name"] = 1654 "VLAN Network Interface Collection"; 1655 1656 nlohmann::json iface_array = nlohmann::json::array(); 1657 1658 for (const std::string &iface_item : iface_list) 1659 { 1660 if (boost::starts_with(iface_item, rootInterfaceName + "_")) 1661 { 1662 iface_array.push_back( 1663 {{"@odata.id", 1664 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1665 rootInterfaceName + "/VLANs/" + iface_item}}); 1666 } 1667 } 1668 1669 if (iface_array.empty()) 1670 { 1671 messages::resourceNotFound( 1672 asyncResp->res, "EthernetInterface", rootInterfaceName); 1673 return; 1674 } 1675 asyncResp->res.jsonValue["Members@odata.count"] = 1676 iface_array.size(); 1677 asyncResp->res.jsonValue["Members"] = std::move(iface_array); 1678 asyncResp->res.jsonValue["@odata.id"] = 1679 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + 1680 rootInterfaceName + "/VLANs"; 1681 }); 1682 } 1683 1684 void doPost(crow::Response &res, const crow::Request &req, 1685 const std::vector<std::string> ¶ms) override 1686 { 1687 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1688 if (params.size() != 1) 1689 { 1690 messages::internalError(asyncResp->res); 1691 return; 1692 } 1693 1694 uint32_t vlanId = 0; 1695 if (!json_util::readJson(req, res, "VLANId", vlanId)) 1696 { 1697 return; 1698 } 1699 const std::string &rootInterfaceName = params[0]; 1700 auto callback = [asyncResp](const boost::system::error_code ec) { 1701 if (ec) 1702 { 1703 // TODO(ed) make more consistent error messages based on 1704 // phosphor-network responses 1705 messages::internalError(asyncResp->res); 1706 return; 1707 } 1708 messages::created(asyncResp->res); 1709 }; 1710 crow::connections::systemBus->async_method_call( 1711 std::move(callback), "xyz.openbmc_project.Network", 1712 "/xyz/openbmc_project/network", 1713 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", 1714 rootInterfaceName, vlanId); 1715 } 1716 }; 1717 } // namespace redfish 1718