1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <boost/container/flat_map.hpp>
19 #include <dbus_singleton.hpp>
20 #include <error_messages.hpp>
21 #include <node.hpp>
22 #include <utils/json_utils.hpp>
23 
24 namespace redfish
25 {
26 
27 /**
28  * DBus types primitives for several generic DBus interfaces
29  * TODO(Pawel) consider move this to separate file into boost::dbus
30  */
31 using PropertiesMapType = boost::container::flat_map<
32     std::string,
33     sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
34                                 int32_t, uint32_t, int64_t, uint64_t, double>>;
35 
36 using GetManagedObjectsType = boost::container::flat_map<
37     sdbusplus::message::object_path,
38     boost::container::flat_map<
39         std::string,
40         boost::container::flat_map<
41             std::string, sdbusplus::message::variant<
42                              std::string, bool, uint8_t, int16_t, uint16_t,
43                              int32_t, uint32_t, int64_t, uint64_t, double>>>>;
44 
45 /**
46  * Structure for keeping IPv4 data required by Redfish
47  * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
48  */
49 struct IPv4AddressData
50 {
51     std::string id;
52     const std::string *address;
53     const std::string *domain;
54     const std::string *gateway;
55     std::string netmask;
56     std::string origin;
57     bool global;
58     /**
59      * @brief Operator< to enable sorting
60      *
61      * @param[in] obj   Object to compare with
62      *
63      * @return This object id < supplied object id
64      */
65     bool operator<(const IPv4AddressData &obj) const
66     {
67         return (id < obj.id);
68     }
69 };
70 
71 /**
72  * Structure for keeping basic single Ethernet Interface information
73  * available from DBus
74  */
75 struct EthernetInterfaceData
76 {
77     const unsigned int *speed;
78     const bool *autoNeg;
79     const std::string *hostname;
80     const std::string *defaultGateway;
81     const std::string *macAddress;
82     const unsigned int *vlanId;
83 };
84 
85 /**
86  * OnDemandEthernetProvider
87  * Ethernet provider class that retrieves data directly from dbus, before
88  * setting it into JSON output. This does not cache any data.
89  *
90  * TODO(Pawel)
91  * This perhaps shall be different file, which has to be chosen on compile time
92  * depending on OEM needs
93  */
94 class OnDemandEthernetProvider
95 {
96   private:
97     // Consts that may have influence on EthernetProvider performance/memory
98     // usage
99     const size_t maxIpV4AddressesPerInterface = 10;
100 
101     // Helper function that allows to extract GetAllPropertiesType from
102     // GetManagedObjectsType, based on object path, and interface name
103     const PropertiesMapType *extractInterfaceProperties(
104         const sdbusplus::message::object_path &objpath,
105         const std::string &interface, const GetManagedObjectsType &dbus_data)
106     {
107         const auto &dbusObj = dbus_data.find(objpath);
108         if (dbusObj != dbus_data.end())
109         {
110             const auto &iface = dbusObj->second.find(interface);
111             if (iface != dbusObj->second.end())
112             {
113                 return &iface->second;
114             }
115         }
116         return nullptr;
117     }
118 
119     // Helper Wrapper that does inline object_path conversion from string
120     // into sdbusplus::message::object_path type
121     inline const PropertiesMapType *
122         extractInterfaceProperties(const std::string &objpath,
123                                    const std::string &interface,
124                                    const GetManagedObjectsType &dbus_data)
125     {
126         const auto &dbusObj = sdbusplus::message::object_path{objpath};
127         return extractInterfaceProperties(dbusObj, interface, dbus_data);
128     }
129 
130     // Helper function that allows to get pointer to the property from
131     // GetAllPropertiesType native, or extracted by GetAllPropertiesType
132     template <typename T>
133     inline T const *const extractProperty(const PropertiesMapType &properties,
134                                           const std::string &name)
135     {
136         const auto &property = properties.find(name);
137         if (property != properties.end())
138         {
139             return mapbox::getPtr<const T>(property->second);
140         }
141         return nullptr;
142     }
143     // TODO(Pawel) Consider to move the above functions to dbus
144     // generic_interfaces.hpp
145 
146     // Helper function that extracts data from several dbus objects and several
147     // interfaces required by single ethernet interface instance
148     void extractEthernetInterfaceData(const std::string &ethifaceId,
149                                       const GetManagedObjectsType &dbus_data,
150                                       EthernetInterfaceData &eth_data)
151     {
152         // Extract data that contains MAC Address
153         const PropertiesMapType *macProperties = extractInterfaceProperties(
154             "/xyz/openbmc_project/network/" + ethifaceId,
155             "xyz.openbmc_project.Network.MACAddress", dbus_data);
156 
157         if (macProperties != nullptr)
158         {
159             eth_data.macAddress =
160                 extractProperty<std::string>(*macProperties, "MACAddress");
161         }
162 
163         const PropertiesMapType *vlanProperties = extractInterfaceProperties(
164             "/xyz/openbmc_project/network/" + ethifaceId,
165             "xyz.openbmc_project.Network.VLAN", dbus_data);
166 
167         if (vlanProperties != nullptr)
168         {
169             eth_data.vlanId =
170                 extractProperty<unsigned int>(*vlanProperties, "Id");
171         }
172 
173         // Extract data that contains link information (auto negotiation and
174         // speed)
175         const PropertiesMapType *ethProperties = extractInterfaceProperties(
176             "/xyz/openbmc_project/network/" + ethifaceId,
177             "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
178 
179         if (ethProperties != nullptr)
180         {
181             eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
182             eth_data.speed =
183                 extractProperty<unsigned int>(*ethProperties, "Speed");
184         }
185 
186         // Extract data that contains network config (HostName and DefaultGW)
187         const PropertiesMapType *configProperties = extractInterfaceProperties(
188             "/xyz/openbmc_project/network/config",
189             "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
190 
191         if (configProperties != nullptr)
192         {
193             eth_data.hostname =
194                 extractProperty<std::string>(*configProperties, "HostName");
195             eth_data.defaultGateway = extractProperty<std::string>(
196                 *configProperties, "DefaultGateway");
197         }
198     }
199 
200     // Helper function that changes bits netmask notation (i.e. /24)
201     // into full dot notation
202     inline std::string getNetmask(unsigned int bits)
203     {
204         uint32_t value = 0xffffffff << (32 - bits);
205         std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
206                               std::to_string((value >> 16) & 0xff) + "." +
207                               std::to_string((value >> 8) & 0xff) + "." +
208                               std::to_string(value & 0xff);
209         return netmask;
210     }
211 
212     // Helper function that extracts data for single ethernet ipv4 address
213     void extractIPv4Data(const std::string &ethifaceId,
214                          const GetManagedObjectsType &dbus_data,
215                          std::vector<IPv4AddressData> &ipv4_config)
216     {
217         const std::string pathStart =
218             "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
219 
220         // Since there might be several IPv4 configurations aligned with
221         // single ethernet interface, loop over all of them
222         for (auto &objpath : dbus_data)
223         {
224             // Check if proper patter for object path appears
225             if (boost::starts_with(
226                     static_cast<const std::string &>(objpath.first), pathStart))
227             {
228                 // and get approrpiate interface
229                 const auto &interface =
230                     objpath.second.find("xyz.openbmc_project.Network.IP");
231                 if (interface != objpath.second.end())
232                 {
233                     // Make a properties 'shortcut', to make everything more
234                     // readable
235                     const PropertiesMapType &properties = interface->second;
236                     // Instance IPv4AddressData structure, and set as
237                     // appropriate
238                     IPv4AddressData ipv4Address;
239 
240                     ipv4Address.id =
241                         static_cast<const std::string &>(objpath.first)
242                             .substr(pathStart.size());
243 
244                     // IPv4 address
245                     ipv4Address.address =
246                         extractProperty<std::string>(properties, "Address");
247                     // IPv4 gateway
248                     ipv4Address.gateway =
249                         extractProperty<std::string>(properties, "Gateway");
250 
251                     // Origin is kind of DBus object so fetch pointer...
252                     const std::string *origin =
253                         extractProperty<std::string>(properties, "Origin");
254                     if (origin != nullptr)
255                     {
256                         ipv4Address.origin =
257                             translateAddressOriginBetweenDBusAndRedfish(
258                                 origin, true, true);
259                     }
260 
261                     // Netmask is presented as PrefixLength
262                     const auto *mask =
263                         extractProperty<uint8_t>(properties, "PrefixLength");
264                     if (mask != nullptr)
265                     {
266                         // convert it to the string
267                         ipv4Address.netmask = getNetmask(*mask);
268                     }
269 
270                     // Attach IPv4 only if address is present
271                     if (ipv4Address.address != nullptr)
272                     {
273                         // Check if given address is local, or global
274                         if (boost::starts_with(*ipv4Address.address, "169.254"))
275                         {
276                             ipv4Address.global = false;
277                         }
278                         else
279                         {
280                             ipv4Address.global = true;
281                         }
282                         ipv4_config.emplace_back(std::move(ipv4Address));
283                     }
284                 }
285             }
286         }
287 
288         /**
289          * We have to sort this vector and ensure that order of IPv4 addresses
290          * is consistent between GETs to allow modification and deletion in
291          * PATCHes
292          */
293         std::sort(ipv4_config.begin(), ipv4_config.end());
294     }
295 
296     static const constexpr int ipV4AddressSectionsCount = 4;
297 
298   public:
299     /**
300      * @brief Creates VLAN for given interface with given Id through D-Bus
301      *
302      * @param[in] ifaceId       Id of interface for which VLAN will be created
303      * @param[in] inputVlanId   ID of the new VLAN
304      * @param[in] callback      Function that will be called after the operation
305      *
306      * @return None.
307      */
308     template <typename CallbackFunc>
309     void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
310                     CallbackFunc &&callback)
311     {
312         crow::connections::systemBus->async_method_call(
313             callback, "xyz.openbmc_project.Network",
314             "/xyz/openbmc_project/network",
315             "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
316             static_cast<uint32_t>(inputVlanId));
317     };
318 
319     /**
320      * @brief Sets given Id on the given VLAN interface through D-Bus
321      *
322      * @param[in] ifaceId       Id of VLAN interface that should be modified
323      * @param[in] inputVlanId   New ID of the VLAN
324      * @param[in] callback      Function that will be called after the operation
325      *
326      * @return None.
327      */
328     template <typename CallbackFunc>
329     static void changeVlanId(const std::string &ifaceId,
330                              const uint32_t &inputVlanId,
331                              CallbackFunc &&callback)
332     {
333         crow::connections::systemBus->async_method_call(
334             callback, "xyz.openbmc_project.Network",
335             std::string("/xyz/openbmc_project/network/") + ifaceId,
336             "org.freedesktop.DBus.Properties", "Set",
337             "xyz.openbmc_project.Network.VLAN", "Id",
338             sdbusplus::message::variant<uint32_t>(inputVlanId));
339     };
340 
341     /**
342      * @brief Helper function that verifies IP address to check if it is in
343      *        proper format. If bits pointer is provided, also calculates active
344      *        bit count for Subnet Mask.
345      *
346      * @param[in]  ip     IP that will be verified
347      * @param[out] bits   Calculated mask in bits notation
348      *
349      * @return true in case of success, false otherwise
350      */
351     bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
352                                     uint8_t *bits = nullptr)
353     {
354         std::vector<std::string> bytesInMask;
355 
356         boost::split(bytesInMask, ip, boost::is_any_of("."));
357 
358         if (bytesInMask.size() != ipV4AddressSectionsCount)
359         {
360             return false;
361         }
362 
363         if (bits != nullptr)
364         {
365             *bits = 0;
366         }
367 
368         char *endPtr;
369         long previousValue = 255;
370         bool firstZeroInByteHit;
371         for (const std::string &byte : bytesInMask)
372         {
373             if (byte.empty())
374             {
375                 return false;
376             }
377 
378             // Use strtol instead of stroi to avoid exceptions
379             long value = std::strtol(byte.c_str(), &endPtr, 10);
380 
381             // endPtr should point to the end of the string, otherwise given
382             // string is not 100% number
383             if (*endPtr != '\0')
384             {
385                 return false;
386             }
387 
388             // Value should be contained in byte
389             if (value < 0 || value > 255)
390             {
391                 return false;
392             }
393 
394             if (bits != nullptr)
395             {
396                 // Mask has to be continuous between bytes
397                 if (previousValue != 255 && value != 0)
398                 {
399                     return false;
400                 }
401 
402                 // Mask has to be continuous inside bytes
403                 firstZeroInByteHit = false;
404 
405                 // Count bits
406                 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
407                 {
408                     if (value & (1 << bitIdx))
409                     {
410                         if (firstZeroInByteHit)
411                         {
412                             // Continuity not preserved
413                             return false;
414                         }
415                         else
416                         {
417                             (*bits)++;
418                         }
419                     }
420                     else
421                     {
422                         firstZeroInByteHit = true;
423                     }
424                 }
425             }
426 
427             previousValue = value;
428         }
429 
430         return true;
431     }
432 
433     /**
434      * @brief Changes IPv4 address type property (Address, Gateway)
435      *
436      * @param[in] ifaceId     Id of interface whose IP should be modified
437      * @param[in] ipIdx       index of IP in input array that should be modified
438      * @param[in] ipHash      DBus Hash id of modified IP
439      * @param[in] name        Name of field in JSON representation
440      * @param[in] newValue    New value that should be written
441      * @param[io] asyncResp   Response object that will be returned to client
442      *
443      * @return true if give IP is valid and has been sent do D-Bus, false
444      * otherwise
445      */
446     void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
447                                    const std::string &ipHash,
448                                    const std::string &name,
449                                    const std::string &newValue,
450                                    const std::shared_ptr<AsyncResp> &asyncResp)
451     {
452         auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
453                          name{std::move(name)}, newValue{std::move(newValue)}](
454                             const boost::system::error_code ec) {
455             if (ec)
456             {
457                 messages::addMessageToJson(
458                     asyncResp->res.jsonValue, messages::internalError(),
459                     "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
460             }
461             else
462             {
463                 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] =
464                     newValue;
465             }
466         };
467 
468         crow::connections::systemBus->async_method_call(
469             std::move(callback), "xyz.openbmc_project.Network",
470             "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
471             "org.freedesktop.DBus.Properties", "Set",
472             "xyz.openbmc_project.Network.IP", name,
473             sdbusplus::message::variant<std::string>(newValue));
474     };
475 
476     /**
477      * @brief Changes IPv4 address origin property
478      *
479      * @param[in] ifaceId       Id of interface whose IP should be modified
480      * @param[in] ipIdx         index of IP in input array that should be
481      * modified
482      * @param[in] ipHash        DBus Hash id of modified IP
483      * @param[in] newValue      New value in Redfish format
484      * @param[in] newValueDbus  New value in D-Bus format
485      * @param[io] asyncResp     Response object that will be returned to client
486      *
487      * @return true if give IP is valid and has been sent do D-Bus, false
488      * otherwise
489      */
490     void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
491                           const std::string &ipHash,
492                           const std::string &newValue,
493                           const std::string &newValueDbus,
494                           const std::shared_ptr<AsyncResp> &asyncResp)
495     {
496         auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
497                          newValue{std::move(newValue)}](
498                             const boost::system::error_code ec) {
499             if (ec)
500             {
501                 messages::addMessageToJson(
502                     asyncResp->res.jsonValue, messages::internalError(),
503                     "/IPv4Addresses/" + std::to_string(ipIdx) +
504                         "/AddressOrigin");
505             }
506             else
507             {
508                 asyncResp->res
509                     .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
510                     newValue;
511             }
512         };
513 
514         crow::connections::systemBus->async_method_call(
515             std::move(callback), "xyz.openbmc_project.Network",
516             "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
517             "org.freedesktop.DBus.Properties", "Set",
518             "xyz.openbmc_project.Network.IP", "Origin",
519             sdbusplus::message::variant<std::string>(newValueDbus));
520     };
521 
522     /**
523      * @brief Modifies SubnetMask for given IP
524      *
525      * @param[in] ifaceId      Id of interface whose IP should be modified
526      * @param[in] ipIdx        index of IP in input array that should be
527      * modified
528      * @param[in] ipHash       DBus Hash id of modified IP
529      * @param[in] newValueStr  Mask in dot notation as string
530      * @param[in] newValue     Mask as PrefixLength in bitcount
531      * @param[io] asyncResp   Response object that will be returned to client
532      *
533      * @return None
534      */
535     void changeIPv4SubnetMaskProperty(
536         const std::string &ifaceId, int ipIdx, const std::string &ipHash,
537         const std::string &newValueStr, uint8_t &newValue,
538         const std::shared_ptr<AsyncResp> &asyncResp)
539     {
540         auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
541                          newValueStr{std::move(newValueStr)}](
542                             const boost::system::error_code ec) {
543             if (ec)
544             {
545                 messages::addMessageToJson(
546                     asyncResp->res.jsonValue, messages::internalError(),
547                     "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
548             }
549             else
550             {
551                 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
552                     newValueStr;
553             }
554         };
555 
556         crow::connections::systemBus->async_method_call(
557             std::move(callback), "xyz.openbmc_project.Network",
558             "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
559             "org.freedesktop.DBus.Properties", "Set",
560             "xyz.openbmc_project.Network.IP", "PrefixLength",
561             sdbusplus::message::variant<uint8_t>(newValue));
562     };
563 
564     /**
565      * @brief Disables VLAN with given ifaceId
566      *
567      * @param[in] ifaceId   Id of VLAN interface that should be disabled
568      * @param[in] callback  Function that will be called after the operation
569      *
570      * @return None.
571      */
572     template <typename CallbackFunc>
573     static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback)
574     {
575         crow::connections::systemBus->async_method_call(
576             callback, "xyz.openbmc_project.Network",
577             std::string("/xyz/openbmc_project/network/") + ifaceId,
578             "xyz.openbmc_project.Object.Delete", "Delete");
579     };
580 
581     /**
582      * @brief Sets given HostName of the machine through D-Bus
583      *
584      * @param[in] newHostname   New name that HostName will be changed to
585      * @param[in] callback      Function that will be called after the operation
586      *
587      * @return None.
588      */
589     template <typename CallbackFunc>
590     void setHostName(const std::string &newHostname, CallbackFunc &&callback)
591     {
592         crow::connections::systemBus->async_method_call(
593             callback, "xyz.openbmc_project.Network",
594             "/xyz/openbmc_project/network/config",
595             "org.freedesktop.DBus.Properties", "Set",
596             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
597             sdbusplus::message::variant<std::string>(newHostname));
598     };
599 
600     /**
601      * @brief Deletes given IPv4
602      *
603      * @param[in] ifaceId     Id of interface whose IP should be deleted
604      * @param[in] ipIdx       index of IP in input array that should be deleted
605      * @param[in] ipHash      DBus Hash id of IP that should be deleted
606      * @param[io] asyncResp   Response object that will be returned to client
607      *
608      * @return None
609      */
610     void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
611                     unsigned int ipIdx,
612                     const std::shared_ptr<AsyncResp> &asyncResp)
613     {
614         crow::connections::systemBus->async_method_call(
615             [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}](
616                 const boost::system::error_code ec) {
617                 if (ec)
618                 {
619                     messages::addMessageToJson(
620                         asyncResp->res.jsonValue, messages::internalError(),
621                         "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
622                 }
623                 else
624                 {
625                     asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
626                 }
627             },
628             "xyz.openbmc_project.Network",
629             "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
630             "xyz.openbmc_project.Object.Delete", "Delete");
631     }
632 
633     /**
634      * @brief Creates IPv4 with given data
635      *
636      * @param[in] ifaceId     Id of interface whose IP should be deleted
637      * @param[in] ipIdx       index of IP in input array that should be deleted
638      * @param[in] ipHash      DBus Hash id of IP that should be deleted
639      * @param[io] asyncResp   Response object that will be returned to client
640      *
641      * @return None
642      */
643     void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
644                     uint8_t subnetMask, const std::string &gateway,
645                     const std::string &address,
646                     const std::shared_ptr<AsyncResp> &asyncResp)
647     {
648         auto createIpHandler = [ipIdx{std::move(ipIdx)},
649                                 asyncResp{std::move(asyncResp)}](
650                                    const boost::system::error_code ec) {
651             if (ec)
652             {
653                 messages::addMessageToJson(
654                     asyncResp->res.jsonValue, messages::internalError(),
655                     "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
656             }
657         };
658 
659         crow::connections::systemBus->async_method_call(
660             std::move(createIpHandler), "xyz.openbmc_project.Network",
661             "/xyz/openbmc_project/network/" + ifaceId,
662             "xyz.openbmc_project.Network.IP.Create", "IP",
663             "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
664             gateway);
665     }
666 
667     /**
668      * @brief Translates Address Origin value from D-Bus to Redfish format and
669      *        vice-versa
670      *
671      * @param[in] inputOrigin Input value that should be translated
672      * @param[in] isIPv4      True for IPv4 origins, False for IPv6
673      * @param[in] isFromDBus  True for DBus->Redfish conversion, false for
674      * reverse
675      *
676      * @return Empty string in case of failure, translated value otherwise
677      */
678     std::string translateAddressOriginBetweenDBusAndRedfish(
679         const std::string *inputOrigin, bool isIPv4, bool isFromDBus)
680     {
681         // Invalid pointer
682         if (inputOrigin == nullptr)
683         {
684             return "";
685         }
686 
687         static const constexpr unsigned int firstIPv4OnlyIdx = 1;
688         static const constexpr unsigned int firstIPv6OnlyIdx = 3;
689 
690         std::array<std::pair<const char *, const char *>, 6> translationTable{
691             {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
692              {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
693              {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
694               "IPv4LinkLocal"},
695              {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
696              {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
697               "LinkLocal"},
698              {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
699 
700         for (unsigned int i = 0; i < translationTable.size(); i++)
701         {
702             // Skip unrelated
703             if (isIPv4 && i >= firstIPv6OnlyIdx)
704                 break;
705             if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx)
706                 continue;
707 
708             // When translating D-Bus to Redfish compare input to first element
709             if (isFromDBus && translationTable[i].first == *inputOrigin)
710                 return translationTable[i].second;
711 
712             // When translating Redfish to D-Bus compare input to second element
713             if (!isFromDBus && translationTable[i].second == *inputOrigin)
714                 return translationTable[i].first;
715         }
716 
717         // If we are still here, that means that value has not been found
718         return "";
719     }
720 
721     /**
722      * Function that retrieves all properties for given Ethernet Interface
723      * Object
724      * from EntityManager Network Manager
725      * @param ethifaceId a eth interface id to query on DBus
726      * @param callback a function that shall be called to convert Dbus output
727      * into JSON
728      */
729     template <typename CallbackFunc>
730     void getEthernetIfaceData(const std::string &ethifaceId,
731                               CallbackFunc &&callback)
732     {
733         crow::connections::systemBus->async_method_call(
734             [this, ethifaceId{std::move(ethifaceId)},
735              callback{std::move(callback)}](
736                 const boost::system::error_code error_code,
737                 const GetManagedObjectsType &resp) {
738                 EthernetInterfaceData ethData{};
739                 std::vector<IPv4AddressData> ipv4Data;
740                 ipv4Data.reserve(maxIpV4AddressesPerInterface);
741 
742                 if (error_code)
743                 {
744                     // Something wrong on DBus, the error_code is not important
745                     // at this moment, just return success=false, and empty
746                     // output. Since size of vector may vary depending on
747                     // information from Network Manager, and empty output could
748                     // not be treated same way as error.
749                     callback(false, ethData, ipv4Data);
750                     return;
751                 }
752 
753                 // Find interface
754                 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
755                     resp.end())
756                 {
757                     // Interface has not been found
758                     callback(false, ethData, ipv4Data);
759                     return;
760                 }
761 
762                 extractEthernetInterfaceData(ethifaceId, resp, ethData);
763                 extractIPv4Data(ethifaceId, resp, ipv4Data);
764 
765                 // Fix global GW
766                 for (IPv4AddressData &ipv4 : ipv4Data)
767                 {
768                     if ((ipv4.global) && ((ipv4.gateway == nullptr) ||
769                                           (*ipv4.gateway == "0.0.0.0")))
770                     {
771                         ipv4.gateway = ethData.defaultGateway;
772                     }
773                 }
774 
775                 // Finally make a callback with useful data
776                 callback(true, ethData, ipv4Data);
777             },
778             "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
779             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
780     };
781 
782     /**
783      * Function that retrieves all Ethernet Interfaces available through Network
784      * Manager
785      * @param callback a function that shall be called to convert Dbus output
786      * into JSON.
787      */
788     template <typename CallbackFunc>
789     void getEthernetIfaceList(CallbackFunc &&callback)
790     {
791         crow::connections::systemBus->async_method_call(
792             [this, callback{std::move(callback)}](
793                 const boost::system::error_code error_code,
794                 GetManagedObjectsType &resp) {
795                 // Callback requires vector<string> to retrieve all available
796                 // ethernet interfaces
797                 std::vector<std::string> ifaceList;
798                 ifaceList.reserve(resp.size());
799                 if (error_code)
800                 {
801                     // Something wrong on DBus, the error_code is not important
802                     // at this moment, just return success=false, and empty
803                     // output. Since size of vector may vary depending on
804                     // information from Network Manager, and empty output could
805                     // not be treated same way as error.
806                     callback(false, ifaceList);
807                     return;
808                 }
809 
810                 // Iterate over all retrieved ObjectPaths.
811                 for (auto &objpath : resp)
812                 {
813                     // And all interfaces available for certain ObjectPath.
814                     for (auto &interface : objpath.second)
815                     {
816                         // If interface is
817                         // xyz.openbmc_project.Network.EthernetInterface, this
818                         // is what we're looking for.
819                         if (interface.first ==
820                             "xyz.openbmc_project.Network.EthernetInterface")
821                         {
822                             // Cut out everything until last "/", ...
823                             const std::string &ifaceId =
824                                 static_cast<const std::string &>(objpath.first);
825                             std::size_t lastPos = ifaceId.rfind("/");
826                             if (lastPos != std::string::npos)
827                             {
828                                 // and put it into output vector.
829                                 ifaceList.emplace_back(
830                                     ifaceId.substr(lastPos + 1));
831                             }
832                         }
833                     }
834                 }
835                 // Finally make a callback with useful data
836                 callback(true, ifaceList);
837             },
838             "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
839             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
840     };
841 };
842 
843 /**
844  * EthernetCollection derived class for delivering Ethernet Collection Schema
845  */
846 class EthernetCollection : public Node
847 {
848   public:
849     // TODO(Pawel) Remove line from below, where we assume that there is only
850     // one manager called openbmc This shall be generic, but requires to update
851     // GetSubroutes method
852     EthernetCollection(CrowApp &app) :
853         Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/")
854     {
855         Node::json["@odata.type"] =
856             "#EthernetInterfaceCollection.EthernetInterfaceCollection";
857         Node::json["@odata.context"] =
858             "/redfish/v1/"
859             "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
860         Node::json["@odata.id"] =
861             "/redfish/v1/Managers/openbmc/EthernetInterfaces";
862         Node::json["Name"] = "Ethernet Network Interface Collection";
863         Node::json["Description"] =
864             "Collection of EthernetInterfaces for this Manager";
865 
866         entityPrivileges = {
867             {boost::beast::http::verb::get, {{"Login"}}},
868             {boost::beast::http::verb::head, {{"Login"}}},
869             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
870             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
871             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
872             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
873     }
874 
875   private:
876     /**
877      * Functions triggers appropriate requests on DBus
878      */
879     void doGet(crow::Response &res, const crow::Request &req,
880                const std::vector<std::string> &params) override
881     {
882         // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
883         // for any Manager, not only hardcoded 'openbmc'.
884         std::string managerId = "openbmc";
885 
886         // get eth interface list, and call the below callback for JSON
887         // preparation
888         ethernetProvider.getEthernetIfaceList(
889             [&, managerId{std::move(managerId)}](
890                 const bool &success,
891                 const std::vector<std::string> &iface_list) {
892                 if (success)
893                 {
894                     nlohmann::json ifaceArray = nlohmann::json::array();
895                     for (const std::string &ifaceItem : iface_list)
896                     {
897                         ifaceArray.push_back(
898                             {{"@odata.id", "/redfish/v1/Managers/" + managerId +
899                                                "/EthernetInterfaces/" +
900                                                ifaceItem}});
901                     }
902                     Node::json["Members"] = ifaceArray;
903                     Node::json["Members@odata.count"] = ifaceArray.size();
904                     Node::json["@odata.id"] = "/redfish/v1/Managers/" +
905                                               managerId + "/EthernetInterfaces";
906                     res.jsonValue = Node::json;
907                 }
908                 else
909                 {
910                     // No success, best what we can do is return INTERNALL ERROR
911                     res.result(
912                         boost::beast::http::status::internal_server_error);
913                 }
914                 res.end();
915             });
916     }
917 
918     // Ethernet Provider object
919     // TODO(Pawel) consider move it to singleton
920     OnDemandEthernetProvider ethernetProvider;
921 };
922 
923 /**
924  * EthernetInterface derived class for delivering Ethernet Schema
925  */
926 class EthernetInterface : public Node
927 {
928   public:
929     /*
930      * Default Constructor
931      */
932     // TODO(Pawel) Remove line from below, where we assume that there is only
933     // one manager called openbmc This shall be generic, but requires to update
934     // GetSubroutes method
935     EthernetInterface(CrowApp &app) :
936         Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
937              std::string())
938     {
939         Node::json["@odata.type"] =
940             "#EthernetInterface.v1_2_0.EthernetInterface";
941         Node::json["@odata.context"] =
942             "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
943         Node::json["Name"] = "Manager Ethernet Interface";
944         Node::json["Description"] = "Management Network Interface";
945 
946         entityPrivileges = {
947             {boost::beast::http::verb::get, {{"Login"}}},
948             {boost::beast::http::verb::head, {{"Login"}}},
949             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
950             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
951             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
952             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
953     }
954 
955     // TODO(kkowalsk) Find a suitable class/namespace for this
956     static void handleVlanPatch(const std::string &ifaceId,
957                                 const nlohmann::json &input,
958                                 const EthernetInterfaceData &eth_data,
959                                 const std::string &pathPrefix,
960                                 const std::shared_ptr<AsyncResp> &asyncResp)
961     {
962         if (!input.is_object())
963         {
964             messages::addMessageToJson(
965                 asyncResp->res.jsonValue,
966                 messages::propertyValueTypeError(input.dump(), "VLAN"),
967                 pathPrefix);
968             return;
969         }
970 
971         const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
972         nlohmann::json &paramsJson =
973             (pathPrefix == "/")
974                 ? asyncResp->res.jsonValue
975                 : asyncResp->res
976                       .jsonValue[nlohmann::json_pointer<nlohmann::json>(
977                           pathPrefix)];
978         bool inputVlanEnabled;
979         uint64_t inputVlanId;
980 
981         json_util::Result inputVlanEnabledState = json_util::getBool(
982             "VLANEnable", input, inputVlanEnabled,
983             static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
984             asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
985         json_util::Result inputVlanIdState = json_util::getUnsigned(
986             "VLANId", input, inputVlanId,
987             static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
988             asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
989         bool inputInvalid = false;
990 
991         // Do not proceed if fields in VLAN object were of wrong type
992         if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
993             inputVlanIdState == json_util::Result::WRONG_TYPE)
994         {
995             return;
996         }
997 
998         // Verify input
999         if (eth_data.vlanId == nullptr)
1000         {
1001             // This interface is not a VLAN. Cannot do anything with it
1002             // TODO(kkowalsk) Change this message
1003             messages::addMessageToJson(asyncResp->res.jsonValue,
1004                                        messages::propertyMissing("VLANEnable"),
1005                                        pathPrefix);
1006 
1007             inputInvalid = true;
1008         }
1009         else
1010         {
1011             // Load actual data into field values if they were not provided
1012             if (inputVlanEnabledState == json_util::Result::NOT_EXIST)
1013             {
1014                 inputVlanEnabled = true;
1015             }
1016 
1017             if (inputVlanIdState == json_util::Result::NOT_EXIST)
1018             {
1019                 inputVlanId = *eth_data.vlanId;
1020             }
1021         }
1022 
1023         // Do not proceed if input has not been valid
1024         if (inputInvalid)
1025         {
1026             return;
1027         }
1028 
1029         // VLAN is configured on the interface
1030         if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId)
1031         {
1032             // Change VLAN Id
1033             paramsJson["VLANId"] = inputVlanId;
1034             OnDemandEthernetProvider::changeVlanId(
1035                 ifaceId, static_cast<uint32_t>(inputVlanId),
1036                 [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1037                     const boost::system::error_code ec) {
1038                     if (ec)
1039                     {
1040                         messages::addMessageToJson(asyncResp->res.jsonValue,
1041                                                    messages::internalError(),
1042                                                    pathPrefix);
1043                     }
1044                     else
1045                     {
1046                         paramsJson["VLANEnable"] = true;
1047                     }
1048                 });
1049         }
1050         else if (inputVlanEnabled == false)
1051         {
1052             // Disable VLAN
1053             OnDemandEthernetProvider::disableVlan(
1054                 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1055                              const boost::system::error_code ec) {
1056                     if (ec)
1057                     {
1058                         messages::addMessageToJson(asyncResp->res.jsonValue,
1059                                                    messages::internalError(),
1060                                                    pathPrefix);
1061                     }
1062                     else
1063                     {
1064                         paramsJson["VLANEnable"] = false;
1065                     }
1066                 });
1067         }
1068     }
1069 
1070   private:
1071     void handleHostnamePatch(const nlohmann::json &input,
1072                              const EthernetInterfaceData &eth_data,
1073                              const std::shared_ptr<AsyncResp> &asyncResp)
1074     {
1075         if (input.is_string())
1076         {
1077             std::string newHostname = input.get<std::string>();
1078 
1079             if (eth_data.hostname == nullptr ||
1080                 newHostname != *eth_data.hostname)
1081             {
1082                 // Change hostname
1083                 ethernetProvider.setHostName(
1084                     newHostname, [asyncResp, newHostname](
1085                                      const boost::system::error_code ec) {
1086                         if (ec)
1087                         {
1088                             messages::addMessageToJson(
1089                                 asyncResp->res.jsonValue,
1090                                 messages::internalError(), "/HostName");
1091                         }
1092                         else
1093                         {
1094                             asyncResp->res.jsonValue["HostName"] = newHostname;
1095                         }
1096                     });
1097             }
1098         }
1099         else
1100         {
1101             messages::addMessageToJson(
1102                 asyncResp->res.jsonValue,
1103                 messages::propertyValueTypeError(input.dump(), "HostName"),
1104                 "/HostName");
1105         }
1106     }
1107 
1108     void handleIPv4Patch(const std::string &ifaceId,
1109                          const nlohmann::json &input,
1110                          const std::vector<IPv4AddressData> &ipv4_data,
1111                          const std::shared_ptr<AsyncResp> &asyncResp)
1112     {
1113         if (!input.is_array())
1114         {
1115             messages::addMessageToJson(
1116                 asyncResp->res.jsonValue,
1117                 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
1118                 "/IPv4Addresses");
1119             return;
1120         }
1121 
1122         // According to Redfish PATCH definition, size must be at least equal
1123         if (input.size() < ipv4_data.size())
1124         {
1125             // TODO(kkowalsk) This should be a message indicating that not
1126             // enough data has been provided
1127             messages::addMessageToJson(asyncResp->res.jsonValue,
1128                                        messages::internalError(),
1129                                        "/IPv4Addresses");
1130             return;
1131         }
1132 
1133         json_util::Result addressFieldState;
1134         json_util::Result subnetMaskFieldState;
1135         json_util::Result addressOriginFieldState;
1136         json_util::Result gatewayFieldState;
1137         const std::string *addressFieldValue;
1138         const std::string *subnetMaskFieldValue;
1139         const std::string *addressOriginFieldValue = nullptr;
1140         const std::string *gatewayFieldValue;
1141         uint8_t subnetMaskAsPrefixLength;
1142         std::string addressOriginInDBusFormat;
1143 
1144         bool errorDetected = false;
1145         for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++)
1146         {
1147             // Check that entry is not of some unexpected type
1148             if (!input[entryIdx].is_object() && !input[entryIdx].is_null())
1149             {
1150                 // Invalid object type
1151                 messages::addMessageToJson(
1152                     asyncResp->res.jsonValue,
1153                     messages::propertyValueTypeError(input[entryIdx].dump(),
1154                                                      "IPv4Address"),
1155                     "/IPv4Addresses/" + std::to_string(entryIdx));
1156 
1157                 continue;
1158             }
1159 
1160             // Try to load fields
1161             addressFieldState = json_util::getString(
1162                 "Address", input[entryIdx], addressFieldValue,
1163                 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1164                 asyncResp->res.jsonValue,
1165                 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1166             subnetMaskFieldState = json_util::getString(
1167                 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1168                 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1169                 asyncResp->res.jsonValue,
1170                 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1171             addressOriginFieldState = json_util::getString(
1172                 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1173                 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1174                 asyncResp->res.jsonValue,
1175                 "/IPv4Addresses/" + std::to_string(entryIdx) +
1176                     "/AddressOrigin");
1177             gatewayFieldState = json_util::getString(
1178                 "Gateway", input[entryIdx], gatewayFieldValue,
1179                 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1180                 asyncResp->res.jsonValue,
1181                 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1182 
1183             if (addressFieldState == json_util::Result::WRONG_TYPE ||
1184                 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1185                 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1186                 gatewayFieldState == json_util::Result::WRONG_TYPE)
1187             {
1188                 return;
1189             }
1190 
1191             if (addressFieldState == json_util::Result::SUCCESS &&
1192                 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1193                     *addressFieldValue))
1194             {
1195                 errorDetected = true;
1196                 messages::addMessageToJson(
1197                     asyncResp->res.jsonValue,
1198                     messages::propertyValueFormatError(*addressFieldValue,
1199                                                        "Address"),
1200                     "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1201             }
1202 
1203             if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1204                 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1205                     *subnetMaskFieldValue, &subnetMaskAsPrefixLength))
1206             {
1207                 errorDetected = true;
1208                 messages::addMessageToJson(
1209                     asyncResp->res.jsonValue,
1210                     messages::propertyValueFormatError(*subnetMaskFieldValue,
1211                                                        "SubnetMask"),
1212                     "/IPv4Addresses/" + std::to_string(entryIdx) +
1213                         "/SubnetMask");
1214             }
1215 
1216             // get Address origin in proper format
1217             addressOriginInDBusFormat =
1218                 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
1219                     addressOriginFieldValue, true, false);
1220 
1221             if (addressOriginFieldState == json_util::Result::SUCCESS &&
1222                 addressOriginInDBusFormat.empty())
1223             {
1224                 errorDetected = true;
1225                 messages::addMessageToJson(
1226                     asyncResp->res.jsonValue,
1227                     messages::propertyValueNotInList(*addressOriginFieldValue,
1228                                                      "AddressOrigin"),
1229                     "/IPv4Addresses/" + std::to_string(entryIdx) +
1230                         "/AddressOrigin");
1231             }
1232 
1233             if (gatewayFieldState == json_util::Result::SUCCESS &&
1234                 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1235                     *gatewayFieldValue))
1236             {
1237                 errorDetected = true;
1238                 messages::addMessageToJson(
1239                     asyncResp->res.jsonValue,
1240                     messages::propertyValueFormatError(*gatewayFieldValue,
1241                                                        "Gateway"),
1242                     "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1243             }
1244 
1245             // If any error occured do not proceed with current entry, but do
1246             // not end loop
1247             if (errorDetected)
1248             {
1249                 errorDetected = false;
1250                 continue;
1251             }
1252 
1253             if (entryIdx >= ipv4_data.size())
1254             {
1255                 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1256                     input[entryIdx];
1257 
1258                 // Verify that all field were provided
1259                 if (addressFieldState == json_util::Result::NOT_EXIST)
1260                 {
1261                     errorDetected = true;
1262                     messages::addMessageToJson(
1263                         asyncResp->res.jsonValue,
1264                         messages::propertyMissing("Address"),
1265                         "/IPv4Addresses/" + std::to_string(entryIdx) +
1266                             "/Address");
1267                 }
1268 
1269                 if (subnetMaskFieldState == json_util::Result::NOT_EXIST)
1270                 {
1271                     errorDetected = true;
1272                     messages::addMessageToJson(
1273                         asyncResp->res.jsonValue,
1274                         messages::propertyMissing("SubnetMask"),
1275                         "/IPv4Addresses/" + std::to_string(entryIdx) +
1276                             "/SubnetMask");
1277                 }
1278 
1279                 if (addressOriginFieldState == json_util::Result::NOT_EXIST)
1280                 {
1281                     errorDetected = true;
1282                     messages::addMessageToJson(
1283                         asyncResp->res.jsonValue,
1284                         messages::propertyMissing("AddressOrigin"),
1285                         "/IPv4Addresses/" + std::to_string(entryIdx) +
1286                             "/AddressOrigin");
1287                 }
1288 
1289                 if (gatewayFieldState == json_util::Result::NOT_EXIST)
1290                 {
1291                     errorDetected = true;
1292                     messages::addMessageToJson(
1293                         asyncResp->res.jsonValue,
1294                         messages::propertyMissing("Gateway"),
1295                         "/IPv4Addresses/" + std::to_string(entryIdx) +
1296                             "/Gateway");
1297                 }
1298 
1299                 // If any error occured do not proceed with current entry, but
1300                 // do not end loop
1301                 if (errorDetected)
1302                 {
1303                     errorDetected = false;
1304                     continue;
1305                 }
1306 
1307                 // Create IPv4 with provided data
1308                 ethernetProvider.createIPv4(
1309                     ifaceId, entryIdx, subnetMaskAsPrefixLength,
1310                     *gatewayFieldValue, *addressFieldValue, asyncResp);
1311             }
1312             else
1313             {
1314                 // Existing object that should be modified/deleted/remain
1315                 // unchanged
1316                 if (input[entryIdx].is_null())
1317                 {
1318                     // Object should be deleted
1319                     ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1320                                                 entryIdx, asyncResp);
1321                 }
1322                 else if (input[entryIdx].is_object())
1323                 {
1324                     if (input[entryIdx].size() == 0)
1325                     {
1326                         // Object shall remain unchanged
1327                         continue;
1328                     }
1329 
1330                     // Apply changes
1331                     if (addressFieldState == json_util::Result::SUCCESS &&
1332                         ipv4_data[entryIdx].address != nullptr &&
1333                         *ipv4_data[entryIdx].address != *addressFieldValue)
1334                     {
1335                         ethernetProvider.changeIPv4AddressProperty(
1336                             ifaceId, entryIdx, ipv4_data[entryIdx].id,
1337                             "Address", *addressFieldValue, asyncResp);
1338                     }
1339 
1340                     if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1341                         ipv4_data[entryIdx].netmask != *subnetMaskFieldValue)
1342                     {
1343                         ethernetProvider.changeIPv4SubnetMaskProperty(
1344                             ifaceId, entryIdx, ipv4_data[entryIdx].id,
1345                             *subnetMaskFieldValue, subnetMaskAsPrefixLength,
1346                             asyncResp);
1347                     }
1348 
1349                     if (addressOriginFieldState == json_util::Result::SUCCESS &&
1350                         ipv4_data[entryIdx].origin != *addressFieldValue)
1351                     {
1352                         ethernetProvider.changeIPv4Origin(
1353                             ifaceId, entryIdx, ipv4_data[entryIdx].id,
1354                             *addressOriginFieldValue, addressOriginInDBusFormat,
1355                             asyncResp);
1356                     }
1357 
1358                     if (gatewayFieldState == json_util::Result::SUCCESS &&
1359                         ipv4_data[entryIdx].gateway != nullptr &&
1360                         *ipv4_data[entryIdx].gateway != *gatewayFieldValue)
1361                     {
1362                         ethernetProvider.changeIPv4AddressProperty(
1363                             ifaceId, entryIdx, ipv4_data[entryIdx].id,
1364                             "Gateway", *gatewayFieldValue, asyncResp);
1365                     }
1366                 }
1367             }
1368         }
1369     }
1370 
1371     nlohmann::json
1372         parseInterfaceData(const std::string &ifaceId,
1373                            const EthernetInterfaceData &eth_data,
1374                            const std::vector<IPv4AddressData> &ipv4_data)
1375     {
1376         // Copy JSON object to avoid race condition
1377         nlohmann::json jsonResponse(Node::json);
1378 
1379         // Fill out obvious data...
1380         jsonResponse["Id"] = ifaceId;
1381         jsonResponse["@odata.id"] =
1382             "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
1383 
1384         // ... then the one from DBus, regarding eth iface...
1385         if (eth_data.speed != nullptr)
1386             jsonResponse["SpeedMbps"] = *eth_data.speed;
1387 
1388         if (eth_data.macAddress != nullptr)
1389             jsonResponse["MACAddress"] = *eth_data.macAddress;
1390 
1391         if (eth_data.hostname != nullptr)
1392             jsonResponse["HostName"] = *eth_data.hostname;
1393 
1394         if (eth_data.vlanId != nullptr)
1395         {
1396             nlohmann::json &vlanObj = jsonResponse["VLAN"];
1397             vlanObj["VLANEnable"] = true;
1398             vlanObj["VLANId"] = *eth_data.vlanId;
1399         }
1400         else
1401         {
1402             nlohmann::json &vlanObj = jsonResponse["VLANs"];
1403             vlanObj["@odata.id"] =
1404                 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
1405                 "/VLANs";
1406         }
1407 
1408         // ... at last, check if there are IPv4 data and prepare appropriate
1409         // collection
1410         if (ipv4_data.size() > 0)
1411         {
1412             nlohmann::json ipv4Array = nlohmann::json::array();
1413             for (auto &ipv4Config : ipv4_data)
1414             {
1415                 nlohmann::json jsonIpv4;
1416                 if (ipv4Config.address != nullptr)
1417                 {
1418                     jsonIpv4["Address"] = *ipv4Config.address;
1419                     if (ipv4Config.gateway != nullptr)
1420                         jsonIpv4["Gateway"] = *ipv4Config.gateway;
1421 
1422                     jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1423                     jsonIpv4["SubnetMask"] = ipv4Config.netmask;
1424 
1425                     ipv4Array.push_back(std::move(jsonIpv4));
1426                 }
1427             }
1428             jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
1429         }
1430 
1431         return jsonResponse;
1432     }
1433 
1434     /**
1435      * Functions triggers appropriate requests on DBus
1436      */
1437     void doGet(crow::Response &res, const crow::Request &req,
1438                const std::vector<std::string> &params) override
1439     {
1440         // TODO(Pawel) this shall be parametrized call (two params) to get
1441         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1442         // Check if there is required param, truly entering this shall be
1443         // impossible.
1444         if (params.size() != 1)
1445         {
1446             res.result(boost::beast::http::status::internal_server_error);
1447             res.end();
1448             return;
1449         }
1450 
1451         const std::string &ifaceId = params[0];
1452 
1453         // get single eth interface data, and call the below callback for JSON
1454         // preparation
1455         ethernetProvider.getEthernetIfaceData(
1456             ifaceId,
1457             [&, ifaceId](const bool &success,
1458                          const EthernetInterfaceData &eth_data,
1459                          const std::vector<IPv4AddressData> &ipv4_data) {
1460                 if (success)
1461                 {
1462                     res.jsonValue =
1463                         parseInterfaceData(ifaceId, eth_data, ipv4_data);
1464                 }
1465                 else
1466                 {
1467                     // ... otherwise return error
1468                     // TODO(Pawel)consider distinguish between non existing
1469                     // object, and other errors
1470                     messages::addMessageToErrorJson(
1471                         res.jsonValue, messages::resourceNotFound(
1472                                            "EthernetInterface", ifaceId));
1473                     res.result(boost::beast::http::status::not_found);
1474                 }
1475                 res.end();
1476             });
1477     }
1478 
1479     void doPatch(crow::Response &res, const crow::Request &req,
1480                  const std::vector<std::string> &params) override
1481     {
1482         // TODO(Pawel) this shall be parametrized call (two params) to get
1483         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1484         // Check if there is required param, truly entering this shall be
1485         // impossible.
1486         if (params.size() != 1)
1487         {
1488             res.result(boost::beast::http::status::internal_server_error);
1489             res.end();
1490             return;
1491         }
1492 
1493         const std::string &ifaceId = params[0];
1494 
1495         nlohmann::json patchReq;
1496 
1497         if (!json_util::processJsonFromRequest(res, req, patchReq))
1498         {
1499             return;
1500         }
1501 
1502         // get single eth interface data, and call the below callback for JSON
1503         // preparation
1504         ethernetProvider.getEthernetIfaceData(
1505             ifaceId,
1506             [&, ifaceId, patchReq = std::move(patchReq)](
1507                 const bool &success, const EthernetInterfaceData &eth_data,
1508                 const std::vector<IPv4AddressData> &ipv4_data) {
1509                 if (!success)
1510                 {
1511                     // ... otherwise return error
1512                     // TODO(Pawel)consider distinguish between non existing
1513                     // object, and other errors
1514                     messages::addMessageToErrorJson(
1515                         res.jsonValue, messages::resourceNotFound(
1516                                            "VLAN Network Interface", ifaceId));
1517                     res.result(boost::beast::http::status::not_found);
1518                     res.end();
1519 
1520                     return;
1521                 }
1522 
1523                 res.jsonValue =
1524                     parseInterfaceData(ifaceId, eth_data, ipv4_data);
1525 
1526                 std::shared_ptr<AsyncResp> asyncResp =
1527                     std::make_shared<AsyncResp>(res);
1528 
1529                 for (auto propertyIt = patchReq.begin();
1530                      propertyIt != patchReq.end(); ++propertyIt)
1531                 {
1532                     if (propertyIt.key() == "VLAN")
1533                     {
1534                         handleVlanPatch(ifaceId, propertyIt.value(), eth_data,
1535                                         "/VLAN", asyncResp);
1536                     }
1537                     else if (propertyIt.key() == "HostName")
1538                     {
1539                         handleHostnamePatch(propertyIt.value(), eth_data,
1540                                             asyncResp);
1541                     }
1542                     else if (propertyIt.key() == "IPv4Addresses")
1543                     {
1544                         handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
1545                                         asyncResp);
1546                     }
1547                     else if (propertyIt.key() == "IPv6Addresses")
1548                     {
1549                         // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1550                         messages::addMessageToJsonRoot(
1551                             res.jsonValue,
1552                             messages::propertyNotWritable(propertyIt.key()));
1553                     }
1554                     else
1555                     {
1556                         auto fieldInJsonIt =
1557                             res.jsonValue.find(propertyIt.key());
1558 
1559                         if (fieldInJsonIt == res.jsonValue.end())
1560                         {
1561                             // Field not in scope of defined fields
1562                             messages::addMessageToJsonRoot(
1563                                 res.jsonValue,
1564                                 messages::propertyUnknown(propertyIt.key()));
1565                         }
1566                         else if (*fieldInJsonIt != *propertyIt)
1567                         {
1568                             // User attempted to modify non-writable field
1569                             messages::addMessageToJsonRoot(
1570                                 res.jsonValue, messages::propertyNotWritable(
1571                                                    propertyIt.key()));
1572                         }
1573                     }
1574                 }
1575             });
1576     }
1577 
1578     // Ethernet Provider object
1579     // TODO(Pawel) consider move it to singleton
1580     OnDemandEthernetProvider ethernetProvider;
1581 };
1582 
1583 class VlanNetworkInterfaceCollection;
1584 
1585 /**
1586  * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1587  */
1588 class VlanNetworkInterface : public Node
1589 {
1590   public:
1591     /*
1592      * Default Constructor
1593      */
1594     template <typename CrowApp>
1595     // TODO(Pawel) Remove line from below, where we assume that there is only
1596     // one manager called openbmc This shall be generic, but requires to update
1597     // GetSubroutes method
1598     VlanNetworkInterface(CrowApp &app) :
1599         Node(
1600             app,
1601             "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1602             std::string(), std::string())
1603     {
1604         Node::json["@odata.type"] =
1605             "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1606         Node::json["@odata.context"] =
1607             "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1608         Node::json["Name"] = "VLAN Network Interface";
1609 
1610         entityPrivileges = {
1611             {boost::beast::http::verb::get, {{"Login"}}},
1612             {boost::beast::http::verb::head, {{"Login"}}},
1613             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1614             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1615             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1616             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1617     }
1618 
1619   private:
1620     nlohmann::json
1621         parseInterfaceData(const std::string &parent_ifaceId,
1622                            const std::string &ifaceId,
1623                            const EthernetInterfaceData &eth_data,
1624                            const std::vector<IPv4AddressData> &ipv4_data)
1625     {
1626         // Copy JSON object to avoid race condition
1627         nlohmann::json jsonResponse(Node::json);
1628 
1629         // Fill out obvious data...
1630         jsonResponse["Id"] = ifaceId;
1631         jsonResponse["@odata.id"] =
1632             "/redfish/v1/Managers/openbmc/EthernetInterfaces/" +
1633             parent_ifaceId + "/VLANs/" + ifaceId;
1634 
1635         jsonResponse["VLANEnable"] = true;
1636         jsonResponse["VLANId"] = *eth_data.vlanId;
1637 
1638         return jsonResponse;
1639     }
1640 
1641     bool verifyNames(crow::Response &res, const std::string &parent,
1642                      const std::string &iface)
1643     {
1644         if (!boost::starts_with(iface, parent + "_"))
1645         {
1646             messages::addMessageToErrorJson(
1647                 res.jsonValue,
1648                 messages::resourceNotFound("VLAN Network Interface", iface));
1649             res.result(boost::beast::http::status::not_found);
1650             res.end();
1651 
1652             return false;
1653         }
1654         else
1655         {
1656             return true;
1657         }
1658     }
1659 
1660     /**
1661      * Functions triggers appropriate requests on DBus
1662      */
1663     void doGet(crow::Response &res, const crow::Request &req,
1664                const std::vector<std::string> &params) override
1665     {
1666         // TODO(Pawel) this shall be parametrized call (two params) to get
1667         // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1668         // Check if there is required param, truly entering this shall be
1669         // impossible.
1670         if (params.size() != 2)
1671         {
1672             res.result(boost::beast::http::status::internal_server_error);
1673             res.end();
1674             return;
1675         }
1676 
1677         const std::string &parentIfaceId = params[0];
1678         const std::string &ifaceId = params[1];
1679 
1680         if (!verifyNames(res, parentIfaceId, ifaceId))
1681         {
1682             return;
1683         }
1684 
1685         // Get single eth interface data, and call the below callback for JSON
1686         // preparation
1687         ethernetProvider.getEthernetIfaceData(
1688             ifaceId, [&, parentIfaceId,
1689                       ifaceId](const bool &success,
1690                                const EthernetInterfaceData &eth_data,
1691                                const std::vector<IPv4AddressData> &ipv4_data) {
1692                 if (success && eth_data.vlanId != nullptr)
1693                 {
1694                     res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1695                                                        eth_data, ipv4_data);
1696                 }
1697                 else
1698                 {
1699                     // ... otherwise return error
1700                     // TODO(Pawel)consider distinguish between non existing
1701                     // object, and other errors
1702                     messages::addMessageToErrorJson(
1703                         res.jsonValue, messages::resourceNotFound(
1704                                            "VLAN Network Interface", ifaceId));
1705                     res.result(boost::beast::http::status::not_found);
1706                 }
1707                 res.end();
1708             });
1709     }
1710 
1711     void doPatch(crow::Response &res, const crow::Request &req,
1712                  const std::vector<std::string> &params) override
1713     {
1714         if (params.size() != 2)
1715         {
1716             res.result(boost::beast::http::status::internal_server_error);
1717             res.end();
1718             return;
1719         }
1720 
1721         const std::string &parentIfaceId = params[0];
1722         const std::string &ifaceId = params[1];
1723 
1724         if (!verifyNames(res, parentIfaceId, ifaceId))
1725         {
1726             return;
1727         }
1728 
1729         nlohmann::json patchReq;
1730 
1731         if (!json_util::processJsonFromRequest(res, req, patchReq))
1732         {
1733             return;
1734         }
1735 
1736         // Get single eth interface data, and call the below callback for JSON
1737         // preparation
1738         ethernetProvider.getEthernetIfaceData(
1739             ifaceId,
1740             [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)](
1741                 const bool &success, const EthernetInterfaceData &eth_data,
1742                 const std::vector<IPv4AddressData> &ipv4_data) {
1743                 if (!success)
1744                 {
1745                     // ... otherwise return error
1746                     // TODO(Pawel)consider distinguish between non existing
1747                     // object, and other errors
1748                     messages::addMessageToErrorJson(
1749                         res.jsonValue, messages::resourceNotFound(
1750                                            "VLAN Network Interface", ifaceId));
1751                     res.result(boost::beast::http::status::not_found);
1752                     res.end();
1753 
1754                     return;
1755                 }
1756 
1757                 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1758                                                    eth_data, ipv4_data);
1759 
1760                 std::shared_ptr<AsyncResp> asyncResp =
1761                     std::make_shared<AsyncResp>(res);
1762 
1763                 for (auto propertyIt = patchReq.begin();
1764                      propertyIt != patchReq.end(); ++propertyIt)
1765                 {
1766                     if (propertyIt.key() != "VLANEnable" &&
1767                         propertyIt.key() != "VLANId")
1768                     {
1769                         auto fieldInJsonIt =
1770                             res.jsonValue.find(propertyIt.key());
1771 
1772                         if (fieldInJsonIt == res.jsonValue.end())
1773                         {
1774                             // Field not in scope of defined fields
1775                             messages::addMessageToJsonRoot(
1776                                 res.jsonValue,
1777                                 messages::propertyUnknown(propertyIt.key()));
1778                         }
1779                         else if (*fieldInJsonIt != *propertyIt)
1780                         {
1781                             // User attempted to modify non-writable field
1782                             messages::addMessageToJsonRoot(
1783                                 res.jsonValue, messages::propertyNotWritable(
1784                                                    propertyIt.key()));
1785                         }
1786                     }
1787                 }
1788 
1789                 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data,
1790                                                    "/", asyncResp);
1791             });
1792     }
1793 
1794     void doDelete(crow::Response &res, const crow::Request &req,
1795                   const std::vector<std::string> &params) override
1796     {
1797         if (params.size() != 2)
1798         {
1799             res.result(boost::beast::http::status::internal_server_error);
1800             res.end();
1801             return;
1802         }
1803 
1804         const std::string &parentIfaceId = params[0];
1805         const std::string &ifaceId = params[1];
1806 
1807         if (!verifyNames(res, parentIfaceId, ifaceId))
1808         {
1809             return;
1810         }
1811 
1812         // Get single eth interface data, and call the below callback for JSON
1813         // preparation
1814         ethernetProvider.getEthernetIfaceData(
1815             ifaceId, [&, parentIfaceId,
1816                       ifaceId](const bool &success,
1817                                const EthernetInterfaceData &eth_data,
1818                                const std::vector<IPv4AddressData> &ipv4_data) {
1819                 if (success && eth_data.vlanId != nullptr)
1820                 {
1821                     res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1822                                                        eth_data, ipv4_data);
1823 
1824                     // Disable VLAN
1825                     OnDemandEthernetProvider::disableVlan(
1826                         ifaceId, [&](const boost::system::error_code ec) {
1827                             if (ec)
1828                             {
1829                                 res.jsonValue = nlohmann::json::object();
1830                                 messages::addMessageToErrorJson(
1831                                     res.jsonValue, messages::internalError());
1832                                 res.result(boost::beast::http::status::
1833                                                internal_server_error);
1834                             }
1835                             res.end();
1836                         });
1837                 }
1838                 else
1839                 {
1840                     // ... otherwise return error
1841                     // TODO(Pawel)consider distinguish between non existing
1842                     // object, and other errors
1843                     messages::addMessageToErrorJson(
1844                         res.jsonValue, messages::resourceNotFound(
1845                                            "VLAN Network Interface", ifaceId));
1846                     res.result(boost::beast::http::status::not_found);
1847                     res.end();
1848                 }
1849             });
1850     }
1851 
1852     /**
1853      * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1854      * method, to maintain consistency of returned data, as Collection's doPost
1855      * should return data for created member which should match member's doGet
1856      * result in 100%.
1857      */
1858     friend VlanNetworkInterfaceCollection;
1859 
1860     // Ethernet Provider object
1861     // TODO(Pawel) consider move it to singleton
1862     OnDemandEthernetProvider ethernetProvider;
1863 };
1864 
1865 /**
1866  * VlanNetworkInterfaceCollection derived class for delivering
1867  * VLANNetworkInterface Collection Schema
1868  */
1869 class VlanNetworkInterfaceCollection : public Node
1870 {
1871   public:
1872     template <typename CrowApp>
1873     // TODO(Pawel) Remove line from below, where we assume that there is only
1874     // one manager called openbmc This shall be generic, but requires to update
1875     // GetSubroutes method
1876     VlanNetworkInterfaceCollection(CrowApp &app) :
1877         Node(app,
1878              "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1879              std::string()),
1880         memberVlan(app)
1881     {
1882         Node::json["@odata.type"] =
1883             "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1884         Node::json["@odata.context"] =
1885             "/redfish/v1/$metadata"
1886             "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1887         Node::json["Name"] = "VLAN Network Interface Collection";
1888 
1889         entityPrivileges = {
1890             {boost::beast::http::verb::get, {{"Login"}}},
1891             {boost::beast::http::verb::head, {{"Login"}}},
1892             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1893             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1894             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1895             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1896     }
1897 
1898   private:
1899     /**
1900      * Functions triggers appropriate requests on DBus
1901      */
1902     void doGet(crow::Response &res, const crow::Request &req,
1903                const std::vector<std::string> &params) override
1904     {
1905         if (params.size() != 1)
1906         {
1907             // This means there is a problem with the router
1908             res.result(boost::beast::http::status::internal_server_error);
1909             res.end();
1910 
1911             return;
1912         }
1913 
1914         // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1915         // for any Manager, not only hardcoded 'openbmc'.
1916         std::string managerId = "openbmc";
1917         std::string rootInterfaceName = params[0];
1918 
1919         // get eth interface list, and call the below callback for JSON
1920         // preparation
1921         ethernetProvider.getEthernetIfaceList(
1922             [&, managerId{std::move(managerId)},
1923              rootInterfaceName{std::move(rootInterfaceName)}](
1924                 const bool &success,
1925                 const std::vector<std::string> &iface_list) {
1926                 if (success)
1927                 {
1928                     bool rootInterfaceFound = false;
1929                     nlohmann::json ifaceArray = nlohmann::json::array();
1930 
1931                     for (const std::string &ifaceItem : iface_list)
1932                     {
1933                         if (ifaceItem == rootInterfaceName)
1934                         {
1935                             rootInterfaceFound = true;
1936                         }
1937                         else if (boost::starts_with(ifaceItem,
1938                                                     rootInterfaceName + "_"))
1939                         {
1940                             ifaceArray.push_back(
1941                                 {{"@odata.id", "/redfish/v1/Managers/" +
1942                                                    managerId +
1943                                                    "/EthernetInterfaces/" +
1944                                                    rootInterfaceName +
1945                                                    "/VLANs/" + ifaceItem}});
1946                         }
1947                     }
1948 
1949                     if (rootInterfaceFound)
1950                     {
1951                         Node::json["Members"] = ifaceArray;
1952                         Node::json["Members@odata.count"] = ifaceArray.size();
1953                         Node::json["@odata.id"] = "/redfish/v1/Managers/" +
1954                                                   managerId +
1955                                                   "/EthernetInterfaces/" +
1956                                                   rootInterfaceName + "/VLANs";
1957                         res.jsonValue = Node::json;
1958                     }
1959                     else
1960                     {
1961                         messages::addMessageToErrorJson(
1962                             res.jsonValue,
1963                             messages::resourceNotFound("EthernetInterface",
1964                                                        rootInterfaceName));
1965                         res.result(boost::beast::http::status::not_found);
1966                         res.end();
1967                     }
1968                 }
1969                 else
1970                 {
1971                     // No success, best what we can do is return INTERNALL ERROR
1972                     res.result(
1973                         boost::beast::http::status::internal_server_error);
1974                 }
1975                 res.end();
1976             });
1977     }
1978 
1979     void doPost(crow::Response &res, const crow::Request &req,
1980                 const std::vector<std::string> &params) override
1981     {
1982         if (params.size() != 1)
1983         {
1984             // This means there is a problem with the router
1985             res.result(boost::beast::http::status::internal_server_error);
1986             res.end();
1987             return;
1988         }
1989 
1990         nlohmann::json postReq;
1991 
1992         if (!json_util::processJsonFromRequest(res, req, postReq))
1993         {
1994             return;
1995         }
1996 
1997         // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1998         // for any Manager, not only hardcoded 'openbmc'.
1999         std::string managerId = "openbmc";
2000         std::string rootInterfaceName = params[0];
2001         uint64_t vlanId;
2002         bool errorDetected;
2003 
2004         if (json_util::getUnsigned(
2005                 "VLANId", postReq, vlanId,
2006                 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
2007                     static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
2008                 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS)
2009         {
2010             res.end();
2011             return;
2012         }
2013 
2014         // get eth interface list, and call the below callback for JSON
2015         // preparation
2016         ethernetProvider.getEthernetIfaceList(
2017             [&, managerId{std::move(managerId)},
2018              rootInterfaceName{std::move(rootInterfaceName)}](
2019                 const bool &success,
2020                 const std::vector<std::string> &iface_list) {
2021                 if (success)
2022                 {
2023                     bool rootInterfaceFound = false;
2024 
2025                     for (const std::string &ifaceItem : iface_list)
2026                     {
2027                         if (ifaceItem == rootInterfaceName)
2028                         {
2029                             rootInterfaceFound = true;
2030                             break;
2031                         }
2032                     }
2033 
2034                     if (rootInterfaceFound)
2035                     {
2036                         ethernetProvider.createVlan(
2037                             rootInterfaceName, vlanId,
2038                             [&, vlanId, rootInterfaceName, req{std::move(req)}](
2039                                 const boost::system::error_code ec) {
2040                                 if (ec)
2041                                 {
2042                                     messages::addMessageToErrorJson(
2043                                         res.jsonValue,
2044                                         messages::internalError());
2045                                     res.end();
2046                                 }
2047                                 else
2048                                 {
2049                                     memberVlan.doGet(
2050                                         res, req,
2051                                         {rootInterfaceName,
2052                                          rootInterfaceName + "_" +
2053                                              std::to_string(vlanId)});
2054                                 }
2055                             });
2056                     }
2057                     else
2058                     {
2059                         messages::addMessageToErrorJson(
2060                             res.jsonValue,
2061                             messages::resourceNotFound("EthernetInterface",
2062                                                        rootInterfaceName));
2063                         res.result(boost::beast::http::status::not_found);
2064                         res.end();
2065                     }
2066                 }
2067                 else
2068                 {
2069                     // No success, best what we can do is return INTERNALL ERROR
2070                     res.result(
2071                         boost::beast::http::status::internal_server_error);
2072                     res.end();
2073                 }
2074             });
2075     }
2076 
2077     // Ethernet Provider object
2078     // TODO(Pawel) consider move it to singleton
2079     OnDemandEthernetProvider ethernetProvider;
2080     VlanNetworkInterface memberVlan;
2081 };
2082 
2083 } // namespace redfish
2084