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