Redfish(Network): Fixes VLAN specific issues

This commit fixes the following issues. Before this fix the VLAN functionality was inconsiststant.
1)Add VLANs property to the EthernetInterface resource
2)Removed VLAN property under EthernetInterface resource
3)Made the property - VLANEnable mandatory for creating vlans (POST command)
4)Fetch the VLANId when GET on the VLAN Interface resource
5)Updated Ethernet schema to EthernetInterface.v1_4_1
6)Changed the propert name "DHCPv4Configuration" to "DHCPv4" as per the schema

Tested by:

GET https://${bmc}/redfish/v1/Managers/bmc/EthernetInterfaces/eth0
GET https://${bmc}/redfish/v1/Managers/bmc/EthernetInterfaces/eth0/VLANs
POST -D headers.txt https://${bmc}/redfish/v1/Managers/bmc/EthernetInterfaces/eth0/VLANs -d '{ "VLANId" : 30, "VLANEnable": true}'
GET https://${bmc}/redfish/v1/Managers/bmc/EthernetInterfaces/eth0/VLANs/eth0_30

Signed-off-by: Sunitha Harish <sunithaharish04@gmail.com>
Change-Id: I2339f6711cdd56fe42fee030701125492911dc26
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 476e03a..25dff50 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -82,7 +82,7 @@
     std::string hostname;
     std::string default_gateway;
     std::string mac_address;
-    std::optional<uint32_t> vlan_id;
+    std::vector<std::uint32_t> vlan_id;
     std::vector<std::string> nameservers;
 };
 
@@ -192,7 +192,7 @@
                                 std::get_if<uint32_t>(&propertyPair.second);
                             if (id != nullptr)
                             {
-                                ethData.vlan_id = *id;
+                                ethData.vlan_id.push_back(*id);
                             }
                         }
                     }
@@ -669,8 +669,7 @@
             messages::internalError(asyncResp->res);
             return;
         }
-        nlohmann::json &DHCPConfigTypeJson =
-            asyncResp->res.jsonValue["DHCPv4Configuration"];
+        nlohmann::json &DHCPConfigTypeJson = asyncResp->res.jsonValue["DHCPv4"];
         for (const auto &property : dbus_data)
         {
             auto value =
@@ -851,12 +850,17 @@
 
                 nlohmann::json &iface_array = res.jsonValue["Members"];
                 iface_array = nlohmann::json::array();
+                std::string tag = "_";
                 for (const std::string &iface_item : iface_list)
                 {
-                    iface_array.push_back(
-                        {{"@odata.id",
-                          "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
-                              iface_item}});
+                    std::size_t found = iface_item.find(tag);
+                    if (found == std::string::npos)
+                    {
+                        iface_array.push_back(
+                            {{"@odata.id",
+                              "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
+                                  iface_item}});
+                    }
                 }
 
                 res.jsonValue["Members@odata.count"] = iface_array.size();
@@ -896,15 +900,6 @@
                                 const EthernetInterfaceData &ethData,
                                 const std::shared_ptr<AsyncResp> asyncResp)
     {
-        if (!ethData.vlan_id)
-        {
-            // This interface is not a VLAN. Cannot do anything with it
-            // TODO(kkowalsk) Change this message
-            messages::propertyNotWritable(asyncResp->res, "VLANEnable");
-
-            return;
-        }
-
         // VLAN is configured on the interface
         if (vlanEnable == true)
         {
@@ -1238,25 +1233,17 @@
         }
         json_response["SpeedMbps"] = ethData.speed;
         json_response["MACAddress"] = ethData.mac_address;
-        json_response["DHCPv4Configuration"]["DHCPEnabled"] =
-            ethData.DHCPEnabled;
+        json_response["DHCPv4"]["DHCPEnabled"] = ethData.DHCPEnabled;
 
         if (!ethData.hostname.empty())
         {
             json_response["HostName"] = ethData.hostname;
         }
 
-        nlohmann::json &vlanObj = json_response["VLAN"];
-        if (ethData.vlan_id)
-        {
-            vlanObj["VLANEnable"] = true;
-            vlanObj["VLANId"] = *ethData.vlan_id;
-        }
-        else
-        {
-            vlanObj["VLANEnable"] = false;
-            vlanObj["VLANId"] = 0;
-        }
+        json_response["VLANs"] = {
+            {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
+                              iface_id + "/VLANs"}};
+
         json_response["NameServers"] = ethData.nameservers;
 
         if (ipv4Data.size() > 0)
@@ -1300,7 +1287,7 @@
                     return;
                 }
                 asyncResp->res.jsonValue["@odata.type"] =
-                    "#EthernetInterface.v1_2_0.EthernetInterface";
+                    "#EthernetInterface.v1_4_1.EthernetInterface";
                 asyncResp->res.jsonValue["@odata.context"] =
                     "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
                 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
@@ -1325,52 +1312,24 @@
 
         const std::string &iface_id = params[0];
 
-        std::optional<nlohmann::json> vlan;
         std::optional<std::string> hostname;
         std::optional<std::string> macAddress;
         std::optional<nlohmann::json> ipv4Addresses;
         std::optional<nlohmann::json> ipv6Addresses;
 
-        if (!json_util::readJson(req, res, "VLAN", vlan, "HostName", hostname,
-                                 "IPv4Addresses", ipv4Addresses,
-                                 "IPv6Addresses", ipv6Addresses, "MACAddress",
-                                 macAddress))
+        if (!json_util::readJson(
+                req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses,
+                "IPv6Addresses", ipv6Addresses, "MACAddress", macAddress))
         {
             return;
         }
 
