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