Update led-manager to use JSON
Current LED manager uses compile time generated led-gen.hpp and creates
DBus objects for the groups.
We would need this changed to use the generated JSON.
Tested: JSON used at runtime when --enable-json given at configure time.
led-gen.hpp not created when using JSON and led-gen.hpp created by
default when using YAML at build time.
Signed-off-by: George Liu <liuxiwei@inspur.com>
Change-Id: I781f8cb090ece8b87730e6c97795624282857c64
diff --git a/Makefile.am b/Makefile.am
index ac8f6c7..2660b32 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -6,11 +6,13 @@
manager.cpp \
group.cpp
+if !LED_USE_JSON
BUILT_SOURCES = led-gen.hpp
CLEANFILES = led-gen.hpp
led-gen.hpp: ${srcdir}/parse_led.py
$(AM_V)@LEDGEN@ > $@
+endif
phosphor_ledmanager_LDFLAGS = $(SDBUSPLUS_LIBS) \
$(PHOSPHOR_LOGGING_LIBS) \
diff --git a/configure.ac b/configure.ac
index 9529fe9..988363e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -65,6 +65,17 @@
LEDGEN="$PYTHON $srcdir/parse_led.py -i $YAML_PATH"
AC_SUBST(LEDGEN)
+# JSON configuration file
+AC_ARG_VAR(LED_JSON_FILE, [The LED configuration JSON file])
+AS_IF([test "x$LED_JSON_FILE" == "x"], [LED_JSON_FILE="/usr/share/phosphor-led-manager/led-group-config.json"])
+AC_DEFINE_UNQUOTED([LED_JSON_FILE], ["$LED_JSON_FILE"], [The LED configuration JSON file])
+
+# enable JSON configuration
+AC_ARG_ENABLE([use_json],
+ AS_HELP_STRING([--enable-use_json], [Enable JSON configuration.]))
+AC_DEFINE([LED_USE_JSON],[],[Enable JSON configuration.])
+AM_CONDITIONAL([LED_USE_JSON], [test "x$enable-use_json" == "xyes"])
+
AC_DEFINE(CALLOUT_FWD_ASSOCIATION, "callout", [The name of the callout's forward association.])
AC_DEFINE(CALLOUT_REV_ASSOCIATION, "fault", [The name of the callout's reverse association.])
AC_DEFINE(ELOG_ENTRY, "entry", [Path element indicates an error log entry under logging namespace.])
diff --git a/json-config.hpp b/json-config.hpp
new file mode 100644
index 0000000..3d1caec
--- /dev/null
+++ b/json-config.hpp
@@ -0,0 +1,104 @@
+#include "config.h"
+
+#include "ledlayout.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+
+namespace fs = std::filesystem;
+
+using Json = nlohmann::json;
+using LedAction = std::set<phosphor::led::Layout::LedAction>;
+using LedMap = std::map<std::string, LedAction>;
+
+/** @brief Parse LED JSON file and output Json object
+ *
+ * @param[in] path - path of LED JSON file
+ *
+ * @return const Json - Json object
+ */
+const Json readJson(const fs::path& path)
+{
+ using namespace phosphor::logging;
+
+ if (!fs::exists(path) || fs::is_empty(path))
+ {
+ log<level::ERR>("Incorrect File Path or empty file",
+ entry("FILE_PATH=%s", path.c_str()));
+ throw std::runtime_error("Incorrect File Path or empty file");
+ }
+
+ try
+ {
+ std::ifstream jsonFile(path);
+ return Json::parse(jsonFile);
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>("Failed to parse config file",
+ entry("ERROR=%s", e.what()),
+ entry("FILE_PATH=%s", path.c_str()));
+ throw std::runtime_error("Failed to parse config file");
+ }
+}
+
+/** @brief Returns action enum based on string
+ *
+ * @param[in] action - action string
+ *
+ * @return Action - action enum (On/Blink)
+ */
+phosphor::led::Layout::Action getAction(const std::string& action)
+{
+ assert(action == "On" || action == "Blink");
+
+ return action == "Blink" ? phosphor::led::Layout::Blink
+ : phosphor::led::Layout::On;
+}
+
+/** @brief Load JSON config and return led map
+ *
+ * @return LedMap - Generated an std::map of LedAction
+ */
+const LedMap loadJsonConfig(const fs::path& path)
+{
+ LedMap ledMap{};
+
+ // define the default JSON as empty
+ const Json empty{};
+ auto json = readJson(path);
+
+ auto leds = json.value("leds", empty);
+ for (const auto& entry : leds)
+ {
+ fs::path tmpPath(std::string{OBJPATH});
+ tmpPath /= entry.value("group", "");
+ auto objpath = tmpPath.string();
+ auto members = entry.value("members", empty);
+
+ LedAction ledActions{};
+ for (const auto& member : members)
+ {
+ auto name = member.value("name", "");
+ auto action = getAction(member.value("Action", ""));
+ uint8_t dutyOn = member.value("DutyOn", 50);
+ uint16_t period = member.value("Period", 0);
+
+ // Since only have Blink/On and default priority is Blink
+ auto priority = getAction(member.value("Priority", "Blink"));
+ phosphor::led::Layout::LedAction ledAction{name, action, dutyOn,
+ period, priority};
+ ledActions.emplace(ledAction);
+ }
+
+ // Generated an std::map of LedGroupNames to std::set of LEDs
+ // containing the name and properties.
+ ledMap.emplace(objpath, ledActions);
+ }
+
+ return ledMap;
+}
diff --git a/led-main.cpp b/led-main.cpp
index cc0e6cf..53e3a71 100644
--- a/led-main.cpp
+++ b/led-main.cpp
@@ -1,7 +1,11 @@
#include "config.h"
#include "group.hpp"
+#ifdef LED_USE_JSON
+#include "json-config.hpp"
+#else
#include "led-gen.hpp"
+#endif
#include "ledlayout.hpp"
#include "manager.hpp"
@@ -12,6 +16,10 @@
/** @brief Dbus constructs used by LED Group manager */
sdbusplus::bus::bus bus = sdbusplus::bus::new_default();
+#ifdef LED_USE_JSON
+ auto systemLedMap = loadJsonConfig(LED_JSON_FILE);
+#endif
+
/** @brief Group manager object */
phosphor::led::Manager manager(bus, systemLedMap);
diff --git a/test/Makefile.am b/test/Makefile.am
index 0bf5215..d4cd3bd 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -8,5 +8,5 @@
utest_CPPFLAGS = -Igtest $(GTEST_CPPFLAGS) $(AM_CPPFLAGS) $(PHOSPHOR_LOGGING_CFLAGS)
utest_CXXFLAGS = $(PTHREAD_CFLAGS)
utest_LDFLAGS = -lgtest_main -lgtest $(PTHREAD_LIBS) $(OESDK_TESTCASE_FLAGS) $(SYSTEMD_LIBS) $(PHOSPHOR_LOGGING_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS)
-utest_SOURCES = utest.cpp
+utest_SOURCES = utest.cpp utest-led-json.cpp
utest_LDADD = $(top_builddir)/manager.o
diff --git a/test/config/led-group-config-malformed.json b/test/config/led-group-config-malformed.json
new file mode 100644
index 0000000..4281cdf
--- /dev/null
+++ b/test/config/led-group-config-malformed.json
@@ -0,0 +1,40 @@
+{
+ "leds": [
+ {
+ "group": "bmc_booted"
+ "members": [
+ {
+ "name": "heartbeat",
+ "Action": "On"
+ }
+ ]
+ },
+ {
+ "group": "power_on",
+ "members": [
+ {
+ "name": "power",
+ "Action": "On",
+ "Priority": "On"
+ }
+ ]
+ },
+ {
+ "group": "enclosure_identify",
+ "members": [
+ {
+ "name": "front_id",
+ "Action": "Blink",
+ "DutyOn": 50,
+ "Period": 1000
+ },
+ {
+ "name": "rear_id",
+ "Action": "Blink",
+ "DutyOn": 50,
+ "Period": 1000
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/config/led-group-config.json b/test/config/led-group-config.json
new file mode 100644
index 0000000..aff1fd4
--- /dev/null
+++ b/test/config/led-group-config.json
@@ -0,0 +1,40 @@
+{
+ "leds": [
+ {
+ "group": "bmc_booted",
+ "members": [
+ {
+ "name": "heartbeat",
+ "Action": "On"
+ }
+ ]
+ },
+ {
+ "group": "power_on",
+ "members": [
+ {
+ "name": "power",
+ "Action": "On",
+ "Priority": "On"
+ }
+ ]
+ },
+ {
+ "group": "enclosure_identify",
+ "members": [
+ {
+ "name": "front_id",
+ "Action": "Blink",
+ "DutyOn": 50,
+ "Period": 1000
+ },
+ {
+ "name": "rear_id",
+ "Action": "Blink",
+ "DutyOn": 50,
+ "Period": 1000
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/test/utest-led-json.cpp b/test/utest-led-json.cpp
new file mode 100644
index 0000000..32d89a8
--- /dev/null
+++ b/test/utest-led-json.cpp
@@ -0,0 +1,68 @@
+#include "json-config.hpp"
+
+#include <gtest/gtest.h>
+
+TEST(loadJsonConfig, testGoodPath)
+{
+ static constexpr auto jsonPath = "config/led-group-config.json";
+ LedMap ledMap = loadJsonConfig(jsonPath);
+
+ std::string objPath = "/xyz/openbmc_project/led/groups";
+ std::string bmcBooted = objPath + "/bmc_booted";
+ std::string powerOn = objPath + "/power_on";
+ std::string enclosureIdentify = objPath + "/enclosure_identify";
+
+ ASSERT_NE(ledMap.find(bmcBooted), ledMap.end());
+ ASSERT_NE(ledMap.find(powerOn), ledMap.end());
+ ASSERT_NE(ledMap.find(enclosureIdentify), ledMap.end());
+
+ LedAction bmcBootedActions = ledMap.at(bmcBooted);
+ LedAction powerOnActions = ledMap.at(powerOn);
+ LedAction enclosureIdentifyActions = ledMap.at(enclosureIdentify);
+
+ for (const auto& group : bmcBootedActions)
+ {
+ ASSERT_EQ(group.name, "heartbeat");
+ ASSERT_EQ(group.action, phosphor::led::Layout::On);
+ ASSERT_EQ(group.dutyOn, 50);
+ ASSERT_EQ(group.period, 0);
+ ASSERT_EQ(group.priority, phosphor::led::Layout::Blink);
+ }
+
+ for (const auto& group : powerOnActions)
+ {
+ ASSERT_EQ(group.name, "power");
+ ASSERT_EQ(group.action, phosphor::led::Layout::On);
+ ASSERT_EQ(group.dutyOn, 50);
+ ASSERT_EQ(group.period, 0);
+ ASSERT_EQ(group.priority, phosphor::led::Layout::On);
+ }
+
+ for (const auto& group : enclosureIdentifyActions)
+ {
+ if (group.name == "front_id")
+ {
+ ASSERT_EQ(group.action, phosphor::led::Layout::Blink);
+ ASSERT_EQ(group.dutyOn, 50);
+ ASSERT_EQ(group.period, 1000);
+ ASSERT_EQ(group.priority, phosphor::led::Layout::Blink);
+ }
+ else if (group.name == "rear_id")
+ {
+ ASSERT_EQ(group.action, phosphor::led::Layout::Blink);
+ ASSERT_EQ(group.dutyOn, 50);
+ ASSERT_EQ(group.period, 1000);
+ ASSERT_EQ(group.priority, phosphor::led::Layout::Blink);
+ }
+ else
+ {
+ ASSERT_TRUE(false);
+ }
+ }
+}
+
+TEST(loadJsonConfig, testBadPath)
+{
+ static constexpr auto jsonPath = "config/led-group-config-malformed.json";
+ ASSERT_THROW(loadJsonConfig(jsonPath), std::exception);
+}
\ No newline at end of file