xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision 9391bb9c6c44dde56b9c63fbd55a8c8af972566b)
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 &ethiface_id,
116*9391bb9cSRapkiewicz, Pawel                                     const GetManagedObjectsType &dbus_data,
117*9391bb9cSRapkiewicz, Pawel                                     EthernetInterfaceData &eth_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 &ethiface_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 &ethiface_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> &params) 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> &params) 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 &eth_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