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