cleanup: move json configuration functions in their own files
Improving maintainability by increasing 'separation of concern'.
Change-Id: I80355abf741c037f8834527e3304c80b83c8166e
Signed-off-by: Christopher Meis <christopher.meis@9elements.com>
diff --git a/src/configuration.cpp b/src/configuration.cpp
new file mode 100644
index 0000000..8646f57
--- /dev/null
+++ b/src/configuration.cpp
@@ -0,0 +1,197 @@
+#include "configuration.hpp"
+
+#include "perform_probe.hpp"
+#include "utils.hpp"
+
+#include <nlohmann/json.hpp>
+#include <valijson/adapters/nlohmann_json_adapter.hpp>
+#include <valijson/schema.hpp>
+#include <valijson/schema_parser.hpp>
+#include <valijson/validator.hpp>
+
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <list>
+#include <string>
+#include <vector>
+
+namespace configuration
+{
+// writes output files to persist data
+bool writeJsonFiles(const nlohmann::json& systemConfiguration)
+{
+ std::filesystem::create_directory(configurationOutDir);
+ std::ofstream output(currentConfiguration);
+ if (!output.good())
+ {
+ return false;
+ }
+ output << systemConfiguration.dump(4);
+ output.close();
+ return true;
+}
+
+// reads json files out of the filesystem
+bool loadConfigurations(std::list<nlohmann::json>& configurations)
+{
+ // find configuration files
+ std::vector<std::filesystem::path> jsonPaths;
+ if (!findFiles(
+ std::vector<std::filesystem::path>{configurationDirectory,
+ hostConfigurationDirectory},
+ R"(.*\.json)", jsonPaths))
+ {
+ std::cerr << "Unable to find any configuration files in "
+ << configurationDirectory << "\n";
+ return false;
+ }
+
+ std::ifstream schemaStream(
+ std::string(schemaDirectory) + "/" + globalSchema);
+ if (!schemaStream.good())
+ {
+ std::cerr
+ << "Cannot open schema file, cannot validate JSON, exiting\n\n";
+ std::exit(EXIT_FAILURE);
+ return false;
+ }
+ nlohmann::json schema =
+ nlohmann::json::parse(schemaStream, nullptr, false, true);
+ if (schema.is_discarded())
+ {
+ std::cerr
+ << "Illegal schema file detected, cannot validate JSON, exiting\n";
+ std::exit(EXIT_FAILURE);
+ return false;
+ }
+
+ for (auto& jsonPath : jsonPaths)
+ {
+ std::ifstream jsonStream(jsonPath.c_str());
+ if (!jsonStream.good())
+ {
+ std::cerr << "unable to open " << jsonPath.string() << "\n";
+ continue;
+ }
+ auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
+ if (data.is_discarded())
+ {
+ std::cerr << "syntax error in " << jsonPath.string() << "\n";
+ continue;
+ }
+ /*
+ * todo(james): reenable this once less things are in flight
+ *
+ if (!validateJson(schema, data))
+ {
+ std::cerr << "Error validating " << jsonPath.string() << "\n";
+ continue;
+ }
+ */
+
+ if (data.type() == nlohmann::json::value_t::array)
+ {
+ for (auto& d : data)
+ {
+ configurations.emplace_back(d);
+ }
+ }
+ else
+ {
+ configurations.emplace_back(data);
+ }
+ }
+ return true;
+}
+
+// Iterate over new configuration and erase items from old configuration.
+void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
+ nlohmann::json& newConfiguration)
+{
+ for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
+ {
+ auto findKey = oldConfiguration.find(it.key());
+ if (findKey != oldConfiguration.end())
+ {
+ it = newConfiguration.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+}
+
+// validates a given input(configuration) with a given json schema file.
+bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
+{
+ valijson::Schema schema;
+ valijson::SchemaParser parser;
+ valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
+ parser.populateSchema(schemaAdapter, schema);
+ valijson::Validator validator;
+ valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
+ return validator.validate(schema, targetAdapter, nullptr);
+}
+
+// Extract the D-Bus interfaces to probe from the JSON config files.
+std::set<std::string> getProbeInterfaces()
+{
+ std::set<std::string> interfaces;
+ std::list<nlohmann::json> configurations;
+ if (!configuration::loadConfigurations(configurations))
+ {
+ return interfaces;
+ }
+
+ for (auto it = configurations.begin(); it != configurations.end();)
+ {
+ auto findProbe = it->find("Probe");
+ if (findProbe == it->end())
+ {
+ std::cerr << "configuration file missing probe:\n " << *it << "\n";
+ it++;
+ continue;
+ }
+
+ nlohmann::json probeCommand;
+ if ((*findProbe).type() != nlohmann::json::value_t::array)
+ {
+ probeCommand = nlohmann::json::array();
+ probeCommand.push_back(*findProbe);
+ }
+ else
+ {
+ probeCommand = *findProbe;
+ }
+
+ for (const nlohmann::json& probeJson : probeCommand)
+ {
+ const std::string* probe = probeJson.get_ptr<const std::string*>();
+ if (probe == nullptr)
+ {
+ std::cerr << "Probe statement wasn't a string, can't parse";
+ continue;
+ }
+ // Skip it if the probe cmd doesn't contain an interface.
+ if (probe::findProbeType(*probe))
+ {
+ continue;
+ }
+
+ // syntax requires probe before first open brace
+ auto findStart = probe->find('(');
+ if (findStart != std::string::npos)
+ {
+ std::string interface = probe->substr(0, findStart);
+ interfaces.emplace(interface);
+ }
+ }
+ it++;
+ }
+
+ return interfaces;
+}
+
+} // namespace configuration
diff --git a/src/configuration.hpp b/src/configuration.hpp
new file mode 100644
index 0000000..b3b2cdc
--- /dev/null
+++ b/src/configuration.hpp
@@ -0,0 +1,45 @@
+#pragma once
+
+#include <nlohmann/json.hpp>
+
+#include <list>
+#include <set>
+
+namespace configuration
+{
+constexpr const char* globalSchema = "global.json";
+constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
+constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
+constexpr const char* currentConfiguration = "/var/configuration/system.json";
+constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
+
+bool writeJsonFiles(const nlohmann::json& systemConfiguration);
+
+bool loadConfigurations(std::list<nlohmann::json>& configurations);
+
+template <typename JsonType>
+bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
+ nlohmann::json& systemConfiguration)
+{
+ try
+ {
+ nlohmann::json::json_pointer ptr(ptrStr);
+ nlohmann::json& ref = systemConfiguration[ptr];
+ ref = value;
+ return true;
+ }
+ catch (const std::out_of_range&)
+ {
+ return false;
+ }
+}
+
+void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
+ nlohmann::json& newConfiguration);
+
+bool validateJson(const nlohmann::json& schemaFile,
+ const nlohmann::json& input);
+
+std::set<std::string> getProbeInterfaces();
+
+} // namespace configuration
diff --git a/src/entity_manager.cpp b/src/entity_manager.cpp
index 187373a..e4e458e 100644
--- a/src/entity_manager.cpp
+++ b/src/entity_manager.cpp
@@ -17,8 +17,8 @@
#include "entity_manager.hpp"
+#include "configuration.hpp"
#include "overlay.hpp"
-#include "perform_probe.hpp"
#include "perform_scan.hpp"
#include "topology.hpp"
#include "utils.hpp"
@@ -47,13 +47,8 @@
#include <map>
#include <regex>
#include <variant>
-constexpr const char* hostConfigurationDirectory = SYSCONF_DIR "configurations";
-constexpr const char* configurationDirectory = PACKAGE_DIR "configurations";
-constexpr const char* schemaDirectory = PACKAGE_DIR "configurations/schemas";
constexpr const char* tempConfigDir = "/tmp/configuration/";
constexpr const char* lastConfiguration = "/tmp/configuration/last.json";
-constexpr const char* currentConfiguration = "/var/configuration/system.json";
-constexpr const char* globalSchema = "global.json";
static constexpr std::array<const char*, 6> settableInterfaces = {
"FanProfile", "Pid", "Pid.Zone", "Stepwise", "Thresholds", "Polling"};
@@ -119,37 +114,6 @@
return ptr;
}
-// writes output files to persist data
-bool writeJsonFiles(const nlohmann::json& systemConfiguration)
-{
- std::filesystem::create_directory(configurationOutDir);
- std::ofstream output(currentConfiguration);
- if (!output.good())
- {
- return false;
- }
- output << systemConfiguration.dump(4);
- output.close();
- return true;
-}
-
-template <typename JsonType>
-bool setJsonFromPointer(const std::string& ptrStr, const JsonType& value,
- nlohmann::json& systemConfiguration)
-{
- try
- {
- nlohmann::json::json_pointer ptr(ptrStr);
- nlohmann::json& ref = systemConfiguration[ptr];
- ref = value;
- return true;
- }
- catch (const std::out_of_range&)
- {
- return false;
- }
-}
-
// template function to add array as dbus property
template <typename PropertyType>
void addArrayToDbus(const std::string& name, const nlohmann::json& array,
@@ -181,13 +145,13 @@
const std::vector<PropertyType>& newVal,
std::vector<PropertyType>& val) {
val = newVal;
- if (!setJsonFromPointer(jsonPointerString, val,
- systemConfiguration))
+ if (!configuration::setJsonFromPointer(jsonPointerString, val,
+ systemConfiguration))
{
std::cerr << "error setting json field\n";
return -1;
}
- if (!writeJsonFiles(systemConfiguration))
+ if (!configuration::writeJsonFiles(systemConfiguration))
{
std::cerr << "error setting json file\n";
return -1;
@@ -215,13 +179,13 @@
jsonPointerString{std::string(jsonPointerString)}](
const PropertyType& newVal, PropertyType& val) {
val = newVal;
- if (!setJsonFromPointer(jsonPointerString, val,
- systemConfiguration))
+ if (!configuration::setJsonFromPointer(jsonPointerString, val,
+ systemConfiguration))
{
std::cerr << "error setting json field\n";
return -1;
}
- if (!writeJsonFiles(systemConfiguration))
+ if (!configuration::writeJsonFiles(systemConfiguration))
{
std::cerr << "error setting json file\n";
return -1;
@@ -257,7 +221,7 @@
objServer.remove_interface(dbusInterface);
});
- if (!writeJsonFiles(systemConfiguration))
+ if (!configuration::writeJsonFiles(systemConfiguration))
{
std::cerr << "error setting json file\n";
throw DBusInternalError();
@@ -503,8 +467,9 @@
lastIndex++;
}
- std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
- boost::to_lower_copy(*type) + ".json");
+ std::ifstream schemaFile(
+ std::string(configuration::schemaDirectory) + "/" +
+ boost::to_lower_copy(*type) + ".json");
// todo(james) we might want to also make a list of 'can add'
// interfaces but for now I think the assumption if there is a
// schema avaliable that it is allowed to update is fine
@@ -520,7 +485,7 @@
std::cerr << "Schema not legal" << *type << ".json\n";
throw DBusInternalError();
}
- if (!validateJson(schema, newData))
+ if (!configuration::validateJson(schema, newData))
{
throw std::invalid_argument("Data does not match schema");
}
@@ -532,7 +497,7 @@
{
findExposes->push_back(newData);
}
- if (!writeJsonFiles(systemConfiguration))
+ if (!configuration::writeJsonFiles(systemConfiguration))
{
std::cerr << "Error writing json files\n";
throw DBusInternalError();
@@ -802,79 +767,6 @@
}
}
-// reads json files out of the filesystem
-bool loadConfigurations(std::list<nlohmann::json>& configurations)
-{
- // find configuration files
- std::vector<std::filesystem::path> jsonPaths;
- if (!findFiles(
- std::vector<std::filesystem::path>{configurationDirectory,
- hostConfigurationDirectory},
- R"(.*\.json)", jsonPaths))
- {
- std::cerr << "Unable to find any configuration files in "
- << configurationDirectory << "\n";
- return false;
- }
-
- std::ifstream schemaStream(
- std::string(schemaDirectory) + "/" + globalSchema);
- if (!schemaStream.good())
- {
- std::cerr
- << "Cannot open schema file, cannot validate JSON, exiting\n\n";
- std::exit(EXIT_FAILURE);
- return false;
- }
- nlohmann::json schema =
- nlohmann::json::parse(schemaStream, nullptr, false, true);
- if (schema.is_discarded())
- {
- std::cerr
- << "Illegal schema file detected, cannot validate JSON, exiting\n";
- std::exit(EXIT_FAILURE);
- return false;
- }
-
- for (auto& jsonPath : jsonPaths)
- {
- std::ifstream jsonStream(jsonPath.c_str());
- if (!jsonStream.good())
- {
- std::cerr << "unable to open " << jsonPath.string() << "\n";
- continue;
- }
- auto data = nlohmann::json::parse(jsonStream, nullptr, false, true);
- if (data.is_discarded())
- {
- std::cerr << "syntax error in " << jsonPath.string() << "\n";
- continue;
- }
- /*
- * todo(james): reenable this once less things are in flight
- *
- if (!validateJson(schema, data))
- {
- std::cerr << "Error validating " << jsonPath.string() << "\n";
- continue;
- }
- */
-
- if (data.type() == nlohmann::json::value_t::array)
- {
- for (auto& d : data)
- {
- configurations.emplace_back(d);
- }
- }
- else
- {
- configurations.emplace_back(data);
- }
- }
- return true;
-}
-
static bool deviceRequiresPowerOn(const nlohmann::json& entity)
{
auto powerState = entity.find("PowerState");
@@ -985,23 +877,6 @@
logDeviceRemoved(device);
}
-static void deriveNewConfiguration(const nlohmann::json& oldConfiguration,
- nlohmann::json& newConfiguration)
-{
- for (auto it = newConfiguration.begin(); it != newConfiguration.end();)
- {
- auto findKey = oldConfiguration.find(it.key());
- if (findKey != oldConfiguration.end())
- {
- it = newConfiguration.erase(it);
- }
- else
- {
- it++;
- }
- }
-}
-
static void publishNewConfiguration(
const size_t& instance, const size_t count,
boost::asio::steady_timer& timer, nlohmann::json& systemConfiguration,
@@ -1018,7 +893,7 @@
loadOverlays(newConfiguration);
boost::asio::post(io, [systemConfiguration]() {
- if (!writeJsonFiles(systemConfiguration))
+ if (!configuration::writeJsonFiles(systemConfiguration))
{
std::cerr << "Error writing json files\n";
}
@@ -1072,7 +947,7 @@
*missingConfigurations = systemConfiguration;
std::list<nlohmann::json> configurations;
- if (!loadConfigurations(configurations))
+ if (!configuration::loadConfigurations(configurations))
{
std::cerr << "Could not load configurations\n";
inProgress = false;
@@ -1096,7 +971,8 @@
nlohmann::json newConfiguration = systemConfiguration;
- deriveNewConfiguration(oldConfiguration, newConfiguration);
+ configuration::deriveNewConfiguration(oldConfiguration,
+ newConfiguration);
for (const auto& [_, device] : newConfiguration.items())
{
@@ -1115,65 +991,6 @@
});
}
-// Extract the D-Bus interfaces to probe from the JSON config files.
-static std::set<std::string> getProbeInterfaces()
-{
- std::set<std::string> interfaces;
- std::list<nlohmann::json> configurations;
- if (!loadConfigurations(configurations))
- {
- return interfaces;
- }
-
- for (auto it = configurations.begin(); it != configurations.end();)
- {
- auto findProbe = it->find("Probe");
- if (findProbe == it->end())
- {
- std::cerr << "configuration file missing probe:\n " << *it << "\n";
- it++;
- continue;
- }
-
- nlohmann::json probeCommand;
- if ((*findProbe).type() != nlohmann::json::value_t::array)
- {
- probeCommand = nlohmann::json::array();
- probeCommand.push_back(*findProbe);
- }
- else
- {
- probeCommand = *findProbe;
- }
-
- for (const nlohmann::json& probeJson : probeCommand)
- {
- const std::string* probe = probeJson.get_ptr<const std::string*>();
- if (probe == nullptr)
- {
- std::cerr << "Probe statement wasn't a string, can't parse";
- continue;
- }
- // Skip it if the probe cmd doesn't contain an interface.
- if (probe::findProbeType(*probe))
- {
- continue;
- }
-
- // syntax requires probe before first open brace
- auto findStart = probe->find('(');
- if (findStart != std::string::npos)
- {
- std::string interface = probe->substr(0, findStart);
- interfaces.emplace(interface);
- }
- }
- it++;
- }
-
- return interfaces;
-}
-
// Check if InterfacesAdded payload contains an iface that needs probing.
static bool iaContainsProbeInterface(
sdbusplus::message_t& msg, const std::set<std::string>& probeInterfaces)
@@ -1237,7 +1054,7 @@
nlohmann::json systemConfiguration = nlohmann::json::object();
- std::set<std::string> probeInterfaces = getProbeInterfaces();
+ std::set<std::string> probeInterfaces = configuration::getProbeInterfaces();
// We need a poke from DBus for static providers that create all their
// objects prior to claiming a well-known name, and thus don't emit any
@@ -1291,13 +1108,15 @@
if (fwVersionIsSame())
{
- if (std::filesystem::is_regular_file(currentConfiguration))
+ if (std::filesystem::is_regular_file(
+ configuration::currentConfiguration))
{
// this file could just be deleted, but it's nice for debug
std::filesystem::create_directory(tempConfigDir);
std::filesystem::remove(lastConfiguration);
- std::filesystem::copy(currentConfiguration, lastConfiguration);
- std::filesystem::remove(currentConfiguration);
+ std::filesystem::copy(configuration::currentConfiguration,
+ lastConfiguration);
+ std::filesystem::remove(configuration::currentConfiguration);
std::ifstream jsonStream(lastConfiguration);
if (jsonStream.good())
@@ -1323,7 +1142,7 @@
{
// not an error, just logging at this level to make it in the journal
std::cerr << "Clearing previous configuration\n";
- std::filesystem::remove(currentConfiguration);
+ std::filesystem::remove(configuration::currentConfiguration);
}
// some boards only show up after power is on, we want to not say they are
diff --git a/src/meson.build b/src/meson.build
index f4b96a8..e4c2759 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -5,6 +5,7 @@
executable(
'entity-manager',
'entity_manager.cpp',
+ 'configuration.cpp',
'expression.cpp',
'perform_scan.cpp',
'perform_probe.cpp',
diff --git a/src/utils.cpp b/src/utils.cpp
index d95fb5e..fd01016 100644
--- a/src/utils.cpp
+++ b/src/utils.cpp
@@ -28,10 +28,6 @@
#include <boost/container/flat_map.hpp>
#include <boost/lexical_cast.hpp>
#include <sdbusplus/bus/match.hpp>
-#include <valijson/adapters/nlohmann_json_adapter.hpp>
-#include <valijson/schema.hpp>
-#include <valijson/schema_parser.hpp>
-#include <valijson/validator.hpp>
#include <charconv>
#include <filesystem>
@@ -130,17 +126,6 @@
return true;
}
-bool validateJson(const nlohmann::json& schemaFile, const nlohmann::json& input)
-{
- valijson::Schema schema;
- valijson::SchemaParser parser;
- valijson::adapters::NlohmannJsonAdapter schemaAdapter(schemaFile);
- parser.populateSchema(schemaAdapter, schema);
- valijson::Validator validator;
- valijson::adapters::NlohmannJsonAdapter targetAdapter(input);
- return validator.validate(schema, targetAdapter, nullptr);
-}
-
bool isPowerOn()
{
if (!powerMatch)