xref: /openbmc/bmcweb/features/redfish/lib/ethernet.hpp (revision a434f2bde1f80e1a0ddcda0961a7ff102de152d6)
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 
18179db1d7SKowalski, Kamil #include <dbus_singleton.hpp>
19588c3f0dSKowalski, Kamil #include <error_messages.hpp>
20179db1d7SKowalski, Kamil #include <node.hpp>
21588c3f0dSKowalski, Kamil #include <utils/json_utils.hpp>
229391bb9cSRapkiewicz, Pawel #include <boost/container/flat_map.hpp>
239391bb9cSRapkiewicz, Pawel 
249391bb9cSRapkiewicz, Pawel namespace redfish {
259391bb9cSRapkiewicz, Pawel 
269391bb9cSRapkiewicz, Pawel /**
279391bb9cSRapkiewicz, Pawel  * DBus types primitives for several generic DBus interfaces
289391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider move this to separate file into boost::dbus
299391bb9cSRapkiewicz, Pawel  */
30aa2e59c1SEd Tanous using PropertiesMapType = boost::container::flat_map<
31aa2e59c1SEd Tanous     std::string,
32aa2e59c1SEd Tanous     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
33aa2e59c1SEd Tanous                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
349391bb9cSRapkiewicz, Pawel 
359391bb9cSRapkiewicz, Pawel using GetManagedObjectsType = boost::container::flat_map<
36aa2e59c1SEd Tanous     sdbusplus::message::object_path,
37aa2e59c1SEd Tanous     boost::container::flat_map<
38aa2e59c1SEd Tanous         std::string,
39aa2e59c1SEd Tanous         boost::container::flat_map<
40aa2e59c1SEd Tanous             std::string, sdbusplus::message::variant<
41aa2e59c1SEd Tanous                              std::string, bool, uint8_t, int16_t, uint16_t,
42aa2e59c1SEd Tanous                              int32_t, uint32_t, int64_t, uint64_t, double>>>>;
439391bb9cSRapkiewicz, Pawel 
449391bb9cSRapkiewicz, Pawel /**
459391bb9cSRapkiewicz, Pawel  * Structure for keeping IPv4 data required by Redfish
469391bb9cSRapkiewicz, Pawel  * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
479391bb9cSRapkiewicz, Pawel  */
489391bb9cSRapkiewicz, Pawel struct IPv4AddressData {
49179db1d7SKowalski, Kamil   std::string id;
509391bb9cSRapkiewicz, Pawel   const std::string *address;
519391bb9cSRapkiewicz, Pawel   const std::string *domain;
529391bb9cSRapkiewicz, Pawel   const std::string *gateway;
539391bb9cSRapkiewicz, Pawel   std::string netmask;
549391bb9cSRapkiewicz, Pawel   std::string origin;
559391bb9cSRapkiewicz, Pawel   bool global;
56179db1d7SKowalski, Kamil   /**
57179db1d7SKowalski, Kamil    * @brief Operator< to enable sorting
58179db1d7SKowalski, Kamil    *
59179db1d7SKowalski, Kamil    * @param[in] obj   Object to compare with
60179db1d7SKowalski, Kamil    *
61179db1d7SKowalski, Kamil    * @return This object id < supplied object id
62179db1d7SKowalski, Kamil    */
63179db1d7SKowalski, Kamil   bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); }
649391bb9cSRapkiewicz, Pawel };
659391bb9cSRapkiewicz, Pawel 
669391bb9cSRapkiewicz, Pawel /**
679391bb9cSRapkiewicz, Pawel  * Structure for keeping basic single Ethernet Interface information
689391bb9cSRapkiewicz, Pawel  * available from DBus
699391bb9cSRapkiewicz, Pawel  */
709391bb9cSRapkiewicz, Pawel struct EthernetInterfaceData {
719391bb9cSRapkiewicz, Pawel   const unsigned int *speed;
7255c7b7a2SEd Tanous   const bool *autoNeg;
739391bb9cSRapkiewicz, Pawel   const std::string *hostname;
7455c7b7a2SEd Tanous   const std::string *defaultGateway;
7555c7b7a2SEd Tanous   const std::string *macAddress;
7655c7b7a2SEd Tanous   const unsigned int *vlanId;
779391bb9cSRapkiewicz, Pawel };
789391bb9cSRapkiewicz, Pawel 
799391bb9cSRapkiewicz, Pawel /**
809391bb9cSRapkiewicz, Pawel  * OnDemandEthernetProvider
81274fad5aSGunnar Mills  * Ethernet provider class that retrieves data directly from dbus, before
82274fad5aSGunnar Mills  * setting it into JSON output. This does not cache any data.
839391bb9cSRapkiewicz, Pawel  *
849391bb9cSRapkiewicz, Pawel  * TODO(Pawel)
859391bb9cSRapkiewicz, Pawel  * This perhaps shall be different file, which has to be chosen on compile time
869391bb9cSRapkiewicz, Pawel  * depending on OEM needs
879391bb9cSRapkiewicz, Pawel  */
889391bb9cSRapkiewicz, Pawel class OnDemandEthernetProvider {
899391bb9cSRapkiewicz, Pawel  private:
909391bb9cSRapkiewicz, Pawel   // Consts that may have influence on EthernetProvider performance/memory usage
9155c7b7a2SEd Tanous   const size_t maxIpV4AddressesPerInterface = 10;
929391bb9cSRapkiewicz, Pawel 
939391bb9cSRapkiewicz, Pawel   // Helper function that allows to extract GetAllPropertiesType from
949391bb9cSRapkiewicz, Pawel   // GetManagedObjectsType, based on object path, and interface name
959391bb9cSRapkiewicz, Pawel   const PropertiesMapType *extractInterfaceProperties(
96aa2e59c1SEd Tanous       const sdbusplus::message::object_path &objpath,
97aa2e59c1SEd Tanous       const std::string &interface, const GetManagedObjectsType &dbus_data) {
9855c7b7a2SEd Tanous     const auto &dbusObj = dbus_data.find(objpath);
9955c7b7a2SEd Tanous     if (dbusObj != dbus_data.end()) {
10055c7b7a2SEd Tanous       const auto &iface = dbusObj->second.find(interface);
10155c7b7a2SEd Tanous       if (iface != dbusObj->second.end()) {
1029391bb9cSRapkiewicz, Pawel         return &iface->second;
1039391bb9cSRapkiewicz, Pawel       }
1049391bb9cSRapkiewicz, Pawel     }
1059391bb9cSRapkiewicz, Pawel     return nullptr;
1069391bb9cSRapkiewicz, Pawel   }
1079391bb9cSRapkiewicz, Pawel 
1089391bb9cSRapkiewicz, Pawel   // Helper Wrapper that does inline object_path conversion from string
109aa2e59c1SEd Tanous   // into sdbusplus::message::object_path type
1109391bb9cSRapkiewicz, Pawel   inline const PropertiesMapType *extractInterfaceProperties(
1119391bb9cSRapkiewicz, Pawel       const std::string &objpath, const std::string &interface,
1129391bb9cSRapkiewicz, Pawel       const GetManagedObjectsType &dbus_data) {
11355c7b7a2SEd Tanous     const auto &dbusObj = sdbusplus::message::object_path{objpath};
11455c7b7a2SEd Tanous     return extractInterfaceProperties(dbusObj, interface, dbus_data);
1159391bb9cSRapkiewicz, Pawel   }
1169391bb9cSRapkiewicz, Pawel 
1179391bb9cSRapkiewicz, Pawel   // Helper function that allows to get pointer to the property from
1189391bb9cSRapkiewicz, Pawel   // GetAllPropertiesType native, or extracted by GetAllPropertiesType
1199391bb9cSRapkiewicz, Pawel   template <typename T>
120aa2e59c1SEd Tanous   inline T const *const extractProperty(const PropertiesMapType &properties,
1219391bb9cSRapkiewicz, Pawel                                         const std::string &name) {
1229391bb9cSRapkiewicz, Pawel     const auto &property = properties.find(name);
1239391bb9cSRapkiewicz, Pawel     if (property != properties.end()) {
12455c7b7a2SEd Tanous       return mapbox::getPtr<const T>(property->second);
1259391bb9cSRapkiewicz, Pawel     }
1269391bb9cSRapkiewicz, Pawel     return nullptr;
1279391bb9cSRapkiewicz, Pawel   }
1289391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Consider to move the above functions to dbus
1299391bb9cSRapkiewicz, Pawel   // generic_interfaces.hpp
1309391bb9cSRapkiewicz, Pawel 
1319391bb9cSRapkiewicz, Pawel   // Helper function that extracts data from several dbus objects and several
1329391bb9cSRapkiewicz, Pawel   // interfaces required by single ethernet interface instance
13355c7b7a2SEd Tanous   void extractEthernetInterfaceData(const std::string &ethifaceId,
1349391bb9cSRapkiewicz, Pawel                                     const GetManagedObjectsType &dbus_data,
1359391bb9cSRapkiewicz, Pawel                                     EthernetInterfaceData &eth_data) {
1369391bb9cSRapkiewicz, Pawel     // Extract data that contains MAC Address
13755c7b7a2SEd Tanous     const PropertiesMapType *macProperties = extractInterfaceProperties(
13855c7b7a2SEd Tanous         "/xyz/openbmc_project/network/" + ethifaceId,
1399391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.MACAddress", dbus_data);
1409391bb9cSRapkiewicz, Pawel 
14155c7b7a2SEd Tanous     if (macProperties != nullptr) {
14255c7b7a2SEd Tanous       eth_data.macAddress =
14355c7b7a2SEd Tanous           extractProperty<std::string>(*macProperties, "MACAddress");
1449391bb9cSRapkiewicz, Pawel     }
1459391bb9cSRapkiewicz, Pawel 
14655c7b7a2SEd Tanous     const PropertiesMapType *vlanProperties = extractInterfaceProperties(
14755c7b7a2SEd Tanous         "/xyz/openbmc_project/network/" + ethifaceId,
148c7070ac2SKowalski, Kamil         "xyz.openbmc_project.Network.VLAN", dbus_data);
149c7070ac2SKowalski, Kamil 
15055c7b7a2SEd Tanous     if (vlanProperties != nullptr) {
15155c7b7a2SEd Tanous       eth_data.vlanId = extractProperty<unsigned int>(*vlanProperties, "Id");
152c7070ac2SKowalski, Kamil     }
153c7070ac2SKowalski, Kamil 
1549391bb9cSRapkiewicz, Pawel     // Extract data that contains link information (auto negotiation and speed)
15555c7b7a2SEd Tanous     const PropertiesMapType *ethProperties = extractInterfaceProperties(
15655c7b7a2SEd Tanous         "/xyz/openbmc_project/network/" + ethifaceId,
1579391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
1589391bb9cSRapkiewicz, Pawel 
15955c7b7a2SEd Tanous     if (ethProperties != nullptr) {
16055c7b7a2SEd Tanous       eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
16155c7b7a2SEd Tanous       eth_data.speed = extractProperty<unsigned int>(*ethProperties, "Speed");
1629391bb9cSRapkiewicz, Pawel     }
1639391bb9cSRapkiewicz, Pawel 
1649391bb9cSRapkiewicz, Pawel     // Extract data that contains network config (HostName and DefaultGW)
16555c7b7a2SEd Tanous     const PropertiesMapType *configProperties = extractInterfaceProperties(
1669391bb9cSRapkiewicz, Pawel         "/xyz/openbmc_project/network/config",
1679391bb9cSRapkiewicz, Pawel         "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
1689391bb9cSRapkiewicz, Pawel 
16955c7b7a2SEd Tanous     if (configProperties != nullptr) {
1709391bb9cSRapkiewicz, Pawel       eth_data.hostname =
17155c7b7a2SEd Tanous           extractProperty<std::string>(*configProperties, "HostName");
17255c7b7a2SEd Tanous       eth_data.defaultGateway =
17355c7b7a2SEd Tanous           extractProperty<std::string>(*configProperties, "DefaultGateway");
1749391bb9cSRapkiewicz, Pawel     }
1759391bb9cSRapkiewicz, Pawel   }
1769391bb9cSRapkiewicz, Pawel 
1779391bb9cSRapkiewicz, Pawel   // Helper function that changes bits netmask notation (i.e. /24)
1789391bb9cSRapkiewicz, Pawel   // into full dot notation
1799391bb9cSRapkiewicz, Pawel   inline std::string getNetmask(unsigned int bits) {
1809391bb9cSRapkiewicz, Pawel     uint32_t value = 0xffffffff << (32 - bits);
1819391bb9cSRapkiewicz, Pawel     std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
1829391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 16) & 0xff) + "." +
1839391bb9cSRapkiewicz, Pawel                           std::to_string((value >> 8) & 0xff) + "." +
1849391bb9cSRapkiewicz, Pawel                           std::to_string(value & 0xff);
1859391bb9cSRapkiewicz, Pawel     return netmask;
1869391bb9cSRapkiewicz, Pawel   }
1879391bb9cSRapkiewicz, Pawel 
1889391bb9cSRapkiewicz, Pawel   // Helper function that extracts data for single ethernet ipv4 address
18955c7b7a2SEd Tanous   void extractIPv4Data(const std::string &ethifaceId,
1909391bb9cSRapkiewicz, Pawel                        const GetManagedObjectsType &dbus_data,
1919391bb9cSRapkiewicz, Pawel                        std::vector<IPv4AddressData> &ipv4_config) {
192179db1d7SKowalski, Kamil     const std::string pathStart =
19355c7b7a2SEd Tanous         "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
194179db1d7SKowalski, Kamil 
1959391bb9cSRapkiewicz, Pawel     // Since there might be several IPv4 configurations aligned with
1969391bb9cSRapkiewicz, Pawel     // single ethernet interface, loop over all of them
1979391bb9cSRapkiewicz, Pawel     for (auto &objpath : dbus_data) {
198274fad5aSGunnar Mills       // Check if proper patter for object path appears
199*a434f2bdSEd Tanous       if (boost::starts_with(static_cast<const std::string &>(objpath.first),
200*a434f2bdSEd Tanous                              pathStart)) {
2019391bb9cSRapkiewicz, Pawel         // and get approrpiate interface
2029391bb9cSRapkiewicz, Pawel         const auto &interface =
2039391bb9cSRapkiewicz, Pawel             objpath.second.find("xyz.openbmc_project.Network.IP");
2049391bb9cSRapkiewicz, Pawel         if (interface != objpath.second.end()) {
2059391bb9cSRapkiewicz, Pawel           // Make a properties 'shortcut', to make everything more readable
2069391bb9cSRapkiewicz, Pawel           const PropertiesMapType &properties = interface->second;
2079391bb9cSRapkiewicz, Pawel           // Instance IPv4AddressData structure, and set as appropriate
20855c7b7a2SEd Tanous           IPv4AddressData ipv4Address;
209179db1d7SKowalski, Kamil 
21055c7b7a2SEd Tanous           ipv4Address.id = static_cast<const std::string &>(objpath.first)
211179db1d7SKowalski, Kamil                                .substr(pathStart.size());
212179db1d7SKowalski, Kamil 
2139391bb9cSRapkiewicz, Pawel           // IPv4 address
21455c7b7a2SEd Tanous           ipv4Address.address =
2159391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Address");
2169391bb9cSRapkiewicz, Pawel           // IPv4 gateway
21755c7b7a2SEd Tanous           ipv4Address.gateway =
2189391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Gateway");
2199391bb9cSRapkiewicz, Pawel 
2209391bb9cSRapkiewicz, Pawel           // Origin is kind of DBus object so fetch pointer...
2219391bb9cSRapkiewicz, Pawel           const std::string *origin =
2229391bb9cSRapkiewicz, Pawel               extractProperty<std::string>(properties, "Origin");
2239391bb9cSRapkiewicz, Pawel           if (origin != nullptr) {
22455c7b7a2SEd Tanous             ipv4Address.origin =
225179db1d7SKowalski, Kamil                 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
2269391bb9cSRapkiewicz, Pawel           }
2279391bb9cSRapkiewicz, Pawel 
2289391bb9cSRapkiewicz, Pawel           // Netmask is presented as PrefixLength
2299391bb9cSRapkiewicz, Pawel           const auto *mask =
2309391bb9cSRapkiewicz, Pawel               extractProperty<uint8_t>(properties, "PrefixLength");
2319391bb9cSRapkiewicz, Pawel           if (mask != nullptr) {
2329391bb9cSRapkiewicz, Pawel             // convert it to the string
23355c7b7a2SEd Tanous             ipv4Address.netmask = getNetmask(*mask);
2349391bb9cSRapkiewicz, Pawel           }
2359391bb9cSRapkiewicz, Pawel 
2369391bb9cSRapkiewicz, Pawel           // Attach IPv4 only if address is present
23755c7b7a2SEd Tanous           if (ipv4Address.address != nullptr) {
238*a434f2bdSEd Tanous             // Check if given address is local, or global
23955c7b7a2SEd Tanous             if (boost::starts_with(*ipv4Address.address, "169.254")) {
24055c7b7a2SEd Tanous               ipv4Address.global = false;
2419391bb9cSRapkiewicz, Pawel             } else {
24255c7b7a2SEd Tanous               ipv4Address.global = true;
2439391bb9cSRapkiewicz, Pawel             }
24455c7b7a2SEd Tanous             ipv4_config.emplace_back(std::move(ipv4Address));
2459391bb9cSRapkiewicz, Pawel           }
2469391bb9cSRapkiewicz, Pawel         }
2479391bb9cSRapkiewicz, Pawel       }
2489391bb9cSRapkiewicz, Pawel     }
249179db1d7SKowalski, Kamil 
250179db1d7SKowalski, Kamil     /**
251179db1d7SKowalski, Kamil      * We have to sort this vector and ensure that order of IPv4 addresses
252179db1d7SKowalski, Kamil      * is consistent between GETs to allow modification and deletion in PATCHes
253179db1d7SKowalski, Kamil      */
254179db1d7SKowalski, Kamil     std::sort(ipv4_config.begin(), ipv4_config.end());
2559391bb9cSRapkiewicz, Pawel   }
2569391bb9cSRapkiewicz, Pawel 
257179db1d7SKowalski, Kamil   static const constexpr int ipV4AddressSectionsCount = 4;
258179db1d7SKowalski, Kamil 
2599391bb9cSRapkiewicz, Pawel  public:
2609391bb9cSRapkiewicz, Pawel   /**
261588c3f0dSKowalski, Kamil    * @brief Creates VLAN for given interface with given Id through D-Bus
262588c3f0dSKowalski, Kamil    *
263588c3f0dSKowalski, Kamil    * @param[in] ifaceId       Id of interface for which VLAN will be created
264588c3f0dSKowalski, Kamil    * @param[in] inputVlanId   ID of the new VLAN
265588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
266588c3f0dSKowalski, Kamil    *
267588c3f0dSKowalski, Kamil    * @return None.
268588c3f0dSKowalski, Kamil    */
269588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
270588c3f0dSKowalski, Kamil   void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
271588c3f0dSKowalski, Kamil                   CallbackFunc &&callback) {
27255c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
273588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
274588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
275588c3f0dSKowalski, Kamil         static_cast<uint32_t>(inputVlanId));
276588c3f0dSKowalski, Kamil   };
277588c3f0dSKowalski, Kamil 
278588c3f0dSKowalski, Kamil   /**
279588c3f0dSKowalski, Kamil    * @brief Sets given Id on the given VLAN interface through D-Bus
280588c3f0dSKowalski, Kamil    *
281588c3f0dSKowalski, Kamil    * @param[in] ifaceId       Id of VLAN interface that should be modified
282588c3f0dSKowalski, Kamil    * @param[in] inputVlanId   New ID of the VLAN
283588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
284588c3f0dSKowalski, Kamil    *
285588c3f0dSKowalski, Kamil    * @return None.
286588c3f0dSKowalski, Kamil    */
287588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
288e439f0f8SKowalski, Kamil   static void changeVlanId(const std::string &ifaceId,
289e439f0f8SKowalski, Kamil                            const uint32_t &inputVlanId,
290588c3f0dSKowalski, Kamil                            CallbackFunc &&callback) {
29155c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
292588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
293588c3f0dSKowalski, Kamil         std::string("/xyz/openbmc_project/network/") + ifaceId,
294588c3f0dSKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
295588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.VLAN", "Id",
296588c3f0dSKowalski, Kamil         sdbusplus::message::variant<uint32_t>(inputVlanId));
297588c3f0dSKowalski, Kamil   };
298588c3f0dSKowalski, Kamil 
299588c3f0dSKowalski, Kamil   /**
300179db1d7SKowalski, Kamil    * @brief Helper function that verifies IP address to check if it is in
301179db1d7SKowalski, Kamil    *        proper format. If bits pointer is provided, also calculates active
302179db1d7SKowalski, Kamil    *        bit count for Subnet Mask.
303179db1d7SKowalski, Kamil    *
304179db1d7SKowalski, Kamil    * @param[in]  ip     IP that will be verified
305179db1d7SKowalski, Kamil    * @param[out] bits   Calculated mask in bits notation
306179db1d7SKowalski, Kamil    *
307179db1d7SKowalski, Kamil    * @return true in case of success, false otherwise
308179db1d7SKowalski, Kamil    */
309179db1d7SKowalski, Kamil   bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
310179db1d7SKowalski, Kamil                                   uint8_t *bits = nullptr) {
311179db1d7SKowalski, Kamil     std::vector<std::string> bytesInMask;
312179db1d7SKowalski, Kamil 
313179db1d7SKowalski, Kamil     boost::split(bytesInMask, ip, boost::is_any_of("."));
314179db1d7SKowalski, Kamil 
315179db1d7SKowalski, Kamil     if (bytesInMask.size() != ipV4AddressSectionsCount) {
316179db1d7SKowalski, Kamil       return false;
317179db1d7SKowalski, Kamil     }
318179db1d7SKowalski, Kamil 
319179db1d7SKowalski, Kamil     if (bits != nullptr) {
320179db1d7SKowalski, Kamil       *bits = 0;
321179db1d7SKowalski, Kamil     }
322179db1d7SKowalski, Kamil 
323179db1d7SKowalski, Kamil     char *endPtr;
324179db1d7SKowalski, Kamil     long previousValue = 255;
325179db1d7SKowalski, Kamil     bool firstZeroInByteHit;
3261db9ca37SKowalski, Kamil     for (const std::string &byte : bytesInMask) {
3271db9ca37SKowalski, Kamil       if (byte.empty()) {
3281db9ca37SKowalski, Kamil         return false;
3291db9ca37SKowalski, Kamil       }
3301db9ca37SKowalski, Kamil 
331179db1d7SKowalski, Kamil       // Use strtol instead of stroi to avoid exceptions
3321db9ca37SKowalski, Kamil       long value = std::strtol(byte.c_str(), &endPtr, 10);
333179db1d7SKowalski, Kamil 
334179db1d7SKowalski, Kamil       // endPtr should point to the end of the string, otherwise given string
335179db1d7SKowalski, Kamil       // is not 100% number
336179db1d7SKowalski, Kamil       if (*endPtr != '\0') {
337179db1d7SKowalski, Kamil         return false;
338179db1d7SKowalski, Kamil       }
339179db1d7SKowalski, Kamil 
340179db1d7SKowalski, Kamil       // Value should be contained in byte
341179db1d7SKowalski, Kamil       if (value < 0 || value > 255) {
342179db1d7SKowalski, Kamil         return false;
343179db1d7SKowalski, Kamil       }
344179db1d7SKowalski, Kamil 
345179db1d7SKowalski, Kamil       if (bits != nullptr) {
346179db1d7SKowalski, Kamil         // Mask has to be continuous between bytes
347179db1d7SKowalski, Kamil         if (previousValue != 255 && value != 0) {
348179db1d7SKowalski, Kamil           return false;
349179db1d7SKowalski, Kamil         }
350179db1d7SKowalski, Kamil 
351179db1d7SKowalski, Kamil         // Mask has to be continuous inside bytes
352179db1d7SKowalski, Kamil         firstZeroInByteHit = false;
353179db1d7SKowalski, Kamil 
354179db1d7SKowalski, Kamil         // Count bits
355179db1d7SKowalski, Kamil         for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
356179db1d7SKowalski, Kamil           if (value & (1 << bitIdx)) {
357179db1d7SKowalski, Kamil             if (firstZeroInByteHit) {
358179db1d7SKowalski, Kamil               // Continuity not preserved
359179db1d7SKowalski, Kamil               return false;
360179db1d7SKowalski, Kamil             } else {
361179db1d7SKowalski, Kamil               (*bits)++;
362179db1d7SKowalski, Kamil             }
363179db1d7SKowalski, Kamil           } else {
364179db1d7SKowalski, Kamil             firstZeroInByteHit = true;
365179db1d7SKowalski, Kamil           }
366179db1d7SKowalski, Kamil         }
367179db1d7SKowalski, Kamil       }
368179db1d7SKowalski, Kamil 
369179db1d7SKowalski, Kamil       previousValue = value;
370179db1d7SKowalski, Kamil     }
371179db1d7SKowalski, Kamil 
372179db1d7SKowalski, Kamil     return true;
373179db1d7SKowalski, Kamil   }
374179db1d7SKowalski, Kamil 
375179db1d7SKowalski, Kamil   /**
376179db1d7SKowalski, Kamil    * @brief Changes IPv4 address type property (Address, Gateway)
377179db1d7SKowalski, Kamil    *
378179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be modified
37955c7b7a2SEd Tanous    * @param[in] ipIdx       index of IP in input array that should be modified
380179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of modified IP
381179db1d7SKowalski, Kamil    * @param[in] name        Name of field in JSON representation
382179db1d7SKowalski, Kamil    * @param[in] newValue    New value that should be written
383179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
384179db1d7SKowalski, Kamil    *
385179db1d7SKowalski, Kamil    * @return true if give IP is valid and has been sent do D-Bus, false
386179db1d7SKowalski, Kamil    * otherwise
387179db1d7SKowalski, Kamil    */
388179db1d7SKowalski, Kamil   void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
389179db1d7SKowalski, Kamil                                  const std::string &ipHash,
390179db1d7SKowalski, Kamil                                  const std::string &name,
391179db1d7SKowalski, Kamil                                  const std::string &newValue,
392179db1d7SKowalski, Kamil                                  const std::shared_ptr<AsyncResp> &asyncResp) {
393*a434f2bdSEd Tanous     auto callback =
394*a434f2bdSEd Tanous         [asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
395*a434f2bdSEd Tanous          newValue{std::move(newValue)}](const boost::system::error_code ec) {
396179db1d7SKowalski, Kamil           if (ec) {
397179db1d7SKowalski, Kamil             messages::addMessageToJson(
39855c7b7a2SEd Tanous                 asyncResp->res.jsonValue, messages::internalError(),
399179db1d7SKowalski, Kamil                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
400179db1d7SKowalski, Kamil           } else {
40155c7b7a2SEd Tanous             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
402179db1d7SKowalski, Kamil           }
403179db1d7SKowalski, Kamil         };
404179db1d7SKowalski, Kamil 
40555c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
406179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
407179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
408179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
409179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", name,
410179db1d7SKowalski, Kamil         sdbusplus::message::variant<std::string>(newValue));
411179db1d7SKowalski, Kamil   };
412179db1d7SKowalski, Kamil 
413179db1d7SKowalski, Kamil   /**
414179db1d7SKowalski, Kamil    * @brief Changes IPv4 address origin property
415179db1d7SKowalski, Kamil    *
416179db1d7SKowalski, Kamil    * @param[in] ifaceId       Id of interface whose IP should be modified
41755c7b7a2SEd Tanous    * @param[in] ipIdx         index of IP in input array that should be modified
418179db1d7SKowalski, Kamil    * @param[in] ipHash        DBus Hash id of modified IP
419179db1d7SKowalski, Kamil    * @param[in] newValue      New value in Redfish format
420179db1d7SKowalski, Kamil    * @param[in] newValueDbus  New value in D-Bus format
421179db1d7SKowalski, Kamil    * @param[io] asyncResp     Response object that will be returned to client
422179db1d7SKowalski, Kamil    *
423179db1d7SKowalski, Kamil    * @return true if give IP is valid and has been sent do D-Bus, false
424179db1d7SKowalski, Kamil    * otherwise
425179db1d7SKowalski, Kamil    */
426179db1d7SKowalski, Kamil   void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
427179db1d7SKowalski, Kamil                         const std::string &ipHash, const std::string &newValue,
428179db1d7SKowalski, Kamil                         const std::string &newValueDbus,
429179db1d7SKowalski, Kamil                         const std::shared_ptr<AsyncResp> &asyncResp) {
430179db1d7SKowalski, Kamil     auto callback =
431179db1d7SKowalski, Kamil         [asyncResp, ipIdx{std::move(ipIdx)},
432179db1d7SKowalski, Kamil          newValue{std::move(newValue)}](const boost::system::error_code ec) {
433179db1d7SKowalski, Kamil           if (ec) {
434179db1d7SKowalski, Kamil             messages::addMessageToJson(
43555c7b7a2SEd Tanous                 asyncResp->res.jsonValue, messages::internalError(),
436179db1d7SKowalski, Kamil                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
437179db1d7SKowalski, Kamil           } else {
43855c7b7a2SEd Tanous             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
439179db1d7SKowalski, Kamil                 newValue;
440179db1d7SKowalski, Kamil           }
441179db1d7SKowalski, Kamil         };
442179db1d7SKowalski, Kamil 
44355c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
444179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
445179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
446179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
447179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", "Origin",
448179db1d7SKowalski, Kamil         sdbusplus::message::variant<std::string>(newValueDbus));
449179db1d7SKowalski, Kamil   };
450179db1d7SKowalski, Kamil 
451179db1d7SKowalski, Kamil   /**
452179db1d7SKowalski, Kamil    * @brief Modifies SubnetMask for given IP
453179db1d7SKowalski, Kamil    *
454179db1d7SKowalski, Kamil    * @param[in] ifaceId      Id of interface whose IP should be modified
45555c7b7a2SEd Tanous    * @param[in] ipIdx        index of IP in input array that should be modified
456179db1d7SKowalski, Kamil    * @param[in] ipHash       DBus Hash id of modified IP
457179db1d7SKowalski, Kamil    * @param[in] newValueStr  Mask in dot notation as string
458179db1d7SKowalski, Kamil    * @param[in] newValue     Mask as PrefixLength in bitcount
459179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
460179db1d7SKowalski, Kamil    *
461179db1d7SKowalski, Kamil    * @return None
462179db1d7SKowalski, Kamil    */
463179db1d7SKowalski, Kamil   void changeIPv4SubnetMaskProperty(
464179db1d7SKowalski, Kamil       const std::string &ifaceId, int ipIdx, const std::string &ipHash,
465179db1d7SKowalski, Kamil       const std::string &newValueStr, uint8_t &newValue,
466179db1d7SKowalski, Kamil       const std::shared_ptr<AsyncResp> &asyncResp) {
467*a434f2bdSEd Tanous     auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
468*a434f2bdSEd Tanous                      newValueStr{std::move(newValueStr)}](
469*a434f2bdSEd Tanous                         const boost::system::error_code ec) {
470179db1d7SKowalski, Kamil       if (ec) {
471179db1d7SKowalski, Kamil         messages::addMessageToJson(
47255c7b7a2SEd Tanous             asyncResp->res.jsonValue, messages::internalError(),
473179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
474179db1d7SKowalski, Kamil       } else {
47555c7b7a2SEd Tanous         asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
476179db1d7SKowalski, Kamil             newValueStr;
477179db1d7SKowalski, Kamil       }
478179db1d7SKowalski, Kamil     };
479179db1d7SKowalski, Kamil 
48055c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
481179db1d7SKowalski, Kamil         std::move(callback), "xyz.openbmc_project.Network",
482179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
483179db1d7SKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
484179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP", "PrefixLength",
485179db1d7SKowalski, Kamil         sdbusplus::message::variant<uint8_t>(newValue));
486179db1d7SKowalski, Kamil   };
487179db1d7SKowalski, Kamil 
488179db1d7SKowalski, Kamil   /**
489588c3f0dSKowalski, Kamil    * @brief Disables VLAN with given ifaceId
490588c3f0dSKowalski, Kamil    *
491588c3f0dSKowalski, Kamil    * @param[in] ifaceId   Id of VLAN interface that should be disabled
492588c3f0dSKowalski, Kamil    * @param[in] callback  Function that will be called after the operation
493588c3f0dSKowalski, Kamil    *
494588c3f0dSKowalski, Kamil    * @return None.
495588c3f0dSKowalski, Kamil    */
496588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
497e439f0f8SKowalski, Kamil   static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
49855c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
499588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
500588c3f0dSKowalski, Kamil         std::string("/xyz/openbmc_project/network/") + ifaceId,
501588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Object.Delete", "Delete");
502588c3f0dSKowalski, Kamil   };
503588c3f0dSKowalski, Kamil 
504588c3f0dSKowalski, Kamil   /**
505588c3f0dSKowalski, Kamil    * @brief Sets given HostName of the machine through D-Bus
506588c3f0dSKowalski, Kamil    *
507588c3f0dSKowalski, Kamil    * @param[in] newHostname   New name that HostName will be changed to
508588c3f0dSKowalski, Kamil    * @param[in] callback      Function that will be called after the operation
509588c3f0dSKowalski, Kamil    *
510588c3f0dSKowalski, Kamil    * @return None.
511588c3f0dSKowalski, Kamil    */
512588c3f0dSKowalski, Kamil   template <typename CallbackFunc>
513588c3f0dSKowalski, Kamil   void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
51455c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
515588c3f0dSKowalski, Kamil         callback, "xyz.openbmc_project.Network",
516588c3f0dSKowalski, Kamil         "/xyz/openbmc_project/network/config",
517588c3f0dSKowalski, Kamil         "org.freedesktop.DBus.Properties", "Set",
518588c3f0dSKowalski, Kamil         "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
519588c3f0dSKowalski, Kamil         sdbusplus::message::variant<std::string>(newHostname));
520588c3f0dSKowalski, Kamil   };
521588c3f0dSKowalski, Kamil 
522588c3f0dSKowalski, Kamil   /**
523179db1d7SKowalski, Kamil    * @brief Deletes given IPv4
524179db1d7SKowalski, Kamil    *
525179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be deleted
52655c7b7a2SEd Tanous    * @param[in] ipIdx       index of IP in input array that should be deleted
527179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of IP that should be deleted
528179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
529179db1d7SKowalski, Kamil    *
530179db1d7SKowalski, Kamil    * @return None
531179db1d7SKowalski, Kamil    */
532179db1d7SKowalski, Kamil   void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
533179db1d7SKowalski, Kamil                   unsigned int ipIdx,
534179db1d7SKowalski, Kamil                   const std::shared_ptr<AsyncResp> &asyncResp) {
53555c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
536*a434f2bdSEd Tanous         [ipIdx{std::move(ipIdx)},
537*a434f2bdSEd Tanous          asyncResp{std::move(asyncResp)}](const boost::system::error_code ec) {
538179db1d7SKowalski, Kamil           if (ec) {
539179db1d7SKowalski, Kamil             messages::addMessageToJson(
54055c7b7a2SEd Tanous                 asyncResp->res.jsonValue, messages::internalError(),
541179db1d7SKowalski, Kamil                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
542179db1d7SKowalski, Kamil           } else {
54355c7b7a2SEd Tanous             asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
544179db1d7SKowalski, Kamil           }
545179db1d7SKowalski, Kamil         },
546179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network",
547179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
548179db1d7SKowalski, Kamil         "xyz.openbmc_project.Object.Delete", "Delete");
549179db1d7SKowalski, Kamil   }
550179db1d7SKowalski, Kamil 
551179db1d7SKowalski, Kamil   /**
552179db1d7SKowalski, Kamil    * @brief Creates IPv4 with given data
553179db1d7SKowalski, Kamil    *
554179db1d7SKowalski, Kamil    * @param[in] ifaceId     Id of interface whose IP should be deleted
55555c7b7a2SEd Tanous    * @param[in] ipIdx       index of IP in input array that should be deleted
556179db1d7SKowalski, Kamil    * @param[in] ipHash      DBus Hash id of IP that should be deleted
557179db1d7SKowalski, Kamil    * @param[io] asyncResp   Response object that will be returned to client
558179db1d7SKowalski, Kamil    *
559179db1d7SKowalski, Kamil    * @return None
560179db1d7SKowalski, Kamil    */
561179db1d7SKowalski, Kamil   void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
562179db1d7SKowalski, Kamil                   uint8_t subnetMask, const std::string &gateway,
563179db1d7SKowalski, Kamil                   const std::string &address,
564179db1d7SKowalski, Kamil                   const std::shared_ptr<AsyncResp> &asyncResp) {
565*a434f2bdSEd Tanous     auto createIpHandler =
566*a434f2bdSEd Tanous         [ipIdx{std::move(ipIdx)},
567*a434f2bdSEd Tanous          asyncResp{std::move(asyncResp)}](const boost::system::error_code ec) {
568179db1d7SKowalski, Kamil           if (ec) {
569179db1d7SKowalski, Kamil             messages::addMessageToJson(
57055c7b7a2SEd Tanous                 asyncResp->res.jsonValue, messages::internalError(),
571179db1d7SKowalski, Kamil                 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
572179db1d7SKowalski, Kamil           }
573179db1d7SKowalski, Kamil         };
574179db1d7SKowalski, Kamil 
57555c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
576179db1d7SKowalski, Kamil         std::move(createIpHandler), "xyz.openbmc_project.Network",
577179db1d7SKowalski, Kamil         "/xyz/openbmc_project/network/" + ifaceId,
578179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP.Create", "IP",
579179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
580179db1d7SKowalski, Kamil         gateway);
581179db1d7SKowalski, Kamil   }
582179db1d7SKowalski, Kamil 
583179db1d7SKowalski, Kamil   /**
584179db1d7SKowalski, Kamil    * @brief Translates Address Origin value from D-Bus to Redfish format and
585179db1d7SKowalski, Kamil    *        vice-versa
586179db1d7SKowalski, Kamil    *
587179db1d7SKowalski, Kamil    * @param[in] inputOrigin Input value that should be translated
588179db1d7SKowalski, Kamil    * @param[in] isIPv4      True for IPv4 origins, False for IPv6
589179db1d7SKowalski, Kamil    * @param[in] isFromDBus  True for DBus->Redfish conversion, false for reverse
590179db1d7SKowalski, Kamil    *
591179db1d7SKowalski, Kamil    * @return Empty string in case of failure, translated value otherwise
592179db1d7SKowalski, Kamil    */
593179db1d7SKowalski, Kamil   std::string translateAddressOriginBetweenDBusAndRedfish(
594179db1d7SKowalski, Kamil       const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
595179db1d7SKowalski, Kamil     // Invalid pointer
596179db1d7SKowalski, Kamil     if (inputOrigin == nullptr) {
597179db1d7SKowalski, Kamil       return "";
598179db1d7SKowalski, Kamil     }
599179db1d7SKowalski, Kamil 
600179db1d7SKowalski, Kamil     static const constexpr unsigned int firstIPv4OnlyIdx = 1;
601179db1d7SKowalski, Kamil     static const constexpr unsigned int firstIPv6OnlyIdx = 3;
602179db1d7SKowalski, Kamil 
603179db1d7SKowalski, Kamil     std::array<std::pair<const char *, const char *>, 6> translationTable{
604179db1d7SKowalski, Kamil         {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
605179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
606179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
607179db1d7SKowalski, Kamil           "IPv4LinkLocal"},
608179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
609179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
610179db1d7SKowalski, Kamil           "LinkLocal"},
611179db1d7SKowalski, Kamil          {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
612179db1d7SKowalski, Kamil 
613179db1d7SKowalski, Kamil     for (unsigned int i = 0; i < translationTable.size(); i++) {
614179db1d7SKowalski, Kamil       // Skip unrelated
615179db1d7SKowalski, Kamil       if (isIPv4 && i >= firstIPv6OnlyIdx) break;
616179db1d7SKowalski, Kamil       if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
617179db1d7SKowalski, Kamil 
618179db1d7SKowalski, Kamil       // When translating D-Bus to Redfish compare input to first element
619179db1d7SKowalski, Kamil       if (isFromDBus && translationTable[i].first == *inputOrigin)
620179db1d7SKowalski, Kamil         return translationTable[i].second;
621179db1d7SKowalski, Kamil 
622179db1d7SKowalski, Kamil       // When translating Redfish to D-Bus compare input to second element
623179db1d7SKowalski, Kamil       if (!isFromDBus && translationTable[i].second == *inputOrigin)
624179db1d7SKowalski, Kamil         return translationTable[i].first;
625179db1d7SKowalski, Kamil     }
626179db1d7SKowalski, Kamil 
627179db1d7SKowalski, Kamil     // If we are still here, that means that value has not been found
628179db1d7SKowalski, Kamil     return "";
629179db1d7SKowalski, Kamil   }
630179db1d7SKowalski, Kamil 
631179db1d7SKowalski, Kamil   /**
632179db1d7SKowalski, Kamil    * Function that retrieves all properties for given Ethernet Interface
633179db1d7SKowalski, Kamil    * Object
634179db1d7SKowalski, Kamil    * from EntityManager Network Manager
63555c7b7a2SEd Tanous    * @param ethifaceId a eth interface id to query on DBus
636179db1d7SKowalski, Kamil    * @param callback a function that shall be called to convert Dbus output
637179db1d7SKowalski, Kamil    * into JSON
638179db1d7SKowalski, Kamil    */
639179db1d7SKowalski, Kamil   template <typename CallbackFunc>
64055c7b7a2SEd Tanous   void getEthernetIfaceData(const std::string &ethifaceId,
641179db1d7SKowalski, Kamil                             CallbackFunc &&callback) {
64255c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
643*a434f2bdSEd Tanous         [this, ethifaceId{std::move(ethifaceId)},
644*a434f2bdSEd Tanous          callback{std::move(callback)}](
645*a434f2bdSEd Tanous             const boost::system::error_code error_code,
646179db1d7SKowalski, Kamil             const GetManagedObjectsType &resp) {
64755c7b7a2SEd Tanous           EthernetInterfaceData ethData{};
64855c7b7a2SEd Tanous           std::vector<IPv4AddressData> ipv4Data;
64955c7b7a2SEd Tanous           ipv4Data.reserve(maxIpV4AddressesPerInterface);
650179db1d7SKowalski, Kamil 
651179db1d7SKowalski, Kamil           if (error_code) {
652179db1d7SKowalski, Kamil             // Something wrong on DBus, the error_code is not important at
653179db1d7SKowalski, Kamil             // this moment, just return success=false, and empty output. Since
654179db1d7SKowalski, Kamil             // size of vector may vary depending on information from Network
655179db1d7SKowalski, Kamil             // Manager, and empty output could not be treated same way as
656179db1d7SKowalski, Kamil             // error.
65755c7b7a2SEd Tanous             callback(false, ethData, ipv4Data);
658179db1d7SKowalski, Kamil             return;
659179db1d7SKowalski, Kamil           }
660179db1d7SKowalski, Kamil 
661927a505aSKowalski, Kamil           // Find interface
66255c7b7a2SEd Tanous           if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
663927a505aSKowalski, Kamil               resp.end()) {
664927a505aSKowalski, Kamil             // Interface has not been found
66555c7b7a2SEd Tanous             callback(false, ethData, ipv4Data);
666927a505aSKowalski, Kamil             return;
667927a505aSKowalski, Kamil           }
668927a505aSKowalski, Kamil 
66955c7b7a2SEd Tanous           extractEthernetInterfaceData(ethifaceId, resp, ethData);
67055c7b7a2SEd Tanous           extractIPv4Data(ethifaceId, resp, ipv4Data);
671179db1d7SKowalski, Kamil 
672179db1d7SKowalski, Kamil           // Fix global GW
67355c7b7a2SEd Tanous           for (IPv4AddressData &ipv4 : ipv4Data) {
674179db1d7SKowalski, Kamil             if ((ipv4.global) &&
675179db1d7SKowalski, Kamil                 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
67655c7b7a2SEd Tanous               ipv4.gateway = ethData.defaultGateway;
677179db1d7SKowalski, Kamil             }
678179db1d7SKowalski, Kamil           }
679179db1d7SKowalski, Kamil 
680*a434f2bdSEd Tanous           // Finally make a callback with useful data
68155c7b7a2SEd Tanous           callback(true, ethData, ipv4Data);
682179db1d7SKowalski, Kamil         },
683179db1d7SKowalski, Kamil         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
684179db1d7SKowalski, Kamil         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
685179db1d7SKowalski, Kamil   };
686179db1d7SKowalski, Kamil 
687179db1d7SKowalski, Kamil   /**
6889391bb9cSRapkiewicz, Pawel    * Function that retrieves all Ethernet Interfaces available through Network
6899391bb9cSRapkiewicz, Pawel    * Manager
6909391bb9cSRapkiewicz, Pawel    * @param callback a function that shall be called to convert Dbus output into
6919391bb9cSRapkiewicz, Pawel    * JSON.
6929391bb9cSRapkiewicz, Pawel    */
6939391bb9cSRapkiewicz, Pawel   template <typename CallbackFunc>
6949391bb9cSRapkiewicz, Pawel   void getEthernetIfaceList(CallbackFunc &&callback) {
69555c7b7a2SEd Tanous     crow::connections::systemBus->async_method_call(
6969391bb9cSRapkiewicz, Pawel         [this, callback{std::move(callback)}](
6979391bb9cSRapkiewicz, Pawel             const boost::system::error_code error_code,
698aa2e59c1SEd Tanous             GetManagedObjectsType &resp) {
6999391bb9cSRapkiewicz, Pawel           // Callback requires vector<string> to retrieve all available ethernet
7009391bb9cSRapkiewicz, Pawel           // interfaces
70155c7b7a2SEd Tanous           std::vector<std::string> ifaceList;
70255c7b7a2SEd Tanous           ifaceList.reserve(resp.size());
7039391bb9cSRapkiewicz, Pawel           if (error_code) {
7049391bb9cSRapkiewicz, Pawel             // Something wrong on DBus, the error_code is not important at this
7059391bb9cSRapkiewicz, Pawel             // moment, just return success=false, and empty output. Since size
7069391bb9cSRapkiewicz, Pawel             // of vector may vary depending on information from Network Manager,
7079391bb9cSRapkiewicz, Pawel             // and empty output could not be treated same way as error.
70855c7b7a2SEd Tanous             callback(false, ifaceList);
7099391bb9cSRapkiewicz, Pawel             return;
7109391bb9cSRapkiewicz, Pawel           }
7119391bb9cSRapkiewicz, Pawel 
7129391bb9cSRapkiewicz, Pawel           // Iterate over all retrieved ObjectPaths.
7139391bb9cSRapkiewicz, Pawel           for (auto &objpath : resp) {
7149391bb9cSRapkiewicz, Pawel             // And all interfaces available for certain ObjectPath.
7159391bb9cSRapkiewicz, Pawel             for (auto &interface : objpath.second) {
7169391bb9cSRapkiewicz, Pawel               // If interface is xyz.openbmc_project.Network.EthernetInterface,
7179391bb9cSRapkiewicz, Pawel               // this is what we're looking for.
7189391bb9cSRapkiewicz, Pawel               if (interface.first ==
7199391bb9cSRapkiewicz, Pawel                   "xyz.openbmc_project.Network.EthernetInterface") {
720*a434f2bdSEd Tanous                 // Cut out everything until last "/", ...
72155c7b7a2SEd Tanous                 const std::string &ifaceId =
722daf36e2eSEd Tanous                     static_cast<const std::string &>(objpath.first);
72355c7b7a2SEd Tanous                 std::size_t lastPos = ifaceId.rfind("/");
72455c7b7a2SEd Tanous                 if (lastPos != std::string::npos) {
7259391bb9cSRapkiewicz, Pawel                   // and put it into output vector.
72655c7b7a2SEd Tanous                   ifaceList.emplace_back(ifaceId.substr(lastPos + 1));
7279391bb9cSRapkiewicz, Pawel                 }
7289391bb9cSRapkiewicz, Pawel               }
7299391bb9cSRapkiewicz, Pawel             }
7309391bb9cSRapkiewicz, Pawel           }
731*a434f2bdSEd Tanous           // Finally make a callback with useful data
73255c7b7a2SEd Tanous           callback(true, ifaceList);
7339391bb9cSRapkiewicz, Pawel         },
734aa2e59c1SEd Tanous         "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
735aa2e59c1SEd Tanous         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
7369391bb9cSRapkiewicz, Pawel   };
7379391bb9cSRapkiewicz, Pawel };
7389391bb9cSRapkiewicz, Pawel 
7399391bb9cSRapkiewicz, Pawel /**
7409391bb9cSRapkiewicz, Pawel  * EthernetCollection derived class for delivering Ethernet Collection Schema
7419391bb9cSRapkiewicz, Pawel  */
7429391bb9cSRapkiewicz, Pawel class EthernetCollection : public Node {
7439391bb9cSRapkiewicz, Pawel  public:
7449391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
7459391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
7469391bb9cSRapkiewicz, Pawel   // GetSubroutes method
7479391bb9cSRapkiewicz, Pawel   EthernetCollection(CrowApp &app)
7489391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
7499391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] =
7509391bb9cSRapkiewicz, Pawel         "#EthernetInterfaceCollection.EthernetInterfaceCollection";
7519391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
7529391bb9cSRapkiewicz, Pawel         "/redfish/v1/"
7539391bb9cSRapkiewicz, Pawel         "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
7549391bb9cSRapkiewicz, Pawel     Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
7559391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Ethernet Network Interface Collection";
7569391bb9cSRapkiewicz, Pawel     Node::json["Description"] =
7579391bb9cSRapkiewicz, Pawel         "Collection of EthernetInterfaces for this Manager";
7589391bb9cSRapkiewicz, Pawel 
759588c3f0dSKowalski, Kamil     entityPrivileges = {
760588c3f0dSKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
761e0d918bcSEd Tanous         {boost::beast::http::verb::head, {{"Login"}}},
762e0d918bcSEd Tanous         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
763e0d918bcSEd Tanous         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
764e0d918bcSEd Tanous         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
765e0d918bcSEd Tanous         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
7669391bb9cSRapkiewicz, Pawel   }
7679391bb9cSRapkiewicz, Pawel 
7689391bb9cSRapkiewicz, Pawel  private:
7699391bb9cSRapkiewicz, Pawel   /**
7709391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
7719391bb9cSRapkiewicz, Pawel    */
77255c7b7a2SEd Tanous   void doGet(crow::Response &res, const crow::Request &req,
7739391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
7749391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
7759391bb9cSRapkiewicz, Pawel     // any Manager, not only hardcoded 'openbmc'.
77655c7b7a2SEd Tanous     std::string managerId = "openbmc";
7779391bb9cSRapkiewicz, Pawel 
77855c7b7a2SEd Tanous     // get eth interface list, and call the below callback for JSON preparation
779*a434f2bdSEd Tanous     ethernetProvider.getEthernetIfaceList(
780*a434f2bdSEd Tanous         [&, managerId{std::move(managerId)}](
7819391bb9cSRapkiewicz, Pawel             const bool &success, const std::vector<std::string> &iface_list) {
7829391bb9cSRapkiewicz, Pawel           if (success) {
78355c7b7a2SEd Tanous             nlohmann::json ifaceArray = nlohmann::json::array();
78455c7b7a2SEd Tanous             for (const std::string &ifaceItem : iface_list) {
78555c7b7a2SEd Tanous               ifaceArray.push_back(
78655c7b7a2SEd Tanous                   {{"@odata.id", "/redfish/v1/Managers/" + managerId +
78755c7b7a2SEd Tanous                                      "/EthernetInterfaces/" + ifaceItem}});
7889391bb9cSRapkiewicz, Pawel             }
78955c7b7a2SEd Tanous             Node::json["Members"] = ifaceArray;
79055c7b7a2SEd Tanous             Node::json["Members@odata.count"] = ifaceArray.size();
7919391bb9cSRapkiewicz, Pawel             Node::json["@odata.id"] =
79255c7b7a2SEd Tanous                 "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces";
79355c7b7a2SEd Tanous             res.jsonValue = Node::json;
7949391bb9cSRapkiewicz, Pawel           } else {
7959391bb9cSRapkiewicz, Pawel             // No success, best what we can do is return INTERNALL ERROR
796e0d918bcSEd Tanous             res.result(boost::beast::http::status::internal_server_error);
7979391bb9cSRapkiewicz, Pawel           }
7989391bb9cSRapkiewicz, Pawel           res.end();
7999391bb9cSRapkiewicz, Pawel         });
8009391bb9cSRapkiewicz, Pawel   }
8019391bb9cSRapkiewicz, Pawel 
8029391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
8039391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
80455c7b7a2SEd Tanous   OnDemandEthernetProvider ethernetProvider;
8059391bb9cSRapkiewicz, Pawel };
8069391bb9cSRapkiewicz, Pawel 
8079391bb9cSRapkiewicz, Pawel /**
8089391bb9cSRapkiewicz, Pawel  * EthernetInterface derived class for delivering Ethernet Schema
8099391bb9cSRapkiewicz, Pawel  */
8109391bb9cSRapkiewicz, Pawel class EthernetInterface : public Node {
8119391bb9cSRapkiewicz, Pawel  public:
8129391bb9cSRapkiewicz, Pawel   /*
8139391bb9cSRapkiewicz, Pawel    * Default Constructor
8149391bb9cSRapkiewicz, Pawel    */
8159391bb9cSRapkiewicz, Pawel   // TODO(Pawel) Remove line from below, where we assume that there is only one
8169391bb9cSRapkiewicz, Pawel   // manager called openbmc This shall be generic, but requires to update
8179391bb9cSRapkiewicz, Pawel   // GetSubroutes method
8189391bb9cSRapkiewicz, Pawel   EthernetInterface(CrowApp &app)
8199391bb9cSRapkiewicz, Pawel       : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
8209391bb9cSRapkiewicz, Pawel              std::string()) {
8219391bb9cSRapkiewicz, Pawel     Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
8229391bb9cSRapkiewicz, Pawel     Node::json["@odata.context"] =
8239391bb9cSRapkiewicz, Pawel         "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
8249391bb9cSRapkiewicz, Pawel     Node::json["Name"] = "Manager Ethernet Interface";
8259391bb9cSRapkiewicz, Pawel     Node::json["Description"] = "Management Network Interface";
8269391bb9cSRapkiewicz, Pawel 
827588c3f0dSKowalski, Kamil     entityPrivileges = {
828588c3f0dSKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
829e0d918bcSEd Tanous         {boost::beast::http::verb::head, {{"Login"}}},
830e0d918bcSEd Tanous         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
831e0d918bcSEd Tanous         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
832e0d918bcSEd Tanous         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
833e0d918bcSEd Tanous         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
8349391bb9cSRapkiewicz, Pawel   }
8359391bb9cSRapkiewicz, Pawel 
836e439f0f8SKowalski, Kamil   // TODO(kkowalsk) Find a suitable class/namespace for this
837e439f0f8SKowalski, Kamil   static void handleVlanPatch(const std::string &ifaceId,
838e439f0f8SKowalski, Kamil                               const nlohmann::json &input,
839588c3f0dSKowalski, Kamil                               const EthernetInterfaceData &eth_data,
840e439f0f8SKowalski, Kamil                               const std::string &pathPrefix,
841588c3f0dSKowalski, Kamil                               const std::shared_ptr<AsyncResp> &asyncResp) {
842588c3f0dSKowalski, Kamil     if (!input.is_object()) {
843588c3f0dSKowalski, Kamil       messages::addMessageToJson(
84455c7b7a2SEd Tanous           asyncResp->res.jsonValue,
845e439f0f8SKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
846588c3f0dSKowalski, Kamil       return;
847588c3f0dSKowalski, Kamil     }
848588c3f0dSKowalski, Kamil 
849e439f0f8SKowalski, Kamil     const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
850e439f0f8SKowalski, Kamil     nlohmann::json &paramsJson =
851e439f0f8SKowalski, Kamil         (pathPrefix == "/")
85255c7b7a2SEd Tanous             ? asyncResp->res.jsonValue
85355c7b7a2SEd Tanous             : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>(
854e439f0f8SKowalski, Kamil                   pathPrefix)];
855588c3f0dSKowalski, Kamil     bool inputVlanEnabled;
856588c3f0dSKowalski, Kamil     uint64_t inputVlanId;
857e439f0f8SKowalski, Kamil 
858588c3f0dSKowalski, Kamil     json_util::Result inputVlanEnabledState = json_util::getBool(
859588c3f0dSKowalski, Kamil         "VLANEnable", input, inputVlanEnabled,
860588c3f0dSKowalski, Kamil         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
86155c7b7a2SEd Tanous         asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
862588c3f0dSKowalski, Kamil     json_util::Result inputVlanIdState = json_util::getUnsigned(
863588c3f0dSKowalski, Kamil         "VLANId", input, inputVlanId,
864588c3f0dSKowalski, Kamil         static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
86555c7b7a2SEd Tanous         asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
866588c3f0dSKowalski, Kamil     bool inputInvalid = false;
867588c3f0dSKowalski, Kamil 
868588c3f0dSKowalski, Kamil     // Do not proceed if fields in VLAN object were of wrong type
869588c3f0dSKowalski, Kamil     if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
870588c3f0dSKowalski, Kamil         inputVlanIdState == json_util::Result::WRONG_TYPE) {
871588c3f0dSKowalski, Kamil       return;
872588c3f0dSKowalski, Kamil     }
873588c3f0dSKowalski, Kamil 
874588c3f0dSKowalski, Kamil     // Verify input
87555c7b7a2SEd Tanous     if (eth_data.vlanId == nullptr) {
876e439f0f8SKowalski, Kamil       // This interface is not a VLAN. Cannot do anything with it
877e439f0f8SKowalski, Kamil       // TODO(kkowalsk) Change this message
87855c7b7a2SEd Tanous       messages::addMessageToJson(asyncResp->res.jsonValue,
879588c3f0dSKowalski, Kamil                                  messages::propertyMissing("VLANEnable"),
880e439f0f8SKowalski, Kamil                                  pathPrefix);
881588c3f0dSKowalski, Kamil 
882588c3f0dSKowalski, Kamil       inputInvalid = true;
883588c3f0dSKowalski, Kamil     } else {
884588c3f0dSKowalski, Kamil       // Load actual data into field values if they were not provided
885588c3f0dSKowalski, Kamil       if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
886588c3f0dSKowalski, Kamil         inputVlanEnabled = true;
887588c3f0dSKowalski, Kamil       }
888588c3f0dSKowalski, Kamil 
889588c3f0dSKowalski, Kamil       if (inputVlanIdState == json_util::Result::NOT_EXIST) {
89055c7b7a2SEd Tanous         inputVlanId = *eth_data.vlanId;
891588c3f0dSKowalski, Kamil       }
892588c3f0dSKowalski, Kamil     }
893588c3f0dSKowalski, Kamil 
894588c3f0dSKowalski, Kamil     // Do not proceed if input has not been valid
895588c3f0dSKowalski, Kamil     if (inputInvalid) {
896588c3f0dSKowalski, Kamil       return;
897588c3f0dSKowalski, Kamil     }
898588c3f0dSKowalski, Kamil 
899588c3f0dSKowalski, Kamil     // VLAN is configured on the interface
90055c7b7a2SEd Tanous     if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) {
901588c3f0dSKowalski, Kamil       // Change VLAN Id
902e439f0f8SKowalski, Kamil       paramsJson["VLANId"] = inputVlanId;
903e439f0f8SKowalski, Kamil       OnDemandEthernetProvider::changeVlanId(
904e439f0f8SKowalski, Kamil           ifaceId, static_cast<uint32_t>(inputVlanId),
905e439f0f8SKowalski, Kamil           [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
906e439f0f8SKowalski, Kamil               const boost::system::error_code ec) {
907588c3f0dSKowalski, Kamil             if (ec) {
90855c7b7a2SEd Tanous               messages::addMessageToJson(asyncResp->res.jsonValue,
909e439f0f8SKowalski, Kamil                                          messages::internalError(), pathPrefix);
910588c3f0dSKowalski, Kamil             } else {
911e439f0f8SKowalski, Kamil               paramsJson["VLANEnable"] = true;
912e439f0f8SKowalski, Kamil             }
913e439f0f8SKowalski, Kamil           });
914e439f0f8SKowalski, Kamil     } else if (inputVlanEnabled == false) {
915e439f0f8SKowalski, Kamil       // Disable VLAN
916e439f0f8SKowalski, Kamil       OnDemandEthernetProvider::disableVlan(
917e439f0f8SKowalski, Kamil           ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
918e439f0f8SKowalski, Kamil                        const boost::system::error_code ec) {
919e439f0f8SKowalski, Kamil             if (ec) {
92055c7b7a2SEd Tanous               messages::addMessageToJson(asyncResp->res.jsonValue,
921e439f0f8SKowalski, Kamil                                          messages::internalError(), pathPrefix);
922e439f0f8SKowalski, Kamil             } else {
923e439f0f8SKowalski, Kamil               paramsJson["VLANEnable"] = false;
924588c3f0dSKowalski, Kamil             }
925588c3f0dSKowalski, Kamil           });
926588c3f0dSKowalski, Kamil     }
927588c3f0dSKowalski, Kamil   }
928588c3f0dSKowalski, Kamil 
929e439f0f8SKowalski, Kamil  private:
930588c3f0dSKowalski, Kamil   void handleHostnamePatch(const nlohmann::json &input,
931588c3f0dSKowalski, Kamil                            const EthernetInterfaceData &eth_data,
932588c3f0dSKowalski, Kamil                            const std::shared_ptr<AsyncResp> &asyncResp) {
933588c3f0dSKowalski, Kamil     if (input.is_string()) {
934588c3f0dSKowalski, Kamil       std::string newHostname = input.get<std::string>();
935588c3f0dSKowalski, Kamil 
936588c3f0dSKowalski, Kamil       if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
937588c3f0dSKowalski, Kamil         // Change hostname
93855c7b7a2SEd Tanous         ethernetProvider.setHostName(
939588c3f0dSKowalski, Kamil             newHostname,
940588c3f0dSKowalski, Kamil             [asyncResp, newHostname](const boost::system::error_code ec) {
941588c3f0dSKowalski, Kamil               if (ec) {
94255c7b7a2SEd Tanous                 messages::addMessageToJson(asyncResp->res.jsonValue,
943588c3f0dSKowalski, Kamil                                            messages::internalError(),
944588c3f0dSKowalski, Kamil                                            "/HostName");
945588c3f0dSKowalski, Kamil               } else {
94655c7b7a2SEd Tanous                 asyncResp->res.jsonValue["HostName"] = newHostname;
947588c3f0dSKowalski, Kamil               }
948588c3f0dSKowalski, Kamil             });
949588c3f0dSKowalski, Kamil       }
950588c3f0dSKowalski, Kamil     } else {
951588c3f0dSKowalski, Kamil       messages::addMessageToJson(
95255c7b7a2SEd Tanous           asyncResp->res.jsonValue,
953588c3f0dSKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "HostName"),
954588c3f0dSKowalski, Kamil           "/HostName");
955588c3f0dSKowalski, Kamil     }
956588c3f0dSKowalski, Kamil   }
957588c3f0dSKowalski, Kamil 
958179db1d7SKowalski, Kamil   void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
959179db1d7SKowalski, Kamil                        const std::vector<IPv4AddressData> &ipv4_data,
960179db1d7SKowalski, Kamil                        const std::shared_ptr<AsyncResp> &asyncResp) {
961179db1d7SKowalski, Kamil     if (!input.is_array()) {
962179db1d7SKowalski, Kamil       messages::addMessageToJson(
96355c7b7a2SEd Tanous           asyncResp->res.jsonValue,
964179db1d7SKowalski, Kamil           messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
965179db1d7SKowalski, Kamil           "/IPv4Addresses");
966179db1d7SKowalski, Kamil       return;
967179db1d7SKowalski, Kamil     }
968179db1d7SKowalski, Kamil 
969179db1d7SKowalski, Kamil     // According to Redfish PATCH definition, size must be at least equal
970179db1d7SKowalski, Kamil     if (input.size() < ipv4_data.size()) {
971179db1d7SKowalski, Kamil       // TODO(kkowalsk) This should be a message indicating that not enough
972179db1d7SKowalski, Kamil       // data has been provided
97355c7b7a2SEd Tanous       messages::addMessageToJson(asyncResp->res.jsonValue,
974179db1d7SKowalski, Kamil                                  messages::internalError(), "/IPv4Addresses");
975179db1d7SKowalski, Kamil       return;
976179db1d7SKowalski, Kamil     }
977179db1d7SKowalski, Kamil 
978179db1d7SKowalski, Kamil     json_util::Result addressFieldState;
979179db1d7SKowalski, Kamil     json_util::Result subnetMaskFieldState;
980179db1d7SKowalski, Kamil     json_util::Result addressOriginFieldState;
981179db1d7SKowalski, Kamil     json_util::Result gatewayFieldState;
982179db1d7SKowalski, Kamil     const std::string *addressFieldValue;
983179db1d7SKowalski, Kamil     const std::string *subnetMaskFieldValue;
984179db1d7SKowalski, Kamil     const std::string *addressOriginFieldValue = nullptr;
985179db1d7SKowalski, Kamil     const std::string *gatewayFieldValue;
986179db1d7SKowalski, Kamil     uint8_t subnetMaskAsPrefixLength;
987179db1d7SKowalski, Kamil     std::string addressOriginInDBusFormat;
988179db1d7SKowalski, Kamil 
989179db1d7SKowalski, Kamil     bool errorDetected = false;
990179db1d7SKowalski, Kamil     for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
991179db1d7SKowalski, Kamil       // Check that entry is not of some unexpected type
992179db1d7SKowalski, Kamil       if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
993179db1d7SKowalski, Kamil         // Invalid object type
994179db1d7SKowalski, Kamil         messages::addMessageToJson(
99555c7b7a2SEd Tanous             asyncResp->res.jsonValue,
996179db1d7SKowalski, Kamil             messages::propertyValueTypeError(input[entryIdx].dump(),
997179db1d7SKowalski, Kamil                                              "IPv4Address"),
998179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx));
999179db1d7SKowalski, Kamil 
1000179db1d7SKowalski, Kamil         continue;
1001179db1d7SKowalski, Kamil       }
1002179db1d7SKowalski, Kamil 
1003179db1d7SKowalski, Kamil       // Try to load fields
1004179db1d7SKowalski, Kamil       addressFieldState = json_util::getString(
1005179db1d7SKowalski, Kamil           "Address", input[entryIdx], addressFieldValue,
1006179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
100755c7b7a2SEd Tanous           asyncResp->res.jsonValue,
1008179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1009179db1d7SKowalski, Kamil       subnetMaskFieldState = json_util::getString(
1010179db1d7SKowalski, Kamil           "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1011179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
101255c7b7a2SEd Tanous           asyncResp->res.jsonValue,
1013179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1014179db1d7SKowalski, Kamil       addressOriginFieldState = json_util::getString(
1015179db1d7SKowalski, Kamil           "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1016179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
101755c7b7a2SEd Tanous           asyncResp->res.jsonValue,
1018179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1019179db1d7SKowalski, Kamil       gatewayFieldState = json_util::getString(
1020179db1d7SKowalski, Kamil           "Gateway", input[entryIdx], gatewayFieldValue,
1021179db1d7SKowalski, Kamil           static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
102255c7b7a2SEd Tanous           asyncResp->res.jsonValue,
1023179db1d7SKowalski, Kamil           "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1024179db1d7SKowalski, Kamil 
1025179db1d7SKowalski, Kamil       if (addressFieldState == json_util::Result::WRONG_TYPE ||
1026179db1d7SKowalski, Kamil           subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1027179db1d7SKowalski, Kamil           addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1028179db1d7SKowalski, Kamil           gatewayFieldState == json_util::Result::WRONG_TYPE) {
1029179db1d7SKowalski, Kamil         return;
1030179db1d7SKowalski, Kamil       }
1031179db1d7SKowalski, Kamil 
1032179db1d7SKowalski, Kamil       if (addressFieldState == json_util::Result::SUCCESS &&
103355c7b7a2SEd Tanous           !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1034179db1d7SKowalski, Kamil         errorDetected = true;
1035179db1d7SKowalski, Kamil         messages::addMessageToJson(
103655c7b7a2SEd Tanous             asyncResp->res.jsonValue,
1037179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*addressFieldValue, "Address"),
1038179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1039179db1d7SKowalski, Kamil       }
1040179db1d7SKowalski, Kamil 
1041179db1d7SKowalski, Kamil       if (subnetMaskFieldState == json_util::Result::SUCCESS &&
104255c7b7a2SEd Tanous           !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1043179db1d7SKowalski, Kamil               *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1044179db1d7SKowalski, Kamil         errorDetected = true;
1045179db1d7SKowalski, Kamil         messages::addMessageToJson(
104655c7b7a2SEd Tanous             asyncResp->res.jsonValue,
1047179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*subnetMaskFieldValue,
1048179db1d7SKowalski, Kamil                                                "SubnetMask"),
1049179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1050179db1d7SKowalski, Kamil       }
1051179db1d7SKowalski, Kamil 
105255c7b7a2SEd Tanous       // get Address origin in proper format
1053179db1d7SKowalski, Kamil       addressOriginInDBusFormat =
105455c7b7a2SEd Tanous           ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
1055179db1d7SKowalski, Kamil               addressOriginFieldValue, true, false);
1056179db1d7SKowalski, Kamil 
1057179db1d7SKowalski, Kamil       if (addressOriginFieldState == json_util::Result::SUCCESS &&
1058179db1d7SKowalski, Kamil           addressOriginInDBusFormat.empty()) {
1059179db1d7SKowalski, Kamil         errorDetected = true;
1060179db1d7SKowalski, Kamil         messages::addMessageToJson(
106155c7b7a2SEd Tanous             asyncResp->res.jsonValue,
1062179db1d7SKowalski, Kamil             messages::propertyValueNotInList(*addressOriginFieldValue,
1063179db1d7SKowalski, Kamil                                              "AddressOrigin"),
1064179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1065179db1d7SKowalski, Kamil       }
1066179db1d7SKowalski, Kamil 
1067179db1d7SKowalski, Kamil       if (gatewayFieldState == json_util::Result::SUCCESS &&
106855c7b7a2SEd Tanous           !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1069179db1d7SKowalski, Kamil         errorDetected = true;
1070179db1d7SKowalski, Kamil         messages::addMessageToJson(
107155c7b7a2SEd Tanous             asyncResp->res.jsonValue,
1072179db1d7SKowalski, Kamil             messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1073179db1d7SKowalski, Kamil             "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1074179db1d7SKowalski, Kamil       }
1075179db1d7SKowalski, Kamil 
1076179db1d7SKowalski, Kamil       // If any error occured do not proceed with current entry, but do not
1077179db1d7SKowalski, Kamil       // end loop
1078179db1d7SKowalski, Kamil       if (errorDetected) {
1079179db1d7SKowalski, Kamil         errorDetected = false;
1080179db1d7SKowalski, Kamil         continue;
1081179db1d7SKowalski, Kamil       }
1082179db1d7SKowalski, Kamil 
1083179db1d7SKowalski, Kamil       if (entryIdx >= ipv4_data.size()) {
108455c7b7a2SEd Tanous         asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx];
1085179db1d7SKowalski, Kamil 
1086179db1d7SKowalski, Kamil         // Verify that all field were provided
1087179db1d7SKowalski, Kamil         if (addressFieldState == json_util::Result::NOT_EXIST) {
1088179db1d7SKowalski, Kamil           errorDetected = true;
1089179db1d7SKowalski, Kamil           messages::addMessageToJson(
109055c7b7a2SEd Tanous               asyncResp->res.jsonValue, messages::propertyMissing("Address"),
1091179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1092179db1d7SKowalski, Kamil         }
1093179db1d7SKowalski, Kamil 
1094179db1d7SKowalski, Kamil         if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1095179db1d7SKowalski, Kamil           errorDetected = true;
1096179db1d7SKowalski, Kamil           messages::addMessageToJson(
109755c7b7a2SEd Tanous               asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"),
1098179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1099179db1d7SKowalski, Kamil         }
1100179db1d7SKowalski, Kamil 
1101179db1d7SKowalski, Kamil         if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1102179db1d7SKowalski, Kamil           errorDetected = true;
1103179db1d7SKowalski, Kamil           messages::addMessageToJson(
110455c7b7a2SEd Tanous               asyncResp->res.jsonValue,
1105179db1d7SKowalski, Kamil               messages::propertyMissing("AddressOrigin"),
1106179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1107179db1d7SKowalski, Kamil         }
1108179db1d7SKowalski, Kamil 
1109179db1d7SKowalski, Kamil         if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1110179db1d7SKowalski, Kamil           errorDetected = true;
1111179db1d7SKowalski, Kamil           messages::addMessageToJson(
111255c7b7a2SEd Tanous               asyncResp->res.jsonValue, messages::propertyMissing("Gateway"),
1113179db1d7SKowalski, Kamil               "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1114179db1d7SKowalski, Kamil         }
1115179db1d7SKowalski, Kamil 
1116179db1d7SKowalski, Kamil         // If any error occured do not proceed with current entry, but do not
1117179db1d7SKowalski, Kamil         // end loop
1118179db1d7SKowalski, Kamil         if (errorDetected) {
1119179db1d7SKowalski, Kamil           errorDetected = false;
1120179db1d7SKowalski, Kamil           continue;
1121179db1d7SKowalski, Kamil         }
1122179db1d7SKowalski, Kamil 
1123179db1d7SKowalski, Kamil         // Create IPv4 with provided data
112455c7b7a2SEd Tanous         ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength,
112555c7b7a2SEd Tanous                                     *gatewayFieldValue, *addressFieldValue,
112655c7b7a2SEd Tanous                                     asyncResp);
1127179db1d7SKowalski, Kamil       } else {
1128179db1d7SKowalski, Kamil         // Existing object that should be modified/deleted/remain unchanged
1129179db1d7SKowalski, Kamil         if (input[entryIdx].is_null()) {
1130179db1d7SKowalski, Kamil           // Object should be deleted
113155c7b7a2SEd Tanous           ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx,
113255c7b7a2SEd Tanous                                       asyncResp);
1133179db1d7SKowalski, Kamil         } else if (input[entryIdx].is_object()) {
1134179db1d7SKowalski, Kamil           if (input[entryIdx].size() == 0) {
1135179db1d7SKowalski, Kamil             // Object shall remain unchanged
1136179db1d7SKowalski, Kamil             continue;
1137179db1d7SKowalski, Kamil           }
1138179db1d7SKowalski, Kamil 
1139179db1d7SKowalski, Kamil           // Apply changes
1140179db1d7SKowalski, Kamil           if (addressFieldState == json_util::Result::SUCCESS &&
1141179db1d7SKowalski, Kamil               ipv4_data[entryIdx].address != nullptr &&
1142179db1d7SKowalski, Kamil               *ipv4_data[entryIdx].address != *addressFieldValue) {
114355c7b7a2SEd Tanous             ethernetProvider.changeIPv4AddressProperty(
1144179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1145179db1d7SKowalski, Kamil                 *addressFieldValue, asyncResp);
1146179db1d7SKowalski, Kamil           }
1147179db1d7SKowalski, Kamil 
1148179db1d7SKowalski, Kamil           if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1149179db1d7SKowalski, Kamil               ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
115055c7b7a2SEd Tanous             ethernetProvider.changeIPv4SubnetMaskProperty(
1151179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1152179db1d7SKowalski, Kamil                 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1153179db1d7SKowalski, Kamil           }
1154179db1d7SKowalski, Kamil 
1155179db1d7SKowalski, Kamil           if (addressOriginFieldState == json_util::Result::SUCCESS &&
1156179db1d7SKowalski, Kamil               ipv4_data[entryIdx].origin != *addressFieldValue) {
115755c7b7a2SEd Tanous             ethernetProvider.changeIPv4Origin(
1158179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1159179db1d7SKowalski, Kamil                 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1160179db1d7SKowalski, Kamil           }
1161179db1d7SKowalski, Kamil 
1162179db1d7SKowalski, Kamil           if (gatewayFieldState == json_util::Result::SUCCESS &&
1163179db1d7SKowalski, Kamil               ipv4_data[entryIdx].gateway != nullptr &&
1164179db1d7SKowalski, Kamil               *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
116555c7b7a2SEd Tanous             ethernetProvider.changeIPv4AddressProperty(
1166179db1d7SKowalski, Kamil                 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1167179db1d7SKowalski, Kamil                 *gatewayFieldValue, asyncResp);
1168179db1d7SKowalski, Kamil           }
1169179db1d7SKowalski, Kamil         }
1170179db1d7SKowalski, Kamil       }
1171179db1d7SKowalski, Kamil     }
1172179db1d7SKowalski, Kamil   }
1173179db1d7SKowalski, Kamil 
1174588c3f0dSKowalski, Kamil   nlohmann::json parseInterfaceData(
117555c7b7a2SEd Tanous       const std::string &ifaceId, const EthernetInterfaceData &eth_data,
1176588c3f0dSKowalski, Kamil       const std::vector<IPv4AddressData> &ipv4_data) {
1177588c3f0dSKowalski, Kamil     // Copy JSON object to avoid race condition
117855c7b7a2SEd Tanous     nlohmann::json jsonResponse(Node::json);
1179588c3f0dSKowalski, Kamil 
1180588c3f0dSKowalski, Kamil     // Fill out obvious data...
118155c7b7a2SEd Tanous     jsonResponse["Id"] = ifaceId;
118255c7b7a2SEd Tanous     jsonResponse["@odata.id"] =
118355c7b7a2SEd Tanous         "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
1184588c3f0dSKowalski, Kamil 
1185588c3f0dSKowalski, Kamil     // ... then the one from DBus, regarding eth iface...
118655c7b7a2SEd Tanous     if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed;
1187588c3f0dSKowalski, Kamil 
118855c7b7a2SEd Tanous     if (eth_data.macAddress != nullptr)
118955c7b7a2SEd Tanous       jsonResponse["MACAddress"] = *eth_data.macAddress;
1190588c3f0dSKowalski, Kamil 
1191588c3f0dSKowalski, Kamil     if (eth_data.hostname != nullptr)
119255c7b7a2SEd Tanous       jsonResponse["HostName"] = *eth_data.hostname;
1193588c3f0dSKowalski, Kamil 
119455c7b7a2SEd Tanous     if (eth_data.vlanId != nullptr) {
119555c7b7a2SEd Tanous       nlohmann::json &vlanObj = jsonResponse["VLAN"];
1196588c3f0dSKowalski, Kamil       vlanObj["VLANEnable"] = true;
119755c7b7a2SEd Tanous       vlanObj["VLANId"] = *eth_data.vlanId;
1198eb547730SKowalski, Kamil     } else {
119955c7b7a2SEd Tanous       nlohmann::json &vlanObj = jsonResponse["VLANs"];
1200eb547730SKowalski, Kamil       vlanObj["@odata.id"] =
120155c7b7a2SEd Tanous           "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
1202eb547730SKowalski, Kamil           "/VLANs";
1203588c3f0dSKowalski, Kamil     }
1204588c3f0dSKowalski, Kamil 
1205588c3f0dSKowalski, Kamil     // ... at last, check if there are IPv4 data and prepare appropriate
1206588c3f0dSKowalski, Kamil     // collection
1207588c3f0dSKowalski, Kamil     if (ipv4_data.size() > 0) {
120855c7b7a2SEd Tanous       nlohmann::json ipv4Array = nlohmann::json::array();
120955c7b7a2SEd Tanous       for (auto &ipv4Config : ipv4_data) {
121055c7b7a2SEd Tanous         nlohmann::json jsonIpv4;
121155c7b7a2SEd Tanous         if (ipv4Config.address != nullptr) {
121255c7b7a2SEd Tanous           jsonIpv4["Address"] = *ipv4Config.address;
121355c7b7a2SEd Tanous           if (ipv4Config.gateway != nullptr)
121455c7b7a2SEd Tanous             jsonIpv4["Gateway"] = *ipv4Config.gateway;
1215588c3f0dSKowalski, Kamil 
121655c7b7a2SEd Tanous           jsonIpv4["AddressOrigin"] = ipv4Config.origin;
121755c7b7a2SEd Tanous           jsonIpv4["SubnetMask"] = ipv4Config.netmask;
1218588c3f0dSKowalski, Kamil 
121955c7b7a2SEd Tanous           ipv4Array.push_back(std::move(jsonIpv4));
1220588c3f0dSKowalski, Kamil         }
1221588c3f0dSKowalski, Kamil       }
122255c7b7a2SEd Tanous       jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
1223588c3f0dSKowalski, Kamil     }
1224588c3f0dSKowalski, Kamil 
122555c7b7a2SEd Tanous     return jsonResponse;
1226588c3f0dSKowalski, Kamil   }
1227588c3f0dSKowalski, Kamil 
12289391bb9cSRapkiewicz, Pawel   /**
12299391bb9cSRapkiewicz, Pawel    * Functions triggers appropriate requests on DBus
12309391bb9cSRapkiewicz, Pawel    */
123155c7b7a2SEd Tanous   void doGet(crow::Response &res, const crow::Request &req,
12329391bb9cSRapkiewicz, Pawel              const std::vector<std::string> &params) override {
12339391bb9cSRapkiewicz, Pawel     // TODO(Pawel) this shall be parametrized call (two params) to get
12349391bb9cSRapkiewicz, Pawel     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
12359391bb9cSRapkiewicz, Pawel     // Check if there is required param, truly entering this shall be
12369391bb9cSRapkiewicz, Pawel     // impossible.
12379391bb9cSRapkiewicz, Pawel     if (params.size() != 1) {
1238e0d918bcSEd Tanous       res.result(boost::beast::http::status::internal_server_error);
12399391bb9cSRapkiewicz, Pawel       res.end();
12409391bb9cSRapkiewicz, Pawel       return;
12419391bb9cSRapkiewicz, Pawel     }
12429391bb9cSRapkiewicz, Pawel 
124355c7b7a2SEd Tanous     const std::string &ifaceId = params[0];
12449391bb9cSRapkiewicz, Pawel 
124555c7b7a2SEd Tanous     // get single eth interface data, and call the below callback for JSON
12469391bb9cSRapkiewicz, Pawel     // preparation
124755c7b7a2SEd Tanous     ethernetProvider.getEthernetIfaceData(
124855c7b7a2SEd Tanous         ifaceId,
124955c7b7a2SEd Tanous         [&, ifaceId](const bool &success, const EthernetInterfaceData &eth_data,
12509391bb9cSRapkiewicz, Pawel                      const std::vector<IPv4AddressData> &ipv4_data) {
12519391bb9cSRapkiewicz, Pawel           if (success) {
125255c7b7a2SEd Tanous             res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
12539391bb9cSRapkiewicz, Pawel           } else {
12549391bb9cSRapkiewicz, Pawel             // ... otherwise return error
12559391bb9cSRapkiewicz, Pawel             // TODO(Pawel)consider distinguish between non existing object, and
12569391bb9cSRapkiewicz, Pawel             // other errors
1257e439f0f8SKowalski, Kamil             messages::addMessageToErrorJson(
125855c7b7a2SEd Tanous                 res.jsonValue,
125955c7b7a2SEd Tanous                 messages::resourceNotFound("EthernetInterface", ifaceId));
1260e0d918bcSEd Tanous             res.result(boost::beast::http::status::not_found);
12619391bb9cSRapkiewicz, Pawel           }
12629391bb9cSRapkiewicz, Pawel           res.end();
12639391bb9cSRapkiewicz, Pawel         });
12649391bb9cSRapkiewicz, Pawel   }
12659391bb9cSRapkiewicz, Pawel 
126655c7b7a2SEd Tanous   void doPatch(crow::Response &res, const crow::Request &req,
1267588c3f0dSKowalski, Kamil                const std::vector<std::string> &params) override {
1268588c3f0dSKowalski, Kamil     // TODO(Pawel) this shall be parametrized call (two params) to get
1269588c3f0dSKowalski, Kamil     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1270588c3f0dSKowalski, Kamil     // Check if there is required param, truly entering this shall be
1271588c3f0dSKowalski, Kamil     // impossible.
1272588c3f0dSKowalski, Kamil     if (params.size() != 1) {
1273588c3f0dSKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1274588c3f0dSKowalski, Kamil       res.end();
1275588c3f0dSKowalski, Kamil       return;
1276588c3f0dSKowalski, Kamil     }
1277588c3f0dSKowalski, Kamil 
127855c7b7a2SEd Tanous     const std::string &ifaceId = params[0];
1279588c3f0dSKowalski, Kamil 
1280179db1d7SKowalski, Kamil     nlohmann::json patchReq;
1281588c3f0dSKowalski, Kamil 
1282179db1d7SKowalski, Kamil     if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1283588c3f0dSKowalski, Kamil       return;
1284588c3f0dSKowalski, Kamil     }
1285588c3f0dSKowalski, Kamil 
128655c7b7a2SEd Tanous     // get single eth interface data, and call the below callback for JSON
1287588c3f0dSKowalski, Kamil     // preparation
128855c7b7a2SEd Tanous     ethernetProvider.getEthernetIfaceData(
128955c7b7a2SEd Tanous         ifaceId, [&, ifaceId, patchReq = std::move(patchReq)](
1290588c3f0dSKowalski, Kamil                      const bool &success, const EthernetInterfaceData &eth_data,
1291588c3f0dSKowalski, Kamil                      const std::vector<IPv4AddressData> &ipv4_data) {
1292588c3f0dSKowalski, Kamil           if (!success) {
1293588c3f0dSKowalski, Kamil             // ... otherwise return error
1294588c3f0dSKowalski, Kamil             // TODO(Pawel)consider distinguish between non existing object, and
1295588c3f0dSKowalski, Kamil             // other errors
1296927a505aSKowalski, Kamil             messages::addMessageToErrorJson(
129755c7b7a2SEd Tanous                 res.jsonValue,
129855c7b7a2SEd Tanous                 messages::resourceNotFound("VLAN Network Interface", ifaceId));
1299588c3f0dSKowalski, Kamil             res.result(boost::beast::http::status::not_found);
1300588c3f0dSKowalski, Kamil             res.end();
1301588c3f0dSKowalski, Kamil 
1302588c3f0dSKowalski, Kamil             return;
1303588c3f0dSKowalski, Kamil           }
1304588c3f0dSKowalski, Kamil 
130555c7b7a2SEd Tanous           res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
1306588c3f0dSKowalski, Kamil 
1307588c3f0dSKowalski, Kamil           std::shared_ptr<AsyncResp> asyncResp =
1308588c3f0dSKowalski, Kamil               std::make_shared<AsyncResp>(res);
1309588c3f0dSKowalski, Kamil 
1310588c3f0dSKowalski, Kamil           for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1311588c3f0dSKowalski, Kamil                ++propertyIt) {
1312588c3f0dSKowalski, Kamil             if (propertyIt.key() == "VLAN") {
131355c7b7a2SEd Tanous               handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN",
1314588c3f0dSKowalski, Kamil                               asyncResp);
1315588c3f0dSKowalski, Kamil             } else if (propertyIt.key() == "HostName") {
1316588c3f0dSKowalski, Kamil               handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
1317179db1d7SKowalski, Kamil             } else if (propertyIt.key() == "IPv4Addresses") {
131855c7b7a2SEd Tanous               handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
1319179db1d7SKowalski, Kamil                               asyncResp);
1320179db1d7SKowalski, Kamil             } else if (propertyIt.key() == "IPv6Addresses") {
1321179db1d7SKowalski, Kamil               // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1322179db1d7SKowalski, Kamil               messages::addMessageToJsonRoot(
132355c7b7a2SEd Tanous                   res.jsonValue,
1324179db1d7SKowalski, Kamil                   messages::propertyNotWritable(propertyIt.key()));
1325588c3f0dSKowalski, Kamil             } else {
132655c7b7a2SEd Tanous               auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
1327588c3f0dSKowalski, Kamil 
132855c7b7a2SEd Tanous               if (fieldInJsonIt == res.jsonValue.end()) {
1329588c3f0dSKowalski, Kamil                 // Field not in scope of defined fields
1330588c3f0dSKowalski, Kamil                 messages::addMessageToJsonRoot(
133155c7b7a2SEd Tanous                     res.jsonValue, messages::propertyUnknown(propertyIt.key()));
1332588c3f0dSKowalski, Kamil               } else if (*fieldInJsonIt != *propertyIt) {
1333588c3f0dSKowalski, Kamil                 // User attempted to modify non-writable field
1334588c3f0dSKowalski, Kamil                 messages::addMessageToJsonRoot(
133555c7b7a2SEd Tanous                     res.jsonValue,
1336588c3f0dSKowalski, Kamil                     messages::propertyNotWritable(propertyIt.key()));
1337588c3f0dSKowalski, Kamil               }
1338588c3f0dSKowalski, Kamil             }
1339588c3f0dSKowalski, Kamil           }
1340588c3f0dSKowalski, Kamil         });
1341588c3f0dSKowalski, Kamil   }
1342588c3f0dSKowalski, Kamil 
13439391bb9cSRapkiewicz, Pawel   // Ethernet Provider object
13449391bb9cSRapkiewicz, Pawel   // TODO(Pawel) consider move it to singleton
134555c7b7a2SEd Tanous   OnDemandEthernetProvider ethernetProvider;
13469391bb9cSRapkiewicz, Pawel };
13479391bb9cSRapkiewicz, Pawel 
1348e439f0f8SKowalski, Kamil class VlanNetworkInterfaceCollection;
1349e439f0f8SKowalski, Kamil 
1350e439f0f8SKowalski, Kamil /**
1351e439f0f8SKowalski, Kamil  * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1352e439f0f8SKowalski, Kamil  */
1353e439f0f8SKowalski, Kamil class VlanNetworkInterface : public Node {
1354e439f0f8SKowalski, Kamil  public:
1355e439f0f8SKowalski, Kamil   /*
1356e439f0f8SKowalski, Kamil    * Default Constructor
1357e439f0f8SKowalski, Kamil    */
1358e439f0f8SKowalski, Kamil   template <typename CrowApp>
1359e439f0f8SKowalski, Kamil   // TODO(Pawel) Remove line from below, where we assume that there is only one
1360e439f0f8SKowalski, Kamil   // manager called openbmc This shall be generic, but requires to update
1361e439f0f8SKowalski, Kamil   // GetSubroutes method
1362e439f0f8SKowalski, Kamil   VlanNetworkInterface(CrowApp &app)
1363eb547730SKowalski, Kamil       : Node(
1364eb547730SKowalski, Kamil             app,
1365eb547730SKowalski, Kamil             "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1366e439f0f8SKowalski, Kamil             std::string(), std::string()) {
1367e439f0f8SKowalski, Kamil     Node::json["@odata.type"] =
1368e439f0f8SKowalski, Kamil         "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1369e439f0f8SKowalski, Kamil     Node::json["@odata.context"] =
1370e439f0f8SKowalski, Kamil         "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1371e439f0f8SKowalski, Kamil     Node::json["Name"] = "VLAN Network Interface";
1372e439f0f8SKowalski, Kamil 
1373e439f0f8SKowalski, Kamil     entityPrivileges = {
1374e439f0f8SKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
1375e439f0f8SKowalski, Kamil         {boost::beast::http::verb::head, {{"Login"}}},
1376e439f0f8SKowalski, Kamil         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1377e439f0f8SKowalski, Kamil         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1378e439f0f8SKowalski, Kamil         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1379e439f0f8SKowalski, Kamil         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1380e439f0f8SKowalski, Kamil   }
1381e439f0f8SKowalski, Kamil 
1382e439f0f8SKowalski, Kamil  private:
1383e439f0f8SKowalski, Kamil   nlohmann::json parseInterfaceData(
138455c7b7a2SEd Tanous       const std::string &parent_ifaceId, const std::string &ifaceId,
1385e439f0f8SKowalski, Kamil       const EthernetInterfaceData &eth_data,
1386e439f0f8SKowalski, Kamil       const std::vector<IPv4AddressData> &ipv4_data) {
1387e439f0f8SKowalski, Kamil     // Copy JSON object to avoid race condition
138855c7b7a2SEd Tanous     nlohmann::json jsonResponse(Node::json);
1389e439f0f8SKowalski, Kamil 
1390e439f0f8SKowalski, Kamil     // Fill out obvious data...
139155c7b7a2SEd Tanous     jsonResponse["Id"] = ifaceId;
139255c7b7a2SEd Tanous     jsonResponse["@odata.id"] =
139355c7b7a2SEd Tanous         "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId +
139455c7b7a2SEd Tanous         "/VLANs/" + ifaceId;
1395e439f0f8SKowalski, Kamil 
139655c7b7a2SEd Tanous     jsonResponse["VLANEnable"] = true;
139755c7b7a2SEd Tanous     jsonResponse["VLANId"] = *eth_data.vlanId;
1398e439f0f8SKowalski, Kamil 
139955c7b7a2SEd Tanous     return jsonResponse;
1400e439f0f8SKowalski, Kamil   }
1401e439f0f8SKowalski, Kamil 
140255c7b7a2SEd Tanous   bool verifyNames(crow::Response &res, const std::string &parent,
1403927a505aSKowalski, Kamil                    const std::string &iface) {
1404927a505aSKowalski, Kamil     if (!boost::starts_with(iface, parent + "_")) {
1405927a505aSKowalski, Kamil       messages::addMessageToErrorJson(
140655c7b7a2SEd Tanous           res.jsonValue,
1407927a505aSKowalski, Kamil           messages::resourceNotFound("VLAN Network Interface", iface));
1408927a505aSKowalski, Kamil       res.result(boost::beast::http::status::not_found);
1409927a505aSKowalski, Kamil       res.end();
1410927a505aSKowalski, Kamil 
1411927a505aSKowalski, Kamil       return false;
1412927a505aSKowalski, Kamil     } else {
1413927a505aSKowalski, Kamil       return true;
1414927a505aSKowalski, Kamil     }
1415927a505aSKowalski, Kamil   }
1416927a505aSKowalski, Kamil 
1417e439f0f8SKowalski, Kamil   /**
1418e439f0f8SKowalski, Kamil    * Functions triggers appropriate requests on DBus
1419e439f0f8SKowalski, Kamil    */
142055c7b7a2SEd Tanous   void doGet(crow::Response &res, const crow::Request &req,
1421e439f0f8SKowalski, Kamil              const std::vector<std::string> &params) override {
1422e439f0f8SKowalski, Kamil     // TODO(Pawel) this shall be parametrized call (two params) to get
1423e439f0f8SKowalski, Kamil     // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1424e439f0f8SKowalski, Kamil     // Check if there is required param, truly entering this shall be
1425e439f0f8SKowalski, Kamil     // impossible.
1426e439f0f8SKowalski, Kamil     if (params.size() != 2) {
1427e439f0f8SKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1428e439f0f8SKowalski, Kamil       res.end();
1429e439f0f8SKowalski, Kamil       return;
1430e439f0f8SKowalski, Kamil     }
1431e439f0f8SKowalski, Kamil 
143255c7b7a2SEd Tanous     const std::string &parentIfaceId = params[0];
143355c7b7a2SEd Tanous     const std::string &ifaceId = params[1];
1434e439f0f8SKowalski, Kamil 
1435*a434f2bdSEd Tanous     if (!verifyNames(res, parentIfaceId, ifaceId)) {
1436*a434f2bdSEd Tanous       return;
1437*a434f2bdSEd Tanous     }
1438*a434f2bdSEd Tanous 
1439e439f0f8SKowalski, Kamil     // Get single eth interface data, and call the below callback for JSON
1440e439f0f8SKowalski, Kamil     // preparation
144155c7b7a2SEd Tanous     ethernetProvider.getEthernetIfaceData(
1442*a434f2bdSEd Tanous         ifaceId, [&, parentIfaceId, ifaceId](
1443e439f0f8SKowalski, Kamil                      const bool &success, const EthernetInterfaceData &eth_data,
1444e439f0f8SKowalski, Kamil                      const std::vector<IPv4AddressData> &ipv4_data) {
144555c7b7a2SEd Tanous           if (success && eth_data.vlanId != nullptr) {
1446*a434f2bdSEd Tanous             res.jsonValue =
1447*a434f2bdSEd Tanous                 parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data);
1448e439f0f8SKowalski, Kamil           } else {
1449e439f0f8SKowalski, Kamil             // ... otherwise return error
1450eb547730SKowalski, Kamil             // TODO(Pawel)consider distinguish between non existing object, and
1451e439f0f8SKowalski, Kamil             // other errors
1452927a505aSKowalski, Kamil             messages::addMessageToErrorJson(
145355c7b7a2SEd Tanous                 res.jsonValue,
145455c7b7a2SEd Tanous                 messages::resourceNotFound("VLAN Network Interface", ifaceId));
1455e439f0f8SKowalski, Kamil             res.result(boost::beast::http::status::not_found);
1456e439f0f8SKowalski, Kamil           }
1457e439f0f8SKowalski, Kamil           res.end();
1458e439f0f8SKowalski, Kamil         });
1459e439f0f8SKowalski, Kamil   }
1460e439f0f8SKowalski, Kamil 
146155c7b7a2SEd Tanous   void doPatch(crow::Response &res, const crow::Request &req,
1462e439f0f8SKowalski, Kamil                const std::vector<std::string> &params) override {
1463e439f0f8SKowalski, Kamil     if (params.size() != 2) {
1464e439f0f8SKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1465e439f0f8SKowalski, Kamil       res.end();
1466e439f0f8SKowalski, Kamil       return;
1467e439f0f8SKowalski, Kamil     }
1468e439f0f8SKowalski, Kamil 
146955c7b7a2SEd Tanous     const std::string &parent_ifaceId = params[0];
147055c7b7a2SEd Tanous     const std::string &ifaceId = params[1];
1471927a505aSKowalski, Kamil 
147255c7b7a2SEd Tanous     if (!verifyNames(res, parent_ifaceId, ifaceId)) {
1473927a505aSKowalski, Kamil       return;
1474927a505aSKowalski, Kamil     }
1475927a505aSKowalski, Kamil 
1476927a505aSKowalski, Kamil     nlohmann::json patchReq;
1477927a505aSKowalski, Kamil 
1478927a505aSKowalski, Kamil     if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1479927a505aSKowalski, Kamil       return;
1480927a505aSKowalski, Kamil     }
1481927a505aSKowalski, Kamil 
1482927a505aSKowalski, Kamil     // Get single eth interface data, and call the below callback for JSON
1483927a505aSKowalski, Kamil     // preparation
148455c7b7a2SEd Tanous     ethernetProvider.getEthernetIfaceData(
1485*a434f2bdSEd Tanous         ifaceId, [&, parent_ifaceId, ifaceId, patchReq = std::move(patchReq)](
1486927a505aSKowalski, Kamil                      const bool &success, const EthernetInterfaceData &eth_data,
1487927a505aSKowalski, Kamil                      const std::vector<IPv4AddressData> &ipv4_data) {
1488927a505aSKowalski, Kamil           if (!success) {
1489927a505aSKowalski, Kamil             // ... otherwise return error
1490927a505aSKowalski, Kamil             // TODO(Pawel)consider distinguish between non existing object,
1491927a505aSKowalski, Kamil             // and
1492927a505aSKowalski, Kamil             // other errors
1493927a505aSKowalski, Kamil             messages::addMessageToErrorJson(
149455c7b7a2SEd Tanous                 res.jsonValue,
149555c7b7a2SEd Tanous                 messages::resourceNotFound("VLAN Network Interface", ifaceId));
1496927a505aSKowalski, Kamil             res.result(boost::beast::http::status::not_found);
1497e439f0f8SKowalski, Kamil             res.end();
1498927a505aSKowalski, Kamil 
1499927a505aSKowalski, Kamil             return;
1500927a505aSKowalski, Kamil           }
1501927a505aSKowalski, Kamil 
1502*a434f2bdSEd Tanous           res.jsonValue =
1503*a434f2bdSEd Tanous               parseInterfaceData(parent_ifaceId, ifaceId, eth_data, ipv4_data);
1504927a505aSKowalski, Kamil 
1505927a505aSKowalski, Kamil           std::shared_ptr<AsyncResp> asyncResp =
1506927a505aSKowalski, Kamil               std::make_shared<AsyncResp>(res);
1507927a505aSKowalski, Kamil 
1508927a505aSKowalski, Kamil           for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1509927a505aSKowalski, Kamil                ++propertyIt) {
1510927a505aSKowalski, Kamil             if (propertyIt.key() != "VLANEnable" &&
1511927a505aSKowalski, Kamil                 propertyIt.key() != "VLANId") {
151255c7b7a2SEd Tanous               auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
1513927a505aSKowalski, Kamil 
151455c7b7a2SEd Tanous               if (fieldInJsonIt == res.jsonValue.end()) {
1515927a505aSKowalski, Kamil                 // Field not in scope of defined fields
1516927a505aSKowalski, Kamil                 messages::addMessageToJsonRoot(
1517*a434f2bdSEd Tanous                     res.jsonValue, messages::propertyUnknown(propertyIt.key()));
1518927a505aSKowalski, Kamil               } else if (*fieldInJsonIt != *propertyIt) {
1519927a505aSKowalski, Kamil                 // User attempted to modify non-writable field
1520927a505aSKowalski, Kamil                 messages::addMessageToJsonRoot(
152155c7b7a2SEd Tanous                     res.jsonValue,
1522927a505aSKowalski, Kamil                     messages::propertyNotWritable(propertyIt.key()));
1523927a505aSKowalski, Kamil               }
1524927a505aSKowalski, Kamil             }
1525927a505aSKowalski, Kamil           }
1526927a505aSKowalski, Kamil 
152755c7b7a2SEd Tanous           EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/",
1528927a505aSKowalski, Kamil                                              asyncResp);
1529927a505aSKowalski, Kamil         });
1530e439f0f8SKowalski, Kamil   }
1531e439f0f8SKowalski, Kamil 
153255c7b7a2SEd Tanous   void doDelete(crow::Response &res, const crow::Request &req,
1533e439f0f8SKowalski, Kamil                 const std::vector<std::string> &params) override {
1534e439f0f8SKowalski, Kamil     if (params.size() != 2) {
1535e439f0f8SKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1536e439f0f8SKowalski, Kamil       res.end();
1537e439f0f8SKowalski, Kamil       return;
1538e439f0f8SKowalski, Kamil     }
1539e439f0f8SKowalski, Kamil 
154055c7b7a2SEd Tanous     const std::string &parent_ifaceId = params[0];
154155c7b7a2SEd Tanous     const std::string &ifaceId = params[1];
1542927a505aSKowalski, Kamil 
154355c7b7a2SEd Tanous     if (!verifyNames(res, parent_ifaceId, ifaceId)) {
1544927a505aSKowalski, Kamil       return;
1545927a505aSKowalski, Kamil     }
1546927a505aSKowalski, Kamil 
1547927a505aSKowalski, Kamil     // Get single eth interface data, and call the below callback for JSON
1548927a505aSKowalski, Kamil     // preparation
154955c7b7a2SEd Tanous     ethernetProvider.getEthernetIfaceData(
1550*a434f2bdSEd Tanous         ifaceId, [&, parent_ifaceId, ifaceId](
1551927a505aSKowalski, Kamil                      const bool &success, const EthernetInterfaceData &eth_data,
1552927a505aSKowalski, Kamil                      const std::vector<IPv4AddressData> &ipv4_data) {
155355c7b7a2SEd Tanous           if (success && eth_data.vlanId != nullptr) {
155455c7b7a2SEd Tanous             res.jsonValue = parseInterfaceData(parent_ifaceId, ifaceId,
1555927a505aSKowalski, Kamil                                                eth_data, ipv4_data);
1556927a505aSKowalski, Kamil 
1557927a505aSKowalski, Kamil             // Disable VLAN
1558927a505aSKowalski, Kamil             OnDemandEthernetProvider::disableVlan(
155955c7b7a2SEd Tanous                 ifaceId, [&](const boost::system::error_code ec) {
1560927a505aSKowalski, Kamil                   if (ec) {
156155c7b7a2SEd Tanous                     res.jsonValue = nlohmann::json::object();
156255c7b7a2SEd Tanous                     messages::addMessageToErrorJson(res.jsonValue,
1563927a505aSKowalski, Kamil                                                     messages::internalError());
1564927a505aSKowalski, Kamil                     res.result(
1565927a505aSKowalski, Kamil                         boost::beast::http::status::internal_server_error);
1566927a505aSKowalski, Kamil                   }
1567e439f0f8SKowalski, Kamil                   res.end();
1568927a505aSKowalski, Kamil                 });
1569927a505aSKowalski, Kamil           } else {
1570927a505aSKowalski, Kamil             // ... otherwise return error
1571927a505aSKowalski, Kamil             // TODO(Pawel)consider distinguish between non existing object,
1572927a505aSKowalski, Kamil             // and
1573927a505aSKowalski, Kamil             // other errors
1574927a505aSKowalski, Kamil             messages::addMessageToErrorJson(
157555c7b7a2SEd Tanous                 res.jsonValue,
157655c7b7a2SEd Tanous                 messages::resourceNotFound("VLAN Network Interface", ifaceId));
1577927a505aSKowalski, Kamil             res.result(boost::beast::http::status::not_found);
1578927a505aSKowalski, Kamil             res.end();
1579927a505aSKowalski, Kamil           }
1580927a505aSKowalski, Kamil         });
1581e439f0f8SKowalski, Kamil   }
1582e439f0f8SKowalski, Kamil 
1583e439f0f8SKowalski, Kamil   /**
1584e439f0f8SKowalski, Kamil    * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1585e439f0f8SKowalski, Kamil    * method, to maintain consistency of returned data, as Collection's doPost
1586e439f0f8SKowalski, Kamil    * should return data for created member which should match member's doGet
1587e439f0f8SKowalski, Kamil    * result in 100%.
1588e439f0f8SKowalski, Kamil    */
1589e439f0f8SKowalski, Kamil   friend VlanNetworkInterfaceCollection;
1590e439f0f8SKowalski, Kamil 
1591e439f0f8SKowalski, Kamil   // Ethernet Provider object
1592e439f0f8SKowalski, Kamil   // TODO(Pawel) consider move it to singleton
159355c7b7a2SEd Tanous   OnDemandEthernetProvider ethernetProvider;
1594e439f0f8SKowalski, Kamil };
1595e439f0f8SKowalski, Kamil 
1596e439f0f8SKowalski, Kamil /**
1597e439f0f8SKowalski, Kamil  * VlanNetworkInterfaceCollection derived class for delivering
1598e439f0f8SKowalski, Kamil  * VLANNetworkInterface Collection Schema
1599e439f0f8SKowalski, Kamil  */
1600e439f0f8SKowalski, Kamil class VlanNetworkInterfaceCollection : public Node {
1601e439f0f8SKowalski, Kamil  public:
1602e439f0f8SKowalski, Kamil   template <typename CrowApp>
1603e439f0f8SKowalski, Kamil   // TODO(Pawel) Remove line from below, where we assume that there is only one
1604e439f0f8SKowalski, Kamil   // manager called openbmc This shall be generic, but requires to update
1605e439f0f8SKowalski, Kamil   // GetSubroutes method
1606e439f0f8SKowalski, Kamil   VlanNetworkInterfaceCollection(CrowApp &app)
1607e439f0f8SKowalski, Kamil       : Node(app,
1608e439f0f8SKowalski, Kamil              "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1609e439f0f8SKowalski, Kamil              std::string()),
1610e439f0f8SKowalski, Kamil         memberVlan(app) {
1611e439f0f8SKowalski, Kamil     Node::json["@odata.type"] =
1612e439f0f8SKowalski, Kamil         "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1613e439f0f8SKowalski, Kamil     Node::json["@odata.context"] =
1614e439f0f8SKowalski, Kamil         "/redfish/v1/$metadata"
1615e439f0f8SKowalski, Kamil         "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1616e439f0f8SKowalski, Kamil     Node::json["Name"] = "VLAN Network Interface Collection";
1617e439f0f8SKowalski, Kamil 
1618e439f0f8SKowalski, Kamil     entityPrivileges = {
1619e439f0f8SKowalski, Kamil         {boost::beast::http::verb::get, {{"Login"}}},
1620e439f0f8SKowalski, Kamil         {boost::beast::http::verb::head, {{"Login"}}},
1621e439f0f8SKowalski, Kamil         {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1622e439f0f8SKowalski, Kamil         {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1623e439f0f8SKowalski, Kamil         {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1624e439f0f8SKowalski, Kamil         {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1625e439f0f8SKowalski, Kamil   }
1626e439f0f8SKowalski, Kamil 
1627e439f0f8SKowalski, Kamil  private:
1628e439f0f8SKowalski, Kamil   /**
1629e439f0f8SKowalski, Kamil    * Functions triggers appropriate requests on DBus
1630e439f0f8SKowalski, Kamil    */
163155c7b7a2SEd Tanous   void doGet(crow::Response &res, const crow::Request &req,
1632e439f0f8SKowalski, Kamil              const std::vector<std::string> &params) override {
1633e439f0f8SKowalski, Kamil     if (params.size() != 1) {
1634e439f0f8SKowalski, Kamil       // This means there is a problem with the router
1635e439f0f8SKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1636e439f0f8SKowalski, Kamil       res.end();
1637e439f0f8SKowalski, Kamil 
1638e439f0f8SKowalski, Kamil       return;
1639e439f0f8SKowalski, Kamil     }
1640e439f0f8SKowalski, Kamil 
1641e439f0f8SKowalski, Kamil     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1642e439f0f8SKowalski, Kamil     // any Manager, not only hardcoded 'openbmc'.
164355c7b7a2SEd Tanous     std::string managerId = "openbmc";
1644e439f0f8SKowalski, Kamil     std::string rootInterfaceName = params[0];
1645e439f0f8SKowalski, Kamil 
164655c7b7a2SEd Tanous     // get eth interface list, and call the below callback for JSON preparation
1647*a434f2bdSEd Tanous     ethernetProvider.getEthernetIfaceList(
1648*a434f2bdSEd Tanous         [&, managerId{std::move(managerId)},
1649*a434f2bdSEd Tanous          rootInterfaceName{std::move(rootInterfaceName)}](
1650*a434f2bdSEd Tanous             const bool &success, const std::vector<std::string> &iface_list) {
1651e439f0f8SKowalski, Kamil           if (success) {
1652e439f0f8SKowalski, Kamil             bool rootInterfaceFound = false;
165355c7b7a2SEd Tanous             nlohmann::json ifaceArray = nlohmann::json::array();
1654e439f0f8SKowalski, Kamil 
165555c7b7a2SEd Tanous             for (const std::string &ifaceItem : iface_list) {
165655c7b7a2SEd Tanous               if (ifaceItem == rootInterfaceName) {
1657e439f0f8SKowalski, Kamil                 rootInterfaceFound = true;
1658*a434f2bdSEd Tanous               } else if (boost::starts_with(ifaceItem,
1659*a434f2bdSEd Tanous                                             rootInterfaceName + "_")) {
166055c7b7a2SEd Tanous                 ifaceArray.push_back(
166155c7b7a2SEd Tanous                     {{"@odata.id", "/redfish/v1/Managers/" + managerId +
1662*a434f2bdSEd Tanous                                        "/EthernetInterfaces/" +
1663*a434f2bdSEd Tanous                                        rootInterfaceName + "/VLANs/" +
1664*a434f2bdSEd Tanous                                        ifaceItem}});
1665e439f0f8SKowalski, Kamil               }
1666e439f0f8SKowalski, Kamil             }
1667e439f0f8SKowalski, Kamil 
1668e439f0f8SKowalski, Kamil             if (rootInterfaceFound) {
166955c7b7a2SEd Tanous               Node::json["Members"] = ifaceArray;
167055c7b7a2SEd Tanous               Node::json["Members@odata.count"] = ifaceArray.size();
167155c7b7a2SEd Tanous               Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId +
1672*a434f2bdSEd Tanous                                         "/EthernetInterfaces/" +
1673*a434f2bdSEd Tanous                                         rootInterfaceName + "/VLANs";
167455c7b7a2SEd Tanous               res.jsonValue = Node::json;
1675e439f0f8SKowalski, Kamil             } else {
1676e439f0f8SKowalski, Kamil               messages::addMessageToErrorJson(
167755c7b7a2SEd Tanous                   res.jsonValue, messages::resourceNotFound("EthernetInterface",
1678e439f0f8SKowalski, Kamil                                                             rootInterfaceName));
1679e439f0f8SKowalski, Kamil               res.result(boost::beast::http::status::not_found);
1680e439f0f8SKowalski, Kamil               res.end();
1681e439f0f8SKowalski, Kamil             }
1682e439f0f8SKowalski, Kamil           } else {
1683e439f0f8SKowalski, Kamil             // No success, best what we can do is return INTERNALL ERROR
1684e439f0f8SKowalski, Kamil             res.result(boost::beast::http::status::internal_server_error);
1685e439f0f8SKowalski, Kamil           }
1686e439f0f8SKowalski, Kamil           res.end();
1687e439f0f8SKowalski, Kamil         });
1688e439f0f8SKowalski, Kamil   }
1689e439f0f8SKowalski, Kamil 
169055c7b7a2SEd Tanous   void doPost(crow::Response &res, const crow::Request &req,
1691e439f0f8SKowalski, Kamil               const std::vector<std::string> &params) override {
1692e439f0f8SKowalski, Kamil     if (params.size() != 1) {
1693e439f0f8SKowalski, Kamil       // This means there is a problem with the router
1694e439f0f8SKowalski, Kamil       res.result(boost::beast::http::status::internal_server_error);
1695e439f0f8SKowalski, Kamil       res.end();
1696e439f0f8SKowalski, Kamil       return;
1697e439f0f8SKowalski, Kamil     }
1698e439f0f8SKowalski, Kamil 
1699e439f0f8SKowalski, Kamil     nlohmann::json postReq;
1700e439f0f8SKowalski, Kamil 
1701e439f0f8SKowalski, Kamil     if (!json_util::processJsonFromRequest(res, req, postReq)) {
1702e439f0f8SKowalski, Kamil       return;
1703e439f0f8SKowalski, Kamil     }
1704e439f0f8SKowalski, Kamil 
1705e439f0f8SKowalski, Kamil     // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1706e439f0f8SKowalski, Kamil     // any Manager, not only hardcoded 'openbmc'.
170755c7b7a2SEd Tanous     std::string managerId = "openbmc";
1708e439f0f8SKowalski, Kamil     std::string rootInterfaceName = params[0];
1709e439f0f8SKowalski, Kamil     uint64_t vlanId;
1710e439f0f8SKowalski, Kamil     bool errorDetected;
1711e439f0f8SKowalski, Kamil 
1712e439f0f8SKowalski, Kamil     if (json_util::getUnsigned(
1713e439f0f8SKowalski, Kamil             "VLANId", postReq, vlanId,
1714e439f0f8SKowalski, Kamil             static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1715e439f0f8SKowalski, Kamil                 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
171655c7b7a2SEd Tanous             res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) {
1717e439f0f8SKowalski, Kamil       res.end();
1718e439f0f8SKowalski, Kamil       return;
1719e439f0f8SKowalski, Kamil     }
1720e439f0f8SKowalski, Kamil 
172155c7b7a2SEd Tanous     // get eth interface list, and call the below callback for JSON preparation
1722*a434f2bdSEd Tanous     ethernetProvider.getEthernetIfaceList(
1723*a434f2bdSEd Tanous         [&, managerId{std::move(managerId)},
1724*a434f2bdSEd Tanous          rootInterfaceName{std::move(rootInterfaceName)}](
1725*a434f2bdSEd Tanous             const bool &success, const std::vector<std::string> &iface_list) {
1726e439f0f8SKowalski, Kamil           if (success) {
1727e439f0f8SKowalski, Kamil             bool rootInterfaceFound = false;
1728e439f0f8SKowalski, Kamil 
172955c7b7a2SEd Tanous             for (const std::string &ifaceItem : iface_list) {
173055c7b7a2SEd Tanous               if (ifaceItem == rootInterfaceName) {
1731e439f0f8SKowalski, Kamil                 rootInterfaceFound = true;
1732e439f0f8SKowalski, Kamil                 break;
1733e439f0f8SKowalski, Kamil               }
1734e439f0f8SKowalski, Kamil             }
1735e439f0f8SKowalski, Kamil 
1736e439f0f8SKowalski, Kamil             if (rootInterfaceFound) {
173755c7b7a2SEd Tanous               ethernetProvider.createVlan(
1738e439f0f8SKowalski, Kamil                   rootInterfaceName, vlanId,
1739e439f0f8SKowalski, Kamil                   [&, vlanId, rootInterfaceName,
1740e439f0f8SKowalski, Kamil                    req{std::move(req)}](const boost::system::error_code ec) {
1741e439f0f8SKowalski, Kamil                     if (ec) {
1742*a434f2bdSEd Tanous                       messages::addMessageToErrorJson(
1743*a434f2bdSEd Tanous                           res.jsonValue, messages::internalError());
1744e439f0f8SKowalski, Kamil                       res.end();
1745e439f0f8SKowalski, Kamil                     } else {
1746e439f0f8SKowalski, Kamil                       memberVlan.doGet(
1747e439f0f8SKowalski, Kamil                           res, req,
1748e439f0f8SKowalski, Kamil                           {rootInterfaceName,
1749e439f0f8SKowalski, Kamil                            rootInterfaceName + "_" + std::to_string(vlanId)});
1750e439f0f8SKowalski, Kamil                     }
1751e439f0f8SKowalski, Kamil                   });
1752e439f0f8SKowalski, Kamil             } else {
1753e439f0f8SKowalski, Kamil               messages::addMessageToErrorJson(
175455c7b7a2SEd Tanous                   res.jsonValue, messages::resourceNotFound("EthernetInterface",
1755e439f0f8SKowalski, Kamil                                                             rootInterfaceName));
1756e439f0f8SKowalski, Kamil               res.result(boost::beast::http::status::not_found);
1757e439f0f8SKowalski, Kamil               res.end();
1758e439f0f8SKowalski, Kamil             }
1759e439f0f8SKowalski, Kamil           } else {
1760e439f0f8SKowalski, Kamil             // No success, best what we can do is return INTERNALL ERROR
1761e439f0f8SKowalski, Kamil             res.result(boost::beast::http::status::internal_server_error);
1762e439f0f8SKowalski, Kamil             res.end();
1763e439f0f8SKowalski, Kamil           }
1764e439f0f8SKowalski, Kamil         });
1765e439f0f8SKowalski, Kamil   }
1766e439f0f8SKowalski, Kamil 
1767e439f0f8SKowalski, Kamil   // Ethernet Provider object
1768e439f0f8SKowalski, Kamil   // TODO(Pawel) consider move it to singleton
176955c7b7a2SEd Tanous   OnDemandEthernetProvider ethernetProvider;
1770e439f0f8SKowalski, Kamil   VlanNetworkInterface memberVlan;
1771e439f0f8SKowalski, Kamil };
1772e439f0f8SKowalski, Kamil 
17739391bb9cSRapkiewicz, Pawel }  // namespace redfish
1774