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 <dbus_singleton.hpp> 20 #include <error_messages.hpp> 21 #include <node.hpp> 22 #include <utils/json_utils.hpp> 23 24 namespace redfish 25 { 26 27 /** 28 * DBus types primitives for several generic DBus interfaces 29 * TODO(Pawel) consider move this to separate file into boost::dbus 30 */ 31 using PropertiesMapType = boost::container::flat_map< 32 std::string, 33 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 34 int32_t, uint32_t, int64_t, uint64_t, double>>; 35 36 using GetManagedObjectsType = boost::container::flat_map< 37 sdbusplus::message::object_path, 38 boost::container::flat_map< 39 std::string, 40 boost::container::flat_map< 41 std::string, sdbusplus::message::variant< 42 std::string, bool, uint8_t, int16_t, uint16_t, 43 int32_t, uint32_t, int64_t, uint64_t, double>>>>; 44 45 /** 46 * Structure for keeping IPv4 data required by Redfish 47 * TODO(Pawel) consider change everything to ptr, or to non-ptr values. 48 */ 49 struct IPv4AddressData 50 { 51 std::string id; 52 const std::string *address; 53 const std::string *domain; 54 const std::string *gateway; 55 std::string netmask; 56 std::string origin; 57 bool global; 58 /** 59 * @brief Operator< to enable sorting 60 * 61 * @param[in] obj Object to compare with 62 * 63 * @return This object id < supplied object id 64 */ 65 bool operator<(const IPv4AddressData &obj) const 66 { 67 return (id < obj.id); 68 } 69 }; 70 71 /** 72 * Structure for keeping basic single Ethernet Interface information 73 * available from DBus 74 */ 75 struct EthernetInterfaceData 76 { 77 const unsigned int *speed; 78 const bool *autoNeg; 79 const std::string *hostname; 80 const std::string *defaultGateway; 81 const std::string *macAddress; 82 const unsigned int *vlanId; 83 }; 84 85 /** 86 * OnDemandEthernetProvider 87 * Ethernet provider class that retrieves data directly from dbus, before 88 * setting it into JSON output. This does not cache any data. 89 * 90 * TODO(Pawel) 91 * This perhaps shall be different file, which has to be chosen on compile time 92 * depending on OEM needs 93 */ 94 class OnDemandEthernetProvider 95 { 96 private: 97 // Consts that may have influence on EthernetProvider performance/memory 98 // usage 99 const size_t maxIpV4AddressesPerInterface = 10; 100 101 // Helper function that allows to extract GetAllPropertiesType from 102 // GetManagedObjectsType, based on object path, and interface name 103 const PropertiesMapType *extractInterfaceProperties( 104 const sdbusplus::message::object_path &objpath, 105 const std::string &interface, const GetManagedObjectsType &dbus_data) 106 { 107 const auto &dbusObj = dbus_data.find(objpath); 108 if (dbusObj != dbus_data.end()) 109 { 110 const auto &iface = dbusObj->second.find(interface); 111 if (iface != dbusObj->second.end()) 112 { 113 return &iface->second; 114 } 115 } 116 return nullptr; 117 } 118 119 // Helper Wrapper that does inline object_path conversion from string 120 // into sdbusplus::message::object_path type 121 inline const PropertiesMapType * 122 extractInterfaceProperties(const std::string &objpath, 123 const std::string &interface, 124 const GetManagedObjectsType &dbus_data) 125 { 126 const auto &dbusObj = sdbusplus::message::object_path{objpath}; 127 return extractInterfaceProperties(dbusObj, interface, dbus_data); 128 } 129 130 // Helper function that allows to get pointer to the property from 131 // GetAllPropertiesType native, or extracted by GetAllPropertiesType 132 template <typename T> 133 inline T const *const extractProperty(const PropertiesMapType &properties, 134 const std::string &name) 135 { 136 const auto &property = properties.find(name); 137 if (property != properties.end()) 138 { 139 return mapbox::getPtr<const T>(property->second); 140 } 141 return nullptr; 142 } 143 // TODO(Pawel) Consider to move the above functions to dbus 144 // generic_interfaces.hpp 145 146 // Helper function that extracts data from several dbus objects and several 147 // interfaces required by single ethernet interface instance 148 void extractEthernetInterfaceData(const std::string ðifaceId, 149 const GetManagedObjectsType &dbus_data, 150 EthernetInterfaceData ð_data) 151 { 152 // Extract data that contains MAC Address 153 const PropertiesMapType *macProperties = extractInterfaceProperties( 154 "/xyz/openbmc_project/network/" + ethifaceId, 155 "xyz.openbmc_project.Network.MACAddress", dbus_data); 156 157 if (macProperties != nullptr) 158 { 159 eth_data.macAddress = 160 extractProperty<std::string>(*macProperties, "MACAddress"); 161 } 162 163 const PropertiesMapType *vlanProperties = extractInterfaceProperties( 164 "/xyz/openbmc_project/network/" + ethifaceId, 165 "xyz.openbmc_project.Network.VLAN", dbus_data); 166 167 if (vlanProperties != nullptr) 168 { 169 eth_data.vlanId = 170 extractProperty<unsigned int>(*vlanProperties, "Id"); 171 } 172 173 // Extract data that contains link information (auto negotiation and 174 // speed) 175 const PropertiesMapType *ethProperties = extractInterfaceProperties( 176 "/xyz/openbmc_project/network/" + ethifaceId, 177 "xyz.openbmc_project.Network.EthernetInterface", dbus_data); 178 179 if (ethProperties != nullptr) 180 { 181 eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg"); 182 eth_data.speed = 183 extractProperty<unsigned int>(*ethProperties, "Speed"); 184 } 185 186 // Extract data that contains network config (HostName and DefaultGW) 187 const PropertiesMapType *configProperties = extractInterfaceProperties( 188 "/xyz/openbmc_project/network/config", 189 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); 190 191 if (configProperties != nullptr) 192 { 193 eth_data.hostname = 194 extractProperty<std::string>(*configProperties, "HostName"); 195 eth_data.defaultGateway = extractProperty<std::string>( 196 *configProperties, "DefaultGateway"); 197 } 198 } 199 200 // Helper function that changes bits netmask notation (i.e. /24) 201 // into full dot notation 202 inline std::string getNetmask(unsigned int bits) 203 { 204 uint32_t value = 0xffffffff << (32 - bits); 205 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 206 std::to_string((value >> 16) & 0xff) + "." + 207 std::to_string((value >> 8) & 0xff) + "." + 208 std::to_string(value & 0xff); 209 return netmask; 210 } 211 212 // Helper function that extracts data for single ethernet ipv4 address 213 void extractIPv4Data(const std::string ðifaceId, 214 const GetManagedObjectsType &dbus_data, 215 std::vector<IPv4AddressData> &ipv4_config) 216 { 217 const std::string pathStart = 218 "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/"; 219 220 // Since there might be several IPv4 configurations aligned with 221 // single ethernet interface, loop over all of them 222 for (auto &objpath : dbus_data) 223 { 224 // Check if proper patter for object path appears 225 if (boost::starts_with( 226 static_cast<const std::string &>(objpath.first), pathStart)) 227 { 228 // and get approrpiate interface 229 const auto &interface = 230 objpath.second.find("xyz.openbmc_project.Network.IP"); 231 if (interface != objpath.second.end()) 232 { 233 // Make a properties 'shortcut', to make everything more 234 // readable 235 const PropertiesMapType &properties = interface->second; 236 // Instance IPv4AddressData structure, and set as 237 // appropriate 238 IPv4AddressData ipv4Address; 239 240 ipv4Address.id = 241 static_cast<const std::string &>(objpath.first) 242 .substr(pathStart.size()); 243 244 // IPv4 address 245 ipv4Address.address = 246 extractProperty<std::string>(properties, "Address"); 247 // IPv4 gateway 248 ipv4Address.gateway = 249 extractProperty<std::string>(properties, "Gateway"); 250 251 // Origin is kind of DBus object so fetch pointer... 252 const std::string *origin = 253 extractProperty<std::string>(properties, "Origin"); 254 if (origin != nullptr) 255 { 256 ipv4Address.origin = 257 translateAddressOriginBetweenDBusAndRedfish( 258 origin, true, true); 259 } 260 261 // Netmask is presented as PrefixLength 262 const auto *mask = 263 extractProperty<uint8_t>(properties, "PrefixLength"); 264 if (mask != nullptr) 265 { 266 // convert it to the string 267 ipv4Address.netmask = getNetmask(*mask); 268 } 269 270 // Attach IPv4 only if address is present 271 if (ipv4Address.address != nullptr) 272 { 273 // Check if given address is local, or global 274 if (boost::starts_with(*ipv4Address.address, "169.254")) 275 { 276 ipv4Address.global = false; 277 } 278 else 279 { 280 ipv4Address.global = true; 281 } 282 ipv4_config.emplace_back(std::move(ipv4Address)); 283 } 284 } 285 } 286 } 287 288 /** 289 * We have to sort this vector and ensure that order of IPv4 addresses 290 * is consistent between GETs to allow modification and deletion in 291 * PATCHes 292 */ 293 std::sort(ipv4_config.begin(), ipv4_config.end()); 294 } 295 296 static const constexpr int ipV4AddressSectionsCount = 4; 297 298 public: 299 /** 300 * @brief Creates VLAN for given interface with given Id through D-Bus 301 * 302 * @param[in] ifaceId Id of interface for which VLAN will be created 303 * @param[in] inputVlanId ID of the new VLAN 304 * @param[in] callback Function that will be called after the operation 305 * 306 * @return None. 307 */ 308 template <typename CallbackFunc> 309 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, 310 CallbackFunc &&callback) 311 { 312 crow::connections::systemBus->async_method_call( 313 callback, "xyz.openbmc_project.Network", 314 "/xyz/openbmc_project/network", 315 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, 316 static_cast<uint32_t>(inputVlanId)); 317 }; 318 319 /** 320 * @brief Sets given Id on the given VLAN interface through D-Bus 321 * 322 * @param[in] ifaceId Id of VLAN interface that should be modified 323 * @param[in] inputVlanId New ID of the VLAN 324 * @param[in] callback Function that will be called after the operation 325 * 326 * @return None. 327 */ 328 template <typename CallbackFunc> 329 static void changeVlanId(const std::string &ifaceId, 330 const uint32_t &inputVlanId, 331 CallbackFunc &&callback) 332 { 333 crow::connections::systemBus->async_method_call( 334 callback, "xyz.openbmc_project.Network", 335 std::string("/xyz/openbmc_project/network/") + ifaceId, 336 "org.freedesktop.DBus.Properties", "Set", 337 "xyz.openbmc_project.Network.VLAN", "Id", 338 sdbusplus::message::variant<uint32_t>(inputVlanId)); 339 }; 340 341 /** 342 * @brief Helper function that verifies IP address to check if it is in 343 * proper format. If bits pointer is provided, also calculates active 344 * bit count for Subnet Mask. 345 * 346 * @param[in] ip IP that will be verified 347 * @param[out] bits Calculated mask in bits notation 348 * 349 * @return true in case of success, false otherwise 350 */ 351 bool ipv4VerifyIpAndGetBitcount(const std::string &ip, 352 uint8_t *bits = nullptr) 353 { 354 std::vector<std::string> bytesInMask; 355 356 boost::split(bytesInMask, ip, boost::is_any_of(".")); 357 358 if (bytesInMask.size() != ipV4AddressSectionsCount) 359 { 360 return false; 361 } 362 363 if (bits != nullptr) 364 { 365 *bits = 0; 366 } 367 368 char *endPtr; 369 long previousValue = 255; 370 bool firstZeroInByteHit; 371 372 for (const std::string &byte : bytesInMask) 373 { 374 if (byte.empty()) 375 { 376 return false; 377 } 378 379 // Use strtol instead of stroi to avoid exceptions 380 long value = std::strtol(byte.c_str(), &endPtr, 10); 381 382 // endPtr should point to the end of the string, otherwise given 383 // string is not 100% number 384 if (*endPtr != '\0') 385 { 386 return false; 387 } 388 389 // Value should be contained in byte 390 if (value < 0 || value > 255) 391 { 392 return false; 393 } 394 395 if (bits != nullptr) 396 { 397 // Mask has to be continuous between bytes 398 if (previousValue != 255 && value != 0) 399 { 400 return false; 401 } 402 403 // Mask has to be continuous inside bytes 404 firstZeroInByteHit = false; 405 406 // Count bits 407 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) 408 { 409 if (value & (1 << bitIdx)) 410 { 411 if (firstZeroInByteHit) 412 { 413 // Continuity not preserved 414 return false; 415 } 416 else 417 { 418 (*bits)++; 419 } 420 } 421 else 422 { 423 firstZeroInByteHit = true; 424 } 425 } 426 } 427 428 previousValue = value; 429 } 430 431 return true; 432 } 433 434 /** 435 * @brief Changes IPv4 address type property (Address, Gateway) 436 * 437 * @param[in] ifaceId Id of interface whose IP should be modified 438 * @param[in] ipIdx index of IP in input array that should be modified 439 * @param[in] ipHash DBus Hash id of modified IP 440 * @param[in] name Name of field in JSON representation 441 * @param[in] newValue New value that should be written 442 * @param[io] asyncResp Response object that will be returned to client 443 * 444 * @return true if give IP is valid and has been sent do D-Bus, false 445 * otherwise 446 */ 447 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, 448 const std::string &ipHash, 449 const std::string &name, 450 const std::string &newValue, 451 const std::shared_ptr<AsyncResp> &asyncResp) 452 { 453 auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, 454 name{std::move(name)}, newValue{std::move(newValue)}]( 455 const boost::system::error_code ec) { 456 if (ec) 457 { 458 messages::addMessageToJson( 459 asyncResp->res.jsonValue, messages::internalError(), 460 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); 461 } 462 else 463 { 464 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = 465 newValue; 466 } 467 }; 468 469 crow::connections::systemBus->async_method_call( 470 std::move(callback), "xyz.openbmc_project.Network", 471 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 472 "org.freedesktop.DBus.Properties", "Set", 473 "xyz.openbmc_project.Network.IP", name, 474 sdbusplus::message::variant<std::string>(newValue)); 475 }; 476 477 /** 478 * @brief Changes IPv4 address origin property 479 * 480 * @param[in] ifaceId Id of interface whose IP should be modified 481 * @param[in] ipIdx index of IP in input array that should be 482 * modified 483 * @param[in] ipHash DBus Hash id of modified IP 484 * @param[in] newValue New value in Redfish format 485 * @param[in] newValueDbus New value in D-Bus format 486 * @param[io] asyncResp Response object that will be returned to client 487 * 488 * @return true if give IP is valid and has been sent do D-Bus, false 489 * otherwise 490 */ 491 void changeIPv4Origin(const std::string &ifaceId, int ipIdx, 492 const std::string &ipHash, 493 const std::string &newValue, 494 const std::string &newValueDbus, 495 const std::shared_ptr<AsyncResp> &asyncResp) 496 { 497 auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, 498 newValue{std::move(newValue)}]( 499 const boost::system::error_code ec) { 500 if (ec) 501 { 502 messages::addMessageToJson( 503 asyncResp->res.jsonValue, messages::internalError(), 504 "/IPv4Addresses/" + std::to_string(ipIdx) + 505 "/AddressOrigin"); 506 } 507 else 508 { 509 asyncResp->res 510 .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] = 511 newValue; 512 } 513 }; 514 515 crow::connections::systemBus->async_method_call( 516 std::move(callback), "xyz.openbmc_project.Network", 517 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 518 "org.freedesktop.DBus.Properties", "Set", 519 "xyz.openbmc_project.Network.IP", "Origin", 520 sdbusplus::message::variant<std::string>(newValueDbus)); 521 }; 522 523 /** 524 * @brief Modifies SubnetMask for given IP 525 * 526 * @param[in] ifaceId Id of interface whose IP should be modified 527 * @param[in] ipIdx index of IP in input array that should be 528 * modified 529 * @param[in] ipHash DBus Hash id of modified IP 530 * @param[in] newValueStr Mask in dot notation as string 531 * @param[in] newValue Mask as PrefixLength in bitcount 532 * @param[io] asyncResp Response object that will be returned to client 533 * 534 * @return None 535 */ 536 void changeIPv4SubnetMaskProperty( 537 const std::string &ifaceId, int ipIdx, const std::string &ipHash, 538 const std::string &newValueStr, uint8_t &newValue, 539 const std::shared_ptr<AsyncResp> &asyncResp) 540 { 541 auto callback = [asyncResp, ipIdx{std::move(ipIdx)}, 542 newValueStr{std::move(newValueStr)}]( 543 const boost::system::error_code ec) { 544 if (ec) 545 { 546 messages::addMessageToJson( 547 asyncResp->res.jsonValue, messages::internalError(), 548 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); 549 } 550 else 551 { 552 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] = 553 newValueStr; 554 } 555 }; 556 557 crow::connections::systemBus->async_method_call( 558 std::move(callback), "xyz.openbmc_project.Network", 559 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 560 "org.freedesktop.DBus.Properties", "Set", 561 "xyz.openbmc_project.Network.IP", "PrefixLength", 562 sdbusplus::message::variant<uint8_t>(newValue)); 563 }; 564 565 /** 566 * @brief Disables VLAN with given ifaceId 567 * 568 * @param[in] ifaceId Id of VLAN interface that should be disabled 569 * @param[in] callback Function that will be called after the operation 570 * 571 * @return None. 572 */ 573 template <typename CallbackFunc> 574 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) 575 { 576 crow::connections::systemBus->async_method_call( 577 callback, "xyz.openbmc_project.Network", 578 std::string("/xyz/openbmc_project/network/") + ifaceId, 579 "xyz.openbmc_project.Object.Delete", "Delete"); 580 }; 581 582 /** 583 * @brief Sets given HostName of the machine through D-Bus 584 * 585 * @param[in] newHostname New name that HostName will be changed to 586 * @param[in] callback Function that will be called after the operation 587 * 588 * @return None. 589 */ 590 template <typename CallbackFunc> 591 void setHostName(const std::string &newHostname, CallbackFunc &&callback) 592 { 593 crow::connections::systemBus->async_method_call( 594 callback, "xyz.openbmc_project.Network", 595 "/xyz/openbmc_project/network/config", 596 "org.freedesktop.DBus.Properties", "Set", 597 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 598 sdbusplus::message::variant<std::string>(newHostname)); 599 }; 600 601 /** 602 * @brief Deletes given IPv4 603 * 604 * @param[in] ifaceId Id of interface whose IP should be deleted 605 * @param[in] ipIdx index of IP in input array that should be deleted 606 * @param[in] ipHash DBus Hash id of IP that should be deleted 607 * @param[io] asyncResp Response object that will be returned to client 608 * 609 * @return None 610 */ 611 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, 612 unsigned int ipIdx, 613 const std::shared_ptr<AsyncResp> &asyncResp) 614 { 615 crow::connections::systemBus->async_method_call( 616 [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}]( 617 const boost::system::error_code ec) { 618 if (ec) 619 { 620 messages::addMessageToJson( 621 asyncResp->res.jsonValue, messages::internalError(), 622 "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 623 } 624 else 625 { 626 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr; 627 } 628 }, 629 "xyz.openbmc_project.Network", 630 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 631 "xyz.openbmc_project.Object.Delete", "Delete"); 632 } 633 634 /** 635 * @brief Creates IPv4 with given data 636 * 637 * @param[in] ifaceId Id of interface whose IP should be deleted 638 * @param[in] ipIdx index of IP in input array that should be deleted 639 * @param[in] ipHash DBus Hash id of IP that should be deleted 640 * @param[io] asyncResp Response object that will be returned to client 641 * 642 * @return None 643 */ 644 void createIPv4(const std::string &ifaceId, unsigned int ipIdx, 645 uint8_t subnetMask, const std::string &gateway, 646 const std::string &address, 647 const std::shared_ptr<AsyncResp> &asyncResp) 648 { 649 auto createIpHandler = [ipIdx{std::move(ipIdx)}, 650 asyncResp{std::move(asyncResp)}]( 651 const boost::system::error_code ec) { 652 if (ec) 653 { 654 messages::addMessageToJson( 655 asyncResp->res.jsonValue, messages::internalError(), 656 "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 657 } 658 }; 659 660 crow::connections::systemBus->async_method_call( 661 std::move(createIpHandler), "xyz.openbmc_project.Network", 662 "/xyz/openbmc_project/network/" + ifaceId, 663 "xyz.openbmc_project.Network.IP.Create", "IP", 664 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, 665 gateway); 666 } 667 668 /** 669 * @brief Translates Address Origin value from D-Bus to Redfish format and 670 * vice-versa 671 * 672 * @param[in] inputOrigin Input value that should be translated 673 * @param[in] isIPv4 True for IPv4 origins, False for IPv6 674 * @param[in] isFromDBus True for DBus->Redfish conversion, false for 675 * reverse 676 * 677 * @return Empty string in case of failure, translated value otherwise 678 */ 679 std::string translateAddressOriginBetweenDBusAndRedfish( 680 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) 681 { 682 // Invalid pointer 683 if (inputOrigin == nullptr) 684 { 685 return ""; 686 } 687 688 static const constexpr unsigned int firstIPv4OnlyIdx = 1; 689 static const constexpr unsigned int firstIPv6OnlyIdx = 3; 690 691 std::array<std::pair<const char *, const char *>, 6> translationTable{ 692 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, 693 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, 694 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 695 "IPv4LinkLocal"}, 696 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, 697 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 698 "LinkLocal"}, 699 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; 700 701 for (unsigned int i = 0; i < translationTable.size(); i++) 702 { 703 // Skip unrelated 704 if (isIPv4 && i >= firstIPv6OnlyIdx) 705 break; 706 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) 707 continue; 708 709 // When translating D-Bus to Redfish compare input to first element 710 if (isFromDBus && translationTable[i].first == *inputOrigin) 711 return translationTable[i].second; 712 713 // When translating Redfish to D-Bus compare input to second element 714 if (!isFromDBus && translationTable[i].second == *inputOrigin) 715 return translationTable[i].first; 716 } 717 718 // If we are still here, that means that value has not been found 719 return ""; 720 } 721 722 /** 723 * Function that retrieves all properties for given Ethernet Interface 724 * Object 725 * from EntityManager Network Manager 726 * @param ethifaceId a eth interface id to query on DBus 727 * @param callback a function that shall be called to convert Dbus output 728 * into JSON 729 */ 730 template <typename CallbackFunc> 731 void getEthernetIfaceData(const std::string ðifaceId, 732 CallbackFunc &&callback) 733 { 734 crow::connections::systemBus->async_method_call( 735 [this, ethifaceId{std::move(ethifaceId)}, 736 callback{std::move(callback)}]( 737 const boost::system::error_code error_code, 738 const GetManagedObjectsType &resp) { 739 EthernetInterfaceData ethData{}; 740 std::vector<IPv4AddressData> ipv4Data; 741 ipv4Data.reserve(maxIpV4AddressesPerInterface); 742 743 if (error_code) 744 { 745 // Something wrong on DBus, the error_code is not important 746 // at this moment, just return success=false, and empty 747 // output. Since size of vector may vary depending on 748 // information from Network Manager, and empty output could 749 // not be treated same way as error. 750 callback(false, ethData, ipv4Data); 751 return; 752 } 753 754 // Find interface 755 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) == 756 resp.end()) 757 { 758 // Interface has not been found 759 callback(false, ethData, ipv4Data); 760 return; 761 } 762 763 extractEthernetInterfaceData(ethifaceId, resp, ethData); 764 extractIPv4Data(ethifaceId, resp, ipv4Data); 765 766 // Fix global GW 767 for (IPv4AddressData &ipv4 : ipv4Data) 768 { 769 if ((ipv4.global) && ((ipv4.gateway == nullptr) || 770 (*ipv4.gateway == "0.0.0.0"))) 771 { 772 ipv4.gateway = ethData.defaultGateway; 773 } 774 } 775 776 // Finally make a callback with useful data 777 callback(true, ethData, ipv4Data); 778 }, 779 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 780 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 781 }; 782 783 /** 784 * Function that retrieves all Ethernet Interfaces available through Network 785 * Manager 786 * @param callback a function that shall be called to convert Dbus output 787 * into JSON. 788 */ 789 template <typename CallbackFunc> 790 void getEthernetIfaceList(CallbackFunc &&callback) 791 { 792 crow::connections::systemBus->async_method_call( 793 [this, callback{std::move(callback)}]( 794 const boost::system::error_code error_code, 795 GetManagedObjectsType &resp) { 796 // Callback requires vector<string> to retrieve all available 797 // ethernet interfaces 798 std::vector<std::string> ifaceList; 799 ifaceList.reserve(resp.size()); 800 if (error_code) 801 { 802 // Something wrong on DBus, the error_code is not important 803 // at this moment, just return success=false, and empty 804 // output. Since size of vector may vary depending on 805 // information from Network Manager, and empty output could 806 // not be treated same way as error. 807 callback(false, ifaceList); 808 return; 809 } 810 811 // Iterate over all retrieved ObjectPaths. 812 for (auto &objpath : resp) 813 { 814 // And all interfaces available for certain ObjectPath. 815 for (auto &interface : objpath.second) 816 { 817 // If interface is 818 // xyz.openbmc_project.Network.EthernetInterface, this 819 // is what we're looking for. 820 if (interface.first == 821 "xyz.openbmc_project.Network.EthernetInterface") 822 { 823 // Cut out everything until last "/", ... 824 const std::string &ifaceId = 825 static_cast<const std::string &>(objpath.first); 826 std::size_t lastPos = ifaceId.rfind("/"); 827 if (lastPos != std::string::npos) 828 { 829 // and put it into output vector. 830 ifaceList.emplace_back( 831 ifaceId.substr(lastPos + 1)); 832 } 833 } 834 } 835 } 836 // Finally make a callback with useful data 837 callback(true, ifaceList); 838 }, 839 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 840 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 841 }; 842 }; 843 844 /** 845 * EthernetCollection derived class for delivering Ethernet Collection Schema 846 */ 847 class EthernetCollection : public Node 848 { 849 public: 850 // TODO(Pawel) Remove line from below, where we assume that there is only 851 // one manager called openbmc This shall be generic, but requires to update 852 // GetSubroutes method 853 EthernetCollection(CrowApp &app) : 854 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") 855 { 856 Node::json["@odata.type"] = 857 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 858 Node::json["@odata.context"] = 859 "/redfish/v1/" 860 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 861 Node::json["@odata.id"] = 862 "/redfish/v1/Managers/openbmc/EthernetInterfaces"; 863 Node::json["Name"] = "Ethernet Network Interface Collection"; 864 Node::json["Description"] = 865 "Collection of EthernetInterfaces for this Manager"; 866 867 entityPrivileges = { 868 {boost::beast::http::verb::get, {{"Login"}}}, 869 {boost::beast::http::verb::head, {{"Login"}}}, 870 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 871 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 872 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 873 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 874 } 875 876 private: 877 /** 878 * Functions triggers appropriate requests on DBus 879 */ 880 void doGet(crow::Response &res, const crow::Request &req, 881 const std::vector<std::string> ¶ms) override 882 { 883 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces 884 // for any Manager, not only hardcoded 'openbmc'. 885 std::string managerId = "openbmc"; 886 887 // get eth interface list, and call the below callback for JSON 888 // preparation 889 ethernetProvider.getEthernetIfaceList( 890 [&, managerId{std::move(managerId)}]( 891 const bool &success, 892 const std::vector<std::string> &iface_list) { 893 if (success) 894 { 895 nlohmann::json ifaceArray = nlohmann::json::array(); 896 for (const std::string &ifaceItem : iface_list) 897 { 898 ifaceArray.push_back( 899 {{"@odata.id", "/redfish/v1/Managers/" + managerId + 900 "/EthernetInterfaces/" + 901 ifaceItem}}); 902 } 903 Node::json["Members"] = ifaceArray; 904 Node::json["Members@odata.count"] = ifaceArray.size(); 905 Node::json["@odata.id"] = "/redfish/v1/Managers/" + 906 managerId + "/EthernetInterfaces"; 907 res.jsonValue = Node::json; 908 } 909 else 910 { 911 // No success, best what we can do is return INTERNALL ERROR 912 res.result( 913 boost::beast::http::status::internal_server_error); 914 } 915 res.end(); 916 }); 917 } 918 919 // Ethernet Provider object 920 // TODO(Pawel) consider move it to singleton 921 OnDemandEthernetProvider ethernetProvider; 922 }; 923 924 /** 925 * EthernetInterface derived class for delivering Ethernet Schema 926 */ 927 class EthernetInterface : public Node 928 { 929 public: 930 /* 931 * Default Constructor 932 */ 933 // TODO(Pawel) Remove line from below, where we assume that there is only 934 // one manager called openbmc This shall be generic, but requires to update 935 // GetSubroutes method 936 EthernetInterface(CrowApp &app) : 937 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", 938 std::string()) 939 { 940 Node::json["@odata.type"] = 941 "#EthernetInterface.v1_2_0.EthernetInterface"; 942 Node::json["@odata.context"] = 943 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 944 Node::json["Name"] = "Manager Ethernet Interface"; 945 Node::json["Description"] = "Management Network Interface"; 946 947 entityPrivileges = { 948 {boost::beast::http::verb::get, {{"Login"}}}, 949 {boost::beast::http::verb::head, {{"Login"}}}, 950 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 951 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 952 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 953 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 954 } 955 956 // TODO(kkowalsk) Find a suitable class/namespace for this 957 static void handleVlanPatch(const std::string &ifaceId, 958 const nlohmann::json &input, 959 const EthernetInterfaceData ð_data, 960 const std::string &pathPrefix, 961 const std::shared_ptr<AsyncResp> &asyncResp) 962 { 963 if (!input.is_object()) 964 { 965 messages::addMessageToJson( 966 asyncResp->res.jsonValue, 967 messages::propertyValueTypeError(input.dump(), "VLAN"), 968 pathPrefix); 969 return; 970 } 971 972 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; 973 nlohmann::json ¶msJson = 974 (pathPrefix == "/") 975 ? asyncResp->res.jsonValue 976 : asyncResp->res 977 .jsonValue[nlohmann::json_pointer<nlohmann::json>( 978 pathPrefix)]; 979 bool inputVlanEnabled; 980 uint64_t inputVlanId; 981 982 json_util::Result inputVlanEnabledState = json_util::getBool( 983 "VLANEnable", input, inputVlanEnabled, 984 static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 985 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable")); 986 json_util::Result inputVlanIdState = json_util::getUnsigned( 987 "VLANId", input, inputVlanId, 988 static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 989 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId")); 990 bool inputInvalid = false; 991 992 // Do not proceed if fields in VLAN object were of wrong type 993 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || 994 inputVlanIdState == json_util::Result::WRONG_TYPE) 995 { 996 return; 997 } 998 999 // Verify input 1000 if (eth_data.vlanId == nullptr) 1001 { 1002 // This interface is not a VLAN. Cannot do anything with it 1003 // TODO(kkowalsk) Change this message 1004 messages::addMessageToJson(asyncResp->res.jsonValue, 1005 messages::propertyMissing("VLANEnable"), 1006 pathPrefix); 1007 1008 inputInvalid = true; 1009 } 1010 else 1011 { 1012 // Load actual data into field values if they were not provided 1013 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) 1014 { 1015 inputVlanEnabled = true; 1016 } 1017 1018 if (inputVlanIdState == json_util::Result::NOT_EXIST) 1019 { 1020 inputVlanId = *eth_data.vlanId; 1021 } 1022 } 1023 1024 // Do not proceed if input has not been valid 1025 if (inputInvalid) 1026 { 1027 return; 1028 } 1029 1030 // VLAN is configured on the interface 1031 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) 1032 { 1033 // Change VLAN Id 1034 paramsJson["VLANId"] = inputVlanId; 1035 OnDemandEthernetProvider::changeVlanId( 1036 ifaceId, static_cast<uint32_t>(inputVlanId), 1037 [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( 1038 const boost::system::error_code ec) { 1039 if (ec) 1040 { 1041 messages::addMessageToJson(asyncResp->res.jsonValue, 1042 messages::internalError(), 1043 pathPrefix); 1044 } 1045 else 1046 { 1047 paramsJson["VLANEnable"] = true; 1048 } 1049 }); 1050 } 1051 else if (inputVlanEnabled == false) 1052 { 1053 // Disable VLAN 1054 OnDemandEthernetProvider::disableVlan( 1055 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}]( 1056 const boost::system::error_code ec) { 1057 if (ec) 1058 { 1059 messages::addMessageToJson(asyncResp->res.jsonValue, 1060 messages::internalError(), 1061 pathPrefix); 1062 } 1063 else 1064 { 1065 paramsJson["VLANEnable"] = false; 1066 } 1067 }); 1068 } 1069 } 1070 1071 private: 1072 void handleHostnamePatch(const nlohmann::json &input, 1073 const EthernetInterfaceData ð_data, 1074 const std::shared_ptr<AsyncResp> &asyncResp) 1075 { 1076 if (input.is_string()) 1077 { 1078 std::string newHostname = input.get<std::string>(); 1079 1080 if (eth_data.hostname == nullptr || 1081 newHostname != *eth_data.hostname) 1082 { 1083 // Change hostname 1084 ethernetProvider.setHostName( 1085 newHostname, [asyncResp, newHostname]( 1086 const boost::system::error_code ec) { 1087 if (ec) 1088 { 1089 messages::addMessageToJson( 1090 asyncResp->res.jsonValue, 1091 messages::internalError(), "/HostName"); 1092 } 1093 else 1094 { 1095 asyncResp->res.jsonValue["HostName"] = newHostname; 1096 } 1097 }); 1098 } 1099 } 1100 else 1101 { 1102 messages::addMessageToJson( 1103 asyncResp->res.jsonValue, 1104 messages::propertyValueTypeError(input.dump(), "HostName"), 1105 "/HostName"); 1106 } 1107 } 1108 1109 void handleIPv4Patch(const std::string &ifaceId, 1110 const nlohmann::json &input, 1111 const std::vector<IPv4AddressData> &ipv4_data, 1112 const std::shared_ptr<AsyncResp> &asyncResp) 1113 { 1114 if (!input.is_array()) 1115 { 1116 messages::addMessageToJson( 1117 asyncResp->res.jsonValue, 1118 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), 1119 "/IPv4Addresses"); 1120 return; 1121 } 1122 1123 // According to Redfish PATCH definition, size must be at least equal 1124 if (input.size() < ipv4_data.size()) 1125 { 1126 // TODO(kkowalsk) This should be a message indicating that not 1127 // enough data has been provided 1128 messages::addMessageToJson(asyncResp->res.jsonValue, 1129 messages::internalError(), 1130 "/IPv4Addresses"); 1131 return; 1132 } 1133 1134 json_util::Result addressFieldState; 1135 json_util::Result subnetMaskFieldState; 1136 json_util::Result addressOriginFieldState; 1137 json_util::Result gatewayFieldState; 1138 const std::string *addressFieldValue; 1139 const std::string *subnetMaskFieldValue; 1140 const std::string *addressOriginFieldValue = nullptr; 1141 const std::string *gatewayFieldValue; 1142 uint8_t subnetMaskAsPrefixLength; 1143 std::string addressOriginInDBusFormat; 1144 1145 bool errorDetected = false; 1146 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) 1147 { 1148 // Check that entry is not of some unexpected type 1149 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) 1150 { 1151 // Invalid object type 1152 messages::addMessageToJson( 1153 asyncResp->res.jsonValue, 1154 messages::propertyValueTypeError(input[entryIdx].dump(), 1155 "IPv4Address"), 1156 "/IPv4Addresses/" + std::to_string(entryIdx)); 1157 1158 continue; 1159 } 1160 1161 // Try to load fields 1162 addressFieldState = json_util::getString( 1163 "Address", input[entryIdx], addressFieldValue, 1164 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1165 asyncResp->res.jsonValue, 1166 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1167 subnetMaskFieldState = json_util::getString( 1168 "SubnetMask", input[entryIdx], subnetMaskFieldValue, 1169 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1170 asyncResp->res.jsonValue, 1171 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1172 addressOriginFieldState = json_util::getString( 1173 "AddressOrigin", input[entryIdx], addressOriginFieldValue, 1174 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1175 asyncResp->res.jsonValue, 1176 "/IPv4Addresses/" + std::to_string(entryIdx) + 1177 "/AddressOrigin"); 1178 gatewayFieldState = json_util::getString( 1179 "Gateway", input[entryIdx], gatewayFieldValue, 1180 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1181 asyncResp->res.jsonValue, 1182 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1183 1184 if (addressFieldState == json_util::Result::WRONG_TYPE || 1185 subnetMaskFieldState == json_util::Result::WRONG_TYPE || 1186 addressOriginFieldState == json_util::Result::WRONG_TYPE || 1187 gatewayFieldState == json_util::Result::WRONG_TYPE) 1188 { 1189 return; 1190 } 1191 1192 if (addressFieldState == json_util::Result::SUCCESS && 1193 !ethernetProvider.ipv4VerifyIpAndGetBitcount( 1194 *addressFieldValue)) 1195 { 1196 errorDetected = true; 1197 messages::addMessageToJson( 1198 asyncResp->res.jsonValue, 1199 messages::propertyValueFormatError(*addressFieldValue, 1200 "Address"), 1201 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1202 } 1203 1204 if (subnetMaskFieldState == json_util::Result::SUCCESS && 1205 !ethernetProvider.ipv4VerifyIpAndGetBitcount( 1206 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) 1207 { 1208 errorDetected = true; 1209 messages::addMessageToJson( 1210 asyncResp->res.jsonValue, 1211 messages::propertyValueFormatError(*subnetMaskFieldValue, 1212 "SubnetMask"), 1213 "/IPv4Addresses/" + std::to_string(entryIdx) + 1214 "/SubnetMask"); 1215 } 1216 1217 // get Address origin in proper format 1218 addressOriginInDBusFormat = 1219 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish( 1220 addressOriginFieldValue, true, false); 1221 1222 if (addressOriginFieldState == json_util::Result::SUCCESS && 1223 addressOriginInDBusFormat.empty()) 1224 { 1225 errorDetected = true; 1226 messages::addMessageToJson( 1227 asyncResp->res.jsonValue, 1228 messages::propertyValueNotInList(*addressOriginFieldValue, 1229 "AddressOrigin"), 1230 "/IPv4Addresses/" + std::to_string(entryIdx) + 1231 "/AddressOrigin"); 1232 } 1233 1234 if (gatewayFieldState == json_util::Result::SUCCESS && 1235 !ethernetProvider.ipv4VerifyIpAndGetBitcount( 1236 *gatewayFieldValue)) 1237 { 1238 errorDetected = true; 1239 messages::addMessageToJson( 1240 asyncResp->res.jsonValue, 1241 messages::propertyValueFormatError(*gatewayFieldValue, 1242 "Gateway"), 1243 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1244 } 1245 1246 // If any error occured do not proceed with current entry, but do 1247 // not end loop 1248 if (errorDetected) 1249 { 1250 errorDetected = false; 1251 continue; 1252 } 1253 1254 if (entryIdx >= ipv4_data.size()) 1255 { 1256 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = 1257 input[entryIdx]; 1258 1259 // Verify that all field were provided 1260 if (addressFieldState == json_util::Result::NOT_EXIST) 1261 { 1262 errorDetected = true; 1263 messages::addMessageToJson( 1264 asyncResp->res.jsonValue, 1265 messages::propertyMissing("Address"), 1266 "/IPv4Addresses/" + std::to_string(entryIdx) + 1267 "/Address"); 1268 } 1269 1270 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) 1271 { 1272 errorDetected = true; 1273 messages::addMessageToJson( 1274 asyncResp->res.jsonValue, 1275 messages::propertyMissing("SubnetMask"), 1276 "/IPv4Addresses/" + std::to_string(entryIdx) + 1277 "/SubnetMask"); 1278 } 1279 1280 if (addressOriginFieldState == json_util::Result::NOT_EXIST) 1281 { 1282 errorDetected = true; 1283 messages::addMessageToJson( 1284 asyncResp->res.jsonValue, 1285 messages::propertyMissing("AddressOrigin"), 1286 "/IPv4Addresses/" + std::to_string(entryIdx) + 1287 "/AddressOrigin"); 1288 } 1289 1290 if (gatewayFieldState == json_util::Result::NOT_EXIST) 1291 { 1292 errorDetected = true; 1293 messages::addMessageToJson( 1294 asyncResp->res.jsonValue, 1295 messages::propertyMissing("Gateway"), 1296 "/IPv4Addresses/" + std::to_string(entryIdx) + 1297 "/Gateway"); 1298 } 1299 1300 // If any error occured do not proceed with current entry, but 1301 // do not end loop 1302 if (errorDetected) 1303 { 1304 errorDetected = false; 1305 continue; 1306 } 1307 1308 // Create IPv4 with provided data 1309 ethernetProvider.createIPv4( 1310 ifaceId, entryIdx, subnetMaskAsPrefixLength, 1311 *gatewayFieldValue, *addressFieldValue, asyncResp); 1312 } 1313 else 1314 { 1315 // Existing object that should be modified/deleted/remain 1316 // unchanged 1317 if (input[entryIdx].is_null()) 1318 { 1319 // Object should be deleted 1320 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, 1321 entryIdx, asyncResp); 1322 } 1323 else if (input[entryIdx].is_object()) 1324 { 1325 if (input[entryIdx].size() == 0) 1326 { 1327 // Object shall remain unchanged 1328 continue; 1329 } 1330 1331 // Apply changes 1332 if (addressFieldState == json_util::Result::SUCCESS && 1333 ipv4_data[entryIdx].address != nullptr && 1334 *ipv4_data[entryIdx].address != *addressFieldValue) 1335 { 1336 ethernetProvider.changeIPv4AddressProperty( 1337 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1338 "Address", *addressFieldValue, asyncResp); 1339 } 1340 1341 if (subnetMaskFieldState == json_util::Result::SUCCESS && 1342 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) 1343 { 1344 ethernetProvider.changeIPv4SubnetMaskProperty( 1345 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1346 *subnetMaskFieldValue, subnetMaskAsPrefixLength, 1347 asyncResp); 1348 } 1349 1350 if (addressOriginFieldState == json_util::Result::SUCCESS && 1351 ipv4_data[entryIdx].origin != *addressFieldValue) 1352 { 1353 ethernetProvider.changeIPv4Origin( 1354 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1355 *addressOriginFieldValue, addressOriginInDBusFormat, 1356 asyncResp); 1357 } 1358 1359 if (gatewayFieldState == json_util::Result::SUCCESS && 1360 ipv4_data[entryIdx].gateway != nullptr && 1361 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) 1362 { 1363 ethernetProvider.changeIPv4AddressProperty( 1364 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1365 "Gateway", *gatewayFieldValue, asyncResp); 1366 } 1367 } 1368 } 1369 } 1370 } 1371 1372 nlohmann::json 1373 parseInterfaceData(const std::string &ifaceId, 1374 const EthernetInterfaceData ð_data, 1375 const std::vector<IPv4AddressData> &ipv4_data) 1376 { 1377 // Copy JSON object to avoid race condition 1378 nlohmann::json jsonResponse(Node::json); 1379 1380 // Fill out obvious data... 1381 jsonResponse["Id"] = ifaceId; 1382 jsonResponse["@odata.id"] = 1383 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId; 1384 1385 // ... then the one from DBus, regarding eth iface... 1386 if (eth_data.speed != nullptr) 1387 jsonResponse["SpeedMbps"] = *eth_data.speed; 1388 1389 if (eth_data.macAddress != nullptr) 1390 jsonResponse["MACAddress"] = *eth_data.macAddress; 1391 1392 if (eth_data.hostname != nullptr) 1393 jsonResponse["HostName"] = *eth_data.hostname; 1394 1395 if (eth_data.vlanId != nullptr) 1396 { 1397 nlohmann::json &vlanObj = jsonResponse["VLAN"]; 1398 vlanObj["VLANEnable"] = true; 1399 vlanObj["VLANId"] = *eth_data.vlanId; 1400 } 1401 else 1402 { 1403 nlohmann::json &vlanObj = jsonResponse["VLANs"]; 1404 vlanObj["@odata.id"] = 1405 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId + 1406 "/VLANs"; 1407 } 1408 1409 // ... at last, check if there are IPv4 data and prepare appropriate 1410 // collection 1411 if (ipv4_data.size() > 0) 1412 { 1413 nlohmann::json ipv4Array = nlohmann::json::array(); 1414 for (auto &ipv4Config : ipv4_data) 1415 { 1416 nlohmann::json jsonIpv4; 1417 if (ipv4Config.address != nullptr) 1418 { 1419 jsonIpv4["Address"] = *ipv4Config.address; 1420 if (ipv4Config.gateway != nullptr) 1421 jsonIpv4["Gateway"] = *ipv4Config.gateway; 1422 1423 jsonIpv4["AddressOrigin"] = ipv4Config.origin; 1424 jsonIpv4["SubnetMask"] = ipv4Config.netmask; 1425 1426 ipv4Array.push_back(std::move(jsonIpv4)); 1427 } 1428 } 1429 jsonResponse["IPv4Addresses"] = std::move(ipv4Array); 1430 } 1431 1432 return jsonResponse; 1433 } 1434 1435 /** 1436 * Functions triggers appropriate requests on DBus 1437 */ 1438 void doGet(crow::Response &res, const crow::Request &req, 1439 const std::vector<std::string> ¶ms) override 1440 { 1441 // TODO(Pawel) this shall be parametrized call (two params) to get 1442 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1443 // Check if there is required param, truly entering this shall be 1444 // impossible. 1445 if (params.size() != 1) 1446 { 1447 res.result(boost::beast::http::status::internal_server_error); 1448 res.end(); 1449 return; 1450 } 1451 1452 const std::string &ifaceId = params[0]; 1453 1454 // get single eth interface data, and call the below callback for JSON 1455 // preparation 1456 ethernetProvider.getEthernetIfaceData( 1457 ifaceId, 1458 [&, ifaceId](const bool &success, 1459 const EthernetInterfaceData ð_data, 1460 const std::vector<IPv4AddressData> &ipv4_data) { 1461 if (success) 1462 { 1463 res.jsonValue = 1464 parseInterfaceData(ifaceId, eth_data, ipv4_data); 1465 } 1466 else 1467 { 1468 // ... otherwise return error 1469 // TODO(Pawel)consider distinguish between non existing 1470 // object, and other errors 1471 messages::addMessageToErrorJson( 1472 res.jsonValue, messages::resourceNotFound( 1473 "EthernetInterface", ifaceId)); 1474 res.result(boost::beast::http::status::not_found); 1475 } 1476 res.end(); 1477 }); 1478 } 1479 1480 void doPatch(crow::Response &res, const crow::Request &req, 1481 const std::vector<std::string> ¶ms) override 1482 { 1483 // TODO(Pawel) this shall be parametrized call (two params) to get 1484 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1485 // Check if there is required param, truly entering this shall be 1486 // impossible. 1487 if (params.size() != 1) 1488 { 1489 res.result(boost::beast::http::status::internal_server_error); 1490 res.end(); 1491 return; 1492 } 1493 1494 const std::string &ifaceId = params[0]; 1495 1496 nlohmann::json patchReq; 1497 1498 if (!json_util::processJsonFromRequest(res, req, patchReq)) 1499 { 1500 return; 1501 } 1502 1503 // get single eth interface data, and call the below callback for JSON 1504 // preparation 1505 ethernetProvider.getEthernetIfaceData( 1506 ifaceId, 1507 [&, ifaceId, patchReq = std::move(patchReq)]( 1508 const bool &success, const EthernetInterfaceData ð_data, 1509 const std::vector<IPv4AddressData> &ipv4_data) { 1510 if (!success) 1511 { 1512 // ... otherwise return error 1513 // TODO(Pawel)consider distinguish between non existing 1514 // object, and other errors 1515 messages::addMessageToErrorJson( 1516 res.jsonValue, messages::resourceNotFound( 1517 "VLAN Network Interface", ifaceId)); 1518 res.result(boost::beast::http::status::not_found); 1519 res.end(); 1520 1521 return; 1522 } 1523 1524 res.jsonValue = 1525 parseInterfaceData(ifaceId, eth_data, ipv4_data); 1526 1527 std::shared_ptr<AsyncResp> asyncResp = 1528 std::make_shared<AsyncResp>(res); 1529 1530 for (auto propertyIt = patchReq.begin(); 1531 propertyIt != patchReq.end(); ++propertyIt) 1532 { 1533 if (propertyIt.key() == "VLAN") 1534 { 1535 handleVlanPatch(ifaceId, propertyIt.value(), eth_data, 1536 "/VLAN", asyncResp); 1537 } 1538 else if (propertyIt.key() == "HostName") 1539 { 1540 handleHostnamePatch(propertyIt.value(), eth_data, 1541 asyncResp); 1542 } 1543 else if (propertyIt.key() == "IPv4Addresses") 1544 { 1545 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data, 1546 asyncResp); 1547 } 1548 else if (propertyIt.key() == "IPv6Addresses") 1549 { 1550 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet 1551 messages::addMessageToJsonRoot( 1552 res.jsonValue, 1553 messages::propertyNotWritable(propertyIt.key())); 1554 } 1555 else 1556 { 1557 auto fieldInJsonIt = 1558 res.jsonValue.find(propertyIt.key()); 1559 1560 if (fieldInJsonIt == res.jsonValue.end()) 1561 { 1562 // Field not in scope of defined fields 1563 messages::addMessageToJsonRoot( 1564 res.jsonValue, 1565 messages::propertyUnknown(propertyIt.key())); 1566 } 1567 else if (*fieldInJsonIt != *propertyIt) 1568 { 1569 // User attempted to modify non-writable field 1570 messages::addMessageToJsonRoot( 1571 res.jsonValue, messages::propertyNotWritable( 1572 propertyIt.key())); 1573 } 1574 } 1575 } 1576 }); 1577 } 1578 1579 // Ethernet Provider object 1580 // TODO(Pawel) consider move it to singleton 1581 OnDemandEthernetProvider ethernetProvider; 1582 }; 1583 1584 class VlanNetworkInterfaceCollection; 1585 1586 /** 1587 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema 1588 */ 1589 class VlanNetworkInterface : public Node 1590 { 1591 public: 1592 /* 1593 * Default Constructor 1594 */ 1595 template <typename CrowApp> 1596 // TODO(Pawel) Remove line from below, where we assume that there is only 1597 // one manager called openbmc This shall be generic, but requires to update 1598 // GetSubroutes method 1599 VlanNetworkInterface(CrowApp &app) : 1600 Node( 1601 app, 1602 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>", 1603 std::string(), std::string()) 1604 { 1605 Node::json["@odata.type"] = 1606 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; 1607 Node::json["@odata.context"] = 1608 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; 1609 Node::json["Name"] = "VLAN Network Interface"; 1610 1611 entityPrivileges = { 1612 {boost::beast::http::verb::get, {{"Login"}}}, 1613 {boost::beast::http::verb::head, {{"Login"}}}, 1614 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1615 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1616 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1617 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1618 } 1619 1620 private: 1621 nlohmann::json 1622 parseInterfaceData(const std::string &parent_ifaceId, 1623 const std::string &ifaceId, 1624 const EthernetInterfaceData ð_data, 1625 const std::vector<IPv4AddressData> &ipv4_data) 1626 { 1627 // Copy JSON object to avoid race condition 1628 nlohmann::json jsonResponse(Node::json); 1629 1630 // Fill out obvious data... 1631 jsonResponse["Id"] = ifaceId; 1632 jsonResponse["@odata.id"] = 1633 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + 1634 parent_ifaceId + "/VLANs/" + ifaceId; 1635 1636 jsonResponse["VLANEnable"] = true; 1637 jsonResponse["VLANId"] = *eth_data.vlanId; 1638 1639 return jsonResponse; 1640 } 1641 1642 bool verifyNames(crow::Response &res, const std::string &parent, 1643 const std::string &iface) 1644 { 1645 if (!boost::starts_with(iface, parent + "_")) 1646 { 1647 messages::addMessageToErrorJson( 1648 res.jsonValue, 1649 messages::resourceNotFound("VLAN Network Interface", iface)); 1650 res.result(boost::beast::http::status::not_found); 1651 res.end(); 1652 1653 return false; 1654 } 1655 else 1656 { 1657 return true; 1658 } 1659 } 1660 1661 /** 1662 * Functions triggers appropriate requests on DBus 1663 */ 1664 void doGet(crow::Response &res, const crow::Request &req, 1665 const std::vector<std::string> ¶ms) override 1666 { 1667 // TODO(Pawel) this shall be parametrized call (two params) to get 1668 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1669 // Check if there is required param, truly entering this shall be 1670 // impossible. 1671 if (params.size() != 2) 1672 { 1673 res.result(boost::beast::http::status::internal_server_error); 1674 res.end(); 1675 return; 1676 } 1677 1678 const std::string &parentIfaceId = params[0]; 1679 const std::string &ifaceId = params[1]; 1680 1681 if (!verifyNames(res, parentIfaceId, ifaceId)) 1682 { 1683 return; 1684 } 1685 1686 // Get single eth interface data, and call the below callback for JSON 1687 // preparation 1688 ethernetProvider.getEthernetIfaceData( 1689 ifaceId, [&, parentIfaceId, 1690 ifaceId](const bool &success, 1691 const EthernetInterfaceData ð_data, 1692 const std::vector<IPv4AddressData> &ipv4_data) { 1693 if (success && eth_data.vlanId != nullptr) 1694 { 1695 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, 1696 eth_data, ipv4_data); 1697 } 1698 else 1699 { 1700 // ... otherwise return error 1701 // TODO(Pawel)consider distinguish between non existing 1702 // object, and other errors 1703 messages::addMessageToErrorJson( 1704 res.jsonValue, messages::resourceNotFound( 1705 "VLAN Network Interface", ifaceId)); 1706 res.result(boost::beast::http::status::not_found); 1707 } 1708 res.end(); 1709 }); 1710 } 1711 1712 void doPatch(crow::Response &res, const crow::Request &req, 1713 const std::vector<std::string> ¶ms) override 1714 { 1715 if (params.size() != 2) 1716 { 1717 res.result(boost::beast::http::status::internal_server_error); 1718 res.end(); 1719 return; 1720 } 1721 1722 const std::string &parentIfaceId = params[0]; 1723 const std::string &ifaceId = params[1]; 1724 1725 if (!verifyNames(res, parentIfaceId, ifaceId)) 1726 { 1727 return; 1728 } 1729 1730 nlohmann::json patchReq; 1731 1732 if (!json_util::processJsonFromRequest(res, req, patchReq)) 1733 { 1734 return; 1735 } 1736 1737 // Get single eth interface data, and call the below callback for JSON 1738 // preparation 1739 ethernetProvider.getEthernetIfaceData( 1740 ifaceId, 1741 [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)]( 1742 const bool &success, const EthernetInterfaceData ð_data, 1743 const std::vector<IPv4AddressData> &ipv4_data) { 1744 if (!success) 1745 { 1746 // ... otherwise return error 1747 // TODO(Pawel)consider distinguish between non existing 1748 // object, and other errors 1749 messages::addMessageToErrorJson( 1750 res.jsonValue, messages::resourceNotFound( 1751 "VLAN Network Interface", ifaceId)); 1752 res.result(boost::beast::http::status::not_found); 1753 res.end(); 1754 1755 return; 1756 } 1757 1758 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, 1759 eth_data, ipv4_data); 1760 1761 std::shared_ptr<AsyncResp> asyncResp = 1762 std::make_shared<AsyncResp>(res); 1763 1764 for (auto propertyIt = patchReq.begin(); 1765 propertyIt != patchReq.end(); ++propertyIt) 1766 { 1767 if (propertyIt.key() != "VLANEnable" && 1768 propertyIt.key() != "VLANId") 1769 { 1770 auto fieldInJsonIt = 1771 res.jsonValue.find(propertyIt.key()); 1772 1773 if (fieldInJsonIt == res.jsonValue.end()) 1774 { 1775 // Field not in scope of defined fields 1776 messages::addMessageToJsonRoot( 1777 res.jsonValue, 1778 messages::propertyUnknown(propertyIt.key())); 1779 } 1780 else if (*fieldInJsonIt != *propertyIt) 1781 { 1782 // User attempted to modify non-writable field 1783 messages::addMessageToJsonRoot( 1784 res.jsonValue, messages::propertyNotWritable( 1785 propertyIt.key())); 1786 } 1787 } 1788 } 1789 1790 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, 1791 "/", asyncResp); 1792 }); 1793 } 1794 1795 void doDelete(crow::Response &res, const crow::Request &req, 1796 const std::vector<std::string> ¶ms) override 1797 { 1798 if (params.size() != 2) 1799 { 1800 res.result(boost::beast::http::status::internal_server_error); 1801 res.end(); 1802 return; 1803 } 1804 1805 const std::string &parentIfaceId = params[0]; 1806 const std::string &ifaceId = params[1]; 1807 1808 if (!verifyNames(res, parentIfaceId, ifaceId)) 1809 { 1810 return; 1811 } 1812 1813 // Get single eth interface data, and call the below callback for JSON 1814 // preparation 1815 ethernetProvider.getEthernetIfaceData( 1816 ifaceId, [&, parentIfaceId, 1817 ifaceId](const bool &success, 1818 const EthernetInterfaceData ð_data, 1819 const std::vector<IPv4AddressData> &ipv4_data) { 1820 if (success && eth_data.vlanId != nullptr) 1821 { 1822 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId, 1823 eth_data, ipv4_data); 1824 1825 // Disable VLAN 1826 OnDemandEthernetProvider::disableVlan( 1827 ifaceId, [&](const boost::system::error_code ec) { 1828 if (ec) 1829 { 1830 res.jsonValue = nlohmann::json::object(); 1831 messages::addMessageToErrorJson( 1832 res.jsonValue, messages::internalError()); 1833 res.result(boost::beast::http::status:: 1834 internal_server_error); 1835 } 1836 res.end(); 1837 }); 1838 } 1839 else 1840 { 1841 // ... otherwise return error 1842 // TODO(Pawel)consider distinguish between non existing 1843 // object, and other errors 1844 messages::addMessageToErrorJson( 1845 res.jsonValue, messages::resourceNotFound( 1846 "VLAN Network Interface", ifaceId)); 1847 res.result(boost::beast::http::status::not_found); 1848 res.end(); 1849 } 1850 }); 1851 } 1852 1853 /** 1854 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet 1855 * method, to maintain consistency of returned data, as Collection's doPost 1856 * should return data for created member which should match member's doGet 1857 * result in 100%. 1858 */ 1859 friend VlanNetworkInterfaceCollection; 1860 1861 // Ethernet Provider object 1862 // TODO(Pawel) consider move it to singleton 1863 OnDemandEthernetProvider ethernetProvider; 1864 }; 1865 1866 /** 1867 * VlanNetworkInterfaceCollection derived class for delivering 1868 * VLANNetworkInterface Collection Schema 1869 */ 1870 class VlanNetworkInterfaceCollection : public Node 1871 { 1872 public: 1873 template <typename CrowApp> 1874 // TODO(Pawel) Remove line from below, where we assume that there is only 1875 // one manager called openbmc This shall be generic, but requires to update 1876 // GetSubroutes method 1877 VlanNetworkInterfaceCollection(CrowApp &app) : 1878 Node(app, 1879 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/", 1880 std::string()), 1881 memberVlan(app) 1882 { 1883 Node::json["@odata.type"] = 1884 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; 1885 Node::json["@odata.context"] = 1886 "/redfish/v1/$metadata" 1887 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; 1888 Node::json["Name"] = "VLAN Network Interface Collection"; 1889 1890 entityPrivileges = { 1891 {boost::beast::http::verb::get, {{"Login"}}}, 1892 {boost::beast::http::verb::head, {{"Login"}}}, 1893 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1894 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1895 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1896 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1897 } 1898 1899 private: 1900 /** 1901 * Functions triggers appropriate requests on DBus 1902 */ 1903 void doGet(crow::Response &res, const crow::Request &req, 1904 const std::vector<std::string> ¶ms) override 1905 { 1906 if (params.size() != 1) 1907 { 1908 // This means there is a problem with the router 1909 res.result(boost::beast::http::status::internal_server_error); 1910 res.end(); 1911 1912 return; 1913 } 1914 1915 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces 1916 // for any Manager, not only hardcoded 'openbmc'. 1917 std::string managerId = "openbmc"; 1918 std::string rootInterfaceName = params[0]; 1919 1920 // get eth interface list, and call the below callback for JSON 1921 // preparation 1922 ethernetProvider.getEthernetIfaceList( 1923 [&, managerId{std::move(managerId)}, 1924 rootInterfaceName{std::move(rootInterfaceName)}]( 1925 const bool &success, 1926 const std::vector<std::string> &iface_list) { 1927 if (success) 1928 { 1929 bool rootInterfaceFound = false; 1930 nlohmann::json ifaceArray = nlohmann::json::array(); 1931 1932 for (const std::string &ifaceItem : iface_list) 1933 { 1934 if (ifaceItem == rootInterfaceName) 1935 { 1936 rootInterfaceFound = true; 1937 } 1938 else if (boost::starts_with(ifaceItem, 1939 rootInterfaceName + "_")) 1940 { 1941 ifaceArray.push_back( 1942 {{"@odata.id", "/redfish/v1/Managers/" + 1943 managerId + 1944 "/EthernetInterfaces/" + 1945 rootInterfaceName + 1946 "/VLANs/" + ifaceItem}}); 1947 } 1948 } 1949 1950 if (rootInterfaceFound) 1951 { 1952 Node::json["Members"] = ifaceArray; 1953 Node::json["Members@odata.count"] = ifaceArray.size(); 1954 Node::json["@odata.id"] = "/redfish/v1/Managers/" + 1955 managerId + 1956 "/EthernetInterfaces/" + 1957 rootInterfaceName + "/VLANs"; 1958 res.jsonValue = Node::json; 1959 } 1960 else 1961 { 1962 messages::addMessageToErrorJson( 1963 res.jsonValue, 1964 messages::resourceNotFound("EthernetInterface", 1965 rootInterfaceName)); 1966 res.result(boost::beast::http::status::not_found); 1967 res.end(); 1968 } 1969 } 1970 else 1971 { 1972 // No success, best what we can do is return INTERNALL ERROR 1973 res.result( 1974 boost::beast::http::status::internal_server_error); 1975 } 1976 res.end(); 1977 }); 1978 } 1979 1980 void doPost(crow::Response &res, const crow::Request &req, 1981 const std::vector<std::string> ¶ms) override 1982 { 1983 if (params.size() != 1) 1984 { 1985 // This means there is a problem with the router 1986 res.result(boost::beast::http::status::internal_server_error); 1987 res.end(); 1988 return; 1989 } 1990 1991 nlohmann::json postReq; 1992 1993 if (!json_util::processJsonFromRequest(res, req, postReq)) 1994 { 1995 return; 1996 } 1997 1998 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces 1999 // for any Manager, not only hardcoded 'openbmc'. 2000 std::string managerId = "openbmc"; 2001 std::string rootInterfaceName = params[0]; 2002 uint64_t vlanId; 2003 bool errorDetected; 2004 2005 if (json_util::getUnsigned( 2006 "VLANId", postReq, vlanId, 2007 static_cast<uint8_t>(json_util::MessageSetting::MISSING) | 2008 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 2009 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) 2010 { 2011 res.end(); 2012 return; 2013 } 2014 2015 // get eth interface list, and call the below callback for JSON 2016 // preparation 2017 ethernetProvider.getEthernetIfaceList( 2018 [&, managerId{std::move(managerId)}, 2019 rootInterfaceName{std::move(rootInterfaceName)}]( 2020 const bool &success, 2021 const std::vector<std::string> &iface_list) { 2022 if (success) 2023 { 2024 bool rootInterfaceFound = false; 2025 2026 for (const std::string &ifaceItem : iface_list) 2027 { 2028 if (ifaceItem == rootInterfaceName) 2029 { 2030 rootInterfaceFound = true; 2031 break; 2032 } 2033 } 2034 2035 if (rootInterfaceFound) 2036 { 2037 ethernetProvider.createVlan( 2038 rootInterfaceName, vlanId, 2039 [&, vlanId, rootInterfaceName, req{std::move(req)}]( 2040 const boost::system::error_code ec) { 2041 if (ec) 2042 { 2043 messages::addMessageToErrorJson( 2044 res.jsonValue, 2045 messages::internalError()); 2046 res.end(); 2047 } 2048 else 2049 { 2050 memberVlan.doGet( 2051 res, req, 2052 {rootInterfaceName, 2053 rootInterfaceName + "_" + 2054 std::to_string(vlanId)}); 2055 } 2056 }); 2057 } 2058 else 2059 { 2060 messages::addMessageToErrorJson( 2061 res.jsonValue, 2062 messages::resourceNotFound("EthernetInterface", 2063 rootInterfaceName)); 2064 res.result(boost::beast::http::status::not_found); 2065 res.end(); 2066 } 2067 } 2068 else 2069 { 2070 // No success, best what we can do is return INTERNALL ERROR 2071 res.result( 2072 boost::beast::http::status::internal_server_error); 2073 res.end(); 2074 } 2075 }); 2076 } 2077 2078 // Ethernet Provider object 2079 // TODO(Pawel) consider move it to singleton 2080 OnDemandEthernetProvider ethernetProvider; 2081 VlanNetworkInterface memberVlan; 2082 }; 2083 2084 } // namespace redfish 2085