Invoke SetNTP and UpdateUseNtpField in time manager

1. When time mode is changed, invoke systemd timedate1's SetNTP
method to update NTP settings;
2. When settings use_dhcp_ntp property is changed, invoke
NetworkManager's UpdateUseNtpField method to update its setting;
3. Move the common code of getProperty() into utils.hpp

Change-Id: I981e0e9de9c0430282b3364c38c282312bb2be89
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/manager.cpp b/manager.cpp
index 9810e14..56e0d0b 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -10,8 +10,6 @@
 constexpr auto SETTINGS_SERVICE = "org.openbmc.settings.Host";
 constexpr auto SETTINGS_PATH = "/org/openbmc/settings/host0";
 constexpr auto SETTINGS_INTERFACE = "org.openbmc.settings.Host";
-constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
-constexpr auto METHOD_GET = "Get";
 
 // TODO: Use new settings in xyz.openbmc_project
 const auto MATCH_PROPERTY_CHANGE =
@@ -26,11 +24,20 @@
     rules::path("/org/openbmc/control/power0") +
     rules::interface("org.freedesktop.DBus.Properties");
 
-// TODO: consider put the get properties related functions into a common place
 constexpr auto POWER_SERVICE = "org.openbmc.control.Power";
 constexpr auto POWER_PATH = "/org/openbmc/control/power0";
 constexpr auto POWER_INTERFACE = POWER_SERVICE;
 constexpr auto PGOOD_STR = "pgood";
+
+constexpr auto SYSTEMD_TIME_SERVICE = "org.freedesktop.timedate1";
+constexpr auto SYSTEMD_TIME_PATH = "/org/freedesktop/timedate1";
+constexpr auto SYSTEMD_TIME_INTERFACE = SYSTEMD_TIME_SERVICE;
+constexpr auto METHOD_SET_NTP = "SetNTP";
+
+constexpr auto OBMC_NETWORK_SERVICE = "org.openbmc.NetworkManager";
+constexpr auto OBMC_NETWORK_PATH = "/org/openbmc/NetworkManager/Interface";
+constexpr auto OBMC_NETWORK_INTERFACE = OBMC_NETWORK_SERVICE;
+constexpr auto METHOD_UPDATE_USE_NTP = "UpdateUseNtpField";
 }
 
 namespace phosphor
@@ -64,6 +71,8 @@
     // Check the settings daemon to process the new settings
     onPropertyChanged(PROPERTY_TIME_MODE, getSettings(PROPERTY_TIME_MODE));
     onPropertyChanged(PROPERTY_TIME_OWNER, getSettings(PROPERTY_TIME_OWNER));
+
+    checkDhcpNtp();
 }
 
 void Manager::addListener(PropertyChangeListner* listener)
@@ -91,26 +100,28 @@
 
 void Manager::checkHostOn()
 {
-    sdbusplus::message::variant<int> pgood = 0;
-    auto method = bus.new_method_call(POWER_SERVICE,
-                                      POWER_PATH,
-                                      PROPERTY_INTERFACE,
-                                      METHOD_GET);
-    method.append(PROPERTY_INTERFACE, PGOOD_STR);
-    auto reply = bus.call(method);
-    if (reply)
-    {
-        reply.read(pgood);
-    }
+    int pgood = utils::getProperty<int>(bus,
+                                        POWER_SERVICE,
+                                        POWER_PATH,
+                                        POWER_INTERFACE,
+                                        PGOOD_STR);
+    hostOn = static_cast<bool>(pgood);
+}
 
