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 18*588c3f0dSKowalski, Kamil #include <error_messages.hpp> 19*588c3f0dSKowalski, Kamil #include <utils/json_utils.hpp> 209391bb9cSRapkiewicz, Pawel #include "node.hpp" 219391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp> 229391bb9cSRapkiewicz, Pawel 239391bb9cSRapkiewicz, Pawel namespace redfish { 249391bb9cSRapkiewicz, Pawel 259391bb9cSRapkiewicz, Pawel /** 269391bb9cSRapkiewicz, Pawel * DBus types primitives for several generic DBus interfaces 279391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider move this to separate file into boost::dbus 289391bb9cSRapkiewicz, Pawel */ 29aa2e59c1SEd Tanous using PropertiesMapType = boost::container::flat_map< 30aa2e59c1SEd Tanous std::string, 31aa2e59c1SEd Tanous sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 32aa2e59c1SEd Tanous int32_t, uint32_t, int64_t, uint64_t, double>>; 339391bb9cSRapkiewicz, Pawel 349391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map< 35aa2e59c1SEd Tanous sdbusplus::message::object_path, 36aa2e59c1SEd Tanous boost::container::flat_map< 37aa2e59c1SEd Tanous std::string, 38aa2e59c1SEd Tanous boost::container::flat_map< 39aa2e59c1SEd Tanous std::string, sdbusplus::message::variant< 40aa2e59c1SEd Tanous std::string, bool, uint8_t, int16_t, uint16_t, 41aa2e59c1SEd Tanous int32_t, uint32_t, int64_t, uint64_t, double>>>>; 429391bb9cSRapkiewicz, Pawel 439391bb9cSRapkiewicz, Pawel /** 449391bb9cSRapkiewicz, Pawel * Structure for keeping IPv4 data required by Redfish 459391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider change everything to ptr, or to non-ptr values. 469391bb9cSRapkiewicz, Pawel */ 479391bb9cSRapkiewicz, Pawel struct IPv4AddressData { 489391bb9cSRapkiewicz, Pawel const std::string *address; 499391bb9cSRapkiewicz, Pawel const std::string *domain; 509391bb9cSRapkiewicz, Pawel const std::string *gateway; 519391bb9cSRapkiewicz, Pawel std::string netmask; 529391bb9cSRapkiewicz, Pawel std::string origin; 539391bb9cSRapkiewicz, Pawel bool global; 549391bb9cSRapkiewicz, Pawel }; 559391bb9cSRapkiewicz, Pawel 569391bb9cSRapkiewicz, Pawel /** 579391bb9cSRapkiewicz, Pawel * Structure for keeping basic single Ethernet Interface information 589391bb9cSRapkiewicz, Pawel * available from DBus 599391bb9cSRapkiewicz, Pawel */ 609391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData { 619391bb9cSRapkiewicz, Pawel const unsigned int *speed; 629391bb9cSRapkiewicz, Pawel const bool *auto_neg; 639391bb9cSRapkiewicz, Pawel const std::string *hostname; 649391bb9cSRapkiewicz, Pawel const std::string *default_gateway; 659391bb9cSRapkiewicz, Pawel const std::string *mac_address; 66c7070ac2SKowalski, Kamil const unsigned int *vlan_id; 679391bb9cSRapkiewicz, Pawel }; 689391bb9cSRapkiewicz, Pawel 699391bb9cSRapkiewicz, Pawel /** 709391bb9cSRapkiewicz, Pawel * OnDemandEthernetProvider 71274fad5aSGunnar Mills * Ethernet provider class that retrieves data directly from dbus, before 72274fad5aSGunnar Mills * setting it into JSON output. This does not cache any data. 739391bb9cSRapkiewicz, Pawel * 749391bb9cSRapkiewicz, Pawel * TODO(Pawel) 759391bb9cSRapkiewicz, Pawel * This perhaps shall be different file, which has to be chosen on compile time 769391bb9cSRapkiewicz, Pawel * depending on OEM needs 779391bb9cSRapkiewicz, Pawel */ 789391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider { 799391bb9cSRapkiewicz, Pawel private: 809391bb9cSRapkiewicz, Pawel // Consts that may have influence on EthernetProvider performance/memory usage 819391bb9cSRapkiewicz, Pawel const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10; 829391bb9cSRapkiewicz, Pawel 839391bb9cSRapkiewicz, Pawel // Helper function that allows to extract GetAllPropertiesType from 849391bb9cSRapkiewicz, Pawel // GetManagedObjectsType, based on object path, and interface name 859391bb9cSRapkiewicz, Pawel const PropertiesMapType *extractInterfaceProperties( 86aa2e59c1SEd Tanous const sdbusplus::message::object_path &objpath, 87aa2e59c1SEd Tanous const std::string &interface, const GetManagedObjectsType &dbus_data) { 889391bb9cSRapkiewicz, Pawel const auto &dbus_obj = dbus_data.find(objpath); 899391bb9cSRapkiewicz, Pawel if (dbus_obj != dbus_data.end()) { 909391bb9cSRapkiewicz, Pawel const auto &iface = dbus_obj->second.find(interface); 919391bb9cSRapkiewicz, Pawel if (iface != dbus_obj->second.end()) { 929391bb9cSRapkiewicz, Pawel return &iface->second; 939391bb9cSRapkiewicz, Pawel } 949391bb9cSRapkiewicz, Pawel } 959391bb9cSRapkiewicz, Pawel return nullptr; 969391bb9cSRapkiewicz, Pawel } 979391bb9cSRapkiewicz, Pawel 989391bb9cSRapkiewicz, Pawel // Helper Wrapper that does inline object_path conversion from string 99aa2e59c1SEd Tanous // into sdbusplus::message::object_path type 1009391bb9cSRapkiewicz, Pawel inline const PropertiesMapType *extractInterfaceProperties( 1019391bb9cSRapkiewicz, Pawel const std::string &objpath, const std::string &interface, 1029391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data) { 103aa2e59c1SEd Tanous const auto &dbus_obj = sdbusplus::message::object_path{objpath}; 1049391bb9cSRapkiewicz, Pawel return extractInterfaceProperties(dbus_obj, interface, dbus_data); 1059391bb9cSRapkiewicz, Pawel } 1069391bb9cSRapkiewicz, Pawel 1079391bb9cSRapkiewicz, Pawel // Helper function that allows to get pointer to the property from 1089391bb9cSRapkiewicz, Pawel // GetAllPropertiesType native, or extracted by GetAllPropertiesType 1099391bb9cSRapkiewicz, Pawel template <typename T> 110aa2e59c1SEd Tanous inline T const *const extractProperty(const PropertiesMapType &properties, 1119391bb9cSRapkiewicz, Pawel const std::string &name) { 1129391bb9cSRapkiewicz, Pawel const auto &property = properties.find(name); 1139391bb9cSRapkiewicz, Pawel if (property != properties.end()) { 114aa2e59c1SEd Tanous return mapbox::get_ptr<const T>(property->second); 1159391bb9cSRapkiewicz, Pawel } 1169391bb9cSRapkiewicz, Pawel return nullptr; 1179391bb9cSRapkiewicz, Pawel } 1189391bb9cSRapkiewicz, Pawel // TODO(Pawel) Consider to move the above functions to dbus 1199391bb9cSRapkiewicz, Pawel // generic_interfaces.hpp 1209391bb9cSRapkiewicz, Pawel 1219391bb9cSRapkiewicz, Pawel // Helper function that extracts data from several dbus objects and several 1229391bb9cSRapkiewicz, Pawel // interfaces required by single ethernet interface instance 1239391bb9cSRapkiewicz, Pawel void extractEthernetInterfaceData(const std::string ðiface_id, 1249391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 1259391bb9cSRapkiewicz, Pawel EthernetInterfaceData ð_data) { 1269391bb9cSRapkiewicz, Pawel // Extract data that contains MAC Address 1279391bb9cSRapkiewicz, Pawel const PropertiesMapType *mac_properties = extractInterfaceProperties( 1289391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 1299391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.MACAddress", dbus_data); 1309391bb9cSRapkiewicz, Pawel 1319391bb9cSRapkiewicz, Pawel if (mac_properties != nullptr) { 1329391bb9cSRapkiewicz, Pawel eth_data.mac_address = 1339391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*mac_properties, "MACAddress"); 1349391bb9cSRapkiewicz, Pawel } 1359391bb9cSRapkiewicz, Pawel 136c7070ac2SKowalski, Kamil const PropertiesMapType *vlan_properties = extractInterfaceProperties( 137c7070ac2SKowalski, Kamil "/xyz/openbmc_project/network/" + ethiface_id, 138c7070ac2SKowalski, Kamil "xyz.openbmc_project.Network.VLAN", dbus_data); 139c7070ac2SKowalski, Kamil 140c7070ac2SKowalski, Kamil if (vlan_properties != nullptr) { 141c7070ac2SKowalski, Kamil eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id"); 142c7070ac2SKowalski, Kamil } 143c7070ac2SKowalski, Kamil 1449391bb9cSRapkiewicz, Pawel // Extract data that contains link information (auto negotiation and speed) 1459391bb9cSRapkiewicz, Pawel const PropertiesMapType *eth_properties = extractInterfaceProperties( 1469391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 1479391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface", dbus_data); 1489391bb9cSRapkiewicz, Pawel 1499391bb9cSRapkiewicz, Pawel if (eth_properties != nullptr) { 1509391bb9cSRapkiewicz, Pawel eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg"); 1519391bb9cSRapkiewicz, Pawel eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed"); 1529391bb9cSRapkiewicz, Pawel } 1539391bb9cSRapkiewicz, Pawel 1549391bb9cSRapkiewicz, Pawel // Extract data that contains network config (HostName and DefaultGW) 1559391bb9cSRapkiewicz, Pawel const PropertiesMapType *config_properties = extractInterfaceProperties( 1569391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/config", 1579391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); 1589391bb9cSRapkiewicz, Pawel 1599391bb9cSRapkiewicz, Pawel if (config_properties != nullptr) { 1609391bb9cSRapkiewicz, Pawel eth_data.hostname = 1619391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "HostName"); 1629391bb9cSRapkiewicz, Pawel eth_data.default_gateway = 1639391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "DefaultGateway"); 1649391bb9cSRapkiewicz, Pawel } 1659391bb9cSRapkiewicz, Pawel } 1669391bb9cSRapkiewicz, Pawel 1679391bb9cSRapkiewicz, Pawel // Helper function that changes bits netmask notation (i.e. /24) 1689391bb9cSRapkiewicz, Pawel // into full dot notation 1699391bb9cSRapkiewicz, Pawel inline std::string getNetmask(unsigned int bits) { 1709391bb9cSRapkiewicz, Pawel uint32_t value = 0xffffffff << (32 - bits); 1719391bb9cSRapkiewicz, Pawel std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 1729391bb9cSRapkiewicz, Pawel std::to_string((value >> 16) & 0xff) + "." + 1739391bb9cSRapkiewicz, Pawel std::to_string((value >> 8) & 0xff) + "." + 1749391bb9cSRapkiewicz, Pawel std::to_string(value & 0xff); 1759391bb9cSRapkiewicz, Pawel return netmask; 1769391bb9cSRapkiewicz, Pawel } 1779391bb9cSRapkiewicz, Pawel 1789391bb9cSRapkiewicz, Pawel // Helper function that extracts data for single ethernet ipv4 address 1799391bb9cSRapkiewicz, Pawel void extractIPv4Data(const std::string ðiface_id, 1809391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 1819391bb9cSRapkiewicz, Pawel std::vector<IPv4AddressData> &ipv4_config) { 1829391bb9cSRapkiewicz, Pawel // Since there might be several IPv4 configurations aligned with 1839391bb9cSRapkiewicz, Pawel // single ethernet interface, loop over all of them 1849391bb9cSRapkiewicz, Pawel for (auto &objpath : dbus_data) { 185274fad5aSGunnar Mills // Check if proper patter for object path appears 1869391bb9cSRapkiewicz, Pawel if (boost::starts_with( 187daf36e2eSEd Tanous static_cast<const std::string &>(objpath.first), 1889391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) { 1899391bb9cSRapkiewicz, Pawel // and get approrpiate interface 1909391bb9cSRapkiewicz, Pawel const auto &interface = 1919391bb9cSRapkiewicz, Pawel objpath.second.find("xyz.openbmc_project.Network.IP"); 1929391bb9cSRapkiewicz, Pawel if (interface != objpath.second.end()) { 1939391bb9cSRapkiewicz, Pawel // Make a properties 'shortcut', to make everything more readable 1949391bb9cSRapkiewicz, Pawel const PropertiesMapType &properties = interface->second; 1959391bb9cSRapkiewicz, Pawel // Instance IPv4AddressData structure, and set as appropriate 1969391bb9cSRapkiewicz, Pawel IPv4AddressData ipv4_address; 1979391bb9cSRapkiewicz, Pawel // IPv4 address 1989391bb9cSRapkiewicz, Pawel ipv4_address.address = 1999391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Address"); 2009391bb9cSRapkiewicz, Pawel // IPv4 gateway 2019391bb9cSRapkiewicz, Pawel ipv4_address.gateway = 2029391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Gateway"); 2039391bb9cSRapkiewicz, Pawel 2049391bb9cSRapkiewicz, Pawel // Origin is kind of DBus object so fetch pointer... 2059391bb9cSRapkiewicz, Pawel const std::string *origin = 2069391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Origin"); 2079391bb9cSRapkiewicz, Pawel if (origin != nullptr) { 2089391bb9cSRapkiewicz, Pawel // ... and get everything after last dot 2099391bb9cSRapkiewicz, Pawel int last = origin->rfind("."); 2109391bb9cSRapkiewicz, Pawel if (last != std::string::npos) { 2119391bb9cSRapkiewicz, Pawel ipv4_address.origin = origin->substr(last + 1); 2129391bb9cSRapkiewicz, Pawel } 2139391bb9cSRapkiewicz, Pawel } 2149391bb9cSRapkiewicz, Pawel 2159391bb9cSRapkiewicz, Pawel // Netmask is presented as PrefixLength 2169391bb9cSRapkiewicz, Pawel const auto *mask = 2179391bb9cSRapkiewicz, Pawel extractProperty<uint8_t>(properties, "PrefixLength"); 2189391bb9cSRapkiewicz, Pawel if (mask != nullptr) { 2199391bb9cSRapkiewicz, Pawel // convert it to the string 2209391bb9cSRapkiewicz, Pawel ipv4_address.netmask = getNetmask(*mask); 2219391bb9cSRapkiewicz, Pawel } 2229391bb9cSRapkiewicz, Pawel 2239391bb9cSRapkiewicz, Pawel // Attach IPv4 only if address is present 2249391bb9cSRapkiewicz, Pawel if (ipv4_address.address != nullptr) { 225274fad5aSGunnar Mills // Check if given address is local, or global 2269391bb9cSRapkiewicz, Pawel if (boost::starts_with(*ipv4_address.address, "169.254")) { 2279391bb9cSRapkiewicz, Pawel ipv4_address.global = false; 2289391bb9cSRapkiewicz, Pawel } else { 2299391bb9cSRapkiewicz, Pawel ipv4_address.global = true; 2309391bb9cSRapkiewicz, Pawel } 2319391bb9cSRapkiewicz, Pawel ipv4_config.emplace_back(std::move(ipv4_address)); 2329391bb9cSRapkiewicz, Pawel } 2339391bb9cSRapkiewicz, Pawel } 2349391bb9cSRapkiewicz, Pawel } 2359391bb9cSRapkiewicz, Pawel } 2369391bb9cSRapkiewicz, Pawel } 2379391bb9cSRapkiewicz, Pawel 2389391bb9cSRapkiewicz, Pawel public: 2399391bb9cSRapkiewicz, Pawel /** 2409391bb9cSRapkiewicz, Pawel * Function that retrieves all properties for given Ethernet Interface Object 2419391bb9cSRapkiewicz, Pawel * from EntityManager Network Manager 2429391bb9cSRapkiewicz, Pawel * @param ethiface_id a eth interface id to query on DBus 2439391bb9cSRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 2449391bb9cSRapkiewicz, Pawel * JSON 2459391bb9cSRapkiewicz, Pawel */ 2469391bb9cSRapkiewicz, Pawel template <typename CallbackFunc> 2479391bb9cSRapkiewicz, Pawel void getEthernetIfaceData(const std::string ðiface_id, 2489391bb9cSRapkiewicz, Pawel CallbackFunc &&callback) { 2499391bb9cSRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 2509391bb9cSRapkiewicz, Pawel [ 2519391bb9cSRapkiewicz, Pawel this, ethiface_id{std::move(ethiface_id)}, 2529391bb9cSRapkiewicz, Pawel callback{std::move(callback)} 2539391bb9cSRapkiewicz, Pawel ](const boost::system::error_code error_code, 254aa2e59c1SEd Tanous GetManagedObjectsType &resp) { 2559391bb9cSRapkiewicz, Pawel 256c7070ac2SKowalski, Kamil EthernetInterfaceData eth_data{}; 2579391bb9cSRapkiewicz, Pawel std::vector<IPv4AddressData> ipv4_data; 2589391bb9cSRapkiewicz, Pawel ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE); 2599391bb9cSRapkiewicz, Pawel 2609391bb9cSRapkiewicz, Pawel if (error_code) { 2619391bb9cSRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 2629391bb9cSRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 2639391bb9cSRapkiewicz, Pawel // of vector may vary depending on information from Network Manager, 2649391bb9cSRapkiewicz, Pawel // and empty output could not be treated same way as error. 2659391bb9cSRapkiewicz, Pawel callback(false, eth_data, ipv4_data); 2669391bb9cSRapkiewicz, Pawel return; 2679391bb9cSRapkiewicz, Pawel } 2689391bb9cSRapkiewicz, Pawel 2699391bb9cSRapkiewicz, Pawel extractEthernetInterfaceData(ethiface_id, resp, eth_data); 2709391bb9cSRapkiewicz, Pawel extractIPv4Data(ethiface_id, resp, ipv4_data); 2719391bb9cSRapkiewicz, Pawel 2729391bb9cSRapkiewicz, Pawel // Fix global GW 2739391bb9cSRapkiewicz, Pawel for (IPv4AddressData &ipv4 : ipv4_data) { 2749391bb9cSRapkiewicz, Pawel if ((ipv4.global) && 2759391bb9cSRapkiewicz, Pawel ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { 2769391bb9cSRapkiewicz, Pawel ipv4.gateway = eth_data.default_gateway; 2779391bb9cSRapkiewicz, Pawel } 2789391bb9cSRapkiewicz, Pawel } 2799391bb9cSRapkiewicz, Pawel 280274fad5aSGunnar Mills // Finally make a callback with useful data 2819391bb9cSRapkiewicz, Pawel callback(true, eth_data, ipv4_data); 2829391bb9cSRapkiewicz, Pawel }, 283aa2e59c1SEd Tanous "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 284aa2e59c1SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 2859391bb9cSRapkiewicz, Pawel }; 2869391bb9cSRapkiewicz, Pawel 2879391bb9cSRapkiewicz, Pawel /** 288*588c3f0dSKowalski, Kamil * @brief Creates VLAN for given interface with given Id through D-Bus 289*588c3f0dSKowalski, Kamil * 290*588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of interface for which VLAN will be created 291*588c3f0dSKowalski, Kamil * @param[in] inputVlanId ID of the new VLAN 292*588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 293*588c3f0dSKowalski, Kamil * 294*588c3f0dSKowalski, Kamil * @return None. 295*588c3f0dSKowalski, Kamil */ 296*588c3f0dSKowalski, Kamil template <typename CallbackFunc> 297*588c3f0dSKowalski, Kamil void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId, 298*588c3f0dSKowalski, Kamil CallbackFunc &&callback) { 299*588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 300*588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 301*588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId, 302*588c3f0dSKowalski, Kamil static_cast<uint32_t>(inputVlanId)); 303*588c3f0dSKowalski, Kamil }; 304*588c3f0dSKowalski, Kamil 305*588c3f0dSKowalski, Kamil /** 306*588c3f0dSKowalski, Kamil * @brief Sets given Id on the given VLAN interface through D-Bus 307*588c3f0dSKowalski, Kamil * 308*588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of VLAN interface that should be modified 309*588c3f0dSKowalski, Kamil * @param[in] inputVlanId New ID of the VLAN 310*588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 311*588c3f0dSKowalski, Kamil * 312*588c3f0dSKowalski, Kamil * @return None. 313*588c3f0dSKowalski, Kamil */ 314*588c3f0dSKowalski, Kamil template <typename CallbackFunc> 315*588c3f0dSKowalski, Kamil void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId, 316*588c3f0dSKowalski, Kamil CallbackFunc &&callback) { 317*588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 318*588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 319*588c3f0dSKowalski, Kamil std::string("/xyz/openbmc_project/network/") + ifaceId, 320*588c3f0dSKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 321*588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.VLAN", "Id", 322*588c3f0dSKowalski, Kamil sdbusplus::message::variant<uint32_t>(inputVlanId)); 323*588c3f0dSKowalski, Kamil }; 324*588c3f0dSKowalski, Kamil 325*588c3f0dSKowalski, Kamil /** 326*588c3f0dSKowalski, Kamil * @brief Disables VLAN with given ifaceId 327*588c3f0dSKowalski, Kamil * 328*588c3f0dSKowalski, Kamil * @param[in] ifaceId Id of VLAN interface that should be disabled 329*588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 330*588c3f0dSKowalski, Kamil * 331*588c3f0dSKowalski, Kamil * @return None. 332*588c3f0dSKowalski, Kamil */ 333*588c3f0dSKowalski, Kamil template <typename CallbackFunc> 334*588c3f0dSKowalski, Kamil void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) { 335*588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 336*588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 337*588c3f0dSKowalski, Kamil std::string("/xyz/openbmc_project/network/") + ifaceId, 338*588c3f0dSKowalski, Kamil "xyz.openbmc_project.Object.Delete", "Delete"); 339*588c3f0dSKowalski, Kamil }; 340*588c3f0dSKowalski, Kamil 341*588c3f0dSKowalski, Kamil /** 342*588c3f0dSKowalski, Kamil * @brief Sets given HostName of the machine through D-Bus 343*588c3f0dSKowalski, Kamil * 344*588c3f0dSKowalski, Kamil * @param[in] newHostname New name that HostName will be changed to 345*588c3f0dSKowalski, Kamil * @param[in] callback Function that will be called after the operation 346*588c3f0dSKowalski, Kamil * 347*588c3f0dSKowalski, Kamil * @return None. 348*588c3f0dSKowalski, Kamil */ 349*588c3f0dSKowalski, Kamil template <typename CallbackFunc> 350*588c3f0dSKowalski, Kamil void setHostName(const std::string &newHostname, CallbackFunc &&callback) { 351*588c3f0dSKowalski, Kamil crow::connections::system_bus->async_method_call( 352*588c3f0dSKowalski, Kamil callback, "xyz.openbmc_project.Network", 353*588c3f0dSKowalski, Kamil "/xyz/openbmc_project/network/config", 354*588c3f0dSKowalski, Kamil "org.freedesktop.DBus.Properties", "Set", 355*588c3f0dSKowalski, Kamil "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 356*588c3f0dSKowalski, Kamil sdbusplus::message::variant<std::string>(newHostname)); 357*588c3f0dSKowalski, Kamil }; 358*588c3f0dSKowalski, Kamil 359*588c3f0dSKowalski, Kamil /** 3609391bb9cSRapkiewicz, Pawel * Function that retrieves all Ethernet Interfaces available through Network 3619391bb9cSRapkiewicz, Pawel * Manager 3629391bb9cSRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 3639391bb9cSRapkiewicz, Pawel * JSON. 3649391bb9cSRapkiewicz, Pawel */ 3659391bb9cSRapkiewicz, Pawel template <typename CallbackFunc> 3669391bb9cSRapkiewicz, Pawel void getEthernetIfaceList(CallbackFunc &&callback) { 3679391bb9cSRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 3689391bb9cSRapkiewicz, Pawel [ this, callback{std::move(callback)} ]( 3699391bb9cSRapkiewicz, Pawel const boost::system::error_code error_code, 370aa2e59c1SEd Tanous GetManagedObjectsType &resp) { 3719391bb9cSRapkiewicz, Pawel // Callback requires vector<string> to retrieve all available ethernet 3729391bb9cSRapkiewicz, Pawel // interfaces 3739391bb9cSRapkiewicz, Pawel std::vector<std::string> iface_list; 3749391bb9cSRapkiewicz, Pawel iface_list.reserve(resp.size()); 3759391bb9cSRapkiewicz, Pawel if (error_code) { 3769391bb9cSRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 3779391bb9cSRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 3789391bb9cSRapkiewicz, Pawel // of vector may vary depending on information from Network Manager, 3799391bb9cSRapkiewicz, Pawel // and empty output could not be treated same way as error. 3809391bb9cSRapkiewicz, Pawel callback(false, iface_list); 3819391bb9cSRapkiewicz, Pawel return; 3829391bb9cSRapkiewicz, Pawel } 3839391bb9cSRapkiewicz, Pawel 3849391bb9cSRapkiewicz, Pawel // Iterate over all retrieved ObjectPaths. 3859391bb9cSRapkiewicz, Pawel for (auto &objpath : resp) { 3869391bb9cSRapkiewicz, Pawel // And all interfaces available for certain ObjectPath. 3879391bb9cSRapkiewicz, Pawel for (auto &interface : objpath.second) { 3889391bb9cSRapkiewicz, Pawel // If interface is xyz.openbmc_project.Network.EthernetInterface, 3899391bb9cSRapkiewicz, Pawel // this is what we're looking for. 3909391bb9cSRapkiewicz, Pawel if (interface.first == 3919391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface") { 392aa2e59c1SEd Tanous // Cut out everyting until last "/", ... 393daf36e2eSEd Tanous const std::string &iface_id = 394daf36e2eSEd Tanous static_cast<const std::string &>(objpath.first); 3959391bb9cSRapkiewicz, Pawel std::size_t last_pos = iface_id.rfind("/"); 3969391bb9cSRapkiewicz, Pawel if (last_pos != std::string::npos) { 3979391bb9cSRapkiewicz, Pawel // and put it into output vector. 3989391bb9cSRapkiewicz, Pawel iface_list.emplace_back(iface_id.substr(last_pos + 1)); 3999391bb9cSRapkiewicz, Pawel } 4009391bb9cSRapkiewicz, Pawel } 4019391bb9cSRapkiewicz, Pawel } 4029391bb9cSRapkiewicz, Pawel } 403274fad5aSGunnar Mills // Finally make a callback with useful data 4049391bb9cSRapkiewicz, Pawel callback(true, iface_list); 4059391bb9cSRapkiewicz, Pawel }, 406aa2e59c1SEd Tanous "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 407aa2e59c1SEd Tanous "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 4089391bb9cSRapkiewicz, Pawel }; 4099391bb9cSRapkiewicz, Pawel }; 4109391bb9cSRapkiewicz, Pawel 4119391bb9cSRapkiewicz, Pawel /** 4129391bb9cSRapkiewicz, Pawel * EthernetCollection derived class for delivering Ethernet Collection Schema 4139391bb9cSRapkiewicz, Pawel */ 4149391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node { 4159391bb9cSRapkiewicz, Pawel public: 4169391bb9cSRapkiewicz, Pawel template <typename CrowApp> 4179391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 4189391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 4199391bb9cSRapkiewicz, Pawel // GetSubroutes method 4209391bb9cSRapkiewicz, Pawel EthernetCollection(CrowApp &app) 4219391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { 4229391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = 4239391bb9cSRapkiewicz, Pawel "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 4249391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 4259391bb9cSRapkiewicz, Pawel "/redfish/v1/" 4269391bb9cSRapkiewicz, Pawel "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 4279391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; 4289391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Ethernet Network Interface Collection"; 4299391bb9cSRapkiewicz, Pawel Node::json["Description"] = 4309391bb9cSRapkiewicz, Pawel "Collection of EthernetInterfaces for this Manager"; 4319391bb9cSRapkiewicz, Pawel 432*588c3f0dSKowalski, Kamil entityPrivileges = { 433*588c3f0dSKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 434e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 435e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 436e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 437e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 438e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 4399391bb9cSRapkiewicz, Pawel } 4409391bb9cSRapkiewicz, Pawel 4419391bb9cSRapkiewicz, Pawel private: 4429391bb9cSRapkiewicz, Pawel /** 4439391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 4449391bb9cSRapkiewicz, Pawel */ 4459391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 4469391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 4479391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 4489391bb9cSRapkiewicz, Pawel // any Manager, not only hardcoded 'openbmc'. 4499391bb9cSRapkiewicz, Pawel std::string manager_id = "openbmc"; 4509391bb9cSRapkiewicz, Pawel 4519391bb9cSRapkiewicz, Pawel // Get eth interface list, and call the below callback for JSON preparation 4529391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceList( 4539391bb9cSRapkiewicz, Pawel [&, manager_id{std::move(manager_id)} ]( 4549391bb9cSRapkiewicz, Pawel const bool &success, const std::vector<std::string> &iface_list) { 4559391bb9cSRapkiewicz, Pawel if (success) { 4569391bb9cSRapkiewicz, Pawel nlohmann::json iface_array = nlohmann::json::array(); 4579391bb9cSRapkiewicz, Pawel for (const std::string &iface_item : iface_list) { 4589391bb9cSRapkiewicz, Pawel iface_array.push_back( 4599391bb9cSRapkiewicz, Pawel {{"@odata.id", "/redfish/v1/Managers/" + manager_id + 4609391bb9cSRapkiewicz, Pawel "/EthernetInterfaces/" + iface_item}}); 4619391bb9cSRapkiewicz, Pawel } 4629391bb9cSRapkiewicz, Pawel Node::json["Members"] = iface_array; 4639391bb9cSRapkiewicz, Pawel Node::json["Members@odata.count"] = iface_array.size(); 4649391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = 4659391bb9cSRapkiewicz, Pawel "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces"; 4669391bb9cSRapkiewicz, Pawel res.json_value = Node::json; 4679391bb9cSRapkiewicz, Pawel } else { 4689391bb9cSRapkiewicz, Pawel // No success, best what we can do is return INTERNALL ERROR 469e0d918bcSEd Tanous res.result(boost::beast::http::status::internal_server_error); 4709391bb9cSRapkiewicz, Pawel } 4719391bb9cSRapkiewicz, Pawel res.end(); 4729391bb9cSRapkiewicz, Pawel }); 4739391bb9cSRapkiewicz, Pawel } 4749391bb9cSRapkiewicz, Pawel 4759391bb9cSRapkiewicz, Pawel // Ethernet Provider object 4769391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 4779391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 4789391bb9cSRapkiewicz, Pawel }; 4799391bb9cSRapkiewicz, Pawel 4809391bb9cSRapkiewicz, Pawel /** 4819391bb9cSRapkiewicz, Pawel * EthernetInterface derived class for delivering Ethernet Schema 4829391bb9cSRapkiewicz, Pawel */ 4839391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node { 4849391bb9cSRapkiewicz, Pawel public: 4859391bb9cSRapkiewicz, Pawel /* 4869391bb9cSRapkiewicz, Pawel * Default Constructor 4879391bb9cSRapkiewicz, Pawel */ 4889391bb9cSRapkiewicz, Pawel template <typename CrowApp> 4899391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 4909391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 4919391bb9cSRapkiewicz, Pawel // GetSubroutes method 4929391bb9cSRapkiewicz, Pawel EthernetInterface(CrowApp &app) 4939391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", 4949391bb9cSRapkiewicz, Pawel std::string()) { 4959391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; 4969391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 4979391bb9cSRapkiewicz, Pawel "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 4989391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Manager Ethernet Interface"; 4999391bb9cSRapkiewicz, Pawel Node::json["Description"] = "Management Network Interface"; 5009391bb9cSRapkiewicz, Pawel 501*588c3f0dSKowalski, Kamil entityPrivileges = { 502*588c3f0dSKowalski, Kamil {boost::beast::http::verb::get, {{"Login"}}}, 503e0d918bcSEd Tanous {boost::beast::http::verb::head, {{"Login"}}}, 504e0d918bcSEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 505e0d918bcSEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 506e0d918bcSEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 507e0d918bcSEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 5089391bb9cSRapkiewicz, Pawel } 5099391bb9cSRapkiewicz, Pawel 5109391bb9cSRapkiewicz, Pawel private: 511*588c3f0dSKowalski, Kamil void handleVlanPatch(const std::string &ifaceId, const nlohmann::json &input, 512*588c3f0dSKowalski, Kamil const EthernetInterfaceData ð_data, 513*588c3f0dSKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 514*588c3f0dSKowalski, Kamil if (!input.is_object()) { 515*588c3f0dSKowalski, Kamil messages::addMessageToJson( 516*588c3f0dSKowalski, Kamil asyncResp->res.json_value, 517*588c3f0dSKowalski, Kamil messages::propertyValueTypeError(input.dump(), "VLAN"), "/VLAN"); 518*588c3f0dSKowalski, Kamil return; 519*588c3f0dSKowalski, Kamil } 520*588c3f0dSKowalski, Kamil 521*588c3f0dSKowalski, Kamil bool inputVlanEnabled; 522*588c3f0dSKowalski, Kamil uint64_t inputVlanId; 523*588c3f0dSKowalski, Kamil json_util::Result inputVlanEnabledState = json_util::getBool( 524*588c3f0dSKowalski, Kamil "VLANEnable", input, inputVlanEnabled, 525*588c3f0dSKowalski, Kamil static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 526*588c3f0dSKowalski, Kamil asyncResp->res.json_value, std::string("/VLAN/VLANEnable")); 527*588c3f0dSKowalski, Kamil json_util::Result inputVlanIdState = json_util::getUnsigned( 528*588c3f0dSKowalski, Kamil "VLANId", input, inputVlanId, 529*588c3f0dSKowalski, Kamil static_cast<int>(json_util::MessageSetting::TYPE_ERROR), 530*588c3f0dSKowalski, Kamil asyncResp->res.json_value, std::string("/VLAN/VLANId")); 531*588c3f0dSKowalski, Kamil bool inputInvalid = false; 532*588c3f0dSKowalski, Kamil 533*588c3f0dSKowalski, Kamil // Do not proceed if fields in VLAN object were of wrong type 534*588c3f0dSKowalski, Kamil if (inputVlanEnabledState == json_util::Result::WRONG_TYPE || 535*588c3f0dSKowalski, Kamil inputVlanIdState == json_util::Result::WRONG_TYPE) { 536*588c3f0dSKowalski, Kamil return; 537*588c3f0dSKowalski, Kamil } 538*588c3f0dSKowalski, Kamil 539*588c3f0dSKowalski, Kamil // Verify input 540*588c3f0dSKowalski, Kamil if (eth_data.vlan_id == nullptr) { 541*588c3f0dSKowalski, Kamil // VLAN is currently disabled. User can only create/enable it. Change of 542*588c3f0dSKowalski, Kamil // VLANId is prohibited, and disable request (VLANEnabled == false) will 543*588c3f0dSKowalski, Kamil // not have any effect. 544*588c3f0dSKowalski, Kamil if (inputVlanEnabledState == json_util::Result::SUCCESS && 545*588c3f0dSKowalski, Kamil inputVlanEnabled == true) { 546*588c3f0dSKowalski, Kamil // Creation requested, user should also provide ID for new VLAN 547*588c3f0dSKowalski, Kamil if (inputVlanIdState != json_util::Result::SUCCESS) { 548*588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 549*588c3f0dSKowalski, Kamil messages::propertyMissing("VLANId"), 550*588c3f0dSKowalski, Kamil "/VLAN"); 551*588c3f0dSKowalski, Kamil inputInvalid = true; 552*588c3f0dSKowalski, Kamil } 553*588c3f0dSKowalski, Kamil } else if (inputVlanIdState == json_util::Result::SUCCESS) { 554*588c3f0dSKowalski, Kamil // VLAN is disabled, but user requested modification. This is not valid. 555*588c3f0dSKowalski, Kamil messages::addMessageToJson( 556*588c3f0dSKowalski, Kamil asyncResp->res.json_value, 557*588c3f0dSKowalski, Kamil messages::actionParameterNotSupported("VLANId", "change VLAN Id"), 558*588c3f0dSKowalski, Kamil "/VLAN"); 559*588c3f0dSKowalski, Kamil 560*588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 561*588c3f0dSKowalski, Kamil messages::propertyMissing("VLANEnable"), 562*588c3f0dSKowalski, Kamil "/VLAN"); 563*588c3f0dSKowalski, Kamil 564*588c3f0dSKowalski, Kamil inputInvalid = true; 565*588c3f0dSKowalski, Kamil } 566*588c3f0dSKowalski, Kamil } else { 567*588c3f0dSKowalski, Kamil // Load actual data into field values if they were not provided 568*588c3f0dSKowalski, Kamil if (inputVlanEnabledState == json_util::Result::NOT_EXIST) { 569*588c3f0dSKowalski, Kamil inputVlanEnabled = true; 570*588c3f0dSKowalski, Kamil } 571*588c3f0dSKowalski, Kamil 572*588c3f0dSKowalski, Kamil if (inputVlanIdState == json_util::Result::NOT_EXIST) { 573*588c3f0dSKowalski, Kamil inputVlanId = *eth_data.vlan_id; 574*588c3f0dSKowalski, Kamil } 575*588c3f0dSKowalski, Kamil } 576*588c3f0dSKowalski, Kamil 577*588c3f0dSKowalski, Kamil // Do not proceed if input has not been valid 578*588c3f0dSKowalski, Kamil if (inputInvalid) { 579*588c3f0dSKowalski, Kamil return; 580*588c3f0dSKowalski, Kamil } 581*588c3f0dSKowalski, Kamil 582*588c3f0dSKowalski, Kamil auto vlanEnabledAfterOperation = 583*588c3f0dSKowalski, Kamil [asyncResp](const boost::system::error_code ec) { 584*588c3f0dSKowalski, Kamil if (ec) { 585*588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 586*588c3f0dSKowalski, Kamil messages::internalError(), "/VLAN"); 587*588c3f0dSKowalski, Kamil } else { 588*588c3f0dSKowalski, Kamil asyncResp->res.json_value["VLAN"]["VLANEnable"] = true; 589*588c3f0dSKowalski, Kamil } 590*588c3f0dSKowalski, Kamil }; 591*588c3f0dSKowalski, Kamil 592*588c3f0dSKowalski, Kamil if (eth_data.vlan_id == nullptr) { 593*588c3f0dSKowalski, Kamil if (inputVlanEnabled == true) { 594*588c3f0dSKowalski, Kamil ethernet_provider.createVlan(ifaceId, inputVlanId, 595*588c3f0dSKowalski, Kamil std::move(vlanEnabledAfterOperation)); 596*588c3f0dSKowalski, Kamil asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId; 597*588c3f0dSKowalski, Kamil } 598*588c3f0dSKowalski, Kamil } else { 599*588c3f0dSKowalski, Kamil // VLAN is configured on the interface 600*588c3f0dSKowalski, Kamil if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) { 601*588c3f0dSKowalski, Kamil // Change VLAN Id 602*588c3f0dSKowalski, Kamil asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId; 603*588c3f0dSKowalski, Kamil ethernet_provider.changeVlanId(ifaceId, 604*588c3f0dSKowalski, Kamil static_cast<uint32_t>(inputVlanId), 605*588c3f0dSKowalski, Kamil std::move(vlanEnabledAfterOperation)); 606*588c3f0dSKowalski, Kamil } else if (inputVlanEnabled == false) { 607*588c3f0dSKowalski, Kamil // Disable VLAN 608*588c3f0dSKowalski, Kamil ethernet_provider.disableVlan( 609*588c3f0dSKowalski, Kamil ifaceId, [asyncResp](const boost::system::error_code ec) { 610*588c3f0dSKowalski, Kamil if (ec) { 611*588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 612*588c3f0dSKowalski, Kamil messages::internalError(), "/VLAN"); 613*588c3f0dSKowalski, Kamil } else { 614*588c3f0dSKowalski, Kamil asyncResp->res.json_value["VLAN"]["VLANEnable"] = false; 615*588c3f0dSKowalski, Kamil } 616*588c3f0dSKowalski, Kamil }); 617*588c3f0dSKowalski, Kamil } 618*588c3f0dSKowalski, Kamil } 619*588c3f0dSKowalski, Kamil } 620*588c3f0dSKowalski, Kamil 621*588c3f0dSKowalski, Kamil void handleHostnamePatch(const nlohmann::json &input, 622*588c3f0dSKowalski, Kamil const EthernetInterfaceData ð_data, 623*588c3f0dSKowalski, Kamil const std::shared_ptr<AsyncResp> &asyncResp) { 624*588c3f0dSKowalski, Kamil if (input.is_string()) { 625*588c3f0dSKowalski, Kamil std::string newHostname = input.get<std::string>(); 626*588c3f0dSKowalski, Kamil 627*588c3f0dSKowalski, Kamil if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) { 628*588c3f0dSKowalski, Kamil // Change hostname 629*588c3f0dSKowalski, Kamil ethernet_provider.setHostName( 630*588c3f0dSKowalski, Kamil newHostname, 631*588c3f0dSKowalski, Kamil [asyncResp, newHostname](const boost::system::error_code ec) { 632*588c3f0dSKowalski, Kamil if (ec) { 633*588c3f0dSKowalski, Kamil messages::addMessageToJson(asyncResp->res.json_value, 634*588c3f0dSKowalski, Kamil messages::internalError(), 635*588c3f0dSKowalski, Kamil "/HostName"); 636*588c3f0dSKowalski, Kamil } else { 637*588c3f0dSKowalski, Kamil asyncResp->res.json_value["HostName"] = newHostname; 638*588c3f0dSKowalski, Kamil } 639*588c3f0dSKowalski, Kamil }); 640*588c3f0dSKowalski, Kamil } 641*588c3f0dSKowalski, Kamil } else { 642*588c3f0dSKowalski, Kamil messages::addMessageToJson( 643*588c3f0dSKowalski, Kamil asyncResp->res.json_value, 644*588c3f0dSKowalski, Kamil messages::propertyValueTypeError(input.dump(), "HostName"), 645*588c3f0dSKowalski, Kamil "/HostName"); 646*588c3f0dSKowalski, Kamil } 647*588c3f0dSKowalski, Kamil } 648*588c3f0dSKowalski, Kamil 649*588c3f0dSKowalski, Kamil nlohmann::json parseInterfaceData( 650*588c3f0dSKowalski, Kamil const std::string &iface_id, const EthernetInterfaceData ð_data, 651*588c3f0dSKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 652*588c3f0dSKowalski, Kamil // Copy JSON object to avoid race condition 653*588c3f0dSKowalski, Kamil nlohmann::json json_response(Node::json); 654*588c3f0dSKowalski, Kamil 655*588c3f0dSKowalski, Kamil // Fill out obvious data... 656*588c3f0dSKowalski, Kamil json_response["Id"] = iface_id; 657*588c3f0dSKowalski, Kamil json_response["@odata.id"] = 658*588c3f0dSKowalski, Kamil "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id; 659*588c3f0dSKowalski, Kamil 660*588c3f0dSKowalski, Kamil // ... then the one from DBus, regarding eth iface... 661*588c3f0dSKowalski, Kamil if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed; 662*588c3f0dSKowalski, Kamil 663*588c3f0dSKowalski, Kamil if (eth_data.mac_address != nullptr) 664*588c3f0dSKowalski, Kamil json_response["MACAddress"] = *eth_data.mac_address; 665*588c3f0dSKowalski, Kamil 666*588c3f0dSKowalski, Kamil if (eth_data.hostname != nullptr) 667*588c3f0dSKowalski, Kamil json_response["HostName"] = *eth_data.hostname; 668*588c3f0dSKowalski, Kamil 669*588c3f0dSKowalski, Kamil if (eth_data.vlan_id != nullptr) { 670*588c3f0dSKowalski, Kamil nlohmann::json &vlanObj = json_response["VLAN"]; 671*588c3f0dSKowalski, Kamil vlanObj["VLANEnable"] = true; 672*588c3f0dSKowalski, Kamil vlanObj["VLANId"] = *eth_data.vlan_id; 673*588c3f0dSKowalski, Kamil } 674*588c3f0dSKowalski, Kamil 675*588c3f0dSKowalski, Kamil // ... at last, check if there are IPv4 data and prepare appropriate 676*588c3f0dSKowalski, Kamil // collection 677*588c3f0dSKowalski, Kamil if (ipv4_data.size() > 0) { 678*588c3f0dSKowalski, Kamil nlohmann::json ipv4_array = nlohmann::json::array(); 679*588c3f0dSKowalski, Kamil for (auto &ipv4_config : ipv4_data) { 680*588c3f0dSKowalski, Kamil nlohmann::json json_ipv4; 681*588c3f0dSKowalski, Kamil if (ipv4_config.address != nullptr) { 682*588c3f0dSKowalski, Kamil json_ipv4["Address"] = *ipv4_config.address; 683*588c3f0dSKowalski, Kamil if (ipv4_config.gateway != nullptr) 684*588c3f0dSKowalski, Kamil json_ipv4["Gateway"] = *ipv4_config.gateway; 685*588c3f0dSKowalski, Kamil 686*588c3f0dSKowalski, Kamil json_ipv4["AddressOrigin"] = ipv4_config.origin; 687*588c3f0dSKowalski, Kamil json_ipv4["SubnetMask"] = ipv4_config.netmask; 688*588c3f0dSKowalski, Kamil 689*588c3f0dSKowalski, Kamil ipv4_array.push_back(std::move(json_ipv4)); 690*588c3f0dSKowalski, Kamil } 691*588c3f0dSKowalski, Kamil } 692*588c3f0dSKowalski, Kamil json_response["IPv4Addresses"] = std::move(ipv4_array); 693*588c3f0dSKowalski, Kamil } 694*588c3f0dSKowalski, Kamil 695*588c3f0dSKowalski, Kamil return json_response; 696*588c3f0dSKowalski, Kamil } 697*588c3f0dSKowalski, Kamil 6989391bb9cSRapkiewicz, Pawel /** 6999391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 7009391bb9cSRapkiewicz, Pawel */ 7019391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 7029391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 7039391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call (two params) to get 7049391bb9cSRapkiewicz, Pawel // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 7059391bb9cSRapkiewicz, Pawel // Check if there is required param, truly entering this shall be 7069391bb9cSRapkiewicz, Pawel // impossible. 7079391bb9cSRapkiewicz, Pawel if (params.size() != 1) { 708e0d918bcSEd Tanous res.result(boost::beast::http::status::internal_server_error); 7099391bb9cSRapkiewicz, Pawel res.end(); 7109391bb9cSRapkiewicz, Pawel return; 7119391bb9cSRapkiewicz, Pawel } 7129391bb9cSRapkiewicz, Pawel 7139391bb9cSRapkiewicz, Pawel const std::string &iface_id = params[0]; 7149391bb9cSRapkiewicz, Pawel 7159391bb9cSRapkiewicz, Pawel // Get single eth interface data, and call the below callback for JSON 7169391bb9cSRapkiewicz, Pawel // preparation 7179391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceData( 7189391bb9cSRapkiewicz, Pawel iface_id, [&, iface_id](const bool &success, 7199391bb9cSRapkiewicz, Pawel const EthernetInterfaceData ð_data, 7209391bb9cSRapkiewicz, Pawel const std::vector<IPv4AddressData> &ipv4_data) { 7219391bb9cSRapkiewicz, Pawel if (success) { 722*588c3f0dSKowalski, Kamil res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 7239391bb9cSRapkiewicz, Pawel } else { 7249391bb9cSRapkiewicz, Pawel // ... otherwise return error 7259391bb9cSRapkiewicz, Pawel // TODO(Pawel)consider distinguish between non existing object, and 7269391bb9cSRapkiewicz, Pawel // other errors 727e0d918bcSEd Tanous res.result(boost::beast::http::status::not_found); 7289391bb9cSRapkiewicz, Pawel } 7299391bb9cSRapkiewicz, Pawel res.end(); 7309391bb9cSRapkiewicz, Pawel }); 7319391bb9cSRapkiewicz, Pawel } 7329391bb9cSRapkiewicz, Pawel 733*588c3f0dSKowalski, Kamil void doPatch(crow::response &res, const crow::request &req, 734*588c3f0dSKowalski, Kamil const std::vector<std::string> ¶ms) override { 735*588c3f0dSKowalski, Kamil // TODO(Pawel) this shall be parametrized call (two params) to get 736*588c3f0dSKowalski, Kamil // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 737*588c3f0dSKowalski, Kamil // Check if there is required param, truly entering this shall be 738*588c3f0dSKowalski, Kamil // impossible. 739*588c3f0dSKowalski, Kamil if (params.size() != 1) { 740*588c3f0dSKowalski, Kamil res.result(boost::beast::http::status::internal_server_error); 741*588c3f0dSKowalski, Kamil res.end(); 742*588c3f0dSKowalski, Kamil return; 743*588c3f0dSKowalski, Kamil } 744*588c3f0dSKowalski, Kamil 745*588c3f0dSKowalski, Kamil const std::string &iface_id = params[0]; 746*588c3f0dSKowalski, Kamil 747*588c3f0dSKowalski, Kamil nlohmann::json patchReq = nlohmann::json::parse(req.body, nullptr, false); 748*588c3f0dSKowalski, Kamil 749*588c3f0dSKowalski, Kamil if (patchReq.is_discarded()) { 750*588c3f0dSKowalski, Kamil messages::addMessageToErrorJson(res.json_value, 751*588c3f0dSKowalski, Kamil messages::malformedJSON()); 752*588c3f0dSKowalski, Kamil 753*588c3f0dSKowalski, Kamil res.result(boost::beast::http::status::bad_request); 754*588c3f0dSKowalski, Kamil res.end(); 755*588c3f0dSKowalski, Kamil 756*588c3f0dSKowalski, Kamil return; 757*588c3f0dSKowalski, Kamil } 758*588c3f0dSKowalski, Kamil 759*588c3f0dSKowalski, Kamil // Get single eth interface data, and call the below callback for JSON 760*588c3f0dSKowalski, Kamil // preparation 761*588c3f0dSKowalski, Kamil ethernet_provider.getEthernetIfaceData( 762*588c3f0dSKowalski, Kamil iface_id, 763*588c3f0dSKowalski, Kamil [&, iface_id, patchReq = std::move(patchReq) ]( 764*588c3f0dSKowalski, Kamil const bool &success, const EthernetInterfaceData ð_data, 765*588c3f0dSKowalski, Kamil const std::vector<IPv4AddressData> &ipv4_data) { 766*588c3f0dSKowalski, Kamil if (!success) { 767*588c3f0dSKowalski, Kamil // ... otherwise return error 768*588c3f0dSKowalski, Kamil // TODO(Pawel)consider distinguish between non existing object, and 769*588c3f0dSKowalski, Kamil // other errors 770*588c3f0dSKowalski, Kamil res.result(boost::beast::http::status::not_found); 771*588c3f0dSKowalski, Kamil res.end(); 772*588c3f0dSKowalski, Kamil 773*588c3f0dSKowalski, Kamil return; 774*588c3f0dSKowalski, Kamil } 775*588c3f0dSKowalski, Kamil 776*588c3f0dSKowalski, Kamil res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data); 777*588c3f0dSKowalski, Kamil 778*588c3f0dSKowalski, Kamil std::shared_ptr<AsyncResp> asyncResp = 779*588c3f0dSKowalski, Kamil std::make_shared<AsyncResp>(res); 780*588c3f0dSKowalski, Kamil 781*588c3f0dSKowalski, Kamil for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end(); 782*588c3f0dSKowalski, Kamil ++propertyIt) { 783*588c3f0dSKowalski, Kamil if (propertyIt.key() == "VLAN") { 784*588c3f0dSKowalski, Kamil handleVlanPatch(iface_id, propertyIt.value(), eth_data, 785*588c3f0dSKowalski, Kamil asyncResp); 786*588c3f0dSKowalski, Kamil } else if (propertyIt.key() == "HostName") { 787*588c3f0dSKowalski, Kamil handleHostnamePatch(propertyIt.value(), eth_data, asyncResp); 788*588c3f0dSKowalski, Kamil /* TODO(kkowalsk) Implement it in further patchset 789*588c3f0dSKowalski, Kamil } else if (propertyIt.key() == "IPv4Addresses" || propertyIt.key() 790*588c3f0dSKowalski, Kamil == "IPv6Addresses") {*/ 791*588c3f0dSKowalski, Kamil } else { 792*588c3f0dSKowalski, Kamil auto fieldInJsonIt = res.json_value.find(propertyIt.key()); 793*588c3f0dSKowalski, Kamil 794*588c3f0dSKowalski, Kamil if (fieldInJsonIt == res.json_value.end()) { 795*588c3f0dSKowalski, Kamil // Field not in scope of defined fields 796*588c3f0dSKowalski, Kamil messages::addMessageToJsonRoot( 797*588c3f0dSKowalski, Kamil res.json_value, 798*588c3f0dSKowalski, Kamil messages::propertyUnknown(propertyIt.key())); 799*588c3f0dSKowalski, Kamil } else if (*fieldInJsonIt != *propertyIt) { 800*588c3f0dSKowalski, Kamil // User attempted to modify non-writable field 801*588c3f0dSKowalski, Kamil messages::addMessageToJsonRoot( 802*588c3f0dSKowalski, Kamil res.json_value, 803*588c3f0dSKowalski, Kamil messages::propertyNotWritable(propertyIt.key())); 804*588c3f0dSKowalski, Kamil } 805*588c3f0dSKowalski, Kamil } 806*588c3f0dSKowalski, Kamil } 807*588c3f0dSKowalski, Kamil }); 808*588c3f0dSKowalski, Kamil } 809*588c3f0dSKowalski, Kamil 8109391bb9cSRapkiewicz, Pawel // Ethernet Provider object 8119391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 8129391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 8139391bb9cSRapkiewicz, Pawel }; 8149391bb9cSRapkiewicz, Pawel 8159391bb9cSRapkiewicz, Pawel } // namespace redfish 816