Redfish(Network): Implemented GET and PATCH support for  NTP servers

Testing:
GET
PATCH {"NTPServers": ["7.7.7.7"]}
PATCH {"NTPServers": ["7.7.7.7","4.4.4.4"]}
PATCH {"NTPEnabled": true, "NTPServers": ["1.1.1.1"]}
PATCH {"NTPEnabled": false}

Change-Id: I324e8779d84b368ada78541596946a23f42ef785
Signed-off-by: raviteja-b <raviteja28031990@gmail.com>
diff --git a/redfish-core/lib/network_protocol.hpp b/redfish-core/lib/network_protocol.hpp
index 083c848..69fabdb 100644
--- a/redfish-core/lib/network_protocol.hpp
+++ b/redfish-core/lib/network_protocol.hpp
@@ -18,8 +18,9 @@
 #include "error_messages.hpp"
 #include "node.hpp"
 
+#include <optional>
+#include <utils/json_utils.hpp>
 #include <variant>
-
 namespace redfish
 {
 
@@ -70,6 +71,61 @@
          {"phosphor-ipmi-net.socket", "/org/freedesktop/systemd1/unit/"
                                       "phosphor_2dipmi_2dnet_2esocket"}}};
 
+inline void extractNTPServersData(const GetManagedObjects& dbus_data,
+                                  std::vector<std::string>& ntpData)
+{
+    for (const auto& obj : dbus_data)
+    {
+        for (const auto& ifacePair : obj.second)
+        {
+            if (obj.first == "/xyz/openbmc_project/network/eth0")
+            {
+                if (ifacePair.first ==
+                    "xyz.openbmc_project.Network.EthernetInterface")
+                {
+                    for (const auto& propertyPair : ifacePair.second)
+                    {
+                        if (propertyPair.first == "NTPServers")
+                        {
+                            const std::vector<std::string>* ntpServers =
+                                sdbusplus::message::variant_ns::get_if<
+                                    std::vector<std::string>>(
+                                    &propertyPair.second);
+                            if (ntpServers != nullptr)
+                            {
+                                ntpData = std::move(*ntpServers);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+template <typename CallbackFunc>
+void getEthernetIfaceData(CallbackFunc&& callback)
+{
+    crow::connections::systemBus->async_method_call(
+        [callback{std::move(callback)}](
+            const boost::system::error_code error_code,
+            const GetManagedObjects& dbus_data) {
+            std::vector<std::string> ntpServers;
+
+            if (error_code)
+            {
+                callback(false, ntpServers);
+                return;
+            }
+
+            extractNTPServersData(dbus_data, ntpServers);
+
+            callback(true, ntpServers);
+        },
+        "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
+        "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
+};
+
 class NetworkProtocol : public Node
 {
   public:
@@ -106,6 +162,30 @@
         return hostName;
     }
 
+    void getNTPProtocolEnabled(const std::shared_ptr<AsyncResp>& asyncResp)
+    {
+        crow::connections::systemBus->async_method_call(
+            [asyncResp](const boost::system::error_code error_code,
+                        const std::variant<std::string>& timeSyncMethod) {
+                const std::string* s =
+                    std::get_if<std::string>(&timeSyncMethod);
+
+                if (*s == "xyz.openbmc_project.Time.Synchronization.Method.NTP")
+                {
+                    asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = true;
+                }
+                else if (*s == "xyz.openbmc_project.Time.Synchronization."
+                               "Method.Manual")
+                {
+                    asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = false;
+                }
+            },
+            "xyz.openbmc_project.Settings",
+            "/xyz/openbmc_project/time/sync_method",
+            "org.freedesktop.DBus.Properties", "Get",
+            "xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod");
+    }
+
     void getData(const std::shared_ptr<AsyncResp>& asyncResp)
     {
         asyncResp->res.jsonValue["@odata.type"] =
@@ -129,6 +209,22 @@
 
         asyncResp->res.jsonValue["HostName"] = getHostName();
 
+        getNTPProtocolEnabled(asyncResp);
+
+        // TODO Get eth0 interface data, and call the below callback for JSON
+        // preparation
+        getEthernetIfaceData(
+            [this, asyncResp](const bool& success,
+                              const std::vector<std::string>& ntpServers) {
+                if (!success)
+                {
+                    messages::resourceNotFound(asyncResp->res,
+                                               "EthernetInterface", "eth0");
+                    return;
+                }
+                asyncResp->res.jsonValue["NTP"]["NTPServers"] = ntpServers;
+            });
+
         crow::connections::systemBus->async_method_call(
             [asyncResp](const boost::system::error_code ec,
                         const std::vector<UnitStruct>& resp) {
@@ -238,13 +334,63 @@
             std::variant<std::string>(hostName));
     }
 
+    void handleNTPProtocolEnabled(const bool& ntpEnabled,
+                                  const std::shared_ptr<AsyncResp>& asyncResp)
+    {
+        std::string timeSyncMethod;
+        if (ntpEnabled)
+        {
+            timeSyncMethod =
+                "xyz.openbmc_project.Time.Synchronization.Method.NTP";
+        }
+        else
+        {
+            timeSyncMethod =
+                "xyz.openbmc_project.Time.Synchronization.Method.Manual";
+        }
+
+        crow::connections::systemBus->async_method_call(
+            [asyncResp,
+             ntpEnabled](const boost::system::error_code error_code) {
+                asyncResp->res.jsonValue["NTP"]["ProtocolEnabled"] = ntpEnabled;
+            },
+            "xyz.openbmc_project.Settings",
+            "/xyz/openbmc_project/time/sync_method",
+            "org.freedesktop.DBus.Properties", "Set",
+            "xyz.openbmc_project.Time.Synchronization", "TimeSyncMethod",
+            std::variant<std::string>{timeSyncMethod});
+    }
+
+    void handleNTPServersPatch(const std::vector<std::string>& ntpServers,
+                               const std::shared_ptr<AsyncResp>& asyncResp)
+    {
+        crow::connections::systemBus->async_method_call(
+            [asyncResp, ntpServers](const boost::system::error_code ec) {
+                if (ec)
+                {
+                    messages::internalError(asyncResp->res);
+                    return;
+                }
+                asyncResp->res.jsonValue["NTP"]["NTPServers"] =
+                    std::move(ntpServers);
+            },
+            "xyz.openbmc_project.Network", "/xyz/openbmc_project/network/eth0",
+            "org.freedesktop.DBus.Properties", "Set",
+            "xyz.openbmc_project.Network.EthernetInterface", "NTPServers",
+            std::variant<std::vector<std::string>>{ntpServers});
+    }
+
     void doPatch(crow::Response& res, const crow::Request& req,
                  const std::vector<std::string>& params) override
     {
         std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
         std::optional<std::string> newHostName;
+        std::optional<std::vector<std::string>> ntpServers;
+        std::optional<bool> ntpEnabled;
 
-        if (!json_util::readJson(req, res, "HostName", newHostName))
+        if (!json_util::readJson(req, res, "HostName", newHostName,
+                                 "NTPServers", ntpServers, "NTPEnabled",
+                                 ntpEnabled))
         {
             return;
         }
@@ -253,6 +399,14 @@
             handleHostnamePatch(*newHostName, asyncResp);
             return;
         }
+        if (ntpEnabled)
+        {
+            handleNTPProtocolEnabled(*ntpEnabled, asyncResp);
+        }
+        if (ntpServers)
+        {
+            handleNTPServersPatch(*ntpServers, asyncResp);
+        }
     }
 };