1*9391bb9cSRapkiewicz, Pawel /* 2*9391bb9cSRapkiewicz, Pawel // Copyright (c) 2018 Intel Corporation 3*9391bb9cSRapkiewicz, Pawel // 4*9391bb9cSRapkiewicz, Pawel // Licensed under the Apache License, Version 2.0 (the "License"); 5*9391bb9cSRapkiewicz, Pawel // you may not use this file except in compliance with the License. 6*9391bb9cSRapkiewicz, Pawel // You may obtain a copy of the License at 7*9391bb9cSRapkiewicz, Pawel // 8*9391bb9cSRapkiewicz, Pawel // http://www.apache.org/licenses/LICENSE-2.0 9*9391bb9cSRapkiewicz, Pawel // 10*9391bb9cSRapkiewicz, Pawel // Unless required by applicable law or agreed to in writing, software 11*9391bb9cSRapkiewicz, Pawel // distributed under the License is distributed on an "AS IS" BASIS, 12*9391bb9cSRapkiewicz, Pawel // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*9391bb9cSRapkiewicz, Pawel // See the License for the specific language governing permissions and 14*9391bb9cSRapkiewicz, Pawel // limitations under the License. 15*9391bb9cSRapkiewicz, Pawel */ 16*9391bb9cSRapkiewicz, Pawel #pragma once 17*9391bb9cSRapkiewicz, Pawel 18*9391bb9cSRapkiewicz, Pawel #include "node.hpp" 19*9391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp> 20*9391bb9cSRapkiewicz, Pawel 21*9391bb9cSRapkiewicz, Pawel namespace redfish { 22*9391bb9cSRapkiewicz, Pawel 23*9391bb9cSRapkiewicz, Pawel /** 24*9391bb9cSRapkiewicz, Pawel * DBus types primitives for several generic DBus interfaces 25*9391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider move this to separate file into boost::dbus 26*9391bb9cSRapkiewicz, Pawel */ 27*9391bb9cSRapkiewicz, Pawel using PropertiesMapType = 28*9391bb9cSRapkiewicz, Pawel boost::container::flat_map<std::string, dbus::dbus_variant>; 29*9391bb9cSRapkiewicz, Pawel 30*9391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map< 31*9391bb9cSRapkiewicz, Pawel dbus::object_path, 32*9391bb9cSRapkiewicz, Pawel boost::container::flat_map<std::string, PropertiesMapType>>; 33*9391bb9cSRapkiewicz, Pawel 34*9391bb9cSRapkiewicz, Pawel using GetAllPropertiesType = PropertiesMapType; 35*9391bb9cSRapkiewicz, Pawel 36*9391bb9cSRapkiewicz, Pawel /** 37*9391bb9cSRapkiewicz, Pawel * Structure for keeping IPv4 data required by Redfish 38*9391bb9cSRapkiewicz, Pawel * TODO(Pawel) consider change everything to ptr, or to non-ptr values. 39*9391bb9cSRapkiewicz, Pawel */ 40*9391bb9cSRapkiewicz, Pawel struct IPv4AddressData { 41*9391bb9cSRapkiewicz, Pawel const std::string *address; 42*9391bb9cSRapkiewicz, Pawel const std::string *domain; 43*9391bb9cSRapkiewicz, Pawel const std::string *gateway; 44*9391bb9cSRapkiewicz, Pawel std::string netmask; 45*9391bb9cSRapkiewicz, Pawel std::string origin; 46*9391bb9cSRapkiewicz, Pawel bool global; 47*9391bb9cSRapkiewicz, Pawel }; 48*9391bb9cSRapkiewicz, Pawel 49*9391bb9cSRapkiewicz, Pawel /** 50*9391bb9cSRapkiewicz, Pawel * Structure for keeping basic single Ethernet Interface information 51*9391bb9cSRapkiewicz, Pawel * available from DBus 52*9391bb9cSRapkiewicz, Pawel */ 53*9391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData { 54*9391bb9cSRapkiewicz, Pawel const unsigned int *speed; 55*9391bb9cSRapkiewicz, Pawel const bool *auto_neg; 56*9391bb9cSRapkiewicz, Pawel const std::string *hostname; 57*9391bb9cSRapkiewicz, Pawel const std::string *default_gateway; 58*9391bb9cSRapkiewicz, Pawel const std::string *mac_address; 59*9391bb9cSRapkiewicz, Pawel }; 60*9391bb9cSRapkiewicz, Pawel 61*9391bb9cSRapkiewicz, Pawel /** 62*9391bb9cSRapkiewicz, Pawel * OnDemandEthernetProvider 63*9391bb9cSRapkiewicz, Pawel * Ethernet provider class that retrieves data directly from dbus, before seting 64*9391bb9cSRapkiewicz, Pawel * it into JSON output. This does not cache any data. 65*9391bb9cSRapkiewicz, Pawel * 66*9391bb9cSRapkiewicz, Pawel * TODO(Pawel) 67*9391bb9cSRapkiewicz, Pawel * This perhaps shall be different file, which has to be chosen on compile time 68*9391bb9cSRapkiewicz, Pawel * depending on OEM needs 69*9391bb9cSRapkiewicz, Pawel */ 70*9391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider { 71*9391bb9cSRapkiewicz, Pawel private: 72*9391bb9cSRapkiewicz, Pawel // Consts that may have influence on EthernetProvider performance/memory usage 73*9391bb9cSRapkiewicz, Pawel const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10; 74*9391bb9cSRapkiewicz, Pawel 75*9391bb9cSRapkiewicz, Pawel // Helper function that allows to extract GetAllPropertiesType from 76*9391bb9cSRapkiewicz, Pawel // GetManagedObjectsType, based on object path, and interface name 77*9391bb9cSRapkiewicz, Pawel const PropertiesMapType *extractInterfaceProperties( 78*9391bb9cSRapkiewicz, Pawel const dbus::object_path &objpath, const std::string &interface, 79*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data) { 80*9391bb9cSRapkiewicz, Pawel const auto &dbus_obj = dbus_data.find(objpath); 81*9391bb9cSRapkiewicz, Pawel if (dbus_obj != dbus_data.end()) { 82*9391bb9cSRapkiewicz, Pawel const auto &iface = dbus_obj->second.find(interface); 83*9391bb9cSRapkiewicz, Pawel if (iface != dbus_obj->second.end()) { 84*9391bb9cSRapkiewicz, Pawel return &iface->second; 85*9391bb9cSRapkiewicz, Pawel } 86*9391bb9cSRapkiewicz, Pawel } 87*9391bb9cSRapkiewicz, Pawel return nullptr; 88*9391bb9cSRapkiewicz, Pawel } 89*9391bb9cSRapkiewicz, Pawel 90*9391bb9cSRapkiewicz, Pawel // Helper Wrapper that does inline object_path conversion from string 91*9391bb9cSRapkiewicz, Pawel // into dbus::object_path type 92*9391bb9cSRapkiewicz, Pawel inline const PropertiesMapType *extractInterfaceProperties( 93*9391bb9cSRapkiewicz, Pawel const std::string &objpath, const std::string &interface, 94*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data) { 95*9391bb9cSRapkiewicz, Pawel const auto &dbus_obj = dbus::object_path{objpath}; 96*9391bb9cSRapkiewicz, Pawel return extractInterfaceProperties(dbus_obj, interface, dbus_data); 97*9391bb9cSRapkiewicz, Pawel } 98*9391bb9cSRapkiewicz, Pawel 99*9391bb9cSRapkiewicz, Pawel // Helper function that allows to get pointer to the property from 100*9391bb9cSRapkiewicz, Pawel // GetAllPropertiesType native, or extracted by GetAllPropertiesType 101*9391bb9cSRapkiewicz, Pawel template <typename T> 102*9391bb9cSRapkiewicz, Pawel inline const T *extractProperty(const PropertiesMapType &properties, 103*9391bb9cSRapkiewicz, Pawel const std::string &name) { 104*9391bb9cSRapkiewicz, Pawel const auto &property = properties.find(name); 105*9391bb9cSRapkiewicz, Pawel if (property != properties.end()) { 106*9391bb9cSRapkiewicz, Pawel return boost::get<T>(&property->second); 107*9391bb9cSRapkiewicz, Pawel } 108*9391bb9cSRapkiewicz, Pawel return nullptr; 109*9391bb9cSRapkiewicz, Pawel } 110*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) Consider to move the above functions to dbus 111*9391bb9cSRapkiewicz, Pawel // generic_interfaces.hpp 112*9391bb9cSRapkiewicz, Pawel 113*9391bb9cSRapkiewicz, Pawel // Helper function that extracts data from several dbus objects and several 114*9391bb9cSRapkiewicz, Pawel // interfaces required by single ethernet interface instance 115*9391bb9cSRapkiewicz, Pawel void extractEthernetInterfaceData(const std::string ðiface_id, 116*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 117*9391bb9cSRapkiewicz, Pawel EthernetInterfaceData ð_data) { 118*9391bb9cSRapkiewicz, Pawel // Extract data that contains MAC Address 119*9391bb9cSRapkiewicz, Pawel const PropertiesMapType *mac_properties = extractInterfaceProperties( 120*9391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 121*9391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.MACAddress", dbus_data); 122*9391bb9cSRapkiewicz, Pawel 123*9391bb9cSRapkiewicz, Pawel if (mac_properties != nullptr) { 124*9391bb9cSRapkiewicz, Pawel eth_data.mac_address = 125*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*mac_properties, "MACAddress"); 126*9391bb9cSRapkiewicz, Pawel } 127*9391bb9cSRapkiewicz, Pawel 128*9391bb9cSRapkiewicz, Pawel // Extract data that contains link information (auto negotiation and speed) 129*9391bb9cSRapkiewicz, Pawel const PropertiesMapType *eth_properties = extractInterfaceProperties( 130*9391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id, 131*9391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface", dbus_data); 132*9391bb9cSRapkiewicz, Pawel 133*9391bb9cSRapkiewicz, Pawel if (eth_properties != nullptr) { 134*9391bb9cSRapkiewicz, Pawel eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg"); 135*9391bb9cSRapkiewicz, Pawel eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed"); 136*9391bb9cSRapkiewicz, Pawel } 137*9391bb9cSRapkiewicz, Pawel 138*9391bb9cSRapkiewicz, Pawel // Extract data that contains network config (HostName and DefaultGW) 139*9391bb9cSRapkiewicz, Pawel const PropertiesMapType *config_properties = extractInterfaceProperties( 140*9391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/config", 141*9391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); 142*9391bb9cSRapkiewicz, Pawel 143*9391bb9cSRapkiewicz, Pawel if (config_properties != nullptr) { 144*9391bb9cSRapkiewicz, Pawel eth_data.hostname = 145*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "HostName"); 146*9391bb9cSRapkiewicz, Pawel eth_data.default_gateway = 147*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(*config_properties, "DefaultGateway"); 148*9391bb9cSRapkiewicz, Pawel } 149*9391bb9cSRapkiewicz, Pawel } 150*9391bb9cSRapkiewicz, Pawel 151*9391bb9cSRapkiewicz, Pawel // Helper function that changes bits netmask notation (i.e. /24) 152*9391bb9cSRapkiewicz, Pawel // into full dot notation 153*9391bb9cSRapkiewicz, Pawel inline std::string getNetmask(unsigned int bits) { 154*9391bb9cSRapkiewicz, Pawel uint32_t value = 0xffffffff << (32 - bits); 155*9391bb9cSRapkiewicz, Pawel std::string netmask = std::to_string((value >> 24) & 0xff) + "." + 156*9391bb9cSRapkiewicz, Pawel std::to_string((value >> 16) & 0xff) + "." + 157*9391bb9cSRapkiewicz, Pawel std::to_string((value >> 8) & 0xff) + "." + 158*9391bb9cSRapkiewicz, Pawel std::to_string(value & 0xff); 159*9391bb9cSRapkiewicz, Pawel return netmask; 160*9391bb9cSRapkiewicz, Pawel } 161*9391bb9cSRapkiewicz, Pawel 162*9391bb9cSRapkiewicz, Pawel // Helper function that extracts data for single ethernet ipv4 address 163*9391bb9cSRapkiewicz, Pawel void extractIPv4Data(const std::string ðiface_id, 164*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &dbus_data, 165*9391bb9cSRapkiewicz, Pawel std::vector<IPv4AddressData> &ipv4_config) { 166*9391bb9cSRapkiewicz, Pawel // Since there might be several IPv4 configurations aligned with 167*9391bb9cSRapkiewicz, Pawel // single ethernet interface, loop over all of them 168*9391bb9cSRapkiewicz, Pawel for (auto &objpath : dbus_data) { 169*9391bb9cSRapkiewicz, Pawel // Check if propper patter for object path appears 170*9391bb9cSRapkiewicz, Pawel if (boost::starts_with( 171*9391bb9cSRapkiewicz, Pawel objpath.first.value, 172*9391bb9cSRapkiewicz, Pawel "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) { 173*9391bb9cSRapkiewicz, Pawel // and get approrpiate interface 174*9391bb9cSRapkiewicz, Pawel const auto &interface = 175*9391bb9cSRapkiewicz, Pawel objpath.second.find("xyz.openbmc_project.Network.IP"); 176*9391bb9cSRapkiewicz, Pawel if (interface != objpath.second.end()) { 177*9391bb9cSRapkiewicz, Pawel // Make a properties 'shortcut', to make everything more readable 178*9391bb9cSRapkiewicz, Pawel const PropertiesMapType &properties = interface->second; 179*9391bb9cSRapkiewicz, Pawel // Instance IPv4AddressData structure, and set as appropriate 180*9391bb9cSRapkiewicz, Pawel IPv4AddressData ipv4_address; 181*9391bb9cSRapkiewicz, Pawel // IPv4 address 182*9391bb9cSRapkiewicz, Pawel ipv4_address.address = 183*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Address"); 184*9391bb9cSRapkiewicz, Pawel // IPv4 gateway 185*9391bb9cSRapkiewicz, Pawel ipv4_address.gateway = 186*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Gateway"); 187*9391bb9cSRapkiewicz, Pawel 188*9391bb9cSRapkiewicz, Pawel // Origin is kind of DBus object so fetch pointer... 189*9391bb9cSRapkiewicz, Pawel const std::string *origin = 190*9391bb9cSRapkiewicz, Pawel extractProperty<std::string>(properties, "Origin"); 191*9391bb9cSRapkiewicz, Pawel if (origin != nullptr) { 192*9391bb9cSRapkiewicz, Pawel // ... and get everything after last dot 193*9391bb9cSRapkiewicz, Pawel int last = origin->rfind("."); 194*9391bb9cSRapkiewicz, Pawel if (last != std::string::npos) { 195*9391bb9cSRapkiewicz, Pawel ipv4_address.origin = origin->substr(last + 1); 196*9391bb9cSRapkiewicz, Pawel } 197*9391bb9cSRapkiewicz, Pawel } 198*9391bb9cSRapkiewicz, Pawel 199*9391bb9cSRapkiewicz, Pawel // Netmask is presented as PrefixLength 200*9391bb9cSRapkiewicz, Pawel const auto *mask = 201*9391bb9cSRapkiewicz, Pawel extractProperty<uint8_t>(properties, "PrefixLength"); 202*9391bb9cSRapkiewicz, Pawel if (mask != nullptr) { 203*9391bb9cSRapkiewicz, Pawel // convert it to the string 204*9391bb9cSRapkiewicz, Pawel ipv4_address.netmask = getNetmask(*mask); 205*9391bb9cSRapkiewicz, Pawel } 206*9391bb9cSRapkiewicz, Pawel 207*9391bb9cSRapkiewicz, Pawel // Attach IPv4 only if address is present 208*9391bb9cSRapkiewicz, Pawel if (ipv4_address.address != nullptr) { 209*9391bb9cSRapkiewicz, Pawel // Check if given addres is local, or global 210*9391bb9cSRapkiewicz, Pawel if (boost::starts_with(*ipv4_address.address, "169.254")) { 211*9391bb9cSRapkiewicz, Pawel ipv4_address.global = false; 212*9391bb9cSRapkiewicz, Pawel } else { 213*9391bb9cSRapkiewicz, Pawel ipv4_address.global = true; 214*9391bb9cSRapkiewicz, Pawel } 215*9391bb9cSRapkiewicz, Pawel ipv4_config.emplace_back(std::move(ipv4_address)); 216*9391bb9cSRapkiewicz, Pawel } 217*9391bb9cSRapkiewicz, Pawel } 218*9391bb9cSRapkiewicz, Pawel } 219*9391bb9cSRapkiewicz, Pawel } 220*9391bb9cSRapkiewicz, Pawel } 221*9391bb9cSRapkiewicz, Pawel 222*9391bb9cSRapkiewicz, Pawel public: 223*9391bb9cSRapkiewicz, Pawel /** 224*9391bb9cSRapkiewicz, Pawel * Function that retrieves all properties for given Ethernet Interface Object 225*9391bb9cSRapkiewicz, Pawel * from EntityManager Network Manager 226*9391bb9cSRapkiewicz, Pawel * @param ethiface_id a eth interface id to query on DBus 227*9391bb9cSRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 228*9391bb9cSRapkiewicz, Pawel * JSON 229*9391bb9cSRapkiewicz, Pawel */ 230*9391bb9cSRapkiewicz, Pawel template <typename CallbackFunc> 231*9391bb9cSRapkiewicz, Pawel void getEthernetIfaceData(const std::string ðiface_id, 232*9391bb9cSRapkiewicz, Pawel CallbackFunc &&callback) { 233*9391bb9cSRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 234*9391bb9cSRapkiewicz, Pawel [ 235*9391bb9cSRapkiewicz, Pawel this, ethiface_id{std::move(ethiface_id)}, 236*9391bb9cSRapkiewicz, Pawel callback{std::move(callback)} 237*9391bb9cSRapkiewicz, Pawel ](const boost::system::error_code error_code, 238*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &resp) { 239*9391bb9cSRapkiewicz, Pawel 240*9391bb9cSRapkiewicz, Pawel EthernetInterfaceData eth_data; 241*9391bb9cSRapkiewicz, Pawel std::vector<IPv4AddressData> ipv4_data; 242*9391bb9cSRapkiewicz, Pawel ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE); 243*9391bb9cSRapkiewicz, Pawel 244*9391bb9cSRapkiewicz, Pawel if (error_code) { 245*9391bb9cSRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 246*9391bb9cSRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 247*9391bb9cSRapkiewicz, Pawel // of vector may vary depending on information from Network Manager, 248*9391bb9cSRapkiewicz, Pawel // and empty output could not be treated same way as error. 249*9391bb9cSRapkiewicz, Pawel callback(false, eth_data, ipv4_data); 250*9391bb9cSRapkiewicz, Pawel return; 251*9391bb9cSRapkiewicz, Pawel } 252*9391bb9cSRapkiewicz, Pawel 253*9391bb9cSRapkiewicz, Pawel extractEthernetInterfaceData(ethiface_id, resp, eth_data); 254*9391bb9cSRapkiewicz, Pawel extractIPv4Data(ethiface_id, resp, ipv4_data); 255*9391bb9cSRapkiewicz, Pawel 256*9391bb9cSRapkiewicz, Pawel // Fix global GW 257*9391bb9cSRapkiewicz, Pawel for (IPv4AddressData &ipv4 : ipv4_data) { 258*9391bb9cSRapkiewicz, Pawel if ((ipv4.global) && 259*9391bb9cSRapkiewicz, Pawel ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { 260*9391bb9cSRapkiewicz, Pawel ipv4.gateway = eth_data.default_gateway; 261*9391bb9cSRapkiewicz, Pawel } 262*9391bb9cSRapkiewicz, Pawel } 263*9391bb9cSRapkiewicz, Pawel 264*9391bb9cSRapkiewicz, Pawel // Finally make a callback with usefull data 265*9391bb9cSRapkiewicz, Pawel callback(true, eth_data, ipv4_data); 266*9391bb9cSRapkiewicz, Pawel }, 267*9391bb9cSRapkiewicz, Pawel {"xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 268*9391bb9cSRapkiewicz, Pawel "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"}); 269*9391bb9cSRapkiewicz, Pawel }; 270*9391bb9cSRapkiewicz, Pawel 271*9391bb9cSRapkiewicz, Pawel /** 272*9391bb9cSRapkiewicz, Pawel * Function that retrieves all Ethernet Interfaces available through Network 273*9391bb9cSRapkiewicz, Pawel * Manager 274*9391bb9cSRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 275*9391bb9cSRapkiewicz, Pawel * JSON. 276*9391bb9cSRapkiewicz, Pawel */ 277*9391bb9cSRapkiewicz, Pawel template <typename CallbackFunc> 278*9391bb9cSRapkiewicz, Pawel void getEthernetIfaceList(CallbackFunc &&callback) { 279*9391bb9cSRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 280*9391bb9cSRapkiewicz, Pawel [ this, callback{std::move(callback)} ]( 281*9391bb9cSRapkiewicz, Pawel const boost::system::error_code error_code, 282*9391bb9cSRapkiewicz, Pawel const GetManagedObjectsType &resp) { 283*9391bb9cSRapkiewicz, Pawel // Callback requires vector<string> to retrieve all available ethernet 284*9391bb9cSRapkiewicz, Pawel // interfaces 285*9391bb9cSRapkiewicz, Pawel std::vector<std::string> iface_list; 286*9391bb9cSRapkiewicz, Pawel iface_list.reserve(resp.size()); 287*9391bb9cSRapkiewicz, Pawel if (error_code) { 288*9391bb9cSRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 289*9391bb9cSRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 290*9391bb9cSRapkiewicz, Pawel // of vector may vary depending on information from Network Manager, 291*9391bb9cSRapkiewicz, Pawel // and empty output could not be treated same way as error. 292*9391bb9cSRapkiewicz, Pawel callback(false, iface_list); 293*9391bb9cSRapkiewicz, Pawel return; 294*9391bb9cSRapkiewicz, Pawel } 295*9391bb9cSRapkiewicz, Pawel 296*9391bb9cSRapkiewicz, Pawel // Iterate over all retrieved ObjectPaths. 297*9391bb9cSRapkiewicz, Pawel for (auto &objpath : resp) { 298*9391bb9cSRapkiewicz, Pawel // And all interfaces available for certain ObjectPath. 299*9391bb9cSRapkiewicz, Pawel for (auto &interface : objpath.second) { 300*9391bb9cSRapkiewicz, Pawel // If interface is xyz.openbmc_project.Network.EthernetInterface, 301*9391bb9cSRapkiewicz, Pawel // this is what we're looking for. 302*9391bb9cSRapkiewicz, Pawel if (interface.first == 303*9391bb9cSRapkiewicz, Pawel "xyz.openbmc_project.Network.EthernetInterface") { 304*9391bb9cSRapkiewicz, Pawel // Cut out everyting until last "/", ... 305*9391bb9cSRapkiewicz, Pawel const std::string &iface_id = objpath.first.value; 306*9391bb9cSRapkiewicz, Pawel std::size_t last_pos = iface_id.rfind("/"); 307*9391bb9cSRapkiewicz, Pawel if (last_pos != std::string::npos) { 308*9391bb9cSRapkiewicz, Pawel // and put it into output vector. 309*9391bb9cSRapkiewicz, Pawel iface_list.emplace_back(iface_id.substr(last_pos + 1)); 310*9391bb9cSRapkiewicz, Pawel } 311*9391bb9cSRapkiewicz, Pawel } 312*9391bb9cSRapkiewicz, Pawel } 313*9391bb9cSRapkiewicz, Pawel } 314*9391bb9cSRapkiewicz, Pawel // Finally make a callback with usefull data 315*9391bb9cSRapkiewicz, Pawel callback(true, iface_list); 316*9391bb9cSRapkiewicz, Pawel }, 317*9391bb9cSRapkiewicz, Pawel {"xyz.openbmc_project.Network", "/xyz/openbmc_project/network", 318*9391bb9cSRapkiewicz, Pawel "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"}); 319*9391bb9cSRapkiewicz, Pawel }; 320*9391bb9cSRapkiewicz, Pawel }; 321*9391bb9cSRapkiewicz, Pawel 322*9391bb9cSRapkiewicz, Pawel /** 323*9391bb9cSRapkiewicz, Pawel * EthernetCollection derived class for delivering Ethernet Collection Schema 324*9391bb9cSRapkiewicz, Pawel */ 325*9391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node { 326*9391bb9cSRapkiewicz, Pawel public: 327*9391bb9cSRapkiewicz, Pawel template <typename CrowApp> 328*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 329*9391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 330*9391bb9cSRapkiewicz, Pawel // GetSubroutes method 331*9391bb9cSRapkiewicz, Pawel EthernetCollection(CrowApp &app) 332*9391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { 333*9391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = 334*9391bb9cSRapkiewicz, Pawel "#EthernetInterfaceCollection.EthernetInterfaceCollection"; 335*9391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 336*9391bb9cSRapkiewicz, Pawel "/redfish/v1/" 337*9391bb9cSRapkiewicz, Pawel "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; 338*9391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; 339*9391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Ethernet Network Interface Collection"; 340*9391bb9cSRapkiewicz, Pawel Node::json["Description"] = 341*9391bb9cSRapkiewicz, Pawel "Collection of EthernetInterfaces for this Manager"; 342*9391bb9cSRapkiewicz, Pawel 343*9391bb9cSRapkiewicz, Pawel entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, 344*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::HEAD, {{"Login"}}}, 345*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 346*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 347*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 348*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; 349*9391bb9cSRapkiewicz, Pawel } 350*9391bb9cSRapkiewicz, Pawel 351*9391bb9cSRapkiewicz, Pawel private: 352*9391bb9cSRapkiewicz, Pawel /** 353*9391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 354*9391bb9cSRapkiewicz, Pawel */ 355*9391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 356*9391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 357*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for 358*9391bb9cSRapkiewicz, Pawel // any Manager, not only hardcoded 'openbmc'. 359*9391bb9cSRapkiewicz, Pawel std::string manager_id = "openbmc"; 360*9391bb9cSRapkiewicz, Pawel 361*9391bb9cSRapkiewicz, Pawel // Get eth interface list, and call the below callback for JSON preparation 362*9391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceList( 363*9391bb9cSRapkiewicz, Pawel [&, manager_id{std::move(manager_id)} ]( 364*9391bb9cSRapkiewicz, Pawel const bool &success, const std::vector<std::string> &iface_list) { 365*9391bb9cSRapkiewicz, Pawel if (success) { 366*9391bb9cSRapkiewicz, Pawel nlohmann::json iface_array = nlohmann::json::array(); 367*9391bb9cSRapkiewicz, Pawel for (const std::string &iface_item : iface_list) { 368*9391bb9cSRapkiewicz, Pawel iface_array.push_back( 369*9391bb9cSRapkiewicz, Pawel {{"@odata.id", "/redfish/v1/Managers/" + manager_id + 370*9391bb9cSRapkiewicz, Pawel "/EthernetInterfaces/" + iface_item}}); 371*9391bb9cSRapkiewicz, Pawel } 372*9391bb9cSRapkiewicz, Pawel Node::json["Members"] = iface_array; 373*9391bb9cSRapkiewicz, Pawel Node::json["Members@odata.count"] = iface_array.size(); 374*9391bb9cSRapkiewicz, Pawel Node::json["@odata.id"] = 375*9391bb9cSRapkiewicz, Pawel "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces"; 376*9391bb9cSRapkiewicz, Pawel res.json_value = Node::json; 377*9391bb9cSRapkiewicz, Pawel } else { 378*9391bb9cSRapkiewicz, Pawel // No success, best what we can do is return INTERNALL ERROR 379*9391bb9cSRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 380*9391bb9cSRapkiewicz, Pawel } 381*9391bb9cSRapkiewicz, Pawel res.end(); 382*9391bb9cSRapkiewicz, Pawel }); 383*9391bb9cSRapkiewicz, Pawel } 384*9391bb9cSRapkiewicz, Pawel 385*9391bb9cSRapkiewicz, Pawel // Ethernet Provider object 386*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 387*9391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 388*9391bb9cSRapkiewicz, Pawel }; 389*9391bb9cSRapkiewicz, Pawel 390*9391bb9cSRapkiewicz, Pawel /** 391*9391bb9cSRapkiewicz, Pawel * EthernetInterface derived class for delivering Ethernet Schema 392*9391bb9cSRapkiewicz, Pawel */ 393*9391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node { 394*9391bb9cSRapkiewicz, Pawel public: 395*9391bb9cSRapkiewicz, Pawel /* 396*9391bb9cSRapkiewicz, Pawel * Default Constructor 397*9391bb9cSRapkiewicz, Pawel */ 398*9391bb9cSRapkiewicz, Pawel template <typename CrowApp> 399*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) Remove line from below, where we assume that there is only one 400*9391bb9cSRapkiewicz, Pawel // manager called openbmc This shall be generic, but requires to update 401*9391bb9cSRapkiewicz, Pawel // GetSubroutes method 402*9391bb9cSRapkiewicz, Pawel EthernetInterface(CrowApp &app) 403*9391bb9cSRapkiewicz, Pawel : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", 404*9391bb9cSRapkiewicz, Pawel std::string()) { 405*9391bb9cSRapkiewicz, Pawel Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; 406*9391bb9cSRapkiewicz, Pawel Node::json["@odata.context"] = 407*9391bb9cSRapkiewicz, Pawel "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; 408*9391bb9cSRapkiewicz, Pawel Node::json["Name"] = "Manager Ethernet Interface"; 409*9391bb9cSRapkiewicz, Pawel Node::json["Description"] = "Management Network Interface"; 410*9391bb9cSRapkiewicz, Pawel 411*9391bb9cSRapkiewicz, Pawel entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, 412*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::HEAD, {{"Login"}}}, 413*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 414*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 415*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 416*9391bb9cSRapkiewicz, Pawel {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; 417*9391bb9cSRapkiewicz, Pawel } 418*9391bb9cSRapkiewicz, Pawel 419*9391bb9cSRapkiewicz, Pawel private: 420*9391bb9cSRapkiewicz, Pawel /** 421*9391bb9cSRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 422*9391bb9cSRapkiewicz, Pawel */ 423*9391bb9cSRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 424*9391bb9cSRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 425*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) this shall be parametrized call (two params) to get 426*9391bb9cSRapkiewicz, Pawel // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. 427*9391bb9cSRapkiewicz, Pawel // Check if there is required param, truly entering this shall be 428*9391bb9cSRapkiewicz, Pawel // impossible. 429*9391bb9cSRapkiewicz, Pawel if (params.size() != 1) { 430*9391bb9cSRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 431*9391bb9cSRapkiewicz, Pawel res.end(); 432*9391bb9cSRapkiewicz, Pawel return; 433*9391bb9cSRapkiewicz, Pawel } 434*9391bb9cSRapkiewicz, Pawel 435*9391bb9cSRapkiewicz, Pawel const std::string &iface_id = params[0]; 436*9391bb9cSRapkiewicz, Pawel 437*9391bb9cSRapkiewicz, Pawel // Get single eth interface data, and call the below callback for JSON 438*9391bb9cSRapkiewicz, Pawel // preparation 439*9391bb9cSRapkiewicz, Pawel ethernet_provider.getEthernetIfaceData( 440*9391bb9cSRapkiewicz, Pawel iface_id, [&, iface_id](const bool &success, 441*9391bb9cSRapkiewicz, Pawel const EthernetInterfaceData ð_data, 442*9391bb9cSRapkiewicz, Pawel const std::vector<IPv4AddressData> &ipv4_data) { 443*9391bb9cSRapkiewicz, Pawel if (success) { 444*9391bb9cSRapkiewicz, Pawel // Copy JSON object to avoid race condition 445*9391bb9cSRapkiewicz, Pawel nlohmann::json json_response(Node::json); 446*9391bb9cSRapkiewicz, Pawel 447*9391bb9cSRapkiewicz, Pawel // Fill out obvious data... 448*9391bb9cSRapkiewicz, Pawel json_response["Id"] = iface_id; 449*9391bb9cSRapkiewicz, Pawel json_response["@odata.id"] = 450*9391bb9cSRapkiewicz, Pawel "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id; 451*9391bb9cSRapkiewicz, Pawel 452*9391bb9cSRapkiewicz, Pawel // ... then the one from DBus, regarding eth iface... 453*9391bb9cSRapkiewicz, Pawel if (eth_data.speed != nullptr) 454*9391bb9cSRapkiewicz, Pawel json_response["SpeedMbps"] = *eth_data.speed; 455*9391bb9cSRapkiewicz, Pawel 456*9391bb9cSRapkiewicz, Pawel if (eth_data.mac_address != nullptr) 457*9391bb9cSRapkiewicz, Pawel json_response["MACAddress"] = *eth_data.mac_address; 458*9391bb9cSRapkiewicz, Pawel 459*9391bb9cSRapkiewicz, Pawel if (eth_data.hostname != nullptr) 460*9391bb9cSRapkiewicz, Pawel json_response["HostName"] = *eth_data.hostname; 461*9391bb9cSRapkiewicz, Pawel 462*9391bb9cSRapkiewicz, Pawel // ... at last, check if there are IPv4 data and prepare appropriate 463*9391bb9cSRapkiewicz, Pawel // collection 464*9391bb9cSRapkiewicz, Pawel if (ipv4_data.size() > 0) { 465*9391bb9cSRapkiewicz, Pawel nlohmann::json ipv4_array = nlohmann::json::array(); 466*9391bb9cSRapkiewicz, Pawel for (auto &ipv4_config : ipv4_data) { 467*9391bb9cSRapkiewicz, Pawel nlohmann::json json_ipv4; 468*9391bb9cSRapkiewicz, Pawel if (ipv4_config.address != nullptr) { 469*9391bb9cSRapkiewicz, Pawel json_ipv4["Address"] = *ipv4_config.address; 470*9391bb9cSRapkiewicz, Pawel if (ipv4_config.gateway != nullptr) 471*9391bb9cSRapkiewicz, Pawel json_ipv4["Gateway"] = *ipv4_config.gateway; 472*9391bb9cSRapkiewicz, Pawel 473*9391bb9cSRapkiewicz, Pawel json_ipv4["AddressOrigin"] = ipv4_config.origin; 474*9391bb9cSRapkiewicz, Pawel json_ipv4["SubnetMask"] = ipv4_config.netmask; 475*9391bb9cSRapkiewicz, Pawel 476*9391bb9cSRapkiewicz, Pawel ipv4_array.push_back(json_ipv4); 477*9391bb9cSRapkiewicz, Pawel } 478*9391bb9cSRapkiewicz, Pawel } 479*9391bb9cSRapkiewicz, Pawel json_response["IPv4Addresses"] = ipv4_array; 480*9391bb9cSRapkiewicz, Pawel } 481*9391bb9cSRapkiewicz, Pawel res.json_value = std::move(json_response); 482*9391bb9cSRapkiewicz, Pawel } else { 483*9391bb9cSRapkiewicz, Pawel // ... otherwise return error 484*9391bb9cSRapkiewicz, Pawel // TODO(Pawel)consider distinguish between non existing object, and 485*9391bb9cSRapkiewicz, Pawel // other errors 486*9391bb9cSRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 487*9391bb9cSRapkiewicz, Pawel } 488*9391bb9cSRapkiewicz, Pawel res.end(); 489*9391bb9cSRapkiewicz, Pawel }); 490*9391bb9cSRapkiewicz, Pawel } 491*9391bb9cSRapkiewicz, Pawel 492*9391bb9cSRapkiewicz, Pawel // Ethernet Provider object 493*9391bb9cSRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 494*9391bb9cSRapkiewicz, Pawel OnDemandEthernetProvider ethernet_provider; 495*9391bb9cSRapkiewicz, Pawel }; 496*9391bb9cSRapkiewicz, Pawel 497*9391bb9cSRapkiewicz, Pawel } // namespace redfish 498