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