-        std::optional<uint64_t> vlanId;
-        std::optional<bool> vlanEnable;
-
-        if (vlan)
-        {
-            if (!json_util::readJson(*vlan, res, "VLANEnable", vlanEnable,
-                                     "VLANId", vlanId))
-            {
-                return;
-            }
-            // Need both vlanId and vlanEnable to service this request
-            if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
-            {
-                if (vlanId)
-                {
-                    messages::propertyMissing(asyncResp->res, "VLANEnable");
-                }
-                else
-                {
-                    messages::propertyMissing(asyncResp->res, "VLANId");
-                }
-
-                return;
-            }
-        }
-
         // Get single eth interface data, and call the below callback for JSON
         // preparation
         getEthernetIfaceData(
             iface_id,
-            [this, asyncResp, iface_id, vlanId, vlanEnable,
-             hostname = std::move(hostname), macAddress = std::move(macAddress),
+            [this, asyncResp, iface_id, hostname = std::move(hostname),
+             macAddress = std::move(macAddress),
              ipv4Addresses = std::move(ipv4Addresses),
              ipv6Addresses = std::move(ipv6Addresses)](
                 const bool &success, const EthernetInterfaceData &ethData,
@@ -1380,20 +1339,14 @@
                     // ... otherwise return error
                     // TODO(Pawel)consider distinguish between non existing
                     // object, and other errors
-                    messages::resourceNotFound(
-                        asyncResp->res, "VLAN Network Interface", iface_id);
+                    messages::resourceNotFound(asyncResp->res,
+                                               "Ethernet Interface", iface_id);
                     return;
                 }
 
                 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
                                    ipv4Data);
 
-                if (vlanId && vlanEnable)
-                {
-                    handleVlanPatch(iface_id, *vlanId, *vlanEnable, ethData,
-                                    asyncResp);
-                }
-
                 if (hostname)
                 {
                     handleHostnamePatch(*hostname, asyncResp);
@@ -1464,20 +1417,16 @@
             "/VLANs/" + iface_id;
 
         json_response["VLANEnable"] = true;
-        if (ethData.vlan_id)
+        if (!ethData.vlan_id.empty())
         {
-            json_response["VLANId"] = *ethData.vlan_id;
+            json_response["VLANId"] = ethData.vlan_id.back();
         }
     }
 
-    bool verifyNames(crow::Response &res, const std::string &parent,
-                     const std::string &iface)
+    bool verifyNames(const std::string &parent, const std::string &iface)
     {
-        std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         if (!boost::starts_with(iface, parent + "_"))
         {
-            messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
-                                       iface);
             return false;
         }
         else
@@ -1512,7 +1461,7 @@
             "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
         res.jsonValue["Name"] = "VLAN Network Interface";
 
-        if (!verifyNames(res, parent_iface_id, iface_id))
+        if (!verifyNames(parent_iface_id, iface_id))
         {
             return;
         }
@@ -1520,11 +1469,12 @@
         // Get single eth interface data, and call the below callback for JSON
         // preparation
         getEthernetIfaceData(
-            iface_id,
-            [this, asyncResp, parent_iface_id, iface_id](
+            params[1],
+            [this, asyncResp, parent_iface_id{std::string(params[0])},
+             iface_id{std::string(params[1])}](
                 const bool &success, const EthernetInterfaceData &ethData,
                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
-                if (success && ethData.vlan_id)
+                if (success && ethData.vlan_id.size() != 0)
                 {
                     parseInterfaceData(asyncResp->res.jsonValue,
                                        parent_iface_id, iface_id, ethData,
@@ -1554,8 +1504,10 @@
         const std::string &parentIfaceId = params[0];
         const std::string &ifaceId = params[1];
 
-        if (!verifyNames(res, parentIfaceId, ifaceId))
+        if (!verifyNames(parentIfaceId, ifaceId))
         {
+            messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
+                                       ifaceId);
             return;
         }
 
@@ -1606,20 +1558,22 @@
         const std::string &parentIfaceId = params[0];
         const std::string &ifaceId = params[1];
 
-        if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
+        if (!verifyNames(parentIfaceId, ifaceId))
         {
+            messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
+                                       ifaceId);
             return;
         }
 
         // Get single eth interface data, and call the below callback for JSON
         // preparation
         getEthernetIfaceData(
-            ifaceId,
-            [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
-             ifaceId{std::string(ifaceId)}](
+            params[1],
+            [this, asyncResp, parentIfaceId{std::string(params[0])},
+             ifaceId{std::string(params[1])}](
                 const bool &success, const EthernetInterfaceData &ethData,
                 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
-                if (success && ethData.vlan_id)
+                if (success && !ethData.vlan_id.empty())
                 {
                     parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
                                        ifaceId, ethData, ipv4Data);
@@ -1720,12 +1674,6 @@
                     }
                 }
 
-                if (iface_array.empty())
-                {
-                    messages::resourceNotFound(
-                        asyncResp->res, "EthernetInterface", rootInterfaceName);
-                    return;
-                }
                 asyncResp->res.jsonValue["Members@odata.count"] =
                     iface_array.size();
                 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
@@ -1744,12 +1692,27 @@
             messages::internalError(asyncResp->res);
             return;
         }
-
+        bool vlanEnable = false;
         uint32_t vlanId = 0;
-        if (!json_util::readJson(req, res, "VLANId", vlanId))
+        if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable",
+                                 vlanEnable))
         {
             return;
         }
+        // Need both vlanId and vlanEnable to service this request
+        if (!vlanId)
+        {
+            messages::propertyMissing(asyncResp->res, "VLANId");
+        }
+        if (!vlanEnable)
+        {
+            messages::propertyMissing(asyncResp->res, "VLANEnable");
+        }
+        if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
+        {
+            return;
+        }
+
         const std::string &rootInterfaceName = params[0];
         auto callback = [asyncResp](const boost::system::error_code ec) {
             if (ec)