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