xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 274fad5ad0e7634d9ed3e174695136e674688e0c)
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 
189391bb9cSRapkiewicz, Pawel #include "node.hpp"
199391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp>
209391bb9cSRapkiewicz, Pawel 
219391bb9cSRapkiewicz, Pawel namespace redfish {
229391bb9cSRapkiewicz, Pawel 
239391bb9cSRapkiewicz, Pawel /**
249391bb9cSRapkiewicz, Pawel  * DBus types primitives for several generic DBus interfaces
259391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider move this to separate file into boost::dbus
269391bb9cSRapkiewicz, Pawel  */
279391bb9cSRapkiewicz, Pawel using PropertiesMapType =
289391bb9cSRapkiewicz, Pawel     boost::container::flat_map<std::string, dbus::dbus_variant>;
299391bb9cSRapkiewicz, Pawel 
309391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map<
319391bb9cSRapkiewicz, Pawel     dbus::object_path,
329391bb9cSRapkiewicz, Pawel     boost::container::flat_map<std::string, PropertiesMapType>>;
339391bb9cSRapkiewicz, Pawel 
349391bb9cSRapkiewicz, Pawel using GetAllPropertiesType = PropertiesMapType;
359391bb9cSRapkiewicz, Pawel 
369391bb9cSRapkiewicz, Pawel /**
379391bb9cSRapkiewicz, Pawel  * Structure for keeping IPv4 data required by Redfish
389391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
399391bb9cSRapkiewicz, Pawel  */
409391bb9cSRapkiewicz, Pawel struct IPv4AddressData {
419391bb9cSRapkiewicz, Pawel   const std::string *address;
429391bb9cSRapkiewicz, Pawel   const std::string *domain;
439391bb9cSRapkiewicz, Pawel   const std::string *gateway;
449391bb9cSRapkiewicz, Pawel   std::string netmask;
459391bb9cSRapkiewicz, Pawel   std::string origin;
469391bb9cSRapkiewicz, Pawel   bool global;
479391bb9cSRapkiewicz, Pawel };
489391bb9cSRapkiewicz, Pawel 
499391bb9cSRapkiewicz, Pawel /**
509391bb9cSRapkiewicz, Pawel  * Structure for keeping basic single Ethernet Interface information
519391bb9cSRapkiewicz, Pawel  * available from DBus
529391bb9cSRapkiewicz, Pawel  */
539391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData {
549391bb9cSRapkiewicz, Pawel   const unsigned int *speed;
559391bb9cSRapkiewicz, Pawel   const bool *auto_neg;
569391bb9cSRapkiewicz, Pawel   const std::string *hostname;
579391bb9cSRapkiewicz, Pawel   const std::string *default_gateway;
589391bb9cSRapkiewicz, Pawel   const std::string *mac_address;
59c7070ac2SKowalski, Kamil   const unsigned int *vlan_id;
609391bb9cSRapkiewicz, Pawel };
619391bb9cSRapkiewicz, Pawel 
629391bb9cSRapkiewicz, Pawel /**
639391bb9cSRapkiewicz, Pawel  * OnDemandEthernetProvider
64*274fad5aSGunnar Mills  * Ethernet provider class that retrieves data directly from dbus, before
65*274fad5aSGunnar Mills  * setting it into JSON output. This does not cache any data.
669391bb9cSRapkiewicz, Pawel  *
679391bb9cSRapkiewicz, Pawel  * TODO(Pawel)
689391bb9cSRapkiewicz, Pawel  * This perhaps shall be different file, which has to be chosen on compile time
699391bb9cSRapkiewicz, Pawel  * depending on OEM needs
709391bb9cSRapkiewicz, Pawel  */
719391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider {
729391bb9cSRapkiewicz, Pawel  private:
739391bb9cSRapkiewicz, Pawel   // Consts that may have influence on EthernetProvider performance/memory usage
749391bb9cSRapkiewicz, Pawel   const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10;
759391bb9cSRapkiewicz, Pawel 
769391bb9cSRapkiewicz, Pawel   // Helper function that allows to extract GetAllPropertiesType from
779391bb9cSRapkiewicz, Pawel   // GetManagedObjectsType, based on object path, and interface name
789391bb9cSRapkiewicz, Pawel   const PropertiesMapType *extractInterfaceProperties(
799391bb9cSRapkiewicz, Pawel       const dbus::object_path &objpath, const std::string &interface,
809391bb9cSRapkiewicz, Pawel       const GetManagedObjectsType &dbus_data) {
819391bb9cSRapkiewicz, Pawel     const auto &dbus_obj = dbus_data.find(objpath);
829391bb9cSRapkiewicz, Pawel     if (dbus_obj != dbus_data.end()) {
839391bb9cSRapkiewicz, Pawel       const auto &iface = dbus_obj->second.find(interface);
849391bb9cSRapkiewicz, Pawel       if (iface != dbus_obj->second.end()) {
859391bb9cSRapkiewicz, Pawel         return &iface->second;
869391bb9cSRapkiewicz, Pawel       }
879391bb9cSRapkiewicz, Pawel     }
889391bb9cSRapkiewicz, Pawel     return nullptr;
899391bb9cSRapkiewicz, Pawel   }
909391bb9cSRapkiewicz, Pawel 
919391bb9cSRapkiewicz, Pawel   // Helper Wrapper that does inline object_path conversion from string
929391bb9cSRapkiewicz, Pawel   // into dbus::object_path type
939391bb9cSRapkiewicz, Pawel   inline const PropertiesMapType *extractInterfaceProperties(
949391bb9cSRapkiewicz, Pawel       const std::string &objpath, const std::string &interface,
959391bb9cSRapkiewicz, Pawel       const GetManagedObjectsType &dbus_data) {
969391bb9cSRapkiewicz, Pawel     const auto &dbus_obj = dbus::object_path{objpath};
979391bb9cSRapkiewicz, Pawel     return extractInterfaceProperties(dbus_obj, interface, dbus_data);
989391bb9cSRapkiewicz, Pawel   }
999391bb9cSRapkiewicz, Pawel 
1009391bb9cSRapkiewicz, Pawel   // Helper function that allows to get pointer to the property from
1019391bb9cSRapkiewicz, Pawel   // GetAllPropertiesType native, or extracted by GetAllPropertiesType
1029391bb9cSRapkiewicz, Pawel   template <typename T>
1039391bb9cSRapkiewicz, Pawel   inline const T *extractProperty(const PropertiesMapType &properties,
1049391bb9cSRapkiewicz, Pawel                                   const std::string &name) {
1059391bb9cSRapkiewicz, Pawel     const auto &property = properties.find(name);
1069391bb9cSRapkiewicz, Pawel     if (property != properties.end()) {
1079391bb9cSRapkiewicz, Pawel       return boost::get<T>(&property->second);
1089391bb9cSRapkiewicz, Pawel     }
1099391bb9cSRapkiewicz, Pawel     return nullptr;
1109391bb9cSRapkiewicz, Pawel   }
1119391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Consider to move the above functions to dbus
1129391bb9cSRapkiewicz, Pawel   // generic_interfaces.hpp
1139391bb9cSRapkiewicz, Pawel 
1149391bb9cSRapkiewicz, Pawel   // Helper function that extracts data from several dbus objects and several
1159391bb9cSRapkiewicz, Pawel   // interfaces required by single ethernet interface instance
1169391bb9cSRapkiewicz, Pawel   void extractEthernetInterfaceData(const std::string &ethiface_id,
1179391bb9cSRapkiewicz, Pawel                                     const GetManagedObjectsType &dbus_data,
1189391bb9cSRapkiewicz, Pawel                                     EthernetInterfaceData &eth_data) {
1199391bb9cSRapkiewicz, Pawel     // Extract data that contains MAC Address
1209391bb9cSRapkiewicz, Pawel     const PropertiesMapType *mac_properties = extractInterfaceProperties(
1219391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/" + ethiface_id,
1229391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.MACAddress", dbus_data);
1239391bb9cSRapkiewicz, Pawel 
1249391bb9cSRapkiewicz, Pawel     if (mac_properties != nullptr) {
1259391bb9cSRapkiewicz, Pawel       eth_data.mac_address =
1269391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*mac_properties, "MACAddress");
1279391bb9cSRapkiewicz, Pawel     }
1289391bb9cSRapkiewicz, Pawel 
129c7070ac2SKowalski, Kamil     const PropertiesMapType *vlan_properties = extractInterfaceProperties(
130c7070ac2SKowalski, Kamil         "/xyz/openbmc_project/network/" + ethiface_id,
131c7070ac2SKowalski, Kamil         "xyz.openbmc_project.Network.VLAN", dbus_data);
132c7070ac2SKowalski, Kamil 
133c7070ac2SKowalski, Kamil     if (vlan_properties != nullptr) {
134c7070ac2SKowalski, Kamil       eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id");
135c7070ac2SKowalski, Kamil     }
136c7070ac2SKowalski, Kamil 
1379391bb9cSRapkiewicz, Pawel     // Extract data that contains link information (auto negotiation and speed)
1389391bb9cSRapkiewicz, Pawel     const PropertiesMapType *eth_properties = extractInterfaceProperties(
1399391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/" + ethiface_id,
1409391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
1419391bb9cSRapkiewicz, Pawel 
1429391bb9cSRapkiewicz, Pawel     if (eth_properties != nullptr) {
1439391bb9cSRapkiewicz, Pawel       eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg");
1449391bb9cSRapkiewicz, Pawel       eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed");
1459391bb9cSRapkiewicz, Pawel     }
1469391bb9cSRapkiewicz, Pawel 
1479391bb9cSRapkiewicz, Pawel     // Extract data that contains network config (HostName and DefaultGW)
1489391bb9cSRapkiewicz, Pawel     const PropertiesMapType *config_properties = extractInterfaceProperties(
1499391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/config",
1509391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
1519391bb9cSRapkiewicz, Pawel 
1529391bb9cSRapkiewicz, Pawel     if (config_properties != nullptr) {
1539391bb9cSRapkiewicz, Pawel       eth_data.hostname =
1549391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*config_properties, "HostName");
1559391bb9cSRapkiewicz, Pawel       eth_data.default_gateway =
1569391bb9cSRapkiewicz, Pawel           extractProperty<std::string>(*config_properties, "DefaultGateway");
1579391bb9cSRapkiewicz, Pawel     }
1589391bb9cSRapkiewicz, Pawel   }
1599391bb9cSRapkiewicz, Pawel 
1609391bb9cSRapkiewicz, Pawel   // Helper function that changes bits netmask notation (i.e. /24)
1619391bb9cSRapkiewicz, Pawel   // into full dot notation
1629391bb9cSRapkiewicz, Pawel   inline std::string getNetmask(unsigned int bits) {
1639391bb9cSRapkiewicz, Pawel     uint32_t value = 0xffffffff << (32 - bits);
1649391bb9cSRapkiewicz, Pawel     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
1659391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 16) & 0xff) + "." +
1669391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 8) & 0xff) + "." +
1679391bb9cSRapkiewicz, Pawel                           std::to_string(value & 0xff);
1689391bb9cSRapkiewicz, Pawel     return netmask;
1699391bb9cSRapkiewicz, Pawel   }
1709391bb9cSRapkiewicz, Pawel 
1719391bb9cSRapkiewicz, Pawel   // Helper function that extracts data for single ethernet ipv4 address
1729391bb9cSRapkiewicz, Pawel   void extractIPv4Data(const std::string &ethiface_id,
1739391bb9cSRapkiewicz, Pawel                        const GetManagedObjectsType &dbus_data,
1749391bb9cSRapkiewicz, Pawel                        std::vector<IPv4AddressData> &ipv4_config) {
1759391bb9cSRapkiewicz, Pawel     // Since there might be several IPv4 configurations aligned with
1769391bb9cSRapkiewicz, Pawel     // single ethernet interface, loop over all of them
1779391bb9cSRapkiewicz, Pawel     for (auto &objpath : dbus_data) {
178*274fad5aSGunnar Mills       // Check if proper patter for object path appears
1799391bb9cSRapkiewicz, Pawel       if (boost::starts_with(
1809391bb9cSRapkiewicz, Pawel               objpath.first.value,
1819391bb9cSRapkiewicz, Pawel               "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) {
1829391bb9cSRapkiewicz, Pawel         // and get approrpiate interface
1839391bb9cSRapkiewicz, Pawel         const auto &interface =
1849391bb9cSRapkiewicz, Pawel             objpath.second.find("xyz.openbmc_project.Network.IP");
1859391bb9cSRapkiewicz, Pawel         if (interface != objpath.second.end()) {
1869391bb9cSRapkiewicz, Pawel           // Make a properties 'shortcut', to make everything more readable
1879391bb9cSRapkiewicz, Pawel           const PropertiesMapType &properties = interface->second;
1889391bb9cSRapkiewicz, Pawel           // Instance IPv4AddressData structure, and set as appropriate
1899391bb9cSRapkiewicz, Pawel           IPv4AddressData ipv4_address;
1909391bb9cSRapkiewicz, Pawel           // IPv4 address
1919391bb9cSRapkiewicz, Pawel           ipv4_address.address =
1929391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Address");
1939391bb9cSRapkiewicz, Pawel           // IPv4 gateway
1949391bb9cSRapkiewicz, Pawel           ipv4_address.gateway =
1959391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Gateway");
1969391bb9cSRapkiewicz, Pawel 
1979391bb9cSRapkiewicz, Pawel           // Origin is kind of DBus object so fetch pointer...
1989391bb9cSRapkiewicz, Pawel           const std::string *origin =
1999391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Origin");
2009391bb9cSRapkiewicz, Pawel           if (origin != nullptr) {
2019391bb9cSRapkiewicz, Pawel             // ... and get everything after last dot
2029391bb9cSRapkiewicz, Pawel             int last = origin->rfind(".");
2039391bb9cSRapkiewicz, Pawel             if (last != std::string::npos) {
2049391bb9cSRapkiewicz, Pawel               ipv4_address.origin = origin->substr(last + 1);
2059391bb9cSRapkiewicz, Pawel             }
2069391bb9cSRapkiewicz, Pawel           }
2079391bb9cSRapkiewicz, Pawel 
2089391bb9cSRapkiewicz, Pawel           // Netmask is presented as PrefixLength
2099391bb9cSRapkiewicz, Pawel           const auto *mask =
2109391bb9cSRapkiewicz, Pawel               extractProperty<uint8_t>(properties, "PrefixLength");
2119391bb9cSRapkiewicz, Pawel           if (mask != nullptr) {
2129391bb9cSRapkiewicz, Pawel             // convert it to the string
2139391bb9cSRapkiewicz, Pawel             ipv4_address.netmask = getNetmask(*mask);
2149391bb9cSRapkiewicz, Pawel           }
2159391bb9cSRapkiewicz, Pawel 
2169391bb9cSRapkiewicz, Pawel           // Attach IPv4 only if address is present
2179391bb9cSRapkiewicz, Pawel           if (ipv4_address.address != nullptr) {
218*274fad5aSGunnar Mills             // Check if given address is local, or global
2199391bb9cSRapkiewicz, Pawel             if (boost::starts_with(*ipv4_address.address, "169.254")) {
2209391bb9cSRapkiewicz, Pawel               ipv4_address.global = false;
2219391bb9cSRapkiewicz, Pawel             } else {
2229391bb9cSRapkiewicz, Pawel               ipv4_address.global = true;
2239391bb9cSRapkiewicz, Pawel             }
2249391bb9cSRapkiewicz, Pawel             ipv4_config.emplace_back(std::move(ipv4_address));
2259391bb9cSRapkiewicz, Pawel           }
2269391bb9cSRapkiewicz, Pawel         }
2279391bb9cSRapkiewicz, Pawel       }
2289391bb9cSRapkiewicz, Pawel     }
2299391bb9cSRapkiewicz, Pawel   }
2309391bb9cSRapkiewicz, Pawel 
2319391bb9cSRapkiewicz, Pawel  public:
2329391bb9cSRapkiewicz, Pawel   /**
2339391bb9cSRapkiewicz, Pawel    * Function that retrieves all properties for given Ethernet Interface Object
2349391bb9cSRapkiewicz, Pawel    * from EntityManager Network Manager
2359391bb9cSRapkiewicz, Pawel    * @param ethiface_id a eth interface id to query on DBus
2369391bb9cSRapkiewicz, Pawel    * @param callback a function that shall be called to convert Dbus output into
2379391bb9cSRapkiewicz, Pawel    * JSON
2389391bb9cSRapkiewicz, Pawel    */
2399391bb9cSRapkiewicz, Pawel   template <typename CallbackFunc>
2409391bb9cSRapkiewicz, Pawel   void getEthernetIfaceData(const std::string &ethiface_id,
2419391bb9cSRapkiewicz, Pawel                             CallbackFunc &&callback) {
2429391bb9cSRapkiewicz, Pawel     crow::connections::system_bus->async_method_call(
2439391bb9cSRapkiewicz, Pawel         [
2449391bb9cSRapkiewicz, Pawel           this, ethiface_id{std::move(ethiface_id)},
2459391bb9cSRapkiewicz, Pawel           callback{std::move(callback)}
2469391bb9cSRapkiewicz, Pawel         ](const boost::system::error_code error_code,
2479391bb9cSRapkiewicz, Pawel           const GetManagedObjectsType &resp) {
2489391bb9cSRapkiewicz, Pawel 
249c7070ac2SKowalski, Kamil           EthernetInterfaceData eth_data{};
2509391bb9cSRapkiewicz, Pawel           std::vector<IPv4AddressData> ipv4_data;
2519391bb9cSRapkiewicz, Pawel           ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
2529391bb9cSRapkiewicz, Pawel 
2539391bb9cSRapkiewicz, Pawel           if (error_code) {
2549391bb9cSRapkiewicz, Pawel             // Something wrong on DBus, the error_code is not important at this
2559391bb9cSRapkiewicz, Pawel             // moment, just return success=false, and empty output. Since size
2569391bb9cSRapkiewicz, Pawel             // of vector may vary depending on information from Network Manager,
2579391bb9cSRapkiewicz, Pawel             // and empty output could not be treated same way as error.
2589391bb9cSRapkiewicz, Pawel             callback(false, eth_data, ipv4_data);
2599391bb9cSRapkiewicz, Pawel             return;
2609391bb9cSRapkiewicz, Pawel           }
2619391bb9cSRapkiewicz, Pawel 
2629391bb9cSRapkiewicz, Pawel           extractEthernetInterfaceData(ethiface_id, resp, eth_data);
2639391bb9cSRapkiewicz, Pawel           extractIPv4Data(ethiface_id, resp, ipv4_data);
2649391bb9cSRapkiewicz, Pawel 
2659391bb9cSRapkiewicz, Pawel           // Fix global GW
2669391bb9cSRapkiewicz, Pawel           for (IPv4AddressData &ipv4 : ipv4_data) {
2679391bb9cSRapkiewicz, Pawel             if ((ipv4.global) &&
2689391bb9cSRapkiewicz, Pawel                 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
2699391bb9cSRapkiewicz, Pawel               ipv4.gateway = eth_data.default_gateway;
2709391bb9cSRapkiewicz, Pawel             }
2719391bb9cSRapkiewicz, Pawel           }
2729391bb9cSRapkiewicz, Pawel 
273*274fad5aSGunnar Mills           // Finally make a callback with useful data
2749391bb9cSRapkiewicz, Pawel           callback(true, eth_data, ipv4_data);
2759391bb9cSRapkiewicz, Pawel         },
2769391bb9cSRapkiewicz, Pawel         {"xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
2779391bb9cSRapkiewicz, Pawel          "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
2789391bb9cSRapkiewicz, Pawel   };
2799391bb9cSRapkiewicz, Pawel 
2809391bb9cSRapkiewicz, Pawel   /**
2819391bb9cSRapkiewicz, Pawel    * Function that retrieves all Ethernet Interfaces available through Network
2829391bb9cSRapkiewicz, Pawel    * Manager
2839391bb9cSRapkiewicz, Pawel    * @param callback a function that shall be called to convert Dbus output into
2849391bb9cSRapkiewicz, Pawel    * JSON.
2859391bb9cSRapkiewicz, Pawel    */
2869391bb9cSRapkiewicz, Pawel   template <typename CallbackFunc>
2879391bb9cSRapkiewicz, Pawel   void getEthernetIfaceList(CallbackFunc &&callback) {
2889391bb9cSRapkiewicz, Pawel     crow::connections::system_bus->async_method_call(
2899391bb9cSRapkiewicz, Pawel         [ this, callback{std::move(callback)} ](
2909391bb9cSRapkiewicz, Pawel             const boost::system::error_code error_code,
2919391bb9cSRapkiewicz, Pawel             const GetManagedObjectsType &resp) {
2929391bb9cSRapkiewicz, Pawel           // Callback requires vector<string> to retrieve all available ethernet
2939391bb9cSRapkiewicz, Pawel           // interfaces
2949391bb9cSRapkiewicz, Pawel           std::vector<std::string> iface_list;
2959391bb9cSRapkiewicz, Pawel           iface_list.reserve(resp.size());
2969391bb9cSRapkiewicz, Pawel           if (error_code) {
2979391bb9cSRapkiewicz, Pawel             // Something wrong on DBus, the error_code is not important at this
2989391bb9cSRapkiewicz, Pawel             // moment, just return success=false, and empty output. Since size
2999391bb9cSRapkiewicz, Pawel             // of vector may vary depending on information from Network Manager,
3009391bb9cSRapkiewicz, Pawel             // and empty output could not be treated same way as error.
3019391bb9cSRapkiewicz, Pawel             callback(false, iface_list);
3029391bb9cSRapkiewicz, Pawel             return;
3039391bb9cSRapkiewicz, Pawel           }
3049391bb9cSRapkiewicz, Pawel 
3059391bb9cSRapkiewicz, Pawel           // Iterate over all retrieved ObjectPaths.
3069391bb9cSRapkiewicz, Pawel           for (auto &objpath : resp) {
3079391bb9cSRapkiewicz, Pawel             // And all interfaces available for certain ObjectPath.
3089391bb9cSRapkiewicz, Pawel             for (auto &interface : objpath.second) {
3099391bb9cSRapkiewicz, Pawel               // If interface is xyz.openbmc_project.Network.EthernetInterface,
3109391bb9cSRapkiewicz, Pawel               // this is what we're looking for.
3119391bb9cSRapkiewicz, Pawel               if (interface.first ==
3129391bb9cSRapkiewicz, Pawel                   "xyz.openbmc_project.Network.EthernetInterface") {
313*274fad5aSGunnar Mills                 // Cut out everything until last "/", ...
3149391bb9cSRapkiewicz, Pawel                 const std::string &iface_id = objpath.first.value;
3159391bb9cSRapkiewicz, Pawel                 std::size_t last_pos = iface_id.rfind("/");
3169391bb9cSRapkiewicz, Pawel                 if (last_pos != std::string::npos) {
3179391bb9cSRapkiewicz, Pawel                   // and put it into output vector.
3189391bb9cSRapkiewicz, Pawel                   iface_list.emplace_back(iface_id.substr(last_pos + 1));
3199391bb9cSRapkiewicz, Pawel                 }
3209391bb9cSRapkiewicz, Pawel               }
3219391bb9cSRapkiewicz, Pawel             }
3229391bb9cSRapkiewicz, Pawel           }
323*274fad5aSGunnar Mills           // Finally make a callback with useful data
3249391bb9cSRapkiewicz, Pawel           callback(true, iface_list);
3259391bb9cSRapkiewicz, Pawel         },
3269391bb9cSRapkiewicz, Pawel         {"xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
3279391bb9cSRapkiewicz, Pawel          "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"});
3289391bb9cSRapkiewicz, Pawel   };
3299391bb9cSRapkiewicz, Pawel };
3309391bb9cSRapkiewicz, Pawel 
3319391bb9cSRapkiewicz, Pawel /**
3329391bb9cSRapkiewicz, Pawel  * EthernetCollection derived class for delivering Ethernet Collection Schema
3339391bb9cSRapkiewicz, Pawel  */
3349391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node {
3359391bb9cSRapkiewicz, Pawel  public:
3369391bb9cSRapkiewicz, Pawel   template <typename CrowApp>
3379391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
3389391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
3399391bb9cSRapkiewicz, Pawel   // GetSubroutes method
3409391bb9cSRapkiewicz, Pawel   EthernetCollection(CrowApp &app)
3419391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
3429391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] =
3439391bb9cSRapkiewicz, Pawel         "#EthernetInterfaceCollection.EthernetInterfaceCollection";
3449391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
3459391bb9cSRapkiewicz, Pawel         "/redfish/v1/"
3469391bb9cSRapkiewicz, Pawel         "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
3479391bb9cSRapkiewicz, Pawel     Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
3489391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Ethernet Network Interface Collection";
3499391bb9cSRapkiewicz, Pawel     Node::json["Description"] =
3509391bb9cSRapkiewicz, Pawel         "Collection of EthernetInterfaces for this Manager";
3519391bb9cSRapkiewicz, Pawel 
3529391bb9cSRapkiewicz, Pawel     entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
3539391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::HEAD, {{"Login"}}},
3549391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
3559391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
3569391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
3579391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
3589391bb9cSRapkiewicz, Pawel   }
3599391bb9cSRapkiewicz, Pawel 
3609391bb9cSRapkiewicz, Pawel  private:
3619391bb9cSRapkiewicz, Pawel   /**
3629391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
3639391bb9cSRapkiewicz, Pawel    */
3649391bb9cSRapkiewicz, Pawel   void doGet(crow::response &res, const crow::request &req,
3659391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
3669391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
3679391bb9cSRapkiewicz, Pawel     // any Manager, not only hardcoded 'openbmc'.
3689391bb9cSRapkiewicz, Pawel     std::string manager_id = "openbmc";
3699391bb9cSRapkiewicz, Pawel 
3709391bb9cSRapkiewicz, Pawel     // Get eth interface list, and call the below callback for JSON preparation
3719391bb9cSRapkiewicz, Pawel     ethernet_provider.getEthernetIfaceList(
3729391bb9cSRapkiewicz, Pawel         [&, manager_id{std::move(manager_id)} ](
3739391bb9cSRapkiewicz, Pawel             const bool &success, const std::vector<std::string> &iface_list) {
3749391bb9cSRapkiewicz, Pawel           if (success) {
3759391bb9cSRapkiewicz, Pawel             nlohmann::json iface_array = nlohmann::json::array();
3769391bb9cSRapkiewicz, Pawel             for (const std::string &iface_item : iface_list) {
3779391bb9cSRapkiewicz, Pawel               iface_array.push_back(
3789391bb9cSRapkiewicz, Pawel                   {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
3799391bb9cSRapkiewicz, Pawel                                      "/EthernetInterfaces/" + iface_item}});
3809391bb9cSRapkiewicz, Pawel             }
3819391bb9cSRapkiewicz, Pawel             Node::json["Members"] = iface_array;
3829391bb9cSRapkiewicz, Pawel             Node::json["Members@odata.count"] = iface_array.size();
3839391bb9cSRapkiewicz, Pawel             Node::json["@odata.id"] =
3849391bb9cSRapkiewicz, Pawel                 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
3859391bb9cSRapkiewicz, Pawel             res.json_value = Node::json;
3869391bb9cSRapkiewicz, Pawel           } else {
3879391bb9cSRapkiewicz, Pawel             // No success, best what we can do is return INTERNALL ERROR
3889391bb9cSRapkiewicz, Pawel             res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
3899391bb9cSRapkiewicz, Pawel           }
3909391bb9cSRapkiewicz, Pawel           res.end();
3919391bb9cSRapkiewicz, Pawel         });
3929391bb9cSRapkiewicz, Pawel   }
3939391bb9cSRapkiewicz, Pawel 
3949391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
3959391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
3969391bb9cSRapkiewicz, Pawel   OnDemandEthernetProvider ethernet_provider;
3979391bb9cSRapkiewicz, Pawel };
3989391bb9cSRapkiewicz, Pawel 
3999391bb9cSRapkiewicz, Pawel /**
4009391bb9cSRapkiewicz, Pawel  * EthernetInterface derived class for delivering Ethernet Schema
4019391bb9cSRapkiewicz, Pawel  */
4029391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node {
4039391bb9cSRapkiewicz, Pawel  public:
4049391bb9cSRapkiewicz, Pawel   /*
4059391bb9cSRapkiewicz, Pawel    * Default Constructor
4069391bb9cSRapkiewicz, Pawel    */
4079391bb9cSRapkiewicz, Pawel   template <typename CrowApp>
4089391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
4099391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
4109391bb9cSRapkiewicz, Pawel   // GetSubroutes method
4119391bb9cSRapkiewicz, Pawel   EthernetInterface(CrowApp &app)
4129391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
4139391bb9cSRapkiewicz, Pawel              std::string()) {
4149391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
4159391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
4169391bb9cSRapkiewicz, Pawel         "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
4179391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Manager Ethernet Interface";
4189391bb9cSRapkiewicz, Pawel     Node::json["Description"] = "Management Network Interface";
4199391bb9cSRapkiewicz, Pawel 
4209391bb9cSRapkiewicz, Pawel     entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}},
4219391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::HEAD, {{"Login"}}},
4229391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}},
4239391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}},
4249391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}},
4259391bb9cSRapkiewicz, Pawel                         {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}};
4269391bb9cSRapkiewicz, Pawel   }
4279391bb9cSRapkiewicz, Pawel 
4289391bb9cSRapkiewicz, Pawel  private:
4299391bb9cSRapkiewicz, Pawel   /**
4309391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
4319391bb9cSRapkiewicz, Pawel    */
4329391bb9cSRapkiewicz, Pawel   void doGet(crow::response &res, const crow::request &req,
4339391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
4349391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call (two params) to get
4359391bb9cSRapkiewicz, Pawel     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
4369391bb9cSRapkiewicz, Pawel     // Check if there is required param, truly entering this shall be
4379391bb9cSRapkiewicz, Pawel     // impossible.
4389391bb9cSRapkiewicz, Pawel     if (params.size() != 1) {
4399391bb9cSRapkiewicz, Pawel       res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR);
4409391bb9cSRapkiewicz, Pawel       res.end();
4419391bb9cSRapkiewicz, Pawel       return;
4429391bb9cSRapkiewicz, Pawel     }
4439391bb9cSRapkiewicz, Pawel 
4449391bb9cSRapkiewicz, Pawel     const std::string &iface_id = params[0];
4459391bb9cSRapkiewicz, Pawel 
4469391bb9cSRapkiewicz, Pawel     // Get single eth interface data, and call the below callback for JSON
4479391bb9cSRapkiewicz, Pawel     // preparation
4489391bb9cSRapkiewicz, Pawel     ethernet_provider.getEthernetIfaceData(
4499391bb9cSRapkiewicz, Pawel         iface_id, [&, iface_id](const bool &success,
4509391bb9cSRapkiewicz, Pawel                                 const EthernetInterfaceData &eth_data,
4519391bb9cSRapkiewicz, Pawel                                 const std::vector<IPv4AddressData> &ipv4_data) {
4529391bb9cSRapkiewicz, Pawel           if (success) {
4539391bb9cSRapkiewicz, Pawel             // Copy JSON object to avoid race condition
4549391bb9cSRapkiewicz, Pawel             nlohmann::json json_response(Node::json);
4559391bb9cSRapkiewicz, Pawel 
4569391bb9cSRapkiewicz, Pawel             // Fill out obvious data...
4579391bb9cSRapkiewicz, Pawel             json_response["Id"] = iface_id;
4589391bb9cSRapkiewicz, Pawel             json_response["@odata.id"] =
4599391bb9cSRapkiewicz, Pawel                 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
4609391bb9cSRapkiewicz, Pawel 
4619391bb9cSRapkiewicz, Pawel             // ... then the one from DBus, regarding eth iface...
4629391bb9cSRapkiewicz, Pawel             if (eth_data.speed != nullptr)
4639391bb9cSRapkiewicz, Pawel               json_response["SpeedMbps"] = *eth_data.speed;
4649391bb9cSRapkiewicz, Pawel 
4659391bb9cSRapkiewicz, Pawel             if (eth_data.mac_address != nullptr)
4669391bb9cSRapkiewicz, Pawel               json_response["MACAddress"] = *eth_data.mac_address;
4679391bb9cSRapkiewicz, Pawel 
4689391bb9cSRapkiewicz, Pawel             if (eth_data.hostname != nullptr)
4699391bb9cSRapkiewicz, Pawel               json_response["HostName"] = *eth_data.hostname;
4709391bb9cSRapkiewicz, Pawel 
471c7070ac2SKowalski, Kamil             if (eth_data.vlan_id != nullptr) {
472c7070ac2SKowalski, Kamil               json_response["VLAN"]["VLANEnable"] = true;
473c7070ac2SKowalski, Kamil               json_response["VLAN"]["VLANId"] = *eth_data.vlan_id;
474c7070ac2SKowalski, Kamil             }
475c7070ac2SKowalski, Kamil 
4769391bb9cSRapkiewicz, Pawel             // ... at last, check if there are IPv4 data and prepare appropriate
4779391bb9cSRapkiewicz, Pawel             // collection
4789391bb9cSRapkiewicz, Pawel             if (ipv4_data.size() > 0) {
4799391bb9cSRapkiewicz, Pawel               nlohmann::json ipv4_array = nlohmann::json::array();
4809391bb9cSRapkiewicz, Pawel               for (auto &ipv4_config : ipv4_data) {
4819391bb9cSRapkiewicz, Pawel                 nlohmann::json json_ipv4;
4829391bb9cSRapkiewicz, Pawel                 if (ipv4_config.address != nullptr) {
4839391bb9cSRapkiewicz, Pawel                   json_ipv4["Address"] = *ipv4_config.address;
4849391bb9cSRapkiewicz, Pawel                   if (ipv4_config.gateway != nullptr)
4859391bb9cSRapkiewicz, Pawel                     json_ipv4["Gateway"] = *ipv4_config.gateway;
4869391bb9cSRapkiewicz, Pawel 
4879391bb9cSRapkiewicz, Pawel                   json_ipv4["AddressOrigin"] = ipv4_config.origin;
4889391bb9cSRapkiewicz, Pawel                   json_ipv4["SubnetMask"] = ipv4_config.netmask;
4899391bb9cSRapkiewicz, Pawel 
4909391bb9cSRapkiewicz, Pawel                   ipv4_array.push_back(json_ipv4);
4919391bb9cSRapkiewicz, Pawel                 }
4929391bb9cSRapkiewicz, Pawel               }
4939391bb9cSRapkiewicz, Pawel               json_response["IPv4Addresses"] = ipv4_array;
4949391bb9cSRapkiewicz, Pawel             }
4959391bb9cSRapkiewicz, Pawel             res.json_value = std::move(json_response);
4969391bb9cSRapkiewicz, Pawel           } else {
4979391bb9cSRapkiewicz, Pawel             // ... otherwise return error
4989391bb9cSRapkiewicz, Pawel             // TODO(Pawel)consider distinguish between non existing object, and
4999391bb9cSRapkiewicz, Pawel             // other errors
5009391bb9cSRapkiewicz, Pawel             res.code = static_cast<int>(HttpRespCode::NOT_FOUND);
5019391bb9cSRapkiewicz, Pawel           }
5029391bb9cSRapkiewicz, Pawel           res.end();
5039391bb9cSRapkiewicz, Pawel         });
5049391bb9cSRapkiewicz, Pawel   }
5059391bb9cSRapkiewicz, Pawel 
5069391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
5079391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
5089391bb9cSRapkiewicz, Pawel   OnDemandEthernetProvider ethernet_provider;
5099391bb9cSRapkiewicz, Pawel };
5109391bb9cSRapkiewicz, Pawel 
5119391bb9cSRapkiewicz, Pawel }  // namespace redfish
512