19391bb9cSRapkiewicz, Pawel /* 29391bb9cSRapkiewicz, Pawel // Copyright (c) 2018 Intel Corporation 39391bb9cSRapkiewicz, Pawel // 49391bb9cSRapkiewicz, Pawel // Licensed under the Apache License, Version 2.0 (the "License"); 59391bb9cSRapkiewicz, Pawel // you may not use this file except in compliance with the License. 69391bb9cSRapkiewicz, Pawel // You may obtain a copy of the License at 79391bb9cSRapkiewicz, Pawel // 89391bb9cSRapkiewicz, Pawel // http://www.apache.org/licenses/LICENSE-2.0 99391bb9cSRapkiewicz, Pawel // 109391bb9cSRapkiewicz, Pawel // Unless required by applicable law or agreed to in writing, software 119391bb9cSRapkiewicz, Pawel // distributed under the License is distributed on an "AS IS" BASIS, 129391bb9cSRapkiewicz, Pawel // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139391bb9cSRapkiewicz, Pawel // See the License for the specific language governing permissions and 149391bb9cSRapkiewicz, Pawel // limitations under the License. 159391bb9cSRapkiewicz, Pawel */ 169391bb9cSRapkiewicz, Pawel #pragma once 179391bb9cSRapkiewicz, Pawel 18179db1d7SKowalski, Kamil #include <dbus_singleton.hpp> 19588c3f0dSKowalski, Kamil #include <error_messages.hpp> 20179db1d7SKowalski, Kamil #include <node.hpp> 21588c3f0dSKowalski, Kamil #include <utils/json_utils.hpp> 229391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp> 239391bb9cSRapkiewicz, Pawel 249391bb9cSRapkiewicz, Pawel namespace redfish { 259391bb9cSRapkiewicz, Pawel 269391bb9cSRapkiewicz, Pawel /** 279391bb9cSRapkiewicz, Pawel * DBus types primitives for several generic DBus interfaces 289391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider move this to separate file into boost::dbus 299391bb9cSRapkiewicz, Pawel */ 30aa2e59c1SEd Tanous using PropertiesMapType = boost::container::flat_map< 31aa2e59c1SEd Tanous std::string, 32aa2e59c1SEd Tanous sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 33aa2e59c1SEd Tanous int32_t, uint32_t, int64_t, uint64_t, double>>; 349391bb9cSRapkiewicz, Pawel 359391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map< 36aa2e59c1SEd Tanous sdbusplus::message::object_path, 37aa2e59c1SEd Tanous boost::container::flat_map< 38aa2e59c1SEd Tanous std::string, 39aa2e59c1SEd Tanous boost::container::flat_map< 40aa2e59c1SEd Tanous std::string, sdbusplus::message::variant< 41aa2e59c1SEd Tanous std::string, bool, uint8_t, int16_t, uint16_t, 42aa2e59c1SEd Tanous int32_t, uint32_t, int64_t, uint64_t, double>>>>; 439391bb9cSRapkiewicz, Pawel 449391bb9cSRapkiewicz, Pawel /** 459391bb9cSRapkiewicz, Pawel * Structure for keeping IPv4 data required by Redfish 469391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider change everything to ptr, or to non-ptr values. 479391bb9cSRapkiewicz, Pawel */ 489391bb9cSRapkiewicz, Pawel struct IPv4AddressData { 49179db1d7SKowalski, Kamil std::string id; 509391bb9cSRapkiewicz, Pawel const std::string *address; 519391bb9cSRapkiewicz, Pawel const std::string *domain; 529391bb9cSRapkiewicz, Pawel const std::string *gateway; 539391bb9cSRapkiewicz, Pawel std::string netmask; 549391bb9cSRapkiewicz, Pawel std::string origin; 559391bb9cSRapkiewicz, Pawel bool global; 56179db1d7SKowalski, Kamil /** 57179db1d7SKowalski, Kamil * @brief Operator< to enable sorting 58179db1d7SKowalski, Kamil * 59179db1d7SKowalski, Kamil * @param[in] obj Object to compare with 60179db1d7SKowalski, Kamil * 61179db1d7SKowalski, Kamil * @return This object id < supplied object id 62179db1d7SKowalski, Kamil */ 63179db1d7SKowalski, Kamil bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); } 649391bb9cSRapkiewicz, Pawel }; 659391bb9cSRapkiewicz, Pawel 669391bb9cSRapkiewicz, Pawel /** 679391bb9cSRapkiewicz, Pawel * Structure for keeping basic single Ethernet Interface information 689391bb9cSRapkiewicz, Pawel * available from DBus 699391bb9cSRapkiewicz, Pawel */ 709391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData { 719391bb9cSRapkiewicz, Pawel const unsigned int *speed; 729391bb9cSRapkiewicz, Pawel const bool *auto_neg; 739391bb9cSRapkiewicz, Pawel const std::string *hostname; 749391bb9cSRapkiewicz, Pawel const std::string *default_gateway; 759391bb9cSRapkiewicz, Pawel const std::string *mac_address; 76c7070ac2SKowalski, Kamil const unsigned int *vlan_id; 779391bb9cSRapkiewicz, Pawel }; 789391bb9cSRapkiewicz, Pawel 799391bb9cSRapkiewicz, Pawel /** 809391bb9cSRapkiewicz, Pawel * OnDemandEthernetProvider 81274fad5aSGunnar Mills * Ethernet provider class that retrieves data directly from dbus, before 82274fad5aSGunnar Mills * setting it into JSON output. This does not cache any data. 839391bb9cSRapkiewicz, Pawel * 849391bb9cSRapkiewicz, Pawel * TODO(Pawel) 859391bb9cSRapkiewicz, Pawel * This perhaps shall be different file, which has to be chosen on compile time 869391bb9cSRapkiewicz, Pawel * depending on OEM needs 879391bb9cSRapkiewicz, Pawel */ 889391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider { 899391bb9cSRapkiewicz, Pawel private: 909391bb9cSRapkiewicz, Pawel // Consts that may have influence on EthernetProvider performance/memory usage 919391bb9cSRapkiewicz, Pawel const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10; 929391bb9cSRapkiewicz, Pawel 939391bb9cSRapkiewicz, Pawel // Helper function that allows to extract GetAllPropertiesType from 949391bb9cSRapkiewicz, Pawel // GetManagedObjectsType, based on object path, and interface name 959391bb9cSRapkiewicz, Pawel const PropertiesMapType *extractInterfaceProperties( 96aa2e59c1SEd Tanous const sdbusplus::message::object_path &objpath, 97aa2e59c1SEd Tanous const std::string &interface, const GetManagedObjectsType &dbus_data) { 989391bb9cSRapkiewicz, Pawel const auto &dbus_obj = dbus_data.find(objpath); 999391bb9cSRapkiewicz, Pawel if (dbus_obj != dbus_data.end()) { 1009391bb9cSRapkiewicz, Pawel const auto &iface = dbus_obj->second.find(interface); 1019391bb9cSRapkiewicz, Pawel if (iface != dbus_obj->second.end()) { 1029391bb9cSRapkiewicz, Pawel return &iface->second; 1039391bb9cSRapkiewicz, Pawel } 1049391bb9cSRapkiewicz, Pawel } 1059391bb9cSRapkiewicz, Pawel return nullptr; 1069391bb9cSRapkiewicz, Pawel } 1079391bb9cSRapkiewicz, Pawel 1089391bb9cSRapkiewicz, Pawel // Helper Wrapper that does inline object_path conversion from string 109aa2e59c1SEd Tanous // into sdbusplus::message::object_path type 1109391bb9cSRapkiewicz, Pawel inline const PropertiesMapType *extractInterfaceProperties( 1119391bb9cSRapkiewicz, Pawel const std::string &objpath, const std::string &interface, 1129391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data) { 113aa2e59c1SEd Tanous const auto &dbus_obj = sdbusplus::message::object_path{objpath}; 1149391bb9cSRapkiewicz, Pawel return extractInterfaceProperties(dbus_obj, interface, dbus_data); 1159391bb9cSRapkiewicz, Pawel } 1169391bb9cSRapkiewicz, Pawel 1179391bb9cSRapkiewicz, Pawel // Helper function that allows to get pointer to the property from 1189391bb9cSRapkiewicz, Pawel // GetAllPropertiesType native, or extracted by GetAllPropertiesType 1199391bb9cSRapkiewicz, Pawel template <typename T> 120aa2e59c1SEd Tanous inline T const *const extractProperty(const PropertiesMapType &properties, 1219391bb9cSRapkiewicz, Pawel const std::string &name) { 1229391bb9cSRapkiewicz, Pawel const auto &property = properties.find(name); 1239391bb9cSRapkiewicz, Pawel if (property != properties.end()) { 124aa2e59c1SEd Tanous return mapbox::get_ptr<const T>(property->second); 1259391bb9cSRapkiewicz, Pawel } 1269391bb9cSRapkiewicz, Pawel return nullptr; 1279391bb9cSRapkiewicz, Pawel } 1289391bb9cSRapkiewicz, Pawel // TODO(Pawel) Consider to move the above functions to dbus 1299391bb9cSRapkiewicz, Pawel // generic_interfaces.hpp 1309391bb9cSRapkiewicz, Pawel 1319391bb9cSRapkiewicz, Pawel // Helper function that extracts data from several dbus objects and several 1329391bb9cSRapkiewicz, Pawel // interfaces required by single ethernet interface instance 1339391bb9cSRapkiewicz, Pawel void extractEthernetInterfaceData(const std::string ðiface_id, 1349391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 1359391bb9cSRapkiewicz, Pawel EthernetInterfaceData ð_data) { 1369391bb9cSRapkiewicz, Pawel // Extract data that contains MAC Address 1379391bb9cSRapkiewicz, Pawel const PropertiesMapType *mac_properties = extractInterfaceProperties( 1389391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 1399391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.MACAddress", dbus_data); 1409391bb9cSRapkiewicz, Pawel 1419391bb9cSRapkiewicz, Pawel if (mac_properties != nullptr) { 1429391bb9cSRapkiewicz, Pawel eth_data.mac_address = 1439391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*mac_properties, "MACAddress"); 1449391bb9cSRapkiewicz, Pawel } 1459391bb9cSRapkiewicz, Pawel 146c7070ac2SKowalski, Kamil const PropertiesMapType *vlan_properties = extractInterfaceProperties( 147c7070ac2SKowalski, Kamil "/xyz/openbmc_project/network/" + ethiface_id, 148c7070ac2SKowalski, Kamil "xyz.openbmc_project.Network.VLAN", dbus_data); 149c7070ac2SKowalski, Kamil 150c7070ac2SKowalski, Kamil if (vlan_properties != nullptr) { 151c7070ac2SKowalski, Kamil eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id"); 152c7070ac2SKowalski, Kamil } 153c7070ac2SKowalski, Kamil 1549391bb9cSRapkiewicz, Pawel // Extract data that contains link information (auto negotiation and speed) 1559391bb9cSRapkiewicz, Pawel const PropertiesMapType *eth_properties = extractInterfaceProperties( 1569391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 1579391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface", dbus_data); 1589391bb9cSRapkiewicz, Pawel 1599391bb9cSRapkiewicz, Pawel if (eth_properties != nullptr) { 1609391bb9cSRapkiewicz, Pawel eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg"); 1619391bb9cSRapkiewicz, Pawel eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed"); 1629391bb9cSRapkiewicz, Pawel } 1639391bb9cSRapkiewicz, Pawel 1649391bb9cSRapkiewicz, Pawel // Extract data that contains network config (HostName and DefaultGW) 1659391bb9cSRapkiewicz, Pawel const PropertiesMapType *config_properties = extractInterfaceProperties( 1669391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/config", 1679391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); 1689391bb9cSRapkiewicz, Pawel 1699391bb9cSRapkiewicz, Pawel if (config_properties != nullptr) { 1709391bb9cSRapkiewicz, Pawel eth_data.hostname = 1719391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "HostName"); 1729391bb9cSRapkiewicz, Pawel eth_data.default_gateway = 1739391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "DefaultGateway"); 1749391bb9cSRapkiewicz, Pawel } 1759391bb9cSRapkiewicz, Pawel } 1769391bb9cSRapkiewicz, Pawel 1779391bb9cSRapkiewicz, Pawel // Helper function that changes bits netmask notation (i.e. /24) 1789391bb9cSRapkiewicz, Pawel // into full dot notation 1799391bb9cSRapkiewicz, Pawel inline std::string getNetmask(unsigned int bits) { 1809391bb9cSRapkiewicz, Pawel uint32_t value = 0xffffffff << (32 - bits); 1819391bb9cSRapkiewicz, Pawel std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 1829391bb9cSRapkiewicz, Pawel std::to_string((value >> 16) & 0xff) + "." + 1839391bb9cSRapkiewicz, Pawel std::to_string((value >> 8) & 0xff) + "." + 1849391bb9cSRapkiewicz, Pawel std::to_string(value & 0xff); 1859391bb9cSRapkiewicz, Pawel return netmask; 1869391bb9cSRapkiewicz, Pawel } 1879391bb9cSRapkiewicz, Pawel 1889391bb9cSRapkiewicz, Pawel // Helper function that extracts data for single ethernet ipv4 address 1899391bb9cSRapkiewicz, Pawel void extractIPv4Data(const std::string ðiface_id, 1909391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 1919391bb9cSRapkiewicz, Pawel std::vector<IPv4AddressData> &ipv4_config) { 192179db1d7SKowalski, Kamil const std::string pathStart = 193179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/"; 194179db1d7SKowalski, Kamil 1959391bb9cSRapkiewicz, Pawel // Since there might be several IPv4 configurations aligned with 1969391bb9cSRapkiewicz, Pawel // single ethernet interface, loop over all of them 1979391bb9cSRapkiewicz, Pawel for (auto &objpath : dbus_data) { 198274fad5aSGunnar Mills // Check if proper patter for object path appears 1999391bb9cSRapkiewicz, Pawel if (boost::starts_with( 200daf36e2eSEd Tanous static_cast<const std::string &>(objpath.first), 2019391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) { 2029391bb9cSRapkiewicz, Pawel // and get approrpiate interface 2039391bb9cSRapkiewicz, Pawel const auto &interface = 2049391bb9cSRapkiewicz, Pawel objpath.second.find("xyz.openbmc_project.Network.IP"); 2059391bb9cSRapkiewicz, Pawel if (interface != objpath.second.end()) { 2069391bb9cSRapkiewicz, Pawel // Make a properties 'shortcut', to make everything more readable 2079391bb9cSRapkiewicz, Pawel const PropertiesMapType &properties = interface->second; 2089391bb9cSRapkiewicz, Pawel // Instance IPv4AddressData structure, and set as appropriate 2099391bb9cSRapkiewicz, Pawel IPv4AddressData ipv4_address; 210179db1d7SKowalski, Kamil 211179db1d7SKowalski, Kamil ipv4_address.id = static_cast<const std::string &>(objpath.first) 212179db1d7SKowalski, Kamil .substr(pathStart.size()); 213179db1d7SKowalski, Kamil 2149391bb9cSRapkiewicz, Pawel // IPv4 address 2159391bb9cSRapkiewicz, Pawel ipv4_address.address = 2169391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Address"); 2179391bb9cSRapkiewicz, Pawel // IPv4 gateway 2189391bb9cSRapkiewicz, Pawel ipv4_address.gateway = 2199391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Gateway"); 2209391bb9cSRapkiewicz, Pawel 2219391bb9cSRapkiewicz, Pawel // Origin is kind of DBus object so fetch pointer... 2229391bb9cSRapkiewicz, Pawel const std::string *origin = 2239391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Origin"); 2249391bb9cSRapkiewicz, Pawel if (origin != nullptr) { 225179db1d7SKowalski, Kamil ipv4_address.origin = 226179db1d7SKowalski, Kamil translateAddressOriginBetweenDBusAndRedfish(origin, true, true); 2279391bb9cSRapkiewicz, Pawel } 2289391bb9cSRapkiewicz, Pawel 2299391bb9cSRapkiewicz, Pawel // Netmask is presented as PrefixLength 2309391bb9cSRapkiewicz, Pawel const auto *mask = 2319391bb9cSRapkiewicz, Pawel extractProperty<uint8_t>(properties, "PrefixLength"); 2329391bb9cSRapkiewicz, Pawel if (mask != nullptr) { 2339391bb9cSRapkiewicz, Pawel // convert it to the string 2349391bb9cSRapkiewicz, Pawel ipv4_address.netmask = getNetmask(*mask); 2359391bb9cSRapkiewicz, Pawel } 2369391bb9cSRapkiewicz, Pawel 2379391bb9cSRapkiewicz, Pawel // Attach IPv4 only if address is present 2389391bb9cSRapkiewicz, Pawel if (ipv4_address.address != nullptr) { 239274fad5aSGunnar Mills // Check if given address is local, or global 2409391bb9cSRapkiewicz, Pawel if (boost::starts_with(*ipv4_address.address, "169.254")) { 2419391bb9cSRapkiewicz, Pawel ipv4_address.global = false; 2429391bb9cSRapkiewicz, Pawel } else { 2439391bb9cSRapkiewicz, Pawel ipv4_address.global = true; 2449391bb9cSRapkiewicz, Pawel } 2459391bb9cSRapkiewicz, Pawel ipv4_config.emplace_back(std::move(ipv4_address)); 2469391bb9cSRapkiewicz, Pawel } 2479391bb9cSRapkiewicz, Pawel } 2489391bb9cSRapkiewicz, Pawel } 2499391bb9cSRapkiewicz, Pawel } 250179db1d7SKowalski, Kamil 251179db1d7SKowalski, Kamil /** 252179db1d7SKowalski, Kamil * We have to sort this vector and ensure that order of IPv4 addresses 253179db1d7SKowalski, Kamil * is consistent between GETs to allow modification and deletion in PATCHes 254179db1d7SKowalski, Kamil */ 255179db1d7SKowalski, Kamil std::sort(ipv4_config.begin(), ipv4_config.end()); 2569391bb9cSRapkiewicz, Pawel } 2579391bb9cSRapkiewicz, Pawel 258179db1d7SKowalski, Kamil static const constexpr int ipV4AddressSectionsCount = 4; 259179db1d7SKowalski, Kamil 2609391bb9cSRapkiewicz, Pawel public: 2619391bb9cSRapkiewicz, Pawel /** 262588c3f0dSKowalski, Kamil * @brief Creates VLAN for given interface with given Id through D-Bus 263588c3f0dSKowalski, Kamil * 264588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of interface for which VLAN will be created 265588c3f0dSKowalski, Kamil * @param[in] inputVlanId ID of the new VLAN 266588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 267588c3f0dSKowalski, Kamil * 268588c3f0dSKowalski, Kamil * @return None. 269588c3f0dSKowalski, Kamil */ 270588c3f0dSKowalski, Kamil template <typename CallbackFunc> 271588c3f0dSKowalski, Kamil void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, 272588c3f0dSKowalski, Kamil CallbackFunc &&callback) { 273588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 274588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 275588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, 276588c3f0dSKowalski, Kamil static_cast<uint32_t>(inputVlanId)); 277588c3f0dSKowalski, Kamil }; 278588c3f0dSKowalski, Kamil 279588c3f0dSKowalski, Kamil /** 280588c3f0dSKowalski, Kamil * @brief Sets given Id on the given VLAN interface through D-Bus 281588c3f0dSKowalski, Kamil * 282588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of VLAN interface that should be modified 283588c3f0dSKowalski, Kamil * @param[in] inputVlanId New ID of the VLAN 284588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 285588c3f0dSKowalski, Kamil * 286588c3f0dSKowalski, Kamil * @return None. 287588c3f0dSKowalski, Kamil */ 288588c3f0dSKowalski, Kamil template <typename CallbackFunc> 289*e439f0f8SKowalski, Kamil static void changeVlanId(const std::string &ifaceId, 290*e439f0f8SKowalski, Kamil const uint32_t &inputVlanId, 291588c3f0dSKowalski, Kamil CallbackFunc &&callback) { 292588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 293588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 294588c3f0dSKowalski, Kamil std::string("/xyz/openbmc_project/network/") + ifaceId, 295588c3f0dSKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 296588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.VLAN", "Id", 297588c3f0dSKowalski, Kamil sdbusplus::message::variant<uint32_t>(inputVlanId)); 298588c3f0dSKowalski, Kamil }; 299588c3f0dSKowalski, Kamil 300588c3f0dSKowalski, Kamil /** 301179db1d7SKowalski, Kamil * @brief Helper function that verifies IP address to check if it is in 302179db1d7SKowalski, Kamil * proper format. If bits pointer is provided, also calculates active 303179db1d7SKowalski, Kamil * bit count for Subnet Mask. 304179db1d7SKowalski, Kamil * 305179db1d7SKowalski, Kamil * @param[in] ip IP that will be verified 306179db1d7SKowalski, Kamil * @param[out] bits Calculated mask in bits notation 307179db1d7SKowalski, Kamil * 308179db1d7SKowalski, Kamil * @return true in case of success, false otherwise 309179db1d7SKowalski, Kamil */ 310179db1d7SKowalski, Kamil bool ipv4VerifyIpAndGetBitcount(const std::string &ip, 311179db1d7SKowalski, Kamil uint8_t *bits = nullptr) { 312179db1d7SKowalski, Kamil std::vector<std::string> bytesInMask; 313179db1d7SKowalski, Kamil 314179db1d7SKowalski, Kamil boost::split(bytesInMask, ip, boost::is_any_of(".")); 315179db1d7SKowalski, Kamil 316179db1d7SKowalski, Kamil if (bytesInMask.size() != ipV4AddressSectionsCount) { 317179db1d7SKowalski, Kamil return false; 318179db1d7SKowalski, Kamil } 319179db1d7SKowalski, Kamil 320179db1d7SKowalski, Kamil if (bits != nullptr) { 321179db1d7SKowalski, Kamil *bits = 0; 322179db1d7SKowalski, Kamil } 323179db1d7SKowalski, Kamil 324179db1d7SKowalski, Kamil char *endPtr; 325179db1d7SKowalski, Kamil long previousValue = 255; 326179db1d7SKowalski, Kamil bool firstZeroInByteHit; 327179db1d7SKowalski, Kamil for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) { 328179db1d7SKowalski, Kamil // Use strtol instead of stroi to avoid exceptions 329179db1d7SKowalski, Kamil long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10); 330179db1d7SKowalski, Kamil 331179db1d7SKowalski, Kamil // endPtr should point to the end of the string, otherwise given string 332179db1d7SKowalski, Kamil // is not 100% number 333179db1d7SKowalski, Kamil if (*endPtr != '\0') { 334179db1d7SKowalski, Kamil return false; 335179db1d7SKowalski, Kamil } 336179db1d7SKowalski, Kamil 337179db1d7SKowalski, Kamil // Value should be contained in byte 338179db1d7SKowalski, Kamil if (value < 0 || value > 255) { 339179db1d7SKowalski, Kamil return false; 340179db1d7SKowalski, Kamil } 341179db1d7SKowalski, Kamil 342179db1d7SKowalski, Kamil if (bits != nullptr) { 343179db1d7SKowalski, Kamil // Mask has to be continuous between bytes 344179db1d7SKowalski, Kamil if (previousValue != 255 && value != 0) { 345179db1d7SKowalski, Kamil return false; 346179db1d7SKowalski, Kamil } 347179db1d7SKowalski, Kamil 348179db1d7SKowalski, Kamil // Mask has to be continuous inside bytes 349179db1d7SKowalski, Kamil firstZeroInByteHit = false; 350179db1d7SKowalski, Kamil 351179db1d7SKowalski, Kamil // Count bits 352179db1d7SKowalski, Kamil for (int bitIdx = 7; bitIdx >= 0; bitIdx--) { 353179db1d7SKowalski, Kamil if (value & (1 << bitIdx)) { 354179db1d7SKowalski, Kamil if (firstZeroInByteHit) { 355179db1d7SKowalski, Kamil // Continuity not preserved 356179db1d7SKowalski, Kamil return false; 357179db1d7SKowalski, Kamil } else { 358179db1d7SKowalski, Kamil (*bits)++; 359179db1d7SKowalski, Kamil } 360179db1d7SKowalski, Kamil } else { 361179db1d7SKowalski, Kamil firstZeroInByteHit = true; 362179db1d7SKowalski, Kamil } 363179db1d7SKowalski, Kamil } 364179db1d7SKowalski, Kamil } 365179db1d7SKowalski, Kamil 366179db1d7SKowalski, Kamil previousValue = value; 367179db1d7SKowalski, Kamil } 368179db1d7SKowalski, Kamil 369179db1d7SKowalski, Kamil return true; 370179db1d7SKowalski, Kamil } 371179db1d7SKowalski, Kamil 372179db1d7SKowalski, Kamil /** 373179db1d7SKowalski, Kamil * @brief Changes IPv4 address type property (Address, Gateway) 374179db1d7SKowalski, Kamil * 375179db1d7SKowalski, Kamil * @param[in] ifaceId Id of interface whose IP should be modified 376179db1d7SKowalski, Kamil * @param[in] ipIdx Index of IP in input array that should be modified 377179db1d7SKowalski, Kamil * @param[in] ipHash DBus Hash id of modified IP 378179db1d7SKowalski, Kamil * @param[in] name Name of field in JSON representation 379179db1d7SKowalski, Kamil * @param[in] newValue New value that should be written 380179db1d7SKowalski, Kamil * @param[io] asyncResp Response object that will be returned to client 381179db1d7SKowalski, Kamil * 382179db1d7SKowalski, Kamil * @return true if give IP is valid and has been sent do D-Bus, false 383179db1d7SKowalski, Kamil * otherwise 384179db1d7SKowalski, Kamil */ 385179db1d7SKowalski, Kamil void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx, 386179db1d7SKowalski, Kamil const std::string &ipHash, 387179db1d7SKowalski, Kamil const std::string &name, 388179db1d7SKowalski, Kamil const std::string &newValue, 389179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 390179db1d7SKowalski, Kamil auto callback = [ 391179db1d7SKowalski, Kamil asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)}, 392179db1d7SKowalski, Kamil newValue{std::move(newValue)} 393179db1d7SKowalski, Kamil ](const boost::system::error_code ec) { 394179db1d7SKowalski, Kamil if (ec) { 395179db1d7SKowalski, Kamil messages::addMessageToJson( 396179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::internalError(), 397179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name); 398179db1d7SKowalski, Kamil } else { 399179db1d7SKowalski, Kamil asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue; 400179db1d7SKowalski, Kamil } 401179db1d7SKowalski, Kamil }; 402179db1d7SKowalski, Kamil 403179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 404179db1d7SKowalski, Kamil std::move(callback), "xyz.openbmc_project.Network", 405179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 406179db1d7SKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 407179db1d7SKowalski, Kamil "xyz.openbmc_project.Network.IP", name, 408179db1d7SKowalski, Kamil sdbusplus::message::variant<std::string>(newValue)); 409179db1d7SKowalski, Kamil }; 410179db1d7SKowalski, Kamil 411179db1d7SKowalski, Kamil /** 412179db1d7SKowalski, Kamil * @brief Changes IPv4 address origin property 413179db1d7SKowalski, Kamil * 414179db1d7SKowalski, Kamil * @param[in] ifaceId Id of interface whose IP should be modified 415179db1d7SKowalski, Kamil * @param[in] ipIdx Index of IP in input array that should be modified 416179db1d7SKowalski, Kamil * @param[in] ipHash DBus Hash id of modified IP 417179db1d7SKowalski, Kamil * @param[in] newValue New value in Redfish format 418179db1d7SKowalski, Kamil * @param[in] newValueDbus New value in D-Bus format 419179db1d7SKowalski, Kamil * @param[io] asyncResp Response object that will be returned to client 420179db1d7SKowalski, Kamil * 421179db1d7SKowalski, Kamil * @return true if give IP is valid and has been sent do D-Bus, false 422179db1d7SKowalski, Kamil * otherwise 423179db1d7SKowalski, Kamil */ 424179db1d7SKowalski, Kamil void changeIPv4Origin(const std::string &ifaceId, int ipIdx, 425179db1d7SKowalski, Kamil const std::string &ipHash, const std::string &newValue, 426179db1d7SKowalski, Kamil const std::string &newValueDbus, 427179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 428179db1d7SKowalski, Kamil auto callback = 429179db1d7SKowalski, Kamil [ asyncResp, ipIdx{std::move(ipIdx)}, 430179db1d7SKowalski, Kamil newValue{std::move(newValue)} ](const boost::system::error_code ec) { 431179db1d7SKowalski, Kamil if (ec) { 432179db1d7SKowalski, Kamil messages::addMessageToJson( 433179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::internalError(), 434179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin"); 435179db1d7SKowalski, Kamil } else { 436179db1d7SKowalski, Kamil asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] = 437179db1d7SKowalski, Kamil newValue; 438179db1d7SKowalski, Kamil } 439179db1d7SKowalski, Kamil }; 440179db1d7SKowalski, Kamil 441179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 442179db1d7SKowalski, Kamil std::move(callback), "xyz.openbmc_project.Network", 443179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 444179db1d7SKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 445179db1d7SKowalski, Kamil "xyz.openbmc_project.Network.IP", "Origin", 446179db1d7SKowalski, Kamil sdbusplus::message::variant<std::string>(newValueDbus)); 447179db1d7SKowalski, Kamil }; 448179db1d7SKowalski, Kamil 449179db1d7SKowalski, Kamil /** 450179db1d7SKowalski, Kamil * @brief Modifies SubnetMask for given IP 451179db1d7SKowalski, Kamil * 452179db1d7SKowalski, Kamil * @param[in] ifaceId Id of interface whose IP should be modified 453179db1d7SKowalski, Kamil * @param[in] ipIdx Index of IP in input array that should be modified 454179db1d7SKowalski, Kamil * @param[in] ipHash DBus Hash id of modified IP 455179db1d7SKowalski, Kamil * @param[in] newValueStr Mask in dot notation as string 456179db1d7SKowalski, Kamil * @param[in] newValue Mask as PrefixLength in bitcount 457179db1d7SKowalski, Kamil * @param[io] asyncResp Response object that will be returned to client 458179db1d7SKowalski, Kamil * 459179db1d7SKowalski, Kamil * @return None 460179db1d7SKowalski, Kamil */ 461179db1d7SKowalski, Kamil void changeIPv4SubnetMaskProperty( 462179db1d7SKowalski, Kamil const std::string &ifaceId, int ipIdx, const std::string &ipHash, 463179db1d7SKowalski, Kamil const std::string &newValueStr, uint8_t &newValue, 464179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 465179db1d7SKowalski, Kamil auto callback = [ 466179db1d7SKowalski, Kamil asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)} 467179db1d7SKowalski, Kamil ](const boost::system::error_code ec) { 468179db1d7SKowalski, Kamil if (ec) { 469179db1d7SKowalski, Kamil messages::addMessageToJson( 470179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::internalError(), 471179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask"); 472179db1d7SKowalski, Kamil } else { 473179db1d7SKowalski, Kamil asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] = 474179db1d7SKowalski, Kamil newValueStr; 475179db1d7SKowalski, Kamil } 476179db1d7SKowalski, Kamil }; 477179db1d7SKowalski, Kamil 478179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 479179db1d7SKowalski, Kamil std::move(callback), "xyz.openbmc_project.Network", 480179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 481179db1d7SKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 482179db1d7SKowalski, Kamil "xyz.openbmc_project.Network.IP", "PrefixLength", 483179db1d7SKowalski, Kamil sdbusplus::message::variant<uint8_t>(newValue)); 484179db1d7SKowalski, Kamil }; 485179db1d7SKowalski, Kamil 486179db1d7SKowalski, Kamil /** 487588c3f0dSKowalski, Kamil * @brief Disables VLAN with given ifaceId 488588c3f0dSKowalski, Kamil * 489588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of VLAN interface that should be disabled 490588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 491588c3f0dSKowalski, Kamil * 492588c3f0dSKowalski, Kamil * @return None. 493588c3f0dSKowalski, Kamil */ 494588c3f0dSKowalski, Kamil template <typename CallbackFunc> 495*e439f0f8SKowalski, Kamil static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) { 496588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 497588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 498588c3f0dSKowalski, Kamil std::string("/xyz/openbmc_project/network/") + ifaceId, 499588c3f0dSKowalski, Kamil "xyz.openbmc_project.Object.Delete", "Delete"); 500588c3f0dSKowalski, Kamil }; 501588c3f0dSKowalski, Kamil 502588c3f0dSKowalski, Kamil /** 503588c3f0dSKowalski, Kamil * @brief Sets given HostName of the machine through D-Bus 504588c3f0dSKowalski, Kamil * 505588c3f0dSKowalski, Kamil * @param[in] newHostname New name that HostName will be changed to 506588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 507588c3f0dSKowalski, Kamil * 508588c3f0dSKowalski, Kamil * @return None. 509588c3f0dSKowalski, Kamil */ 510588c3f0dSKowalski, Kamil template <typename CallbackFunc> 511588c3f0dSKowalski, Kamil void setHostName(const std::string &newHostname, CallbackFunc &&callback) { 512588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 513588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 514588c3f0dSKowalski, Kamil "/xyz/openbmc_project/network/config", 515588c3f0dSKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 516588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 517588c3f0dSKowalski, Kamil sdbusplus::message::variant<std::string>(newHostname)); 518588c3f0dSKowalski, Kamil }; 519588c3f0dSKowalski, Kamil 520588c3f0dSKowalski, Kamil /** 521179db1d7SKowalski, Kamil * @brief Deletes given IPv4 522179db1d7SKowalski, Kamil * 523179db1d7SKowalski, Kamil * @param[in] ifaceId Id of interface whose IP should be deleted 524179db1d7SKowalski, Kamil * @param[in] ipIdx Index of IP in input array that should be deleted 525179db1d7SKowalski, Kamil * @param[in] ipHash DBus Hash id of IP that should be deleted 526179db1d7SKowalski, Kamil * @param[io] asyncResp Response object that will be returned to client 527179db1d7SKowalski, Kamil * 528179db1d7SKowalski, Kamil * @return None 529179db1d7SKowalski, Kamil */ 530179db1d7SKowalski, Kamil void deleteIPv4(const std::string &ifaceId, const std::string &ipHash, 531179db1d7SKowalski, Kamil unsigned int ipIdx, 532179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 533179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 534179db1d7SKowalski, Kamil [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ]( 535179db1d7SKowalski, Kamil const boost::system::error_code ec) { 536179db1d7SKowalski, Kamil if (ec) { 537179db1d7SKowalski, Kamil messages::addMessageToJson( 538179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::internalError(), 539179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 540179db1d7SKowalski, Kamil } else { 541179db1d7SKowalski, Kamil asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr; 542179db1d7SKowalski, Kamil } 543179db1d7SKowalski, Kamil }, 544179db1d7SKowalski, Kamil "xyz.openbmc_project.Network", 545179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash, 546179db1d7SKowalski, Kamil "xyz.openbmc_project.Object.Delete", "Delete"); 547179db1d7SKowalski, Kamil } 548179db1d7SKowalski, Kamil 549179db1d7SKowalski, Kamil /** 550179db1d7SKowalski, Kamil * @brief Creates IPv4 with given data 551179db1d7SKowalski, Kamil * 552179db1d7SKowalski, Kamil * @param[in] ifaceId Id of interface whose IP should be deleted 553179db1d7SKowalski, Kamil * @param[in] ipIdx Index of IP in input array that should be deleted 554179db1d7SKowalski, Kamil * @param[in] ipHash DBus Hash id of IP that should be deleted 555179db1d7SKowalski, Kamil * @param[io] asyncResp Response object that will be returned to client 556179db1d7SKowalski, Kamil * 557179db1d7SKowalski, Kamil * @return None 558179db1d7SKowalski, Kamil */ 559179db1d7SKowalski, Kamil void createIPv4(const std::string &ifaceId, unsigned int ipIdx, 560179db1d7SKowalski, Kamil uint8_t subnetMask, const std::string &gateway, 561179db1d7SKowalski, Kamil const std::string &address, 562179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 563179db1d7SKowalski, Kamil auto createIpHandler = [ 564179db1d7SKowalski, Kamil ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} 565179db1d7SKowalski, Kamil ](const boost::system::error_code ec) { 566179db1d7SKowalski, Kamil if (ec) { 567179db1d7SKowalski, Kamil messages::addMessageToJson( 568179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::internalError(), 569179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(ipIdx) + "/"); 570179db1d7SKowalski, Kamil } 571179db1d7SKowalski, Kamil }; 572179db1d7SKowalski, Kamil 573179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 574179db1d7SKowalski, Kamil std::move(createIpHandler), "xyz.openbmc_project.Network", 575179db1d7SKowalski, Kamil "/xyz/openbmc_project/network/" + ifaceId, 576179db1d7SKowalski, Kamil "xyz.openbmc_project.Network.IP.Create", "IP", 577179db1d7SKowalski, Kamil "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask, 578179db1d7SKowalski, Kamil gateway); 579179db1d7SKowalski, Kamil } 580179db1d7SKowalski, Kamil 581179db1d7SKowalski, Kamil /** 582179db1d7SKowalski, Kamil * @brief Translates Address Origin value from D-Bus to Redfish format and 583179db1d7SKowalski, Kamil * vice-versa 584179db1d7SKowalski, Kamil * 585179db1d7SKowalski, Kamil * @param[in] inputOrigin Input value that should be translated 586179db1d7SKowalski, Kamil * @param[in] isIPv4 True for IPv4 origins, False for IPv6 587179db1d7SKowalski, Kamil * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse 588179db1d7SKowalski, Kamil * 589179db1d7SKowalski, Kamil * @return Empty string in case of failure, translated value otherwise 590179db1d7SKowalski, Kamil */ 591179db1d7SKowalski, Kamil std::string translateAddressOriginBetweenDBusAndRedfish( 592179db1d7SKowalski, Kamil const std::string *inputOrigin, bool isIPv4, bool isFromDBus) { 593179db1d7SKowalski, Kamil // Invalid pointer 594179db1d7SKowalski, Kamil if (inputOrigin == nullptr) { 595179db1d7SKowalski, Kamil return ""; 596179db1d7SKowalski, Kamil } 597179db1d7SKowalski, Kamil 598179db1d7SKowalski, Kamil static const constexpr unsigned int firstIPv4OnlyIdx = 1; 599179db1d7SKowalski, Kamil static const constexpr unsigned int firstIPv6OnlyIdx = 3; 600179db1d7SKowalski, Kamil 601179db1d7SKowalski, Kamil std::array<std::pair<const char *, const char *>, 6> translationTable{ 602179db1d7SKowalski, Kamil {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"}, 603179db1d7SKowalski, Kamil {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"}, 604179db1d7SKowalski, Kamil {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 605179db1d7SKowalski, Kamil "IPv4LinkLocal"}, 606179db1d7SKowalski, Kamil {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"}, 607179db1d7SKowalski, Kamil {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal", 608179db1d7SKowalski, Kamil "LinkLocal"}, 609179db1d7SKowalski, Kamil {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}}; 610179db1d7SKowalski, Kamil 611179db1d7SKowalski, Kamil for (unsigned int i = 0; i < translationTable.size(); i++) { 612179db1d7SKowalski, Kamil // Skip unrelated 613179db1d7SKowalski, Kamil if (isIPv4 && i >= firstIPv6OnlyIdx) break; 614179db1d7SKowalski, Kamil if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue; 615179db1d7SKowalski, Kamil 616179db1d7SKowalski, Kamil // When translating D-Bus to Redfish compare input to first element 617179db1d7SKowalski, Kamil if (isFromDBus && translationTable[i].first == *inputOrigin) 618179db1d7SKowalski, Kamil return translationTable[i].second; 619179db1d7SKowalski, Kamil 620179db1d7SKowalski, Kamil // When translating Redfish to D-Bus compare input to second element 621179db1d7SKowalski, Kamil if (!isFromDBus && translationTable[i].second == *inputOrigin) 622179db1d7SKowalski, Kamil return translationTable[i].first; 623179db1d7SKowalski, Kamil } 624179db1d7SKowalski, Kamil 625179db1d7SKowalski, Kamil // If we are still here, that means that value has not been found 626179db1d7SKowalski, Kamil return ""; 627179db1d7SKowalski, Kamil } 628179db1d7SKowalski, Kamil 629179db1d7SKowalski, Kamil /** 630179db1d7SKowalski, Kamil * Function that retrieves all properties for given Ethernet Interface 631179db1d7SKowalski, Kamil * Object 632179db1d7SKowalski, Kamil * from EntityManager Network Manager 633179db1d7SKowalski, Kamil * @param ethiface_id a eth interface id to query on DBus 634179db1d7SKowalski, Kamil * @param callback a function that shall be called to convert Dbus output 635179db1d7SKowalski, Kamil * into JSON 636179db1d7SKowalski, Kamil */ 637179db1d7SKowalski, Kamil template <typename CallbackFunc> 638179db1d7SKowalski, Kamil void getEthernetIfaceData(const std::string ðiface_id, 639179db1d7SKowalski, Kamil CallbackFunc &&callback) { 640179db1d7SKowalski, Kamil crow::connections::system_bus->async_method_call( 641179db1d7SKowalski, Kamil [ 642179db1d7SKowalski, Kamil this, ethiface_id{std::move(ethiface_id)}, 643179db1d7SKowalski, Kamil callback{std::move(callback)} 644179db1d7SKowalski, Kamil ](const boost::system::error_code error_code, 645179db1d7SKowalski, Kamil const GetManagedObjectsType &resp) { 646179db1d7SKowalski, Kamil 647179db1d7SKowalski, Kamil EthernetInterfaceData eth_data{}; 648179db1d7SKowalski, Kamil std::vector<IPv4AddressData> ipv4_data; 649179db1d7SKowalski, Kamil ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE); 650179db1d7SKowalski, Kamil 651179db1d7SKowalski, Kamil if (error_code) { 652179db1d7SKowalski, Kamil // Something wrong on DBus, the error_code is not important at 653179db1d7SKowalski, Kamil // this moment, just return success=false, and empty output. Since 654179db1d7SKowalski, Kamil // size of vector may vary depending on information from Network 655179db1d7SKowalski, Kamil // Manager, and empty output could not be treated same way as 656179db1d7SKowalski, Kamil // error. 657179db1d7SKowalski, Kamil callback(false, eth_data, ipv4_data); 658179db1d7SKowalski, Kamil return; 659179db1d7SKowalski, Kamil } 660179db1d7SKowalski, Kamil 661179db1d7SKowalski, Kamil extractEthernetInterfaceData(ethiface_id, resp, eth_data); 662179db1d7SKowalski, Kamil extractIPv4Data(ethiface_id, resp, ipv4_data); 663179db1d7SKowalski, Kamil 664179db1d7SKowalski, Kamil // Fix global GW 665179db1d7SKowalski, Kamil for (IPv4AddressData &ipv4 : ipv4_data) { 666179db1d7SKowalski, Kamil if ((ipv4.global) && 667179db1d7SKowalski, Kamil ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { 668179db1d7SKowalski, Kamil ipv4.gateway = eth_data.default_gateway; 669179db1d7SKowalski, Kamil } 670179db1d7SKowalski, Kamil } 671179db1d7SKowalski, Kamil 672179db1d7SKowalski, Kamil // Finally make a callback with usefull data 673179db1d7SKowalski, Kamil callback(true, eth_data, ipv4_data); 674179db1d7SKowalski, Kamil }, 675179db1d7SKowalski, Kamil "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 676179db1d7SKowalski, Kamil "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 677179db1d7SKowalski, Kamil }; 678179db1d7SKowalski, Kamil 679179db1d7SKowalski, Kamil /** 6809391bb9cSRapkiewicz, Pawel * Function that retrieves all Ethernet Interfaces available through Network 6819391bb9cSRapkiewicz, Pawel * Manager 6829391bb9cSRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 6839391bb9cSRapkiewicz, Pawel * JSON. 6849391bb9cSRapkiewicz, Pawel */ 6859391bb9cSRapkiewicz, Pawel template <typename CallbackFunc> 6869391bb9cSRapkiewicz, Pawel void getEthernetIfaceList(CallbackFunc &&callback) { 6879391bb9cSRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 6889391bb9cSRapkiewicz, Pawel [ this, callback{std::move(callback)} ]( 6899391bb9cSRapkiewicz, Pawel const boost::system::error_code error_code, 690aa2e59c1SEd Tanous GetManagedObjectsType &resp) { 6919391bb9cSRapkiewicz, Pawel // Callback requires vector<string> to retrieve all available ethernet 6929391bb9cSRapkiewicz, Pawel // interfaces 6939391bb9cSRapkiewicz, Pawel std::vector<std::string> iface_list; 6949391bb9cSRapkiewicz, Pawel iface_list.reserve(resp.size()); 6959391bb9cSRapkiewicz, Pawel if (error_code) { 6969391bb9cSRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 6979391bb9cSRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 6989391bb9cSRapkiewicz, Pawel // of vector may vary depending on information from Network Manager, 6999391bb9cSRapkiewicz, Pawel // and empty output could not be treated same way as error. 7009391bb9cSRapkiewicz, Pawel callback(false, iface_list); 7019391bb9cSRapkiewicz, Pawel return; 7029391bb9cSRapkiewicz, Pawel } 7039391bb9cSRapkiewicz, Pawel 7049391bb9cSRapkiewicz, Pawel // Iterate over all retrieved ObjectPaths. 7059391bb9cSRapkiewicz, Pawel for (auto &objpath : resp) { 7069391bb9cSRapkiewicz, Pawel // And all interfaces available for certain ObjectPath. 7079391bb9cSRapkiewicz, Pawel for (auto &interface : objpath.second) { 7089391bb9cSRapkiewicz, Pawel // If interface is xyz.openbmc_project.Network.EthernetInterface, 7099391bb9cSRapkiewicz, Pawel // this is what we're looking for. 7109391bb9cSRapkiewicz, Pawel if (interface.first == 7119391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface") { 712aa2e59c1SEd Tanous // Cut out everyting until last "/", ... 713daf36e2eSEd Tanous const std::string &iface_id = 714daf36e2eSEd Tanous static_cast<const std::string &>(objpath.first); 7159391bb9cSRapkiewicz, Pawel std::size_t last_pos = iface_id.rfind("/"); 7169391bb9cSRapkiewicz, Pawel if (last_pos != std::string::npos) { 7179391bb9cSRapkiewicz, Pawel // and put it into output vector. 7189391bb9cSRapkiewicz, Pawel iface_list.emplace_back(iface_id.substr(last_pos + 1)); 7199391bb9cSRapkiewicz, Pawel } 7209391bb9cSRapkiewicz, Pawel } 7219391bb9cSRapkiewicz, Pawel } 7229391bb9cSRapkiewicz, Pawel } 723274fad5aSGunnar Mills // Finally make a callback with useful data 7249391bb9cSRapkiewicz, Pawel callback(true, iface_list); 7259391bb9cSRapkiewicz, Pawel }, 726aa2e59c1SEd Tanous "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 727aa2e59c1SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 7289391bb9cSRapkiewicz, Pawel }; 7299391bb9cSRapkiewicz, Pawel }; 7309391bb9cSRapkiewicz, Pawel 7319391bb9cSRapkiewicz, Pawel /** 7329391bb9cSRapkiewicz, Pawel * EthernetCollection derived class for delivering Ethernet Collection Schema 7339391bb9cSRapkiewicz, Pawel */ 7349391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node { 7359391bb9cSRapkiewicz, Pawel public: 7369391bb9cSRapkiewicz, Pawel template <typename CrowApp> 7379391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 7389391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 7399391bb9cSRapkiewicz, Pawel // GetSubroutes method 7409391bb9cSRapkiewicz, Pawel EthernetCollection(CrowApp &app) 7419391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { 7429391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = 7439391bb9cSRapkiewicz, Pawel "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 7449391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 7459391bb9cSRapkiewicz, Pawel "/redfish/v1/" 7469391bb9cSRapkiewicz, Pawel "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 7479391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; 7489391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Ethernet Network Interface Collection"; 7499391bb9cSRapkiewicz, Pawel Node::json["Description"] = 7509391bb9cSRapkiewicz, Pawel "Collection of EthernetInterfaces for this Manager"; 7519391bb9cSRapkiewicz, Pawel 752588c3f0dSKowalski, Kamil entityPrivileges = { 753588c3f0dSKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 754e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 755e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 756e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 757e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 758e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 7599391bb9cSRapkiewicz, Pawel } 7609391bb9cSRapkiewicz, Pawel 7619391bb9cSRapkiewicz, Pawel private: 7629391bb9cSRapkiewicz, Pawel /** 7639391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 7649391bb9cSRapkiewicz, Pawel */ 7659391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 7669391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 7679391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 7689391bb9cSRapkiewicz, Pawel // any Manager, not only hardcoded 'openbmc'. 7699391bb9cSRapkiewicz, Pawel std::string manager_id = "openbmc"; 7709391bb9cSRapkiewicz, Pawel 7719391bb9cSRapkiewicz, Pawel // Get eth interface list, and call the below callback for JSON preparation 7729391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceList( 7739391bb9cSRapkiewicz, Pawel [&, manager_id{std::move(manager_id)} ]( 7749391bb9cSRapkiewicz, Pawel const bool &success, const std::vector<std::string> &iface_list) { 7759391bb9cSRapkiewicz, Pawel if (success) { 7769391bb9cSRapkiewicz, Pawel nlohmann::json iface_array = nlohmann::json::array(); 7779391bb9cSRapkiewicz, Pawel for (const std::string &iface_item : iface_list) { 7789391bb9cSRapkiewicz, Pawel iface_array.push_back( 7799391bb9cSRapkiewicz, Pawel {{"@odata.id", "/redfish/v1/Managers/" + manager_id + 7809391bb9cSRapkiewicz, Pawel "/EthernetInterfaces/" + iface_item}}); 7819391bb9cSRapkiewicz, Pawel } 7829391bb9cSRapkiewicz, Pawel Node::json["Members"] = iface_array; 7839391bb9cSRapkiewicz, Pawel Node::json["Members@odata.count"] = iface_array.size(); 7849391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = 7859391bb9cSRapkiewicz, Pawel "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces"; 7869391bb9cSRapkiewicz, Pawel res.json_value = Node::json; 7879391bb9cSRapkiewicz, Pawel } else { 7889391bb9cSRapkiewicz, Pawel // No success, best what we can do is return INTERNALL ERROR 789e0d918bcSEd Tanous res.result(boost::beast::http::status::internal_server_error); 7909391bb9cSRapkiewicz, Pawel } 7919391bb9cSRapkiewicz, Pawel res.end(); 7929391bb9cSRapkiewicz, Pawel }); 7939391bb9cSRapkiewicz, Pawel } 7949391bb9cSRapkiewicz, Pawel 7959391bb9cSRapkiewicz, Pawel // Ethernet Provider object 7969391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 7979391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 7989391bb9cSRapkiewicz, Pawel }; 7999391bb9cSRapkiewicz, Pawel 8009391bb9cSRapkiewicz, Pawel /** 8019391bb9cSRapkiewicz, Pawel * EthernetInterface derived class for delivering Ethernet Schema 8029391bb9cSRapkiewicz, Pawel */ 8039391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node { 8049391bb9cSRapkiewicz, Pawel public: 8059391bb9cSRapkiewicz, Pawel /* 8069391bb9cSRapkiewicz, Pawel * Default Constructor 8079391bb9cSRapkiewicz, Pawel */ 8089391bb9cSRapkiewicz, Pawel template <typename CrowApp> 8099391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 8109391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 8119391bb9cSRapkiewicz, Pawel // GetSubroutes method 8129391bb9cSRapkiewicz, Pawel EthernetInterface(CrowApp &app) 8139391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", 8149391bb9cSRapkiewicz, Pawel std::string()) { 8159391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; 8169391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 8179391bb9cSRapkiewicz, Pawel "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 8189391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Manager Ethernet Interface"; 8199391bb9cSRapkiewicz, Pawel Node::json["Description"] = "Management Network Interface"; 8209391bb9cSRapkiewicz, Pawel 821588c3f0dSKowalski, Kamil entityPrivileges = { 822588c3f0dSKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 823e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 824e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 825e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 826e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 827e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 8289391bb9cSRapkiewicz, Pawel } 8299391bb9cSRapkiewicz, Pawel 830*e439f0f8SKowalski, Kamil // TODO(kkowalsk) Find a suitable class/namespace for this 831*e439f0f8SKowalski, Kamil static void handleVlanPatch(const std::string &ifaceId, 832*e439f0f8SKowalski, Kamil const nlohmann::json &input, 833588c3f0dSKowalski, Kamil const EthernetInterfaceData ð_data, 834*e439f0f8SKowalski, Kamil const std::string &pathPrefix, 835588c3f0dSKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 836588c3f0dSKowalski, Kamil if (!input.is_object()) { 837588c3f0dSKowalski, Kamil messages::addMessageToJson( 838588c3f0dSKowalski, Kamil asyncResp->res.json_value, 839*e439f0f8SKowalski, Kamil messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix); 840588c3f0dSKowalski, Kamil return; 841588c3f0dSKowalski, Kamil } 842588c3f0dSKowalski, Kamil 843*e439f0f8SKowalski, Kamil const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix; 844*e439f0f8SKowalski, Kamil nlohmann::json ¶msJson = 845*e439f0f8SKowalski, Kamil (pathPrefix == "/") 846*e439f0f8SKowalski, Kamil ? asyncResp->res.json_value 847*e439f0f8SKowalski, Kamil : asyncResp->res.json_value[nlohmann::json_pointer<nlohmann::json>( 848*e439f0f8SKowalski, Kamil pathPrefix)]; 849588c3f0dSKowalski, Kamil bool inputVlanEnabled; 850588c3f0dSKowalski, Kamil uint64_t inputVlanId; 851*e439f0f8SKowalski, Kamil 852588c3f0dSKowalski, Kamil json_util::Result inputVlanEnabledState = json_util::getBool( 853588c3f0dSKowalski, Kamil "VLANEnable", input, inputVlanEnabled, 854588c3f0dSKowalski, Kamil static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 855*e439f0f8SKowalski, Kamil asyncResp->res.json_value, std::string(pathStart + "/VLANEnable")); 856588c3f0dSKowalski, Kamil json_util::Result inputVlanIdState = json_util::getUnsigned( 857588c3f0dSKowalski, Kamil "VLANId", input, inputVlanId, 858588c3f0dSKowalski, Kamil static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 859*e439f0f8SKowalski, Kamil asyncResp->res.json_value, std::string(pathStart + "/VLANId")); 860588c3f0dSKowalski, Kamil bool inputInvalid = false; 861588c3f0dSKowalski, Kamil 862588c3f0dSKowalski, Kamil // Do not proceed if fields in VLAN object were of wrong type 863588c3f0dSKowalski, Kamil if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || 864588c3f0dSKowalski, Kamil inputVlanIdState == json_util::Result::WRONG_TYPE) { 865588c3f0dSKowalski, Kamil return; 866588c3f0dSKowalski, Kamil } 867588c3f0dSKowalski, Kamil 868588c3f0dSKowalski, Kamil // Verify input 869588c3f0dSKowalski, Kamil if (eth_data.vlan_id == nullptr) { 870*e439f0f8SKowalski, Kamil // This interface is not a VLAN. Cannot do anything with it 871*e439f0f8SKowalski, Kamil // TODO(kkowalsk) Change this message 872588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 873588c3f0dSKowalski, Kamil messages::propertyMissing("VLANEnable"), 874*e439f0f8SKowalski, Kamil pathPrefix); 875588c3f0dSKowalski, Kamil 876588c3f0dSKowalski, Kamil inputInvalid = true; 877588c3f0dSKowalski, Kamil } else { 878588c3f0dSKowalski, Kamil // Load actual data into field values if they were not provided 879588c3f0dSKowalski, Kamil if (inputVlanEnabledState == json_util::Result::NOT_EXIST) { 880588c3f0dSKowalski, Kamil inputVlanEnabled = true; 881588c3f0dSKowalski, Kamil } 882588c3f0dSKowalski, Kamil 883588c3f0dSKowalski, Kamil if (inputVlanIdState == json_util::Result::NOT_EXIST) { 884588c3f0dSKowalski, Kamil inputVlanId = *eth_data.vlan_id; 885588c3f0dSKowalski, Kamil } 886588c3f0dSKowalski, Kamil } 887588c3f0dSKowalski, Kamil 888588c3f0dSKowalski, Kamil // Do not proceed if input has not been valid 889588c3f0dSKowalski, Kamil if (inputInvalid) { 890588c3f0dSKowalski, Kamil return; 891588c3f0dSKowalski, Kamil } 892588c3f0dSKowalski, Kamil 893588c3f0dSKowalski, Kamil // VLAN is configured on the interface 894588c3f0dSKowalski, Kamil if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) { 895588c3f0dSKowalski, Kamil // Change VLAN Id 896*e439f0f8SKowalski, Kamil paramsJson["VLANId"] = inputVlanId; 897*e439f0f8SKowalski, Kamil OnDemandEthernetProvider::changeVlanId( 898*e439f0f8SKowalski, Kamil ifaceId, static_cast<uint32_t>(inputVlanId), 899*e439f0f8SKowalski, Kamil [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( 900*e439f0f8SKowalski, Kamil const boost::system::error_code ec) { 901588c3f0dSKowalski, Kamil if (ec) { 902588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 903*e439f0f8SKowalski, Kamil messages::internalError(), pathPrefix); 904588c3f0dSKowalski, Kamil } else { 905*e439f0f8SKowalski, Kamil paramsJson["VLANEnable"] = true; 906*e439f0f8SKowalski, Kamil } 907*e439f0f8SKowalski, Kamil }); 908*e439f0f8SKowalski, Kamil } else if (inputVlanEnabled == false) { 909*e439f0f8SKowalski, Kamil // Disable VLAN 910*e439f0f8SKowalski, Kamil OnDemandEthernetProvider::disableVlan( 911*e439f0f8SKowalski, Kamil ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ]( 912*e439f0f8SKowalski, Kamil const boost::system::error_code ec) { 913*e439f0f8SKowalski, Kamil if (ec) { 914*e439f0f8SKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 915*e439f0f8SKowalski, Kamil messages::internalError(), pathPrefix); 916*e439f0f8SKowalski, Kamil } else { 917*e439f0f8SKowalski, Kamil paramsJson["VLANEnable"] = false; 918588c3f0dSKowalski, Kamil } 919588c3f0dSKowalski, Kamil }); 920588c3f0dSKowalski, Kamil } 921588c3f0dSKowalski, Kamil } 922588c3f0dSKowalski, Kamil 923*e439f0f8SKowalski, Kamil private: 924588c3f0dSKowalski, Kamil void handleHostnamePatch(const nlohmann::json &input, 925588c3f0dSKowalski, Kamil const EthernetInterfaceData ð_data, 926588c3f0dSKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 927588c3f0dSKowalski, Kamil if (input.is_string()) { 928588c3f0dSKowalski, Kamil std::string newHostname = input.get<std::string>(); 929588c3f0dSKowalski, Kamil 930588c3f0dSKowalski, Kamil if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) { 931588c3f0dSKowalski, Kamil // Change hostname 932588c3f0dSKowalski, Kamil ethernet_provider.setHostName( 933588c3f0dSKowalski, Kamil newHostname, 934588c3f0dSKowalski, Kamil [asyncResp, newHostname](const boost::system::error_code ec) { 935588c3f0dSKowalski, Kamil if (ec) { 936588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 937588c3f0dSKowalski, Kamil messages::internalError(), 938588c3f0dSKowalski, Kamil "/HostName"); 939588c3f0dSKowalski, Kamil } else { 940588c3f0dSKowalski, Kamil asyncResp->res.json_value["HostName"] = newHostname; 941588c3f0dSKowalski, Kamil } 942588c3f0dSKowalski, Kamil }); 943588c3f0dSKowalski, Kamil } 944588c3f0dSKowalski, Kamil } else { 945588c3f0dSKowalski, Kamil messages::addMessageToJson( 946588c3f0dSKowalski, Kamil asyncResp->res.json_value, 947588c3f0dSKowalski, Kamil messages::propertyValueTypeError(input.dump(), "HostName"), 948588c3f0dSKowalski, Kamil "/HostName"); 949588c3f0dSKowalski, Kamil } 950588c3f0dSKowalski, Kamil } 951588c3f0dSKowalski, Kamil 952179db1d7SKowalski, Kamil void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input, 953179db1d7SKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data, 954179db1d7SKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 955179db1d7SKowalski, Kamil if (!input.is_array()) { 956179db1d7SKowalski, Kamil messages::addMessageToJson( 957179db1d7SKowalski, Kamil asyncResp->res.json_value, 958179db1d7SKowalski, Kamil messages::propertyValueTypeError(input.dump(), "IPv4Addresses"), 959179db1d7SKowalski, Kamil "/IPv4Addresses"); 960179db1d7SKowalski, Kamil return; 961179db1d7SKowalski, Kamil } 962179db1d7SKowalski, Kamil 963179db1d7SKowalski, Kamil // According to Redfish PATCH definition, size must be at least equal 964179db1d7SKowalski, Kamil if (input.size() < ipv4_data.size()) { 965179db1d7SKowalski, Kamil // TODO(kkowalsk) This should be a message indicating that not enough 966179db1d7SKowalski, Kamil // data has been provided 967179db1d7SKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 968179db1d7SKowalski, Kamil messages::internalError(), "/IPv4Addresses"); 969179db1d7SKowalski, Kamil return; 970179db1d7SKowalski, Kamil } 971179db1d7SKowalski, Kamil 972179db1d7SKowalski, Kamil json_util::Result addressFieldState; 973179db1d7SKowalski, Kamil json_util::Result subnetMaskFieldState; 974179db1d7SKowalski, Kamil json_util::Result addressOriginFieldState; 975179db1d7SKowalski, Kamil json_util::Result gatewayFieldState; 976179db1d7SKowalski, Kamil const std::string *addressFieldValue; 977179db1d7SKowalski, Kamil const std::string *subnetMaskFieldValue; 978179db1d7SKowalski, Kamil const std::string *addressOriginFieldValue = nullptr; 979179db1d7SKowalski, Kamil const std::string *gatewayFieldValue; 980179db1d7SKowalski, Kamil uint8_t subnetMaskAsPrefixLength; 981179db1d7SKowalski, Kamil std::string addressOriginInDBusFormat; 982179db1d7SKowalski, Kamil 983179db1d7SKowalski, Kamil bool errorDetected = false; 984179db1d7SKowalski, Kamil for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) { 985179db1d7SKowalski, Kamil // Check that entry is not of some unexpected type 986179db1d7SKowalski, Kamil if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) { 987179db1d7SKowalski, Kamil // Invalid object type 988179db1d7SKowalski, Kamil messages::addMessageToJson( 989179db1d7SKowalski, Kamil asyncResp->res.json_value, 990179db1d7SKowalski, Kamil messages::propertyValueTypeError(input[entryIdx].dump(), 991179db1d7SKowalski, Kamil "IPv4Address"), 992179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx)); 993179db1d7SKowalski, Kamil 994179db1d7SKowalski, Kamil continue; 995179db1d7SKowalski, Kamil } 996179db1d7SKowalski, Kamil 997179db1d7SKowalski, Kamil // Try to load fields 998179db1d7SKowalski, Kamil addressFieldState = json_util::getString( 999179db1d7SKowalski, Kamil "Address", input[entryIdx], addressFieldValue, 1000179db1d7SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1001179db1d7SKowalski, Kamil asyncResp->res.json_value, 1002179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1003179db1d7SKowalski, Kamil subnetMaskFieldState = json_util::getString( 1004179db1d7SKowalski, Kamil "SubnetMask", input[entryIdx], subnetMaskFieldValue, 1005179db1d7SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1006179db1d7SKowalski, Kamil asyncResp->res.json_value, 1007179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1008179db1d7SKowalski, Kamil addressOriginFieldState = json_util::getString( 1009179db1d7SKowalski, Kamil "AddressOrigin", input[entryIdx], addressOriginFieldValue, 1010179db1d7SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1011179db1d7SKowalski, Kamil asyncResp->res.json_value, 1012179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1013179db1d7SKowalski, Kamil gatewayFieldState = json_util::getString( 1014179db1d7SKowalski, Kamil "Gateway", input[entryIdx], gatewayFieldValue, 1015179db1d7SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1016179db1d7SKowalski, Kamil asyncResp->res.json_value, 1017179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1018179db1d7SKowalski, Kamil 1019179db1d7SKowalski, Kamil if (addressFieldState == json_util::Result::WRONG_TYPE || 1020179db1d7SKowalski, Kamil subnetMaskFieldState == json_util::Result::WRONG_TYPE || 1021179db1d7SKowalski, Kamil addressOriginFieldState == json_util::Result::WRONG_TYPE || 1022179db1d7SKowalski, Kamil gatewayFieldState == json_util::Result::WRONG_TYPE) { 1023179db1d7SKowalski, Kamil return; 1024179db1d7SKowalski, Kamil } 1025179db1d7SKowalski, Kamil 1026179db1d7SKowalski, Kamil if (addressFieldState == json_util::Result::SUCCESS && 1027179db1d7SKowalski, Kamil !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) { 1028179db1d7SKowalski, Kamil errorDetected = true; 1029179db1d7SKowalski, Kamil messages::addMessageToJson( 1030179db1d7SKowalski, Kamil asyncResp->res.json_value, 1031179db1d7SKowalski, Kamil messages::propertyValueFormatError(*addressFieldValue, "Address"), 1032179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1033179db1d7SKowalski, Kamil } 1034179db1d7SKowalski, Kamil 1035179db1d7SKowalski, Kamil if (subnetMaskFieldState == json_util::Result::SUCCESS && 1036179db1d7SKowalski, Kamil !ethernet_provider.ipv4VerifyIpAndGetBitcount( 1037179db1d7SKowalski, Kamil *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) { 1038179db1d7SKowalski, Kamil errorDetected = true; 1039179db1d7SKowalski, Kamil messages::addMessageToJson( 1040179db1d7SKowalski, Kamil asyncResp->res.json_value, 1041179db1d7SKowalski, Kamil messages::propertyValueFormatError(*subnetMaskFieldValue, 1042179db1d7SKowalski, Kamil "SubnetMask"), 1043179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1044179db1d7SKowalski, Kamil } 1045179db1d7SKowalski, Kamil 1046179db1d7SKowalski, Kamil // Get Address origin in proper format 1047179db1d7SKowalski, Kamil addressOriginInDBusFormat = 1048179db1d7SKowalski, Kamil ethernet_provider.translateAddressOriginBetweenDBusAndRedfish( 1049179db1d7SKowalski, Kamil addressOriginFieldValue, true, false); 1050179db1d7SKowalski, Kamil 1051179db1d7SKowalski, Kamil if (addressOriginFieldState == json_util::Result::SUCCESS && 1052179db1d7SKowalski, Kamil addressOriginInDBusFormat.empty()) { 1053179db1d7SKowalski, Kamil errorDetected = true; 1054179db1d7SKowalski, Kamil messages::addMessageToJson( 1055179db1d7SKowalski, Kamil asyncResp->res.json_value, 1056179db1d7SKowalski, Kamil messages::propertyValueNotInList(*addressOriginFieldValue, 1057179db1d7SKowalski, Kamil "AddressOrigin"), 1058179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1059179db1d7SKowalski, Kamil } 1060179db1d7SKowalski, Kamil 1061179db1d7SKowalski, Kamil if (gatewayFieldState == json_util::Result::SUCCESS && 1062179db1d7SKowalski, Kamil !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) { 1063179db1d7SKowalski, Kamil errorDetected = true; 1064179db1d7SKowalski, Kamil messages::addMessageToJson( 1065179db1d7SKowalski, Kamil asyncResp->res.json_value, 1066179db1d7SKowalski, Kamil messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"), 1067179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1068179db1d7SKowalski, Kamil } 1069179db1d7SKowalski, Kamil 1070179db1d7SKowalski, Kamil // If any error occured do not proceed with current entry, but do not 1071179db1d7SKowalski, Kamil // end loop 1072179db1d7SKowalski, Kamil if (errorDetected) { 1073179db1d7SKowalski, Kamil errorDetected = false; 1074179db1d7SKowalski, Kamil continue; 1075179db1d7SKowalski, Kamil } 1076179db1d7SKowalski, Kamil 1077179db1d7SKowalski, Kamil if (entryIdx >= ipv4_data.size()) { 1078179db1d7SKowalski, Kamil asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx]; 1079179db1d7SKowalski, Kamil 1080179db1d7SKowalski, Kamil // Verify that all field were provided 1081179db1d7SKowalski, Kamil if (addressFieldState == json_util::Result::NOT_EXIST) { 1082179db1d7SKowalski, Kamil errorDetected = true; 1083179db1d7SKowalski, Kamil messages::addMessageToJson( 1084179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::propertyMissing("Address"), 1085179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address"); 1086179db1d7SKowalski, Kamil } 1087179db1d7SKowalski, Kamil 1088179db1d7SKowalski, Kamil if (subnetMaskFieldState == json_util::Result::NOT_EXIST) { 1089179db1d7SKowalski, Kamil errorDetected = true; 1090179db1d7SKowalski, Kamil messages::addMessageToJson( 1091179db1d7SKowalski, Kamil asyncResp->res.json_value, 1092179db1d7SKowalski, Kamil messages::propertyMissing("SubnetMask"), 1093179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask"); 1094179db1d7SKowalski, Kamil } 1095179db1d7SKowalski, Kamil 1096179db1d7SKowalski, Kamil if (addressOriginFieldState == json_util::Result::NOT_EXIST) { 1097179db1d7SKowalski, Kamil errorDetected = true; 1098179db1d7SKowalski, Kamil messages::addMessageToJson( 1099179db1d7SKowalski, Kamil asyncResp->res.json_value, 1100179db1d7SKowalski, Kamil messages::propertyMissing("AddressOrigin"), 1101179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin"); 1102179db1d7SKowalski, Kamil } 1103179db1d7SKowalski, Kamil 1104179db1d7SKowalski, Kamil if (gatewayFieldState == json_util::Result::NOT_EXIST) { 1105179db1d7SKowalski, Kamil errorDetected = true; 1106179db1d7SKowalski, Kamil messages::addMessageToJson( 1107179db1d7SKowalski, Kamil asyncResp->res.json_value, messages::propertyMissing("Gateway"), 1108179db1d7SKowalski, Kamil "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway"); 1109179db1d7SKowalski, Kamil } 1110179db1d7SKowalski, Kamil 1111179db1d7SKowalski, Kamil // If any error occured do not proceed with current entry, but do not 1112179db1d7SKowalski, Kamil // end loop 1113179db1d7SKowalski, Kamil if (errorDetected) { 1114179db1d7SKowalski, Kamil errorDetected = false; 1115179db1d7SKowalski, Kamil continue; 1116179db1d7SKowalski, Kamil } 1117179db1d7SKowalski, Kamil 1118179db1d7SKowalski, Kamil // Create IPv4 with provided data 1119179db1d7SKowalski, Kamil ethernet_provider.createIPv4( 1120179db1d7SKowalski, Kamil ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue, 1121179db1d7SKowalski, Kamil *addressFieldValue, asyncResp); 1122179db1d7SKowalski, Kamil } else { 1123179db1d7SKowalski, Kamil // Existing object that should be modified/deleted/remain unchanged 1124179db1d7SKowalski, Kamil if (input[entryIdx].is_null()) { 1125179db1d7SKowalski, Kamil // Object should be deleted 1126179db1d7SKowalski, Kamil ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, 1127179db1d7SKowalski, Kamil entryIdx, asyncResp); 1128179db1d7SKowalski, Kamil } else if (input[entryIdx].is_object()) { 1129179db1d7SKowalski, Kamil if (input[entryIdx].size() == 0) { 1130179db1d7SKowalski, Kamil // Object shall remain unchanged 1131179db1d7SKowalski, Kamil continue; 1132179db1d7SKowalski, Kamil } 1133179db1d7SKowalski, Kamil 1134179db1d7SKowalski, Kamil // Apply changes 1135179db1d7SKowalski, Kamil if (addressFieldState == json_util::Result::SUCCESS && 1136179db1d7SKowalski, Kamil ipv4_data[entryIdx].address != nullptr && 1137179db1d7SKowalski, Kamil *ipv4_data[entryIdx].address != *addressFieldValue) { 1138179db1d7SKowalski, Kamil ethernet_provider.changeIPv4AddressProperty( 1139179db1d7SKowalski, Kamil ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address", 1140179db1d7SKowalski, Kamil *addressFieldValue, asyncResp); 1141179db1d7SKowalski, Kamil } 1142179db1d7SKowalski, Kamil 1143179db1d7SKowalski, Kamil if (subnetMaskFieldState == json_util::Result::SUCCESS && 1144179db1d7SKowalski, Kamil ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) { 1145179db1d7SKowalski, Kamil ethernet_provider.changeIPv4SubnetMaskProperty( 1146179db1d7SKowalski, Kamil ifaceId, entryIdx, ipv4_data[entryIdx].id, 1147179db1d7SKowalski, Kamil *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp); 1148179db1d7SKowalski, Kamil } 1149179db1d7SKowalski, Kamil 1150179db1d7SKowalski, Kamil if (addressOriginFieldState == json_util::Result::SUCCESS && 1151179db1d7SKowalski, Kamil ipv4_data[entryIdx].origin != *addressFieldValue) { 1152179db1d7SKowalski, Kamil ethernet_provider.changeIPv4Origin( 1153179db1d7SKowalski, Kamil ifaceId, entryIdx, ipv4_data[entryIdx].id, 1154179db1d7SKowalski, Kamil *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp); 1155179db1d7SKowalski, Kamil } 1156179db1d7SKowalski, Kamil 1157179db1d7SKowalski, Kamil if (gatewayFieldState == json_util::Result::SUCCESS && 1158179db1d7SKowalski, Kamil ipv4_data[entryIdx].gateway != nullptr && 1159179db1d7SKowalski, Kamil *ipv4_data[entryIdx].gateway != *gatewayFieldValue) { 1160179db1d7SKowalski, Kamil ethernet_provider.changeIPv4AddressProperty( 1161179db1d7SKowalski, Kamil ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway", 1162179db1d7SKowalski, Kamil *gatewayFieldValue, asyncResp); 1163179db1d7SKowalski, Kamil } 1164179db1d7SKowalski, Kamil } 1165179db1d7SKowalski, Kamil } 1166179db1d7SKowalski, Kamil } 1167179db1d7SKowalski, Kamil } 1168179db1d7SKowalski, Kamil 1169588c3f0dSKowalski, Kamil nlohmann::json parseInterfaceData( 1170588c3f0dSKowalski, Kamil const std::string &iface_id, const EthernetInterfaceData ð_data, 1171588c3f0dSKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1172588c3f0dSKowalski, Kamil // Copy JSON object to avoid race condition 1173588c3f0dSKowalski, Kamil nlohmann::json json_response(Node::json); 1174588c3f0dSKowalski, Kamil 1175588c3f0dSKowalski, Kamil // Fill out obvious data... 1176588c3f0dSKowalski, Kamil json_response["Id"] = iface_id; 1177588c3f0dSKowalski, Kamil json_response["@odata.id"] = 1178588c3f0dSKowalski, Kamil "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id; 1179588c3f0dSKowalski, Kamil 1180588c3f0dSKowalski, Kamil // ... then the one from DBus, regarding eth iface... 1181588c3f0dSKowalski, Kamil if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed; 1182588c3f0dSKowalski, Kamil 1183588c3f0dSKowalski, Kamil if (eth_data.mac_address != nullptr) 1184588c3f0dSKowalski, Kamil json_response["MACAddress"] = *eth_data.mac_address; 1185588c3f0dSKowalski, Kamil 1186588c3f0dSKowalski, Kamil if (eth_data.hostname != nullptr) 1187588c3f0dSKowalski, Kamil json_response["HostName"] = *eth_data.hostname; 1188588c3f0dSKowalski, Kamil 1189588c3f0dSKowalski, Kamil if (eth_data.vlan_id != nullptr) { 1190588c3f0dSKowalski, Kamil nlohmann::json &vlanObj = json_response["VLAN"]; 1191588c3f0dSKowalski, Kamil vlanObj["VLANEnable"] = true; 1192588c3f0dSKowalski, Kamil vlanObj["VLANId"] = *eth_data.vlan_id; 1193588c3f0dSKowalski, Kamil } 1194588c3f0dSKowalski, Kamil 1195588c3f0dSKowalski, Kamil // ... at last, check if there are IPv4 data and prepare appropriate 1196588c3f0dSKowalski, Kamil // collection 1197588c3f0dSKowalski, Kamil if (ipv4_data.size() > 0) { 1198588c3f0dSKowalski, Kamil nlohmann::json ipv4_array = nlohmann::json::array(); 1199588c3f0dSKowalski, Kamil for (auto &ipv4_config : ipv4_data) { 1200588c3f0dSKowalski, Kamil nlohmann::json json_ipv4; 1201588c3f0dSKowalski, Kamil if (ipv4_config.address != nullptr) { 1202588c3f0dSKowalski, Kamil json_ipv4["Address"] = *ipv4_config.address; 1203588c3f0dSKowalski, Kamil if (ipv4_config.gateway != nullptr) 1204588c3f0dSKowalski, Kamil json_ipv4["Gateway"] = *ipv4_config.gateway; 1205588c3f0dSKowalski, Kamil 1206588c3f0dSKowalski, Kamil json_ipv4["AddressOrigin"] = ipv4_config.origin; 1207588c3f0dSKowalski, Kamil json_ipv4["SubnetMask"] = ipv4_config.netmask; 1208588c3f0dSKowalski, Kamil 1209588c3f0dSKowalski, Kamil ipv4_array.push_back(std::move(json_ipv4)); 1210588c3f0dSKowalski, Kamil } 1211588c3f0dSKowalski, Kamil } 1212588c3f0dSKowalski, Kamil json_response["IPv4Addresses"] = std::move(ipv4_array); 1213588c3f0dSKowalski, Kamil } 1214588c3f0dSKowalski, Kamil 1215588c3f0dSKowalski, Kamil return json_response; 1216588c3f0dSKowalski, Kamil } 1217588c3f0dSKowalski, Kamil 12189391bb9cSRapkiewicz, Pawel /** 12199391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 12209391bb9cSRapkiewicz, Pawel */ 12219391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 12229391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 12239391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call (two params) to get 12249391bb9cSRapkiewicz, Pawel // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 12259391bb9cSRapkiewicz, Pawel // Check if there is required param, truly entering this shall be 12269391bb9cSRapkiewicz, Pawel // impossible. 12279391bb9cSRapkiewicz, Pawel if (params.size() != 1) { 1228e0d918bcSEd Tanous res.result(boost::beast::http::status::internal_server_error); 12299391bb9cSRapkiewicz, Pawel res.end(); 12309391bb9cSRapkiewicz, Pawel return; 12319391bb9cSRapkiewicz, Pawel } 12329391bb9cSRapkiewicz, Pawel 12339391bb9cSRapkiewicz, Pawel const std::string &iface_id = params[0]; 12349391bb9cSRapkiewicz, Pawel 12359391bb9cSRapkiewicz, Pawel // Get single eth interface data, and call the below callback for JSON 12369391bb9cSRapkiewicz, Pawel // preparation 12379391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceData( 12389391bb9cSRapkiewicz, Pawel iface_id, [&, iface_id](const bool &success, 12399391bb9cSRapkiewicz, Pawel const EthernetInterfaceData ð_data, 12409391bb9cSRapkiewicz, Pawel const std::vector<IPv4AddressData> &ipv4_data) { 12419391bb9cSRapkiewicz, Pawel if (success) { 1242588c3f0dSKowalski, Kamil res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 12439391bb9cSRapkiewicz, Pawel } else { 12449391bb9cSRapkiewicz, Pawel // ... otherwise return error 12459391bb9cSRapkiewicz, Pawel // TODO(Pawel)consider distinguish between non existing object, and 12469391bb9cSRapkiewicz, Pawel // other errors 1247*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson( 1248*e439f0f8SKowalski, Kamil res.json_value, 1249*e439f0f8SKowalski, Kamil messages::resourceNotFound("EthernetInterface", iface_id)); 1250e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 12519391bb9cSRapkiewicz, Pawel } 12529391bb9cSRapkiewicz, Pawel res.end(); 12539391bb9cSRapkiewicz, Pawel }); 12549391bb9cSRapkiewicz, Pawel } 12559391bb9cSRapkiewicz, Pawel 1256588c3f0dSKowalski, Kamil void doPatch(crow::response &res, const crow::request &req, 1257588c3f0dSKowalski, Kamil const std::vector<std::string> ¶ms) override { 1258588c3f0dSKowalski, Kamil // TODO(Pawel) this shall be parametrized call (two params) to get 1259588c3f0dSKowalski, Kamil // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1260588c3f0dSKowalski, Kamil // Check if there is required param, truly entering this shall be 1261588c3f0dSKowalski, Kamil // impossible. 1262588c3f0dSKowalski, Kamil if (params.size() != 1) { 1263588c3f0dSKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1264588c3f0dSKowalski, Kamil res.end(); 1265588c3f0dSKowalski, Kamil return; 1266588c3f0dSKowalski, Kamil } 1267588c3f0dSKowalski, Kamil 1268588c3f0dSKowalski, Kamil const std::string &iface_id = params[0]; 1269588c3f0dSKowalski, Kamil 1270179db1d7SKowalski, Kamil nlohmann::json patchReq; 1271588c3f0dSKowalski, Kamil 1272179db1d7SKowalski, Kamil if (!json_util::processJsonFromRequest(res, req, patchReq)) { 1273588c3f0dSKowalski, Kamil return; 1274588c3f0dSKowalski, Kamil } 1275588c3f0dSKowalski, Kamil 1276588c3f0dSKowalski, Kamil // Get single eth interface data, and call the below callback for JSON 1277588c3f0dSKowalski, Kamil // preparation 1278588c3f0dSKowalski, Kamil ethernet_provider.getEthernetIfaceData( 1279588c3f0dSKowalski, Kamil iface_id, 1280588c3f0dSKowalski, Kamil [&, iface_id, patchReq = std::move(patchReq) ]( 1281588c3f0dSKowalski, Kamil const bool &success, const EthernetInterfaceData ð_data, 1282588c3f0dSKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1283588c3f0dSKowalski, Kamil if (!success) { 1284588c3f0dSKowalski, Kamil // ... otherwise return error 1285588c3f0dSKowalski, Kamil // TODO(Pawel)consider distinguish between non existing object, and 1286588c3f0dSKowalski, Kamil // other errors 1287588c3f0dSKowalski, Kamil res.result(boost::beast::http::status::not_found); 1288588c3f0dSKowalski, Kamil res.end(); 1289588c3f0dSKowalski, Kamil 1290588c3f0dSKowalski, Kamil return; 1291588c3f0dSKowalski, Kamil } 1292588c3f0dSKowalski, Kamil 1293588c3f0dSKowalski, Kamil res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 1294588c3f0dSKowalski, Kamil 1295588c3f0dSKowalski, Kamil std::shared_ptr<AsyncResp> asyncResp = 1296588c3f0dSKowalski, Kamil std::make_shared<AsyncResp>(res); 1297588c3f0dSKowalski, Kamil 1298588c3f0dSKowalski, Kamil for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); 1299588c3f0dSKowalski, Kamil ++propertyIt) { 1300588c3f0dSKowalski, Kamil if (propertyIt.key() == "VLAN") { 1301*e439f0f8SKowalski, Kamil handleVlanPatch(iface_id, propertyIt.value(), eth_data, "/VLAN", 1302588c3f0dSKowalski, Kamil asyncResp); 1303588c3f0dSKowalski, Kamil } else if (propertyIt.key() == "HostName") { 1304588c3f0dSKowalski, Kamil handleHostnamePatch(propertyIt.value(), eth_data, asyncResp); 1305179db1d7SKowalski, Kamil } else if (propertyIt.key() == "IPv4Addresses") { 1306179db1d7SKowalski, Kamil handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data, 1307179db1d7SKowalski, Kamil asyncResp); 1308179db1d7SKowalski, Kamil } else if (propertyIt.key() == "IPv6Addresses") { 1309179db1d7SKowalski, Kamil // TODO(kkowalsk) IPv6 Not supported on D-Bus yet 1310179db1d7SKowalski, Kamil messages::addMessageToJsonRoot( 1311179db1d7SKowalski, Kamil res.json_value, 1312179db1d7SKowalski, Kamil messages::propertyNotWritable(propertyIt.key())); 1313588c3f0dSKowalski, Kamil } else { 1314588c3f0dSKowalski, Kamil auto fieldInJsonIt = res.json_value.find(propertyIt.key()); 1315588c3f0dSKowalski, Kamil 1316588c3f0dSKowalski, Kamil if (fieldInJsonIt == res.json_value.end()) { 1317588c3f0dSKowalski, Kamil // Field not in scope of defined fields 1318588c3f0dSKowalski, Kamil messages::addMessageToJsonRoot( 1319588c3f0dSKowalski, Kamil res.json_value, 1320588c3f0dSKowalski, Kamil messages::propertyUnknown(propertyIt.key())); 1321588c3f0dSKowalski, Kamil } else if (*fieldInJsonIt != *propertyIt) { 1322588c3f0dSKowalski, Kamil // User attempted to modify non-writable field 1323588c3f0dSKowalski, Kamil messages::addMessageToJsonRoot( 1324588c3f0dSKowalski, Kamil res.json_value, 1325588c3f0dSKowalski, Kamil messages::propertyNotWritable(propertyIt.key())); 1326588c3f0dSKowalski, Kamil } 1327588c3f0dSKowalski, Kamil } 1328588c3f0dSKowalski, Kamil } 1329588c3f0dSKowalski, Kamil }); 1330588c3f0dSKowalski, Kamil } 1331588c3f0dSKowalski, Kamil 13329391bb9cSRapkiewicz, Pawel // Ethernet Provider object 13339391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 13349391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 13359391bb9cSRapkiewicz, Pawel }; 13369391bb9cSRapkiewicz, Pawel 1337*e439f0f8SKowalski, Kamil class VlanNetworkInterfaceCollection; 1338*e439f0f8SKowalski, Kamil 1339*e439f0f8SKowalski, Kamil /** 1340*e439f0f8SKowalski, Kamil * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema 1341*e439f0f8SKowalski, Kamil */ 1342*e439f0f8SKowalski, Kamil class VlanNetworkInterface : public Node { 1343*e439f0f8SKowalski, Kamil public: 1344*e439f0f8SKowalski, Kamil /* 1345*e439f0f8SKowalski, Kamil * Default Constructor 1346*e439f0f8SKowalski, Kamil */ 1347*e439f0f8SKowalski, Kamil template <typename CrowApp> 1348*e439f0f8SKowalski, Kamil // TODO(Pawel) Remove line from below, where we assume that there is only one 1349*e439f0f8SKowalski, Kamil // manager called openbmc This shall be generic, but requires to update 1350*e439f0f8SKowalski, Kamil // GetSubroutes method 1351*e439f0f8SKowalski, Kamil VlanNetworkInterface(CrowApp &app) 1352*e439f0f8SKowalski, Kamil : Node(app, 1353*e439f0f8SKowalski, Kamil "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/" 1354*e439f0f8SKowalski, Kamil "<str>", 1355*e439f0f8SKowalski, Kamil std::string(), std::string()) { 1356*e439f0f8SKowalski, Kamil Node::json["@odata.type"] = 1357*e439f0f8SKowalski, Kamil "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface"; 1358*e439f0f8SKowalski, Kamil Node::json["@odata.context"] = 1359*e439f0f8SKowalski, Kamil "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface"; 1360*e439f0f8SKowalski, Kamil Node::json["Name"] = "VLAN Network Interface"; 1361*e439f0f8SKowalski, Kamil 1362*e439f0f8SKowalski, Kamil entityPrivileges = { 1363*e439f0f8SKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 1364*e439f0f8SKowalski, Kamil {boost::beast::http::verb::head, {{"Login"}}}, 1365*e439f0f8SKowalski, Kamil {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1366*e439f0f8SKowalski, Kamil {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1367*e439f0f8SKowalski, Kamil {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1368*e439f0f8SKowalski, Kamil {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1369*e439f0f8SKowalski, Kamil } 1370*e439f0f8SKowalski, Kamil 1371*e439f0f8SKowalski, Kamil private: 1372*e439f0f8SKowalski, Kamil nlohmann::json parseInterfaceData( 1373*e439f0f8SKowalski, Kamil const std::string &parent_iface_id, const std::string &iface_id, 1374*e439f0f8SKowalski, Kamil const EthernetInterfaceData ð_data, 1375*e439f0f8SKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1376*e439f0f8SKowalski, Kamil // Copy JSON object to avoid race condition 1377*e439f0f8SKowalski, Kamil nlohmann::json json_response(Node::json); 1378*e439f0f8SKowalski, Kamil 1379*e439f0f8SKowalski, Kamil // Fill out obvious data... 1380*e439f0f8SKowalski, Kamil json_response["Id"] = iface_id; 1381*e439f0f8SKowalski, Kamil json_response["@odata.id"] = 1382*e439f0f8SKowalski, Kamil "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_iface_id + 1383*e439f0f8SKowalski, Kamil "/VLANs/" + iface_id; 1384*e439f0f8SKowalski, Kamil 1385*e439f0f8SKowalski, Kamil json_response["VLANEnable"] = true; 1386*e439f0f8SKowalski, Kamil json_response["VLANId"] = *eth_data.vlan_id; 1387*e439f0f8SKowalski, Kamil 1388*e439f0f8SKowalski, Kamil return json_response; 1389*e439f0f8SKowalski, Kamil } 1390*e439f0f8SKowalski, Kamil 1391*e439f0f8SKowalski, Kamil bool verifyNames(crow::response &res, const std::string &parent, 1392*e439f0f8SKowalski, Kamil const std::string &iface) { 1393*e439f0f8SKowalski, Kamil if (!boost::starts_with(iface, parent + "_")) { 1394*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson( 1395*e439f0f8SKowalski, Kamil res.json_value, 1396*e439f0f8SKowalski, Kamil messages::resourceNotFound("VLAN Network Interface", iface)); 1397*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::bad_request); 1398*e439f0f8SKowalski, Kamil res.end(); 1399*e439f0f8SKowalski, Kamil 1400*e439f0f8SKowalski, Kamil return false; 1401*e439f0f8SKowalski, Kamil } else { 1402*e439f0f8SKowalski, Kamil return true; 1403*e439f0f8SKowalski, Kamil } 1404*e439f0f8SKowalski, Kamil } 1405*e439f0f8SKowalski, Kamil 1406*e439f0f8SKowalski, Kamil /** 1407*e439f0f8SKowalski, Kamil * Functions triggers appropriate requests on DBus 1408*e439f0f8SKowalski, Kamil */ 1409*e439f0f8SKowalski, Kamil void doGet(crow::response &res, const crow::request &req, 1410*e439f0f8SKowalski, Kamil const std::vector<std::string> ¶ms) override { 1411*e439f0f8SKowalski, Kamil // TODO(Pawel) this shall be parametrized call (two params) to get 1412*e439f0f8SKowalski, Kamil // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 1413*e439f0f8SKowalski, Kamil // Check if there is required param, truly entering this shall be 1414*e439f0f8SKowalski, Kamil // impossible. 1415*e439f0f8SKowalski, Kamil if (params.size() != 2) { 1416*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1417*e439f0f8SKowalski, Kamil res.end(); 1418*e439f0f8SKowalski, Kamil return; 1419*e439f0f8SKowalski, Kamil } 1420*e439f0f8SKowalski, Kamil 1421*e439f0f8SKowalski, Kamil const std::string &parent_iface_id = params[0]; 1422*e439f0f8SKowalski, Kamil const std::string &iface_id = params[1]; 1423*e439f0f8SKowalski, Kamil 1424*e439f0f8SKowalski, Kamil if (!verifyNames(res, parent_iface_id, iface_id)) { 1425*e439f0f8SKowalski, Kamil return; 1426*e439f0f8SKowalski, Kamil } 1427*e439f0f8SKowalski, Kamil 1428*e439f0f8SKowalski, Kamil // Get single eth interface data, and call the below callback for JSON 1429*e439f0f8SKowalski, Kamil // preparation 1430*e439f0f8SKowalski, Kamil ethernet_provider.getEthernetIfaceData( 1431*e439f0f8SKowalski, Kamil iface_id, 1432*e439f0f8SKowalski, Kamil [&, parent_iface_id, iface_id]( 1433*e439f0f8SKowalski, Kamil const bool &success, const EthernetInterfaceData ð_data, 1434*e439f0f8SKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1435*e439f0f8SKowalski, Kamil if (success && eth_data.vlan_id != nullptr) { 1436*e439f0f8SKowalski, Kamil res.json_value = parseInterfaceData(parent_iface_id, iface_id, 1437*e439f0f8SKowalski, Kamil eth_data, ipv4_data); 1438*e439f0f8SKowalski, Kamil } else { 1439*e439f0f8SKowalski, Kamil // ... otherwise return error 1440*e439f0f8SKowalski, Kamil // TODO(Pawel)consider distinguish between non existing object, 1441*e439f0f8SKowalski, Kamil // and 1442*e439f0f8SKowalski, Kamil // other errors 1443*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::not_found); 1444*e439f0f8SKowalski, Kamil } 1445*e439f0f8SKowalski, Kamil res.end(); 1446*e439f0f8SKowalski, Kamil }); 1447*e439f0f8SKowalski, Kamil } 1448*e439f0f8SKowalski, Kamil 1449*e439f0f8SKowalski, Kamil void doPatch(crow::response &res, const crow::request &req, 1450*e439f0f8SKowalski, Kamil const std::vector<std::string> ¶ms) override { 1451*e439f0f8SKowalski, Kamil if (params.size() != 2) { 1452*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1453*e439f0f8SKowalski, Kamil res.end(); 1454*e439f0f8SKowalski, Kamil return; 1455*e439f0f8SKowalski, Kamil } 1456*e439f0f8SKowalski, Kamil 1457*e439f0f8SKowalski, Kamil const std::string &parent_iface_id = params[0]; 1458*e439f0f8SKowalski, Kamil const std::string &iface_id = params[1]; 1459*e439f0f8SKowalski, Kamil 1460*e439f0f8SKowalski, Kamil if (!verifyNames(res, parent_iface_id, iface_id)) { 1461*e439f0f8SKowalski, Kamil return; 1462*e439f0f8SKowalski, Kamil } 1463*e439f0f8SKowalski, Kamil 1464*e439f0f8SKowalski, Kamil nlohmann::json patchReq; 1465*e439f0f8SKowalski, Kamil 1466*e439f0f8SKowalski, Kamil if (!json_util::processJsonFromRequest(res, req, patchReq)) { 1467*e439f0f8SKowalski, Kamil return; 1468*e439f0f8SKowalski, Kamil } 1469*e439f0f8SKowalski, Kamil 1470*e439f0f8SKowalski, Kamil // Get single eth interface data, and call the below callback for JSON 1471*e439f0f8SKowalski, Kamil // preparation 1472*e439f0f8SKowalski, Kamil ethernet_provider.getEthernetIfaceData( 1473*e439f0f8SKowalski, Kamil iface_id, 1474*e439f0f8SKowalski, Kamil [&, parent_iface_id, iface_id, patchReq = std::move(patchReq) ]( 1475*e439f0f8SKowalski, Kamil const bool &success, const EthernetInterfaceData ð_data, 1476*e439f0f8SKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1477*e439f0f8SKowalski, Kamil if (!success) { 1478*e439f0f8SKowalski, Kamil // ... otherwise return error 1479*e439f0f8SKowalski, Kamil // TODO(Pawel)consider distinguish between non existing object, 1480*e439f0f8SKowalski, Kamil // and 1481*e439f0f8SKowalski, Kamil // other errors 1482*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::not_found); 1483*e439f0f8SKowalski, Kamil res.end(); 1484*e439f0f8SKowalski, Kamil 1485*e439f0f8SKowalski, Kamil return; 1486*e439f0f8SKowalski, Kamil } 1487*e439f0f8SKowalski, Kamil 1488*e439f0f8SKowalski, Kamil res.json_value = parseInterfaceData(parent_iface_id, iface_id, 1489*e439f0f8SKowalski, Kamil eth_data, ipv4_data); 1490*e439f0f8SKowalski, Kamil 1491*e439f0f8SKowalski, Kamil std::shared_ptr<AsyncResp> asyncResp = 1492*e439f0f8SKowalski, Kamil std::make_shared<AsyncResp>(res); 1493*e439f0f8SKowalski, Kamil 1494*e439f0f8SKowalski, Kamil for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); 1495*e439f0f8SKowalski, Kamil ++propertyIt) { 1496*e439f0f8SKowalski, Kamil if (propertyIt.key() != "VLANEnable" && 1497*e439f0f8SKowalski, Kamil propertyIt.key() != "VLANId") { 1498*e439f0f8SKowalski, Kamil auto fieldInJsonIt = res.json_value.find(propertyIt.key()); 1499*e439f0f8SKowalski, Kamil 1500*e439f0f8SKowalski, Kamil if (fieldInJsonIt == res.json_value.end()) { 1501*e439f0f8SKowalski, Kamil // Field not in scope of defined fields 1502*e439f0f8SKowalski, Kamil messages::addMessageToJsonRoot( 1503*e439f0f8SKowalski, Kamil res.json_value, 1504*e439f0f8SKowalski, Kamil messages::propertyUnknown(propertyIt.key())); 1505*e439f0f8SKowalski, Kamil } else if (*fieldInJsonIt != *propertyIt) { 1506*e439f0f8SKowalski, Kamil // User attempted to modify non-writable field 1507*e439f0f8SKowalski, Kamil messages::addMessageToJsonRoot( 1508*e439f0f8SKowalski, Kamil res.json_value, 1509*e439f0f8SKowalski, Kamil messages::propertyNotWritable(propertyIt.key())); 1510*e439f0f8SKowalski, Kamil } 1511*e439f0f8SKowalski, Kamil } 1512*e439f0f8SKowalski, Kamil } 1513*e439f0f8SKowalski, Kamil 1514*e439f0f8SKowalski, Kamil EthernetInterface::handleVlanPatch(iface_id, patchReq, eth_data, "/", 1515*e439f0f8SKowalski, Kamil asyncResp); 1516*e439f0f8SKowalski, Kamil }); 1517*e439f0f8SKowalski, Kamil } 1518*e439f0f8SKowalski, Kamil 1519*e439f0f8SKowalski, Kamil void doDelete(crow::response &res, const crow::request &req, 1520*e439f0f8SKowalski, Kamil const std::vector<std::string> ¶ms) override { 1521*e439f0f8SKowalski, Kamil if (params.size() != 2) { 1522*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1523*e439f0f8SKowalski, Kamil res.end(); 1524*e439f0f8SKowalski, Kamil return; 1525*e439f0f8SKowalski, Kamil } 1526*e439f0f8SKowalski, Kamil 1527*e439f0f8SKowalski, Kamil const std::string &parent_iface_id = params[0]; 1528*e439f0f8SKowalski, Kamil const std::string &iface_id = params[1]; 1529*e439f0f8SKowalski, Kamil 1530*e439f0f8SKowalski, Kamil if (!verifyNames(res, parent_iface_id, iface_id)) { 1531*e439f0f8SKowalski, Kamil return; 1532*e439f0f8SKowalski, Kamil } 1533*e439f0f8SKowalski, Kamil 1534*e439f0f8SKowalski, Kamil // Get single eth interface data, and call the below callback for JSON 1535*e439f0f8SKowalski, Kamil // preparation 1536*e439f0f8SKowalski, Kamil ethernet_provider.getEthernetIfaceData( 1537*e439f0f8SKowalski, Kamil iface_id, 1538*e439f0f8SKowalski, Kamil [&, parent_iface_id, iface_id]( 1539*e439f0f8SKowalski, Kamil const bool &success, const EthernetInterfaceData ð_data, 1540*e439f0f8SKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 1541*e439f0f8SKowalski, Kamil if (success && eth_data.vlan_id != nullptr) { 1542*e439f0f8SKowalski, Kamil res.json_value = parseInterfaceData(parent_iface_id, iface_id, 1543*e439f0f8SKowalski, Kamil eth_data, ipv4_data); 1544*e439f0f8SKowalski, Kamil 1545*e439f0f8SKowalski, Kamil // Disable VLAN 1546*e439f0f8SKowalski, Kamil OnDemandEthernetProvider::disableVlan( 1547*e439f0f8SKowalski, Kamil iface_id, [&](const boost::system::error_code ec) { 1548*e439f0f8SKowalski, Kamil if (ec) { 1549*e439f0f8SKowalski, Kamil res.json_value = nlohmann::json::object(); 1550*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson(res.json_value, 1551*e439f0f8SKowalski, Kamil messages::internalError()); 1552*e439f0f8SKowalski, Kamil res.result( 1553*e439f0f8SKowalski, Kamil boost::beast::http::status::internal_server_error); 1554*e439f0f8SKowalski, Kamil } 1555*e439f0f8SKowalski, Kamil res.end(); 1556*e439f0f8SKowalski, Kamil }); 1557*e439f0f8SKowalski, Kamil } else { 1558*e439f0f8SKowalski, Kamil // ... otherwise return error 1559*e439f0f8SKowalski, Kamil // TODO(Pawel)consider distinguish between non existing object, 1560*e439f0f8SKowalski, Kamil // and 1561*e439f0f8SKowalski, Kamil // other errors 1562*e439f0f8SKowalski, Kamil 1563*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::not_found); 1564*e439f0f8SKowalski, Kamil res.end(); 1565*e439f0f8SKowalski, Kamil } 1566*e439f0f8SKowalski, Kamil }); 1567*e439f0f8SKowalski, Kamil } 1568*e439f0f8SKowalski, Kamil 1569*e439f0f8SKowalski, Kamil /** 1570*e439f0f8SKowalski, Kamil * This allows VlanNetworkInterfaceCollection to reuse this class' doGet 1571*e439f0f8SKowalski, Kamil * method, to maintain consistency of returned data, as Collection's doPost 1572*e439f0f8SKowalski, Kamil * should return data for created member which should match member's doGet 1573*e439f0f8SKowalski, Kamil * result in 100%. 1574*e439f0f8SKowalski, Kamil */ 1575*e439f0f8SKowalski, Kamil friend VlanNetworkInterfaceCollection; 1576*e439f0f8SKowalski, Kamil 1577*e439f0f8SKowalski, Kamil // Ethernet Provider object 1578*e439f0f8SKowalski, Kamil // TODO(Pawel) consider move it to singleton 1579*e439f0f8SKowalski, Kamil OnDemandEthernetProvider ethernet_provider; 1580*e439f0f8SKowalski, Kamil }; 1581*e439f0f8SKowalski, Kamil 1582*e439f0f8SKowalski, Kamil /** 1583*e439f0f8SKowalski, Kamil * VlanNetworkInterfaceCollection derived class for delivering 1584*e439f0f8SKowalski, Kamil * VLANNetworkInterface Collection Schema 1585*e439f0f8SKowalski, Kamil */ 1586*e439f0f8SKowalski, Kamil class VlanNetworkInterfaceCollection : public Node { 1587*e439f0f8SKowalski, Kamil public: 1588*e439f0f8SKowalski, Kamil template <typename CrowApp> 1589*e439f0f8SKowalski, Kamil // TODO(Pawel) Remove line from below, where we assume that there is only one 1590*e439f0f8SKowalski, Kamil // manager called openbmc This shall be generic, but requires to update 1591*e439f0f8SKowalski, Kamil // GetSubroutes method 1592*e439f0f8SKowalski, Kamil VlanNetworkInterfaceCollection(CrowApp &app) 1593*e439f0f8SKowalski, Kamil : Node(app, 1594*e439f0f8SKowalski, Kamil "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/", 1595*e439f0f8SKowalski, Kamil std::string()), 1596*e439f0f8SKowalski, Kamil memberVlan(app) { 1597*e439f0f8SKowalski, Kamil Node::json["@odata.type"] = 1598*e439f0f8SKowalski, Kamil "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; 1599*e439f0f8SKowalski, Kamil Node::json["@odata.context"] = 1600*e439f0f8SKowalski, Kamil "/redfish/v1/$metadata" 1601*e439f0f8SKowalski, Kamil "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection"; 1602*e439f0f8SKowalski, Kamil Node::json["Name"] = "VLAN Network Interface Collection"; 1603*e439f0f8SKowalski, Kamil 1604*e439f0f8SKowalski, Kamil entityPrivileges = { 1605*e439f0f8SKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 1606*e439f0f8SKowalski, Kamil {boost::beast::http::verb::head, {{"Login"}}}, 1607*e439f0f8SKowalski, Kamil {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1608*e439f0f8SKowalski, Kamil {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1609*e439f0f8SKowalski, Kamil {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1610*e439f0f8SKowalski, Kamil {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1611*e439f0f8SKowalski, Kamil } 1612*e439f0f8SKowalski, Kamil 1613*e439f0f8SKowalski, Kamil private: 1614*e439f0f8SKowalski, Kamil /** 1615*e439f0f8SKowalski, Kamil * Functions triggers appropriate requests on DBus 1616*e439f0f8SKowalski, Kamil */ 1617*e439f0f8SKowalski, Kamil void doGet(crow::response &res, const crow::request &req, 1618*e439f0f8SKowalski, Kamil const std::vector<std::string> ¶ms) override { 1619*e439f0f8SKowalski, Kamil if (params.size() != 1) { 1620*e439f0f8SKowalski, Kamil // This means there is a problem with the router 1621*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1622*e439f0f8SKowalski, Kamil res.end(); 1623*e439f0f8SKowalski, Kamil 1624*e439f0f8SKowalski, Kamil return; 1625*e439f0f8SKowalski, Kamil } 1626*e439f0f8SKowalski, Kamil 1627*e439f0f8SKowalski, Kamil // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 1628*e439f0f8SKowalski, Kamil // any Manager, not only hardcoded 'openbmc'. 1629*e439f0f8SKowalski, Kamil std::string manager_id = "openbmc"; 1630*e439f0f8SKowalski, Kamil std::string rootInterfaceName = params[0]; 1631*e439f0f8SKowalski, Kamil 1632*e439f0f8SKowalski, Kamil // Get eth interface list, and call the below callback for JSON preparation 1633*e439f0f8SKowalski, Kamil ethernet_provider.getEthernetIfaceList([ 1634*e439f0f8SKowalski, Kamil &, manager_id{std::move(manager_id)}, 1635*e439f0f8SKowalski, Kamil rootInterfaceName{std::move(rootInterfaceName)} 1636*e439f0f8SKowalski, Kamil ](const bool &success, const std::vector<std::string> &iface_list) { 1637*e439f0f8SKowalski, Kamil if (success) { 1638*e439f0f8SKowalski, Kamil bool rootInterfaceFound = false; 1639*e439f0f8SKowalski, Kamil nlohmann::json iface_array = nlohmann::json::array(); 1640*e439f0f8SKowalski, Kamil 1641*e439f0f8SKowalski, Kamil for (const std::string &iface_item : iface_list) { 1642*e439f0f8SKowalski, Kamil if (iface_item == rootInterfaceName) { 1643*e439f0f8SKowalski, Kamil rootInterfaceFound = true; 1644*e439f0f8SKowalski, Kamil } else if (boost::starts_with(iface_item, rootInterfaceName + "_")) { 1645*e439f0f8SKowalski, Kamil iface_array.push_back( 1646*e439f0f8SKowalski, Kamil {{"@odata.id", "/redfish/v1/Managers/" + manager_id + 1647*e439f0f8SKowalski, Kamil "/EthernetInterfaces/" + rootInterfaceName + 1648*e439f0f8SKowalski, Kamil "/VLANs/" + iface_item}}); 1649*e439f0f8SKowalski, Kamil } 1650*e439f0f8SKowalski, Kamil } 1651*e439f0f8SKowalski, Kamil 1652*e439f0f8SKowalski, Kamil if (rootInterfaceFound) { 1653*e439f0f8SKowalski, Kamil Node::json["Members"] = iface_array; 1654*e439f0f8SKowalski, Kamil Node::json["Members@odata.count"] = iface_array.size(); 1655*e439f0f8SKowalski, Kamil Node::json["@odata.id"] = "/redfish/v1/Managers/" + manager_id + 1656*e439f0f8SKowalski, Kamil "/EthernetInterfaces/" + rootInterfaceName + 1657*e439f0f8SKowalski, Kamil "/VLANs"; 1658*e439f0f8SKowalski, Kamil res.json_value = Node::json; 1659*e439f0f8SKowalski, Kamil } else { 1660*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson( 1661*e439f0f8SKowalski, Kamil res.json_value, messages::resourceNotFound("EthernetInterface", 1662*e439f0f8SKowalski, Kamil rootInterfaceName)); 1663*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::not_found); 1664*e439f0f8SKowalski, Kamil res.end(); 1665*e439f0f8SKowalski, Kamil } 1666*e439f0f8SKowalski, Kamil } else { 1667*e439f0f8SKowalski, Kamil // No success, best what we can do is return INTERNALL ERROR 1668*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1669*e439f0f8SKowalski, Kamil } 1670*e439f0f8SKowalski, Kamil res.end(); 1671*e439f0f8SKowalski, Kamil }); 1672*e439f0f8SKowalski, Kamil } 1673*e439f0f8SKowalski, Kamil 1674*e439f0f8SKowalski, Kamil void doPost(crow::response &res, const crow::request &req, 1675*e439f0f8SKowalski, Kamil const std::vector<std::string> ¶ms) override { 1676*e439f0f8SKowalski, Kamil if (params.size() != 1) { 1677*e439f0f8SKowalski, Kamil // This means there is a problem with the router 1678*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1679*e439f0f8SKowalski, Kamil res.end(); 1680*e439f0f8SKowalski, Kamil return; 1681*e439f0f8SKowalski, Kamil } 1682*e439f0f8SKowalski, Kamil 1683*e439f0f8SKowalski, Kamil nlohmann::json postReq; 1684*e439f0f8SKowalski, Kamil 1685*e439f0f8SKowalski, Kamil if (!json_util::processJsonFromRequest(res, req, postReq)) { 1686*e439f0f8SKowalski, Kamil return; 1687*e439f0f8SKowalski, Kamil } 1688*e439f0f8SKowalski, Kamil 1689*e439f0f8SKowalski, Kamil // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 1690*e439f0f8SKowalski, Kamil // any Manager, not only hardcoded 'openbmc'. 1691*e439f0f8SKowalski, Kamil std::string manager_id = "openbmc"; 1692*e439f0f8SKowalski, Kamil std::string rootInterfaceName = params[0]; 1693*e439f0f8SKowalski, Kamil uint64_t vlanId; 1694*e439f0f8SKowalski, Kamil bool errorDetected; 1695*e439f0f8SKowalski, Kamil 1696*e439f0f8SKowalski, Kamil if (json_util::getUnsigned( 1697*e439f0f8SKowalski, Kamil "VLANId", postReq, vlanId, 1698*e439f0f8SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::MISSING) | 1699*e439f0f8SKowalski, Kamil static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR), 1700*e439f0f8SKowalski, Kamil res.json_value, "/VLANId") != json_util::Result::SUCCESS) { 1701*e439f0f8SKowalski, Kamil res.end(); 1702*e439f0f8SKowalski, Kamil return; 1703*e439f0f8SKowalski, Kamil } 1704*e439f0f8SKowalski, Kamil 1705*e439f0f8SKowalski, Kamil // Get eth interface list, and call the below callback for JSON preparation 1706*e439f0f8SKowalski, Kamil ethernet_provider.getEthernetIfaceList([ 1707*e439f0f8SKowalski, Kamil &, manager_id{std::move(manager_id)}, 1708*e439f0f8SKowalski, Kamil rootInterfaceName{std::move(rootInterfaceName)} 1709*e439f0f8SKowalski, Kamil ](const bool &success, const std::vector<std::string> &iface_list) { 1710*e439f0f8SKowalski, Kamil if (success) { 1711*e439f0f8SKowalski, Kamil bool rootInterfaceFound = false; 1712*e439f0f8SKowalski, Kamil 1713*e439f0f8SKowalski, Kamil for (const std::string &iface_item : iface_list) { 1714*e439f0f8SKowalski, Kamil if (iface_item == rootInterfaceName) { 1715*e439f0f8SKowalski, Kamil rootInterfaceFound = true; 1716*e439f0f8SKowalski, Kamil break; 1717*e439f0f8SKowalski, Kamil } 1718*e439f0f8SKowalski, Kamil } 1719*e439f0f8SKowalski, Kamil 1720*e439f0f8SKowalski, Kamil if (rootInterfaceFound) { 1721*e439f0f8SKowalski, Kamil ethernet_provider.createVlan( 1722*e439f0f8SKowalski, Kamil rootInterfaceName, vlanId, 1723*e439f0f8SKowalski, Kamil [&, vlanId, rootInterfaceName, 1724*e439f0f8SKowalski, Kamil req{std::move(req)} ](const boost::system::error_code ec) { 1725*e439f0f8SKowalski, Kamil if (ec) { 1726*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson(res.json_value, 1727*e439f0f8SKowalski, Kamil messages::internalError()); 1728*e439f0f8SKowalski, Kamil res.end(); 1729*e439f0f8SKowalski, Kamil } else { 1730*e439f0f8SKowalski, Kamil memberVlan.doGet( 1731*e439f0f8SKowalski, Kamil res, req, 1732*e439f0f8SKowalski, Kamil {rootInterfaceName, 1733*e439f0f8SKowalski, Kamil rootInterfaceName + "_" + std::to_string(vlanId)}); 1734*e439f0f8SKowalski, Kamil } 1735*e439f0f8SKowalski, Kamil }); 1736*e439f0f8SKowalski, Kamil } else { 1737*e439f0f8SKowalski, Kamil messages::addMessageToErrorJson( 1738*e439f0f8SKowalski, Kamil res.json_value, messages::resourceNotFound("EthernetInterface", 1739*e439f0f8SKowalski, Kamil rootInterfaceName)); 1740*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::not_found); 1741*e439f0f8SKowalski, Kamil res.end(); 1742*e439f0f8SKowalski, Kamil } 1743*e439f0f8SKowalski, Kamil } else { 1744*e439f0f8SKowalski, Kamil // No success, best what we can do is return INTERNALL ERROR 1745*e439f0f8SKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 1746*e439f0f8SKowalski, Kamil res.end(); 1747*e439f0f8SKowalski, Kamil } 1748*e439f0f8SKowalski, Kamil }); 1749*e439f0f8SKowalski, Kamil } 1750*e439f0f8SKowalski, Kamil 1751*e439f0f8SKowalski, Kamil // Ethernet Provider object 1752*e439f0f8SKowalski, Kamil // TODO(Pawel) consider move it to singleton 1753*e439f0f8SKowalski, Kamil OnDemandEthernetProvider ethernet_provider; 1754*e439f0f8SKowalski, Kamil VlanNetworkInterface memberVlan; 1755*e439f0f8SKowalski, Kamil }; 1756*e439f0f8SKowalski, Kamil 17579391bb9cSRapkiewicz, Pawel } // namespace redfish 1758