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