-    hostOn = static_cast<bool>(pgood.get<int>());
+void Manager::checkDhcpNtp()
+{
+    std::string useDhcpNtp = utils::getProperty<std::string>(
+                                 bus,
+                                 SETTINGS_SERVICE,
+                                 SETTINGS_PATH,
+                                 SETTINGS_INTERFACE,
+                                 PROPERTY_DHCP_NTP);
+    updateDhcpNtpSetting(useDhcpNtp);
 }
 
 void Manager::onPropertyChanged(const std::string& key,
                                 const std::string& value)
 {
-    // TODO: Check dhcp_ntp
-
     if (hostOn)
     {
         // If host is on, set the values as requested time mode/owner.
@@ -127,6 +138,8 @@
             {
                 listener->onModeChanged(timeMode);
             }
+            // When time_mode is updated, update the NTP setting
+            updateNtpSetting(value);
         }
         else if (key == PROPERTY_TIME_OWNER)
         {
@@ -150,13 +163,20 @@
     std::string ignore;
     properties props;
     m.read(ignore, props);
+    auto manager = static_cast<Manager*>(userData);
     for (const auto& item : props)
     {
         if (managedProperties.find(item.first) != managedProperties.end())
         {
-            static_cast<Manager*>(userData)->onPropertyChanged(
+            // For managed properties, notify listeners
+            manager->onPropertyChanged(
                 item.first, item.second.get<std::string>());
         }
+        else if (item.first == PROPERTY_DHCP_NTP)
+        {
+            // For other manager interested properties, handle specifically
+            manager->updateDhcpNtpSetting(item.second.get<std::string>());
+        }
     }
     return 0;
 }
@@ -190,6 +210,46 @@
     requestedOwner = owner;
 }
 
+void Manager::updateNtpSetting(const std::string& value)
+{
+    bool isNtp = (value == "NTP");
+    auto method = bus.new_method_call(SYSTEMD_TIME_SERVICE,
+                                      SYSTEMD_TIME_PATH,
+                                      SYSTEMD_TIME_INTERFACE,
+                                      METHOD_SET_NTP);
+    method.append(isNtp, false); // isNtp: 'true/false' means Enable/Disable
+                                 // 'false' meaning no policy-kit
+
+    if (bus.call(method))
+    {
+        log<level::INFO>("Updated NTP setting",
+                         entry("ENABLED:%d", isNtp));
+    }
+    else
+    {
+        log<level::ERR>("Failed to update NTP setting");
+    }
+}
+
+void Manager::updateDhcpNtpSetting(const std::string& useDhcpNtp)
+{
+    auto method = bus.new_method_call(OBMC_NETWORK_SERVICE,
+                                      OBMC_NETWORK_PATH,
+                                      OBMC_NETWORK_INTERFACE,
+                                      METHOD_UPDATE_USE_NTP);
+    method.append(useDhcpNtp);
+
+    if (bus.call(method))
+    {
+        log<level::INFO>("Updated use ntp field",
+                         entry("USENTPFIELD:%s", useDhcpNtp.c_str()));
+    }
+    else
+    {
+        log<level::ERR>("Failed to update UseNtpField");
+    }
+}
+
 void Manager::onPgoodChanged(bool pgood)
 {
     hostOn = pgood;
@@ -204,6 +264,7 @@
         {
             listener->onModeChanged(timeMode);
         }
+        updateNtpSetting(requestedMode);
         setRequestedMode({}); // Clear requested mode
     }
     if (!requestedOwner.empty())
@@ -257,23 +318,12 @@
 
 std::string Manager::getSettings(const char* value) const
 {
-    sdbusplus::message::variant<std::string> mode;
-    auto method = bus.new_method_call(SETTINGS_SERVICE,
-                                      SETTINGS_PATH,
-                                      PROPERTY_INTERFACE,
-                                      METHOD_GET);
-    method.append(SETTINGS_INTERFACE, value);
-    auto reply = bus.call(method);
-    if (reply)
-    {
-        reply.read(mode);
-    }
-    else
-    {
-        log<level::ERR>("Failed to get settings");
-    }
-
-    return mode.get<std::string>();
+    return utils::getProperty<std::string>(
+        bus,
+        SETTINGS_SERVICE,
+        SETTINGS_PATH,
+        SETTINGS_INTERFACE,
+        value);
 }
 
 Mode Manager::convertToMode(const std::string& mode)
diff --git a/manager.hpp b/manager.hpp
index 7edb30f..7c00122 100644
--- a/manager.hpp
+++ b/manager.hpp
@@ -70,6 +70,9 @@
         /** @brief Check if host is on and update hostOn variable */
         void checkHostOn();
 
+        /** @brief Check if use_dhcp_ntp is used and update NTP setting */
+        void checkDhcpNtp();
+
         /** @brief Get setting from settingsd service
          *
          * @param[in] setting - The string of the setting
@@ -126,6 +129,18 @@
          */
         void setRequestedOwner(const std::string& owner);
 
+        /** @brief Update the NTP setting to systemd time service
+         *
+         * @param[in] value - The time mode value, e.g. "NTP" or "MANUAL"
+         */
+        void updateNtpSetting(const std::string& value);
+
+        /** @brief Update dhcp_ntp setting to OpenBMC network service
+         *
+         * @param[in] value - The use_dhcp_ntp value, e.g. "yes" or "no"
+         */
+        void updateDhcpNtpSetting(const std::string& useDhcpNtp);
+
         /** @brief The static function called on settings property changed
          *
          * @param[in] msg - Data associated with subscribed signal
@@ -176,6 +191,9 @@
         /** @brief The string of time owner property */
         static constexpr auto PROPERTY_TIME_OWNER = "time_owner";
 
+        /** @brief The string of use dhcp ntp property */
+        static constexpr auto PROPERTY_DHCP_NTP = "use_dhcp_ntp";
+
         using Updater = std::function<void(const std::string&)>;
 
         /** @brief Map the property string to functions that shall
diff --git a/test/TestManager.cpp b/test/TestManager.cpp
index 935590e..1e4096e 100644
--- a/test/TestManager.cpp
+++ b/test/TestManager.cpp
@@ -135,5 +135,8 @@
     ASSERT_DEATH(notifyPropertyChanged("invalid property", "whatever"), "");
 }
 
+// TODO: if gmock is ready, add case to test
+// updateNtpSetting() and updateNetworkSetting()
+
 }
 }
diff --git a/utils.hpp b/utils.hpp
index 0589613..97b26e7 100644
--- a/utils.hpp
+++ b/utils.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include <phosphor-logging/log.hpp>
+
 #include <fstream>
 
 namespace phosphor
@@ -9,6 +11,8 @@
 namespace utils
 {
 
+using namespace phosphor::logging;
+
 /** @brief Read data with type T from file
  *
  * @param[in] fileName - The name of file to read from
@@ -42,6 +46,46 @@
     }
 }
 
+/** @brief The template function to get property from the requested dbus path
+ *
+ * @param[in] bus          - The Dbus bus object
+ * @param[in] service      - The Dbus service name
+ * @param[in] path         - The Dbus object path
+ * @param[in] interface    - The Dbus interface
+ * @param[in] propertyName - The property name to get
+ *
+ * @return The value of the property
+ */
+template <typename T>
+T getProperty(sdbusplus::bus::bus& bus,
+              const char* service,
+              const char* path,
+              const char* interface,
+              const char* propertyName)
+{
+    sdbusplus::message::variant<T> value{};
+    auto method = bus.new_method_call(service,
+                                      path,
+                                      "org.freedesktop.DBus.Properties",
+                                      "Get");
+    method.append(interface, propertyName);
+    auto reply = bus.call(method);
+    if (reply)
+    {
+        reply.read(value);
+    }
+    else
+    {
+        // TODO: use elog to throw exception
+        log<level::ERR>("Failed to get property",
+                        entry("SERVICE=%s", service),
+                        entry("PATH=%s", path),
+                        entry("INTERFACE=%s", interface),
+                        entry("PROPERTY=%s", propertyName));
+    }
+    return value.template get<T>();
+}
+
 }
 }
 }