diff options
| author | Lei YU <mine260309@gmail.com> | 2017-01-20 14:02:03 +0800 |
|---|---|---|
| committer | Lei YU <mine260309@gmail.com> | 2017-08-01 10:31:07 +0800 |
| commit | 2f9c0cc25478709f25f449d46ab9836152cf1cd9 (patch) | |
| tree | 6af0f8cee0041350da38ca7b91a85e5b3a520577 | |
| parent | 4188567ae87609943489e559ab2067f1cbc76f40 (diff) | |
| download | phosphor-time-manager-2f9c0cc25478709f25f449d46ab9836152cf1cd9.tar.gz phosphor-time-manager-2f9c0cc25478709f25f449d46ab9836152cf1cd9.zip | |
Intialize new phosphor-time-manager
phosphor-time-manager will be refactored to use sdbusplus interfaces.
This is a initial commit that EpochBase is implemented based on dbus
interface xyz/openbmc_project/Time/EpochTime.interface.yaml.
EpochBase is the base class that wraps EpochTime interface, and is
initialized with time mode and owner from settingsd.
An initial unit test case is added.
Change-Id: Ic944b70f63ec3c0329762cc8874f0f57b09ddce3
Signed-off-by: Lei YU <mine260309@gmail.com>
| -rw-r--r-- | Makefile.am | 41 | ||||
| -rw-r--r-- | configure.ac | 52 | ||||
| -rw-r--r-- | epoch_base.cpp | 131 | ||||
| -rw-r--r-- | epoch_base.hpp | 146 | ||||
| -rw-r--r-- | main.cpp | 17 | ||||
| -rw-r--r-- | test/Makefile.am | 26 | ||||
| -rw-r--r-- | test/TestEpochBase.cpp | 64 |
7 files changed, 440 insertions, 37 deletions
diff --git a/Makefile.am b/Makefile.am index b25fa12..4b4cf02 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,17 +1,28 @@ AM_DEFAULT_SOURCE_EXT = .cpp -sbin_PROGRAMS = \ - timemanager - -timemanager_SOURCES = \ - time-register.c \ - time-config.cpp \ - time-manager.cpp \ - settings.cpp - -timemanager_LDFLAGS = \ - $(SYSTEMD_LIBS) \ - $(libmapper_LIBS) \ - $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ - $(SDBUSPLUS_LIBS) \ - $(PHOSPHOR_LOGGING_LIBS) +sbin_PROGRAMS = phosphor-timemanager + +noinst_LTLIBRARIES = libtimemanager.la + +libtimemanager_la_SOURCES = \ + epoch_base.cpp + +phosphor_timemanager_SOURCES = \ + main.cpp + +phosphor_timemanager_LDADD = libtimemanager.la + +generic_cxx_flags = $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \ + $(SDBUSPLUS_CFLAGS) + +generic_ld_flags = $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ + $(SDBUSPLUS_LIBS) + +libtimemanager_la_CXXFLAGS = $(generic_cxx_flags) +libtimemanager_la_LIBADD = $(generic_ld_flags) + +phosphor_timemanager_CXXFLAGS = $(generic_cxx_flags) + +phosphor_timemanager_LDFLAGS = $(generic_ld_flags) + +SUBDIRS = . test diff --git a/configure.ac b/configure.ac index fc0da82..03f8347 100644 --- a/configure.ac +++ b/configure.ac @@ -1,43 +1,34 @@ # Initialization AC_PREREQ([2.69]) AC_INIT([phosphor-time-manager], [1.0], [https://github.com/openbmc/phosphor-time-manager/issues]) +AC_LANG([C++]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([subdir-objects -Wall -Werror foreign dist-xz]) AM_SILENT_RULES([yes]) -# Checks for programs. +# Checks for programs AC_PROG_CXX -AX_CXX_COMPILE_STDCXX_14([noext]) -AC_PROG_CC AM_PROG_AR -AC_PROG_INSTALL -AC_PROG_MAKE_SET -# Checks for libraries. -AC_CHECK_LIB([mapper], [mapper_get_service], ,[AC_MSG_ERROR([Could not find libmapper...openbmc/phosphor-objmgr package required])]) -PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221]) +# Surpress the --with-libtool-sysroot error +LT_INIT + +# Check for libraries +PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221],,\ + AC_MSG_ERROR(["Systemd required and not found"])) PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],,\ AC_MSG_ERROR(["Requires phosphor-dbus-interfaces package."])) -PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],, +PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,\ AC_MSG_ERROR(["Requires sdbusplus package."])) PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],,\ AC_MSG_ERROR(["Requires phosphor-logging package."])) -# Checks for header files. -AC_CHECK_HEADER(systemd/sd-bus.h, ,[AC_MSG_ERROR([Could not find systemd/sd-bus.h...systemd developement package required])]) - -# Checks for typedefs, structures, and compiler characteristics. -AX_CXX_COMPILE_STDCXX_14([noext]) -AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CFLAGS]) -AX_APPEND_COMPILE_FLAGS([-Wall -Werror], [CXXFLAGS]) - -# Checks for library functions. -LT_INIT # Removes 'unrecognized options: --with-libtool-sysroot' - +# gtest # Check/set gtest specific functions. AX_PTHREAD([GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=1"],[GTEST_CPPFLAGS="-DGTEST_HAS_PTHREAD=0"]) AC_SUBST(GTEST_CPPFLAGS) +# Test cases require SDK so only build if we're told to (and SDK is available) AC_ARG_ENABLE([oe-sdk], AS_HELP_STRING([--enable-oe-sdk], [Link testcases absolutely against OE SDK so they can be ran within it.]) ) @@ -56,6 +47,23 @@ AS_IF([test "x$enable_oe_sdk" == "xyes"], AC_SUBST([OESDK_TESTCASE_FLAGS], [$testcase_flags]) ) -# Create configured output -AC_CONFIG_FILES([Makefile]) +# Checks for typedefs, structures, and compiler characteristics. +AX_CXX_COMPILE_STDCXX_14([noext]) +AX_APPEND_COMPILE_FLAGS([-fpic -Wall -Werror], [CXXFLAGS]) + +# DBUS interface +AC_ARG_VAR(BUSNAME, [The Time Manager Dbus busname to own]) +AS_IF([test "x$BUSNAME" == "x"], [BUSNAME="xyz.openbmc_project.Time.Manager"]) +AC_DEFINE_UNQUOTED([BUSNAME], ["$BUSNAME"], [The Time Manager DBus busname to own]) + +AC_ARG_VAR(OBJPATH_BMC, [The bmc epoch Dbus root]) +AS_IF([test "x$OBJPATH_BMC" == "x"], [OBJPATH_BMC="/xyz/openbmc_project/time/bmc"]) +AC_DEFINE_UNQUOTED([OBJPATH_BMC], ["$OBJPATH_BMC"], [The bmc epoch Dbus root]) + +AC_ARG_VAR(OBJPATH_HOST, [The host epoch Dbus root]) +AS_IF([test "x$OBJPATH_HOST" == "x"], [OBJPATH_HOST="/xyz/openbmc_project/time/host"]) +AC_DEFINE_UNQUOTED([OBJPATH_HOST], ["$OBJPATH_HOST"], [The host epoch Dbus root]) + + +AC_CONFIG_FILES([Makefile test/Makefile]) AC_OUTPUT diff --git a/epoch_base.cpp b/epoch_base.cpp new file mode 100644 index 0000000..06232bb --- /dev/null +++ b/epoch_base.cpp @@ -0,0 +1,131 @@ +#include "epoch_base.hpp" + +#include <phosphor-logging/log.hpp> + +#include <iomanip> +#include <sstream> + +namespace // anonymous +{ +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"; +} + +namespace phosphor +{ +namespace time +{ + +using namespace phosphor::logging; + +const std::map<std::string, EpochBase::Owner> +EpochBase::ownerMap = { + { "BMC", EpochBase::Owner::BMC }, + { "HOST", EpochBase::Owner::HOST }, + { "SPLIT", EpochBase::Owner::SPLIT }, + { "BOTH", EpochBase::Owner::BOTH }, +}; + +EpochBase::EpochBase(sdbusplus::bus::bus& bus, + const char* objPath) + : sdbusplus::server::object::object<EpochTime>(bus, objPath, true), + bus(bus) +{ + initialize(); + // Deferred this until we could get our property correct + emit_object_added(); +} + +void EpochBase::setCurrentTimeMode(const std::string& mode) +{ + log<level::INFO>("Time mode is changed", + entry("MODE=%s", mode.c_str())); + timeMode = convertToMode(mode); +} + +void EpochBase::setCurrentTimeOwner(const std::string& owner) +{ + log<level::INFO>("Time owner is changed", + entry("OWNER=%s", owner.c_str())); + timeOwner = convertToOwner(owner); +} + +void EpochBase::initialize() +{ + setCurrentTimeMode(getSettings("time_mode")); + setCurrentTimeOwner(getSettings("time_owner")); + // TODO: subscribe settingsd's property changes callback +} + +std::string EpochBase::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); + } + + return mode.get<std::string>(); +} + +EpochBase::Mode EpochBase::convertToMode(const std::string& mode) +{ + if (mode == "NTP") + { + return Mode::NTP; + } + else if (mode == "MANUAL") + { + return Mode::MANUAL; + } + else + { + log<level::ERR>("Unrecognized mode", + entry("%s", mode.c_str())); + return Mode::NTP; + } +} + +EpochBase::Owner EpochBase::convertToOwner(const std::string& owner) +{ + auto it = ownerMap.find(owner); + if (it == ownerMap.end()) + { + log<level::ERR>("Unrecognized owner", + entry("%s", owner.c_str())); + return Owner::BMC; + } + return it->second; +} + +using namespace std::chrono; +void EpochBase::setTime(const microseconds& usec) +{ + auto method = bus.new_method_call("org.freedesktop.timedate1", + "/org/freedesktop/timedate1", + "org.freedesktop.timedate1", + "SetTime"); + method.append(static_cast<int64_t>(usec.count()), + false, // relative + false); // user_interaction + bus.call_noreply(method); +} + +microseconds EpochBase::getTime() const +{ + auto now = system_clock::now(); + return duration_cast<microseconds> + (now.time_since_epoch()); +} + +} // namespace time +} // namespace phosphor diff --git a/epoch_base.hpp b/epoch_base.hpp new file mode 100644 index 0000000..fc505d0 --- /dev/null +++ b/epoch_base.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include <sdbusplus/bus.hpp> +#include <xyz/openbmc_project/Time/EpochTime/server.hpp> + +#include <chrono> + +namespace phosphor +{ +namespace time +{ + +/** @class EpochBase + * @brief Base class for OpenBMC EpochTime implementation. + * @details A base class that implements xyz.openbmc_project.Time.EpochTime + * DBus API for epoch time. + */ +class EpochBase : public sdbusplus::server::object::object < + sdbusplus::xyz::openbmc_project::Time::server::EpochTime > +{ + public: + friend class TestEpochBase; + + /** @brief Supported time modes + * NTP Time sourced by Network Time Server + * MANUAL User of the system need to set the time + */ + enum class Mode + { + NTP, + MANUAL, + }; + + /** @brief Supported time owners + * BMC Time source may be NTP or MANUAL but it has to be set natively + * on the BMC. Meaning, host can not set the time. What it also + * means is that when BMC gets IPMI_SET_SEL_TIME, then its ignored. + * similarly, when BMC gets IPMI_GET_SEL_TIME, then the BMC's time + * is returned. + * + * HOST Its only IPMI_SEL_SEL_TIME that will set the time on BMC. + * Meaning, IPMI_GET_SEL_TIME and request to get BMC time will + * result in same value. + * + * SPLIT Both BMC and HOST will maintain their individual clocks but then + * the time information is stored in BMC. BMC can have either NTP + * or MANUAL as it's source of time and will set the time directly + * on the BMC. When IPMI_SET_SEL_TIME is received, then the delta + * between that and BMC's time is calculated and is stored. + * When BMC reads the time, the current time is returned. + * When IPMI_GET_SEL_TIME is received, BMC's time is retrieved and + * then the delta offset is factored in prior to returning. + * + * BOTH: BMC's time is set with whoever that sets the time. Similarly, + * BMC's time is returned to whoever that asks the time. + */ + enum class Owner + { + BMC, + HOST, + SPLIT, + BOTH, + }; + + EpochBase(sdbusplus::bus::bus& bus, + const char* objPath); + + protected: + /** @brief Persistent sdbusplus DBus connection */ + sdbusplus::bus::bus& bus; + + /** @brief The current time mode */ + Mode timeMode; + + /** @brief The current time owner */ + Owner timeOwner; + + /** @brief Set current time to system + * + * This function set the time to system by invoking systemd + * org.freedesktop.timedate1's SetTime method. + * + * @param[in] timeOfDayUsec - Microseconds since UTC + */ + void setTime(const std::chrono::microseconds& timeOfDayUsec); + + /** @brief Get current time + * + * @return Microseconds since UTC + */ + std::chrono::microseconds getTime() const; + + /** @brief Convert a string to enum Mode + * + * Convert the time mode string to enum. + * Valid strings are "NTP", "MANUAL" + * If it's not a valid time mode string, return NTP. + * + * @param[in] mode - The string of time mode + * + * @return The Mode enum + */ + static Mode convertToMode(const std::string& mode); + + /** @brief Convert a string to enum Owner + * + * Convert the time owner string to enum. + * Valid strings are "BMC", "HOST", "SPLIT", "BOTH" + * If it's not a valid time owner string, return BMC. + * + * @param[in] owner - The string of time owner + * + * @return The Owner enum + */ + static Owner convertToOwner(const std::string& owner); + + private: + /** @brief Initialize the time mode and owner */ + void initialize(); + + /** @brief Set current time mode + * + * @param[in] mode - The string of time mode + */ + void setCurrentTimeMode(const std::string& mode); + + /** @brief Set current time owner + * + * @param[in] owner - The string of time owner + */ + void setCurrentTimeOwner(const std::string& owner); + + /** @brief Get setting value from settings manager + * + * @param[in] setting - The string of the setting to get + * + * @return The value of the setting + */ + std::string getSettings(const char* setting) const; + + /** @brief The map maps the string key to enum Owner */ + static const std::map<std::string, Owner> ownerMap; +}; + +} // namespace time +} // namespace phosphor diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..0cf2807 --- /dev/null +++ b/main.cpp @@ -0,0 +1,17 @@ +#include <sdbusplus/bus.hpp> + +#include "config.h" + +int main() +{ + auto bus = sdbusplus::bus::new_default(); + + bus.request_name(BUSNAME); + + while (true) + { + bus.process_discard(); + bus.wait(); + } + return 0; +} diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..66e1ae5 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,26 @@ +AM_CPPFLAGS = -I${top_srcdir} +check_PROGRAMS = + +TESTS = $(check_PROGRAMS) + +check_PROGRAMS += test + +test_SOURCES = \ + TestEpochBase.cpp + +test_LDADD = $(top_builddir)/libtimemanager.la + +test_CPPFLAGS = $(GTEST_CPPFLAGS) \ + $(AM_CPPFLAGS) + +test_CXXFLAGS = $(PTHREAD_CFLAGS) \ + $(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \ + $(SDBUSPLUS_CFLAGS) + +test_LDFLAGS = -lgmock_main \ + -lgmock \ + $(PTHREAD_LIBS) \ + $(OESDK_TESTCASE_FLAGS) + +test_LDFLAGS += $(PHOSPHOR_DBUS_INTERFACES_LIBS) \ + $(SDBUSPLUS_LIBS) diff --git a/test/TestEpochBase.cpp b/test/TestEpochBase.cpp new file mode 100644 index 0000000..c3ca7e1 --- /dev/null +++ b/test/TestEpochBase.cpp @@ -0,0 +1,64 @@ +#include <sdbusplus/bus.hpp> +#include <gtest/gtest.h> + +#include "epoch_base.hpp" + +namespace phosphor +{ +namespace time +{ + +class TestEpochBase : public testing::Test +{ + public: + using Mode = EpochBase::Mode; + using Owner = EpochBase::Owner; + + sdbusplus::bus::bus bus; + EpochBase epochBase; + + TestEpochBase() + : bus(sdbusplus::bus::new_default()), + epochBase(bus, "") + { + // Empty + } + + // Proxies for EpochBase's private members and functions + Mode convertToMode(const std::string& mode) + { + return EpochBase::convertToMode(mode); + } + Owner convertToOwner(const std::string& owner) + { + return EpochBase::convertToOwner(owner); + } +}; + +TEST_F(TestEpochBase, convertToMode) +{ + EXPECT_EQ(Mode::NTP, convertToMode("NTP")); + EXPECT_EQ(Mode::MANUAL, convertToMode("MANUAL")); + + // All unrecognized strings are mapped to Ntp + EXPECT_EQ(Mode::NTP, convertToMode("")); + EXPECT_EQ(Mode::NTP, convertToMode("Manual")); + EXPECT_EQ(Mode::NTP, convertToMode("whatever")); +} + + +TEST_F(TestEpochBase, convertToOwner) +{ + EXPECT_EQ(Owner::BMC, convertToOwner("BMC")); + EXPECT_EQ(Owner::HOST, convertToOwner("HOST")); + EXPECT_EQ(Owner::SPLIT, convertToOwner("SPLIT")); + EXPECT_EQ(Owner::BOTH, convertToOwner("BOTH")); + + // All unrecognized strings are mapped to Bmc + EXPECT_EQ(Owner::BMC, convertToOwner("")); + EXPECT_EQ(Owner::BMC, convertToOwner("Split")); + EXPECT_EQ(Owner::BMC, convertToOwner("xyz")); +} + +} +} |

