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 <dbus_singleton.hpp> 19 #include <error_messages.hpp> 20 #include <node.hpp> 21 #include <utils/json_utils.hpp> 22 #include <boost/container/flat_map.hpp> 23 24 namespace redfish { 25 26 /** 27 * DBus types primitives for several generic DBus interfaces 28 * TODO(Pawel) consider move this to separate file into boost::dbus 29 */ 30 using PropertiesMapType = boost::container::flat_map< 31 std::string, 32 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 33 int32_t, uint32_t, int64_t, uint64_t, double>>; 34 35 using GetManagedObjectsType = boost::container::flat_map< 36 sdbusplus::message::object_path, 37 boost::container::flat_map< 38 std::string, 39 boost::container::flat_map< 40 std::string, sdbusplus::message::variant< 41 std::string, bool, uint8_t, int16_t, uint16_t, 42 int32_t, uint32_t, int64_t, uint64_t, double>>>>; 43 44 /** 45 * Structure for keeping IPv4 data required by Redfish 46 * TODO(Pawel) consider change everything to ptr, or to non-ptr values. 47 */ 48 struct IPv4AddressData { 49 std::string id; 50 const std::string *address; 51 const std::string *domain; 52 const std::string *gateway; 53 std::string netmask; 54 std::string origin; 55 bool global; 56 /** 57 * @brief Operator< to enable sorting 58 * 59 * @param[in] obj Object to compare with 60 * 61 * @return This object id < supplied object id 62 */ 63 bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); } 64 }; 65 66 /** 67 * Structure for keeping basic single Ethernet Interface information 68 * available from DBus 69 */ 70 struct EthernetInterfaceData { 71 const unsigned int *speed; 72 const bool *auto_neg; 73 const std::string *hostname; 74 const std::string *default_gateway; 75 const std::string *mac_address; 76 const unsigned int *vlan_id; 77 }; 78 79 /** 80 * OnDemandEthernetProvider 81 * Ethernet provider class that retrieves data directly from dbus, before 82 * setting it into JSON output. This does not cache any data. 83 * 84 * TODO(Pawel) 85 * This perhaps shall be different file, which has to be chosen on compile time 86 * depending on OEM needs 87 */ 88 class OnDemandEthernetProvider { 89 private: 90 // Consts that may have influence on EthernetProvider performance/memory usage 91 const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10; 92 93 // Helper function that allows to extract GetAllPropertiesType from 94 // GetManagedObjectsType, based on object path, and interface name 95 const PropertiesMapType *extractInterfaceProperties( 96 const sdbusplus::message::object_path &objpath, 97 const std::string &interface, const GetManagedObjectsType &dbus_data) { 98 const auto &dbus_obj = dbus_data.find(objpath); 99 if (dbus_obj != dbus_data.end()) { 100 const auto &iface = dbus_obj->second.find(interface); 101 if (iface != dbus_obj->second.end()) { 102 return &iface->second; 103 } 104 } 105 return nullptr; 106 } 107 108 // Helper Wrapper that does inline object_path conversion from string 109 // into sdbusplus::message::object_path type 110 inline const PropertiesMapType *extractInterfaceProperties( 111 const std::string &objpath, const std::string &interface, 112 const GetManagedObjectsType &dbus_data) { 113 const auto &dbus_obj = sdbusplus::message::object_path{objpath}; 114 return extractInterfaceProperties(dbus_obj, interface, dbus_data); 115 } 116 117 // Helper function that allows to get pointer to the property from 118 // GetAllPropertiesType native, or extracted by GetAllPropertiesType 119 template <typename T> 120 inline T const *const extractProperty(const PropertiesMapType &properties, 121 const std::string &name) { 122 const auto &property = properties.find(name); 123 if (property != properties.end()) { 124 return mapbox::get_ptr<const T>(property->second); 125 } 126 return nullptr; 127 } 128 // TODO(Pawel) Consider to move the above functions to dbus 129 // generic_interfaces.hpp 130 131 // Helper function that extracts data from several dbus objects and several 132 // interfaces required by single ethernet interface instance 133 void extractEthernetInterfaceData(const std::string ðiface_id, 134 const GetManagedObjectsType &dbus_data, 135 EthernetInterfaceData ð_data) { 136 // Extract data that contains MAC Address 137 const PropertiesMapType *mac_properties = extractInterfaceProperties( 138 "/xyz/openbmc_project/network/" + ethiface_id, 139 "xyz.openbmc_project.Network.MACAddress", dbus_data); 140 141 if (mac_properties != nullptr) { 142 eth_data.mac_address = 143 extractProperty<std::string>(*mac_properties, "MACAddress"); 144 } 145 146 const PropertiesMapType *vlan_properties = extractInterfaceProperties( 147 "/xyz/openbmc_project/network/" + ethiface_id, 148 "xyz.openbmc_project.Network.VLAN", dbus_data); 149 150 if (vlan_properties != nullptr) { 151 eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id"); 152 } 153 154 // Extract data that contains link information (auto negotiation and speed) 155 const PropertiesMapType *eth_properties = extractInterfaceProperties( 156 "/xyz/openbmc_project/network/" + ethiface_id, 157 "xyz.openbmc_project.Network.EthernetInterface", dbus_data); 158 159 if (eth_properties != nullptr) { 160 eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg"); 161 eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed"); 162 } 163 164 // Extract data that contains network config (HostName and DefaultGW) 165 const PropertiesMapType *config_properties = extractInterfaceProperties( 166 "/xyz/openbmc_project/network/config", 167 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); 168 169 if (config_properties != nullptr) { 170 eth_data.hostname = 171 extractProperty<std::string>(*config_properties, "HostName"); 172 eth_data.default_gateway = 173 extractProperty<std::string>(*config_properties, "DefaultGateway"); 174 } 175 } 176 177 // Helper function that changes bits netmask notation (i.e. /24) 178 // into full dot notation 179 inline std::string getNetmask(unsigned int bits) { 180 uint32_t value = 0xffffffff << (32 - bits); 181 std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 182 std::to_string((value >> 16) & 0xff) + "." + 183 std::to_string((value >> 8) & 0xff) + "." + 184 std::to_string(value & 0xff); 185 return netmask; 186 } 187 188 // Helper function that extracts data for single ethernet ipv4 address 189 void extractIPv4Data(const std::string ðiface_id, 190 const GetManagedObjectsType &dbus_data, 191 std::vector<IPv4AddressData> &ipv4_config) { 192 const std::string pathStart = 193 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/"; 194 195 // Since there might be several IPv4 configurations aligned with 196 // single ethernet interface, loop over all of them 197 for (auto &objpath : dbus_data) { 198 // Check if proper patter for object path appears 199 if (boost::starts_with( 200 static_cast<const std::string &>(objpath.first), 201 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) { 202 // and get approrpiate interface 203 const auto &interface = 204 objpath.second.find("xyz.openbmc_project.Network.IP"); 205 if (interface != objpath.second.end()) { 206 // Make a properties 'shortcut', to make everything more readable 207 const PropertiesMapType &properties = interface->second; 208 // Instance IPv4AddressData structure, and set as appropriate 209 IPv4AddressData ipv4_address; 210 211 ipv4_address.id = static_cast<const std::string &>(objpath.first) 212 .substr(pathStart.size()); 213 214 // IPv4 address 215 ipv4_address.address = 216 extractProperty<std::string>(properties, "Address"); 217 // IPv4 gateway 218 ipv4_address.gateway = 219 extractProperty<std::string>(properties, "Gateway"); 220 221 // Origin is kind of DBus object so fetch pointer... 222 const std::string *origin = 223 extractProperty<std::string>(properties, "Origin"); 224 if (origin != nullptr) { 225 ipv4_address.origin = 226 translateAddressOriginBetweenDBusAndRedfish(origin, true, true); 227 } 228 229 // Netmask is presented as PrefixLength 230 const auto *mask = 231 extractProperty<uint8_t>(properties, "PrefixLength"); 232 if (mask != nullptr) { 233 // convert it to the string 234 ipv4_address.netmask = getNetmask(*mask); 235 } 236 237 // Attach IPv4 only if address is present 238 if (ipv4_address.address != nullptr) { 239 // Check if given address is local, or global 240 if (boost::starts_with(*ipv4_address.address, "169.254")) { 241 ipv4_address.global = false; 242 } else { 243 ipv4_address.global = true; 244 } 245 ipv4_config.emplace_back(std::move(ipv4_address)); 246 } 247 } 248 } 249 } 250 251 /** 252 * We have to sort this vector and ensure that order of IPv4 addresses 253 * is consistent between GETs to allow modification and deletion in PATCHes 254 */ 255 std::sort(ipv4_config.begin(), ipv4_config.end()); 256 } 257 258 static const constexpr int ipV4AddressSectionsCount = 4; 259 260 public: 261 /** 262 * @brief Creates VLAN for given interface with given Id through D-Bus 263 * 264 * @param[in] ifaceId Id of interface for which VLAN will be created 265 * @param[in] inputVlanId ID of the new VLAN 266 * @param[in] callback Function that will be called after the operation 267 * 268 * @return None. 269 */ 270 template <typename CallbackFunc> 271 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, 272 CallbackFunc &&callback) { 273 crow::connections::system_bus->async_method_call( 274 callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 275 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, 276 static_cast<uint32_t>(inputVlanId)); 277 }; 278 279 /** 280 * @brief Sets given Id on the given VLAN interface through D-Bus 281 * 282 * @param[in] ifaceId Id of VLAN interface that should be modified 283 * @param[in] inputVlanId New ID of the VLAN 284 * @param[in] callback Function that will be called after the operation 285 * 286 * @return None. 287 */ 288 template <typename CallbackFunc> 289 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId, 290 CallbackFunc &&callback) { 291 crow::connections::system_bus->async_method_call( 292 callback, "xyz.openbmc_project.Network", 293 std::string("/xyz/openbmc_project/network/") + ifaceId, 294 "org.freedesktop.DBus.Properties", "Set", 295 "xyz.openbmc_project.Network.VLAN", "Id", 296 sdbusplus::message::variant<uint32_t>(inputVlanId)); 297 }; 298 299 /** 300 * @brief Helper function that verifies IP address to check if it is in 301 * proper format. If bits pointer is provided, also calculates active 302 * bit count for Subnet Mask. 303 * 304 * @param[in] ip IP that will be verified 305 * @param[out] bits Calculated mask in bits notation 306 * 307 * @return true in case of success, false otherwise 308 */ 309 bool ipv4VerifyIpAndGetBitcount(const std::string &ip, 310 uint8_t *bits = nullptr) { 311 std::vector<std::string> bytesInMask; 312 313 boost::split(bytesInMask, ip, boost::is_any_of(".")); 314 315 if (bytesInMask.size() != ipV4AddressSectionsCount) { 316 return false; 317 } 318 319 if (bits != nullptr) { 320 *bits = 0; 321 } 322 323 char *endPtr; 324 long previousValue = 255; 325 bool firstZeroInByteHit; 326 for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) { 327 // Use strtol instead of stroi to avoid exceptions 328 long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10); 329 330 // endPtr should point to the end of the string, otherwise given string 331 // is not 100% number 332 if (*endPtr != '\0') { 333 return false; 334 } 335 336 // Value should be contained in byte 337 if (value < 0 || value > 255) { 338 return false; 339 } 340 341 if (bits != nullptr) { 342 // Mask has to be continuous between bytes 343 if (previousValue != 255 && value != 0) { 344 return false; 345 } 346 347 // Mask has to be continuous inside bytes 348 firstZeroInByteHit = false; 349 350 // Count bits 351 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) { 352 if (value & (1 << bitIdx)) { 353 if (firstZeroInByteHit) { 354 // Continuity not preserved 355 return false; 356 } else { 357 (*bits)++; 358 } 359 } else { 360 firstZeroInByteHit = true; 361 } 362 } 363 } 364 365 previousValue = value; 366 } 367 368 return true; 369 } 370 371 /** 372 * @brief Changes IPv4 address type property (Address, Gateway) 373 * 374 * @param[in] ifaceId Id of interface whose IP should be modified 375 * @param[in] ipIdx Index of IP in input array that should be modified 376 * @param[in] ipHash DBus Hash id of modified IP 377 * @param[in] name Name of field in JSON representation 378 * @param[in] newValue New value that should be written 379 * @param[io] asyncResp Response object that will be returned to client 380 * 381 * @return true if give IP is valid and has been sent do D-Bus, false 382 * otherwise 383 */ 384 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, 385 const std::string &ipHash, 386 const std::string &name, 387 const std::string &newValue, 388 const std::shared_ptr<AsyncResp> &asyncResp) { 389 auto callback = [ 390 asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)}, 391 newValue{std::move(newValue)} 392 ](const boost::system::error_code ec) { 393 if (ec) { 394 messages::addMessageToJson( 395 asyncResp->res.json_value, messages::internalError(), 396 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); 397 } else { 398 asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue; 399 } 400 }; 401 402 crow::connections::system_bus->async_method_call( 403 std::move(callback), "xyz.openbmc_project.Network", 404 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 405 "org.freedesktop.DBus.Properties", "Set", 406 "xyz.openbmc_project.Network.IP", name, 407 sdbusplus::message::variant<std::string>(newValue)); 408 }; 409 410 /** 411 * @brief Changes IPv4 address origin property 412 * 413 * @param[in] ifaceId Id of interface whose IP should be modified 414 * @param[in] ipIdx Index of IP in input array that should be modified 415 * @param[in] ipHash DBus Hash id of modified IP 416 * @param[in] newValue New value in Redfish format 417 * @param[in] newValueDbus New value in D-Bus format 418 * @param[io] asyncResp Response object that will be returned to client 419 * 420 * @return true if give IP is valid and has been sent do D-Bus, false 421 * otherwise 422 */ 423 void changeIPv4Origin(const std::string &ifaceId, int ipIdx, 424 const std::string &ipHash, const std::string &newValue, 425 const std::string &newValueDbus, 426 const std::shared_ptr<AsyncResp> &asyncResp) { 427 auto callback = 428 [ asyncResp, ipIdx{std::move(ipIdx)}, 429 newValue{std::move(newValue)} ](const boost::system::error_code ec) { 430 if (ec) { 431 messages::addMessageToJson( 432 asyncResp->res.json_value, messages::internalError(), 433 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin"); 434 } else { 435 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] = 436 newValue; 437 } 438 }; 439 440 crow::connections::system_bus->async_method_call( 441 std::move(callback), "xyz.openbmc_project.Network", 442 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 443 "org.freedesktop.DBus.Properties", "Set", 444 "xyz.openbmc_project.Network.IP", "Origin", 445 sdbusplus::message::variant<std::string>(newValueDbus)); 446 }; 447 448 /** 449 * @brief Modifies SubnetMask for given IP 450 * 451 * @param[in] ifaceId Id of interface whose IP should be modified 452 * @param[in] ipIdx Index of IP in input array that should be modified 453 * @param[in] ipHash DBus Hash id of modified IP 454 * @param[in] newValueStr Mask in dot notation as string 455 * @param[in] newValue Mask as PrefixLength in bitcount 456 * @param[io] asyncResp Response object that will be returned to client 457 * 458 * @return None 459 */ 460 void changeIPv4SubnetMaskProperty( 461 const std::string &ifaceId, int ipIdx, const std::string &ipHash, 462 const std::string &newValueStr, uint8_t &newValue, 463 const std::shared_ptr<AsyncResp> &asyncResp) { 464 auto callback = [ 465 asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)} 466 ](const boost::system::error_code ec) { 467 if (ec) { 468 messages::addMessageToJson( 469 asyncResp->res.json_value, messages::internalError(), 470 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); 471 } else { 472 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] = 473 newValueStr; 474 } 475 }; 476 477 crow::connections::system_bus->async_method_call( 478 std::move(callback), "xyz.openbmc_project.Network", 479 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 480 "org.freedesktop.DBus.Properties", "Set", 481 "xyz.openbmc_project.Network.IP", "PrefixLength", 482 sdbusplus::message::variant<uint8_t>(newValue)); 483 }; 484 485 /** 486 * @brief Disables VLAN with given ifaceId 487 * 488 * @param[in] ifaceId Id of VLAN interface that should be disabled 489 * @param[in] callback Function that will be called after the operation 490 * 491 * @return None. 492 */ 493 template <typename CallbackFunc> 494 void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) { 495 crow::connections::system_bus->async_method_call( 496 callback, "xyz.openbmc_project.Network", 497 std::string("/xyz/openbmc_project/network/") + ifaceId, 498 "xyz.openbmc_project.Object.Delete", "Delete"); 499 }; 500 501 /** 502 * @brief Sets given HostName of the machine through D-Bus 503 * 504 * @param[in] newHostname New name that HostName will be changed to 505 * @param[in] callback Function that will be called after the operation 506 * 507 * @return None. 508 */ 509 template <typename CallbackFunc> 510 void setHostName(const std::string &newHostname, CallbackFunc &&callback) { 511 crow::connections::system_bus->async_method_call( 512 callback, "xyz.openbmc_project.Network", 513 "/xyz/openbmc_project/network/config", 514 "org.freedesktop.DBus.Properties", "Set", 515 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 516 sdbusplus::message::variant<std::string>(newHostname)); 517 }; 518 519 /** 520 * @brief Deletes given IPv4 521 * 522 * @param[in] ifaceId Id of interface whose IP should be deleted 523 * @param[in] ipIdx Index of IP in input array that should be deleted 524 * @param[in] ipHash DBus Hash id of IP that should be deleted 525 * @param[io] asyncResp Response object that will be returned to client 526 * 527 * @return None 528 */ 529 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, 530 unsigned int ipIdx, 531 const std::shared_ptr<AsyncResp> &asyncResp) { 532 crow::connections::system_bus->async_method_call( 533 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ]( 534 const boost::system::error_code ec) { 535 if (ec) { 536 messages::addMessageToJson( 537 asyncResp->res.json_value, messages::internalError(), 538 "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 539 } else { 540 asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr; 541 } 542 }, 543 "xyz.openbmc_project.Network", 544 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 545 "xyz.openbmc_project.Object.Delete", "Delete"); 546 } 547 548 /** 549 * @brief Creates IPv4 with given data 550 * 551 * @param[in] ifaceId Id of interface whose IP should be deleted 552 * @param[in] ipIdx Index of IP in input array that should be deleted 553 * @param[in] ipHash DBus Hash id of IP that should be deleted 554 * @param[io] asyncResp Response object that will be returned to client 555 * 556 * @return None 557 */ 558 void createIPv4(const std::string &ifaceId, unsigned int ipIdx, 559 uint8_t subnetMask, const std::string &gateway, 560 const std::string &address, 561 const std::shared_ptr<AsyncResp> &asyncResp) { 562 auto createIpHandler = [ 563 ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} 564 ](const boost::system::error_code ec) { 565 if (ec) { 566 messages::addMessageToJson( 567 asyncResp->res.json_value, messages::internalError(), 568 "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 569 } 570 }; 571 572 crow::connections::system_bus->async_method_call( 573 std::move(createIpHandler), "xyz.openbmc_project.Network", 574 "/xyz/openbmc_project/network/" + ifaceId, 575 "xyz.openbmc_project.Network.IP.Create", "IP", 576 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, 577 gateway); 578 } 579 580 /** 581 * @brief Translates Address Origin value from D-Bus to Redfish format and 582 * vice-versa 583 * 584 * @param[in] inputOrigin Input value that should be translated 585 * @param[in] isIPv4 True for IPv4 origins, False for IPv6 586 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse 587 * 588 * @return Empty string in case of failure, translated value otherwise 589 */ 590 std::string translateAddressOriginBetweenDBusAndRedfish( 591 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) { 592 // Invalid pointer 593 if (inputOrigin == nullptr) { 594 return ""; 595 } 596 597 static const constexpr unsigned int firstIPv4OnlyIdx = 1; 598 static const constexpr unsigned int firstIPv6OnlyIdx = 3; 599 600 std::array<std::pair<const char *, const char *>, 6> translationTable{ 601 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, 602 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, 603 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 604 "IPv4LinkLocal"}, 605 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, 606 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 607 "LinkLocal"}, 608 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; 609 610 for (unsigned int i = 0; i < translationTable.size(); i++) { 611 // Skip unrelated 612 if (isIPv4 && i >= firstIPv6OnlyIdx) break; 613 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue; 614 615 // When translating D-Bus to Redfish compare input to first element 616 if (isFromDBus && translationTable[i].first == *inputOrigin) 617 return translationTable[i].second; 618 619 // When translating Redfish to D-Bus compare input to second element 620 if (!isFromDBus && translationTable[i].second == *inputOrigin) 621 return translationTable[i].first; 622 } 623 624 // If we are still here, that means that value has not been found 625 return ""; 626 } 627 628 /** 629 * Function that retrieves all properties for given Ethernet Interface 630 * Object 631 * from EntityManager Network Manager 632 * @param ethiface_id a eth interface id to query on DBus 633 * @param callback a function that shall be called to convert Dbus output 634 * into JSON 635 */ 636 template <typename CallbackFunc> 637 void getEthernetIfaceData(const std::string ðiface_id, 638 CallbackFunc &&callback) { 639 crow::connections::system_bus->async_method_call( 640 [ 641 this, ethiface_id{std::move(ethiface_id)}, 642 callback{std::move(callback)} 643 ](const boost::system::error_code error_code, 644 const GetManagedObjectsType &resp) { 645 646 EthernetInterfaceData eth_data{}; 647 std::vector<IPv4AddressData> ipv4_data; 648 ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE); 649 650 if (error_code) { 651 // Something wrong on DBus, the error_code is not important at 652 // this moment, just return success=false, and empty output. Since 653 // size of vector may vary depending on information from Network 654 // Manager, and empty output could not be treated same way as 655 // error. 656 callback(false, eth_data, ipv4_data); 657 return; 658 } 659 660 extractEthernetInterfaceData(ethiface_id, resp, eth_data); 661 extractIPv4Data(ethiface_id, resp, ipv4_data); 662 663 // Fix global GW 664 for (IPv4AddressData &ipv4 : ipv4_data) { 665 if ((ipv4.global) && 666 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { 667 ipv4.gateway = eth_data.default_gateway; 668 } 669 } 670 671 // Finally make a callback with usefull data 672 callback(true, eth_data, ipv4_data); 673 }, 674 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 675 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 676 }; 677 678 /** 679 * Function that retrieves all Ethernet Interfaces available through Network 680 * Manager 681 * @param callback a function that shall be called to convert Dbus output into 682 * JSON. 683 */ 684 template <typename CallbackFunc> 685 void getEthernetIfaceList(CallbackFunc &&callback) { 686 crow::connections::system_bus->async_method_call( 687 [ this, callback{std::move(callback)} ]( 688 const boost::system::error_code error_code, 689 GetManagedObjectsType &resp) { 690 // Callback requires vector<string> to retrieve all available ethernet 691 // interfaces 692 std::vector<std::string> iface_list; 693 iface_list.reserve(resp.size()); 694 if (error_code) { 695 // Something wrong on DBus, the error_code is not important at this 696 // moment, just return success=false, and empty output. Since size 697 // of vector may vary depending on information from Network Manager, 698 // and empty output could not be treated same way as error. 699 callback(false, iface_list); 700 return; 701 } 702 703 // Iterate over all retrieved ObjectPaths. 704 for (auto &objpath : resp) { 705 // And all interfaces available for certain ObjectPath. 706 for (auto &interface : objpath.second) { 707 // If interface is xyz.openbmc_project.Network.EthernetInterface, 708 // this is what we're looking for. 709 if (interface.first == 710 "xyz.openbmc_project.Network.EthernetInterface") { 711 // Cut out everyting until last "/", ... 712 const std::string &iface_id = 713 static_cast<const std::string &>(objpath.first); 714 std::size_t last_pos = iface_id.rfind("/"); 715 if (last_pos != std::string::npos) { 716 // and put it into output vector. 717 iface_list.emplace_back(iface_id.substr(last_pos + 1)); 718 } 719 } 720 } 721 } 722 // Finally make a callback with useful data 723 callback(true, iface_list); 724 }, 725 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 726 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 727 }; 728 }; 729 730 /** 731 * EthernetCollection derived class for delivering Ethernet Collection Schema 732 */ 733 class EthernetCollection : public Node { 734 public: 735 template <typename CrowApp> 736 // TODO(Pawel) Remove line from below, where we assume that there is only one 737 // manager called openbmc This shall be generic, but requires to update 738 // GetSubroutes method 739 EthernetCollection(CrowApp &app) 740 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { 741 Node::json["@odata.type"] = 742 "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 743 Node::json["@odata.context"] = 744 "/redfish/v1/" 745 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 746 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; 747 Node::json["Name"] = "Ethernet Network Interface Collection"; 748 Node::json["Description"] = 749 "Collection of EthernetInterfaces for this Manager"; 750 751 entityPrivileges = { 752 {boost::beast::http::verb::get, {{"Login"}}}, 753 {boost::beast::http::verb::head, {{"Login"}}}, 754 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 755 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 756 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 757 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 758 } 759 760 private: 761 /** 762 * Functions triggers appropriate requests on DBus 763 */ 764 void doGet(crow::response &res, const crow::request &req, 765 const std::vector<std::string> ¶ms) override { 766 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 767 // any Manager, not only hardcoded 'openbmc'. 768 std::string manager_id = "openbmc"; 769 770 // Get eth interface list, and call the below callback for JSON preparation 771 ethernet_provider.getEthernetIfaceList( 772 [&, manager_id{std::move(manager_id)} ]( 773 const bool &success, const std::vector<std::string> &iface_list) { 774 if (success) { 775 nlohmann::json iface_array = nlohmann::json::array(); 776 for (const std::string &iface_item : iface_list) { 777 iface_array.push_back( 778 {{"@odata.id", "/redfish/v1/Managers/" + manager_id + 779 "/EthernetInterfaces/" + iface_item}}); 780 } 781 Node::json["Members"] = iface_array; 782 Node::json["Members@odata.count"] = iface_array.size(); 783 Node::json["@odata.id"] = 784 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces"; 785 res.json_value = Node::json; 786 } else { 787 // No success, best what we can do is return INTERNALL ERROR 788 res.result(boost::beast::http::status::internal_server_error); 789 } 790 res.end(); 791 }); 792 } 793 794 // Ethernet Provider object 795 // TODO(Pawel) consider move it to singleton 796 OnDemandEthernetProvider ethernet_provider; 797 }; 798 799 /** 800 * EthernetInterface derived class for delivering Ethernet Schema 801 */ 802 class EthernetInterface : public Node { 803 public: 804 /* 805 * Default Constructor 806 */ 807 template <typename CrowApp> 808 // TODO(Pawel) Remove line from below, where we assume that there is only one 809 // manager called openbmc This shall be generic, but requires to update 810 // GetSubroutes method 811 EthernetInterface(CrowApp &app) 812 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", 813 std::string()) { 814 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; 815 Node::json["@odata.context"] = 816 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 817 Node::json["Name"] = "Manager Ethernet Interface"; 818 Node::json["Description"] = "Management Network Interface"; 819 820 entityPrivileges = { 821 {boost::beast::http::verb::get, {{"Login"}}}, 822 {boost::beast::http::verb::head, {{"Login"}}}, 823 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 824 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 825 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 826 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 827 } 828 829 private: 830 void handleVlanPatch(const std::string &ifaceId, const nlohmann::json &input, 831 const EthernetInterfaceData ð_data, 832 const std::shared_ptr<AsyncResp> &asyncResp) { 833 if (!input.is_object()) { 834 messages::addMessageToJson( 835 asyncResp->res.json_value, 836 messages::propertyValueTypeError(input.dump(), "VLAN"), "/VLAN"); 837 return; 838 } 839 840 bool inputVlanEnabled; 841 uint64_t inputVlanId; 842 json_util::Result inputVlanEnabledState = json_util::getBool( 843 "VLANEnable", input, inputVlanEnabled, 844 static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 845 asyncResp->res.json_value, std::string("/VLAN/VLANEnable")); 846 json_util::Result inputVlanIdState = json_util::getUnsigned( 847 "VLANId", input, inputVlanId, 848 static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 849 asyncResp->res.json_value, std::string("/VLAN/VLANId")); 850 bool inputInvalid = false; 851 852 // Do not proceed if fields in VLAN object were of wrong type 853 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || 854 inputVlanIdState == json_util::Result::WRONG_TYPE) { 855 return; 856 } 857 858 // Verify input 859 if (eth_data.vlan_id == nullptr) { 860 // VLAN is currently disabled. User can only create/enable it. Change of 861 // VLANId is prohibited, and disable request (VLANEnabled == false) will 862 // not have any effect. 863 if (inputVlanEnabledState == json_util::Result::SUCCESS && 864 inputVlanEnabled == true) { 865 // Creation requested, user should also provide ID for new VLAN 866 if (inputVlanIdState != json_util::Result::SUCCESS) { 867 messages::addMessageToJson(asyncResp->res.json_value, 868 messages::propertyMissing("VLANId"), 869 "/VLAN"); 870 inputInvalid = true; 871 } 872 } else if (inputVlanIdState == json_util::Result::SUCCESS) { 873 // VLAN is disabled, but user requested modification. This is not valid. 874 messages::addMessageToJson( 875 asyncResp->res.json_value, 876 messages::actionParameterNotSupported("VLANId", "change VLAN Id"), 877 "/VLAN"); 878 879 messages::addMessageToJson(asyncResp->res.json_value, 880 messages::propertyMissing("VLANEnable"), 881 "/VLAN"); 882 883 inputInvalid = true; 884 } 885 } else { 886 // Load actual data into field values if they were not provided 887 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) { 888 inputVlanEnabled = true; 889 } 890 891 if (inputVlanIdState == json_util::Result::NOT_EXIST) { 892 inputVlanId = *eth_data.vlan_id; 893 } 894 } 895 896 // Do not proceed if input has not been valid 897 if (inputInvalid) { 898 return; 899 } 900 901 auto vlanEnabledAfterOperation = 902 [asyncResp](const boost::system::error_code ec) { 903 if (ec) { 904 messages::addMessageToJson(asyncResp->res.json_value, 905 messages::internalError(), "/VLAN"); 906 } else { 907 asyncResp->res.json_value["VLAN"]["VLANEnable"] = true; 908 } 909 }; 910 911 if (eth_data.vlan_id == nullptr) { 912 if (inputVlanEnabled == true) { 913 ethernet_provider.createVlan(ifaceId, inputVlanId, 914 std::move(vlanEnabledAfterOperation)); 915 asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId; 916 } 917 } else { 918 // VLAN is configured on the interface 919 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) { 920 // Change VLAN Id 921 asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId; 922 ethernet_provider.changeVlanId(ifaceId, 923 static_cast<uint32_t>(inputVlanId), 924 std::move(vlanEnabledAfterOperation)); 925 } else if (inputVlanEnabled == false) { 926 // Disable VLAN 927 ethernet_provider.disableVlan( 928 ifaceId, [asyncResp](const boost::system::error_code ec) { 929 if (ec) { 930 messages::addMessageToJson(asyncResp->res.json_value, 931 messages::internalError(), "/VLAN"); 932 } else { 933 asyncResp->res.json_value["VLAN"]["VLANEnable"] = false; 934 } 935 }); 936 } 937 } 938 } 939 940 void handleHostnamePatch(const nlohmann::json &input, 941 const EthernetInterfaceData ð_data, 942 const std::shared_ptr<AsyncResp> &asyncResp) { 943 if (input.is_string()) { 944 std::string newHostname = input.get<std::string>(); 945 946 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) { 947 // Change hostname 948 ethernet_provider.setHostName( 949 newHostname, 950 [asyncResp, newHostname](const boost::system::error_code ec) { 951 if (ec) { 952 messages::addMessageToJson(asyncResp->res.json_value, 953 messages::internalError(), 954 "/HostName"); 955 } else { 956 asyncResp->res.json_value["HostName"] = newHostname; 957 } 958 }); 959 } 960 } else { 961 messages::addMessageToJson( 962 asyncResp->res.json_value, 963 messages::propertyValueTypeError(input.dump(), "HostName"), 964 "/HostName"); 965 } 966 } 967 968 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input, 969 const std::vector<IPv4AddressData> &ipv4_data, 970 const std::shared_ptr<AsyncResp> &asyncResp) { 971 if (!input.is_array()) { 972 messages::addMessageToJson( 973 asyncResp->res.json_value, 974 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), 975 "/IPv4Addresses"); 976 return; 977 } 978 979 // According to Redfish PATCH definition, size must be at least equal 980 if (input.size() < ipv4_data.size()) { 981 // TODO(kkowalsk) This should be a message indicating that not enough 982 // data has been provided 983 messages::addMessageToJson(asyncResp->res.json_value, 984 messages::internalError(), "/IPv4Addresses"); 985 return; 986 } 987 988 json_util::Result addressFieldState; 989 json_util::Result subnetMaskFieldState; 990 json_util::Result addressOriginFieldState; 991 json_util::Result gatewayFieldState; 992 const std::string *addressFieldValue; 993 const std::string *subnetMaskFieldValue; 994 const std::string *addressOriginFieldValue = nullptr; 995 const std::string *gatewayFieldValue; 996 uint8_t subnetMaskAsPrefixLength; 997 std::string addressOriginInDBusFormat; 998 999 bool errorDetected = false; 1000 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) { 1001 // Check that entry is not of some unexpected type 1002 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) { 1003 // Invalid object type 1004 messages::addMessageToJson( 1005 asyncResp->res.json_value, 1006 messages::propertyValueTypeError(input[entryIdx].dump(), 1007 "IPv4Address"), 1008 "/IPv4Addresses/" + std::to_string(entryIdx)); 1009 1010 continue; 1011 } 1012 1013 // Try to load fields 1014 addressFieldState = json_util::getString( 1015 "Address", input[entryIdx], addressFieldValue, 1016 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1017 asyncResp->res.json_value, 1018 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1019 subnetMaskFieldState = json_util::getString( 1020 "SubnetMask", input[entryIdx], subnetMaskFieldValue, 1021 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1022 asyncResp->res.json_value, 1023 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1024 addressOriginFieldState = json_util::getString( 1025 "AddressOrigin", input[entryIdx], addressOriginFieldValue, 1026 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1027 asyncResp->res.json_value, 1028 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1029 gatewayFieldState = json_util::getString( 1030 "Gateway", input[entryIdx], gatewayFieldValue, 1031 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1032 asyncResp->res.json_value, 1033 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1034 1035 if (addressFieldState == json_util::Result::WRONG_TYPE || 1036 subnetMaskFieldState == json_util::Result::WRONG_TYPE || 1037 addressOriginFieldState == json_util::Result::WRONG_TYPE || 1038 gatewayFieldState == json_util::Result::WRONG_TYPE) { 1039 return; 1040 } 1041 1042 if (addressFieldState == json_util::Result::SUCCESS && 1043 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) { 1044 errorDetected = true; 1045 messages::addMessageToJson( 1046 asyncResp->res.json_value, 1047 messages::propertyValueFormatError(*addressFieldValue, "Address"), 1048 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1049 } 1050 1051 if (subnetMaskFieldState == json_util::Result::SUCCESS && 1052 !ethernet_provider.ipv4VerifyIpAndGetBitcount( 1053 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) { 1054 errorDetected = true; 1055 messages::addMessageToJson( 1056 asyncResp->res.json_value, 1057 messages::propertyValueFormatError(*subnetMaskFieldValue, 1058 "SubnetMask"), 1059 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1060 } 1061 1062 // Get Address origin in proper format 1063 addressOriginInDBusFormat = 1064 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish( 1065 addressOriginFieldValue, true, false); 1066 1067 if (addressOriginFieldState == json_util::Result::SUCCESS && 1068 addressOriginInDBusFormat.empty()) { 1069 errorDetected = true; 1070 messages::addMessageToJson( 1071 asyncResp->res.json_value, 1072 messages::propertyValueNotInList(*addressOriginFieldValue, 1073 "AddressOrigin"), 1074 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1075 } 1076 1077 if (gatewayFieldState == json_util::Result::SUCCESS && 1078 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) { 1079 errorDetected = true; 1080 messages::addMessageToJson( 1081 asyncResp->res.json_value, 1082 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"), 1083 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1084 } 1085 1086 // If any error occured do not proceed with current entry, but do not 1087 // end loop 1088 if (errorDetected) { 1089 errorDetected = false; 1090 continue; 1091 } 1092 1093 if (entryIdx >= ipv4_data.size()) { 1094 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx]; 1095 1096 // Verify that all field were provided 1097 if (addressFieldState == json_util::Result::NOT_EXIST) { 1098 errorDetected = true; 1099 messages::addMessageToJson( 1100 asyncResp->res.json_value, messages::propertyMissing("Address"), 1101 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1102 } 1103 1104 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) { 1105 errorDetected = true; 1106 messages::addMessageToJson( 1107 asyncResp->res.json_value, 1108 messages::propertyMissing("SubnetMask"), 1109 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1110 } 1111 1112 if (addressOriginFieldState == json_util::Result::NOT_EXIST) { 1113 errorDetected = true; 1114 messages::addMessageToJson( 1115 asyncResp->res.json_value, 1116 messages::propertyMissing("AddressOrigin"), 1117 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1118 } 1119 1120 if (gatewayFieldState == json_util::Result::NOT_EXIST) { 1121 errorDetected = true; 1122 messages::addMessageToJson( 1123 asyncResp->res.json_value, messages::propertyMissing("Gateway"), 1124 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1125 } 1126 1127 // If any error occured do not proceed with current entry, but do not 1128 // end loop 1129 if (errorDetected) { 1130 errorDetected = false; 1131 continue; 1132 } 1133 1134 // Create IPv4 with provided data 1135 ethernet_provider.createIPv4( 1136 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue, 1137 *addressFieldValue, asyncResp); 1138 } else { 1139 // Existing object that should be modified/deleted/remain unchanged 1140 if (input[entryIdx].is_null()) { 1141 // Object should be deleted 1142 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, 1143 entryIdx, asyncResp); 1144 } else if (input[entryIdx].is_object()) { 1145 if (input[entryIdx].size() == 0) { 1146 // Object shall remain unchanged 1147 continue; 1148 } 1149 1150 // Apply changes 1151 if (addressFieldState == json_util::Result::SUCCESS && 1152 ipv4_data[entryIdx].address != nullptr && 1153 *ipv4_data[entryIdx].address != *addressFieldValue) { 1154 ethernet_provider.changeIPv4AddressProperty( 1155 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address", 1156 *addressFieldValue, asyncResp); 1157 } 1158 1159 if (subnetMaskFieldState == json_util::Result::SUCCESS && 1160 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) { 1161 ethernet_provider.changeIPv4SubnetMaskProperty( 1162 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1163 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp); 1164 } 1165 1166 if (addressOriginFieldState == json_util::Result::SUCCESS && 1167 ipv4_data[entryIdx].origin != *addressFieldValue) { 1168 ethernet_provider.changeIPv4Origin( 1169 ifaceId, entryIdx, ipv4_data[entryIdx].id, 1170 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp); 1171 } 1172 1173 if (gatewayFieldState == json_util::Result::SUCCESS && 1174 ipv4_data[entryIdx].gateway != nullptr && 1175 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) { 1176 ethernet_provider.changeIPv4AddressProperty( 1177 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway", 1178 *gatewayFieldValue, asyncResp); 1179 } 1180 } 1181 } 1182 } 1183 } 1184 1185 nlohmann::json parseInterfaceData( 1186 const std::string &iface_id, const EthernetInterfaceData ð_data, 1187 const std::vector<IPv4AddressData> &ipv4_data) { 1188 // Copy JSON object to avoid race condition 1189 nlohmann::json json_response(Node::json); 1190 1191 // Fill out obvious data... 1192 json_response["Id"] = iface_id; 1193 json_response["@odata.id"] = 1194 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id; 1195 1196 // ... then the one from DBus, regarding eth iface... 1197 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed; 1198 1199 if (eth_data.mac_address != nullptr) 1200 json_response["MACAddress"] = *eth_data.mac_address; 1201 1202 if (eth_data.hostname != nullptr) 1203 json_response["HostName"] = *eth_data.hostname; 1204 1205 if (eth_data.vlan_id != nullptr) { 1206 nlohmann::json &vlanObj = json_response["VLAN"]; 1207 vlanObj["VLANEnable"] = true; 1208 vlanObj["VLANId"] = *eth_data.vlan_id; 1209 } 1210 1211 // ... at last, check if there are IPv4 data and prepare appropriate 1212 // collection 1213 if (ipv4_data.size() > 0) { 1214 nlohmann::json ipv4_array = nlohmann::json::array(); 1215 for (auto &ipv4_config : ipv4_data) { 1216 nlohmann::json json_ipv4; 1217 if (ipv4_config.address != nullptr) { 1218 json_ipv4["Address"] = *ipv4_config.address; 1219 if (ipv4_config.gateway != nullptr) 1220 json_ipv4["Gateway"] = *ipv4_config.gateway; 1221 1222 json_ipv4["AddressOrigin"] = ipv4_config.origin; 1223 json_ipv4["SubnetMask"] = ipv4_config.netmask; 1224 1225 ipv4_array.push_back(std::move(json_ipv4)); 1226 } 1227 } 1228 json_response["IPv4Addresses"] = std::move(ipv4_array); 1229 } 1230 1231 return json_response; 1232 } 1233 1234 /** 1235 * Functions triggers appropriate requests on DBus 1236 */ 1237 void doGet(crow::response &res, const crow::request &req, 1238 const std::vector<std::string> ¶ms) override { 1239 // TODO(Pawel) this shall be parametrized call (two params) to get 1240 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1241 // Check if there is required param, truly entering this shall be 1242 // impossible. 1243 if (params.size() != 1) { 1244 res.result(boost::beast::http::status::internal_server_error); 1245 res.end(); 1246 return; 1247 } 1248 1249 const std::string &iface_id = params[0]; 1250 1251 // Get single eth interface data, and call the below callback for JSON 1252 // preparation 1253 ethernet_provider.getEthernetIfaceData( 1254 iface_id, [&, iface_id](const bool &success, 1255 const EthernetInterfaceData ð_data, 1256 const std::vector<IPv4AddressData> &ipv4_data) { 1257 if (success) { 1258 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 1259 } else { 1260 // ... otherwise return error 1261 // TODO(Pawel)consider distinguish between non existing object, and 1262 // other errors 1263 res.result(boost::beast::http::status::not_found); 1264 } 1265 res.end(); 1266 }); 1267 } 1268 1269 void doPatch(crow::response &res, const crow::request &req, 1270 const std::vector<std::string> ¶ms) override { 1271 // TODO(Pawel) this shall be parametrized call (two params) to get 1272 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1273 // Check if there is required param, truly entering this shall be 1274 // impossible. 1275 if (params.size() != 1) { 1276 res.result(boost::beast::http::status::internal_server_error); 1277 res.end(); 1278 return; 1279 } 1280 1281 const std::string &iface_id = params[0]; 1282 1283 nlohmann::json patchReq; 1284 1285 if (!json_util::processJsonFromRequest(res, req, patchReq)) { 1286 return; 1287 } 1288 1289 // Get single eth interface data, and call the below callback for JSON 1290 // preparation 1291 ethernet_provider.getEthernetIfaceData( 1292 iface_id, 1293 [&, iface_id, patchReq = std::move(patchReq) ]( 1294 const bool &success, const EthernetInterfaceData ð_data, 1295 const std::vector<IPv4AddressData> &ipv4_data) { 1296 if (!success) { 1297 // ... otherwise return error 1298 // TODO(Pawel)consider distinguish between non existing object, and 1299 // other errors 1300 res.result(boost::beast::http::status::not_found); 1301 res.end(); 1302 1303 return; 1304 } 1305 1306 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 1307 1308 std::shared_ptr<AsyncResp> asyncResp = 1309 std::make_shared<AsyncResp>(res); 1310 1311 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); 1312 ++propertyIt) { 1313 if (propertyIt.key() == "VLAN") { 1314 handleVlanPatch(iface_id, propertyIt.value(), eth_data, 1315 asyncResp); 1316 } else if (propertyIt.key() == "HostName") { 1317 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp); 1318 } else if (propertyIt.key() == "IPv4Addresses") { 1319 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data, 1320 asyncResp); 1321 } else if (propertyIt.key() == "IPv6Addresses") { 1322 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet 1323 messages::addMessageToJsonRoot( 1324 res.json_value, 1325 messages::propertyNotWritable(propertyIt.key())); 1326 } else { 1327 auto fieldInJsonIt = res.json_value.find(propertyIt.key()); 1328 1329 if (fieldInJsonIt == res.json_value.end()) { 1330 // Field not in scope of defined fields 1331 messages::addMessageToJsonRoot( 1332 res.json_value, 1333 messages::propertyUnknown(propertyIt.key())); 1334 } else if (*fieldInJsonIt != *propertyIt) { 1335 // User attempted to modify non-writable field 1336 messages::addMessageToJsonRoot( 1337 res.json_value, 1338 messages::propertyNotWritable(propertyIt.key())); 1339 } 1340 } 1341 } 1342 }); 1343 } 1344 1345 // Ethernet Provider object 1346 // TODO(Pawel) consider move it to singleton 1347 OnDemandEthernetProvider ethernet_provider; 1348 }; 1349 1350 } // namespace redfish 1351