presence: Use shared JSON config file finding & loading header
Use the shared JSON config determination header for loading its
JSON configuration file.
Tested:
run phosphor-fan-presence-tach can find and load config.json from
/usr/share/phosphor-fan-presence/presence and
/etc/phosphor-fan-presence/presence/ in witherspoon qemu
Change-Id: I5c4fdf7d96de7c956ca24b4a5751911cc1bf1f03
Signed-off-by: Jolie Ku <jolie_ku@wistron.com>
diff --git a/presence/json_parser.cpp b/presence/json_parser.cpp
new file mode 100644
index 0000000..83267a7
--- /dev/null
+++ b/presence/json_parser.cpp
@@ -0,0 +1,281 @@
+/**
+ * Copyright © 2019 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "json_parser.hpp"
+
+#include "anyof.hpp"
+#include "fallback.hpp"
+#include "gpio.hpp"
+#include "json_config.hpp"
+#include "sdbusplus.hpp"
+#include "tach.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <string>
+
+namespace phosphor
+{
+namespace fan
+{
+namespace presence
+{
+
+using json = nlohmann::json;
+namespace fs = std::filesystem;
+using namespace phosphor::logging;
+
+policies JsonConfig::_policies;
+const std::map<std::string, methodHandler> JsonConfig::_methods = {
+ {"tach", method::getTach}, {"gpio", method::getGpio}};
+const std::map<std::string, rpolicyHandler> JsonConfig::_rpolicies = {
+ {"anyof", rpolicy::getAnyof}, {"fallback", rpolicy::getFallback}};
+
+JsonConfig::JsonConfig(sdbusplus::bus::bus& bus) : _bus(bus)
+{
+ using config = fan::JsonConfig;
+
+ // Load and process the json configuration
+ process(config::load(config::getConfFile(bus, confAppName, confFileName)));
+}
+
+const policies& JsonConfig::get()
+{
+ return _policies;
+}
+
+void JsonConfig::sighupHandler(sdeventplus::source::Signal& sigSrc,
+ const struct signalfd_siginfo* sigInfo)
+{
+ try
+ {
+ using config = fan::JsonConfig;
+
+ // Load and process the json configuration
+ process(
+ config::load(config::getConfFile(_bus, confAppName, confFileName)));
+
+ for (auto& p : _policies)
+ {
+ p->monitor();
+ }
+ log<level::INFO>("Configuration loaded successfully");
+ }
+ catch (std::runtime_error& re)
+ {
+ log<level::ERR>("Error loading config, no config changes made",
+ entry("LOAD_ERROR=%s", re.what()));
+ }
+}
+
+void JsonConfig::process(const json& jsonConf)
+{
+ policies policies;
+ std::vector<fanPolicy> fans;
+ // Set the expected number of fan entries
+ // to be size of the list of fan json config entries
+ // (Must be done to eliminate vector reallocation of fan references)
+ fans.reserve(jsonConf.size());
+ for (auto& member : jsonConf)
+ {
+ if (!member.contains("name") || !member.contains("path") ||
+ !member.contains("methods") || !member.contains("rpolicy"))
+ {
+ log<level::ERR>("Missing required fan presence properties",
+ entry("REQUIRED_PROPERTIES=%s",
+ "{name, path, methods, rpolicy}"));
+ throw std::runtime_error(
+ "Missing required fan presence properties");
+ }
+
+ // Loop thru the configured methods of presence detection
+ std::vector<std::unique_ptr<PresenceSensor>> sensors;
+ for (auto& method : member["methods"].items())
+ {
+ if (!method.value().contains("type"))
+ {
+ log<level::ERR>(
+ "Missing required fan presence method type",
+ entry("FAN_NAME=%s",
+ member["name"].get<std::string>().c_str()));
+ throw std::runtime_error(
+ "Missing required fan presence method type");
+ }
+ // The method type of fan presence detection
+ // (Must have a supported function within the method namespace)
+ auto type = method.value()["type"].get<std::string>();
+ std::transform(type.begin(), type.end(), type.begin(), tolower);
+ auto func = _methods.find(type);
+ if (func != _methods.end())
+ {
+ // Call function for method type
+ auto sensor = func->second(fans.size(), method.value());
+ if (sensor)
+ {
+ sensors.emplace_back(std::move(sensor));
+ }
+ }
+ else
+ {
+ log<level::ERR>(
+ "Invalid fan presence method type",
+ entry("FAN_NAME=%s",
+ member["name"].get<std::string>().c_str()),
+ entry("METHOD_TYPE=%s", type.c_str()));
+ throw std::runtime_error("Invalid fan presence method type");
+ }
+ }
+ auto fan = std::make_tuple(member["name"], member["path"]);
+ // Create a fan object
+ fans.emplace_back(std::make_tuple(fan, std::move(sensors)));
+
+ // Add fan presence policy
+ auto policy = getPolicy(member["rpolicy"], fans.back());
+ if (policy)
+ {
+ policies.emplace_back(std::move(policy));
+ }
+ }
+
+ // Success, refresh fans and policies lists
+ _fans.clear();
+ _fans.swap(fans);
+
+ _policies.clear();
+ _policies.swap(policies);
+}
+
+std::unique_ptr<RedundancyPolicy>
+ JsonConfig::getPolicy(const json& rpolicy, const fanPolicy& fpolicy)
+{
+ if (!rpolicy.contains("type"))
+ {
+ log<level::ERR>(
+ "Missing required fan presence policy type",
+ entry("FAN_NAME=%s",
+ std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
+ entry("REQUIRED_PROPERTIES=%s", "{type}"));
+ throw std::runtime_error("Missing required fan presence policy type");
+ }
+
+ // The redundancy policy type for fan presence detection
+ // (Must have a supported function within the rpolicy namespace)
+ auto type = rpolicy["type"].get<std::string>();
+ std::transform(type.begin(), type.end(), type.begin(), tolower);
+ auto func = _rpolicies.find(type);
+ if (func != _rpolicies.end())
+ {
+ // Call function for redundancy policy type and return the policy
+ return func->second(fpolicy);
+ }
+ else
+ {
+ log<level::ERR>(
+ "Invalid fan presence policy type",
+ entry("FAN_NAME=%s",
+ std::get<fanPolicyFanPos>(std::get<Fan>(fpolicy)).c_str()),
+ entry("RPOLICY_TYPE=%s", type.c_str()));
+ throw std::runtime_error("Invalid fan presence methods policy type");
+ }
+}
+
+/**
+ * Methods of fan presence detection function definitions
+ */
+namespace method
+{
+// Get a constructed presence sensor for fan presence detection by tach
+std::unique_ptr<PresenceSensor> getTach(size_t fanIndex, const json& method)
+{
+ if (!method.contains("sensors") || method["sensors"].size() == 0)
+ {
+ log<level::ERR>("Missing required tach method properties",
+ entry("FAN_ENTRY=%d", fanIndex),
+ entry("REQUIRED_PROPERTIES=%s", "{sensors}"));
+ throw std::runtime_error("Missing required tach method properties");
+ }
+
+ std::vector<std::string> sensors;
+ for (auto& sensor : method["sensors"])
+ {
+ sensors.emplace_back(sensor.get<std::string>());
+ }
+
+ return std::make_unique<PolicyAccess<Tach, JsonConfig>>(fanIndex,
+ std::move(sensors));
+}
+
+// Get a constructed presence sensor for fan presence detection by gpio
+std::unique_ptr<PresenceSensor> getGpio(size_t fanIndex, const json& method)
+{
+ if (!method.contains("physpath") || !method.contains("devpath") ||
+ !method.contains("key"))
+ {
+ log<level::ERR>(
+ "Missing required gpio method properties",
+ entry("FAN_ENTRY=%d", fanIndex),
+ entry("REQUIRED_PROPERTIES=%s", "{physpath, devpath, key}"));
+ throw std::runtime_error("Missing required gpio method properties");
+ }
+
+ auto physpath = method["physpath"].get<std::string>();
+ auto devpath = method["devpath"].get<std::string>();
+ auto key = method["key"].get<unsigned int>();
+
+ return std::make_unique<PolicyAccess<Gpio, JsonConfig>>(fanIndex, physpath,
+ devpath, key);
+}
+
+} // namespace method
+
+/**
+ * Redundancy policies for fan presence detection function definitions
+ */
+namespace rpolicy
+{
+// Get an `Anyof` redundancy policy for the fan
+std::unique_ptr<RedundancyPolicy> getAnyof(const fanPolicy& fan)
+{
+ std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
+ for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
+ {
+ pSensors.emplace_back(*fanSensor);
+ }
+
+ return std::make_unique<AnyOf>(std::get<fanPolicyFanPos>(fan), pSensors);
+}
+
+// Get a `Fallback` redundancy policy for the fan
+std::unique_ptr<RedundancyPolicy> getFallback(const fanPolicy& fan)
+{
+ std::vector<std::reference_wrapper<PresenceSensor>> pSensors;
+ for (auto& fanSensor : std::get<fanPolicySensorListPos>(fan))
+ {
+ // Place in the order given to fallback correctly
+ pSensors.emplace_back(*fanSensor);
+ }
+
+ return std::make_unique<Fallback>(std::get<fanPolicyFanPos>(fan), pSensors);
+}
+
+} // namespace rpolicy
+
+} // namespace presence
+} // namespace fan
+} // namespace phosphor