Add create object function
This function takes in a object name, and a map of
variants, converts it to json, and creates a dbus-interface
after validating the json matches the correct schema.
Tested-by: Uploaded new Pid interface through D-Bus.
Change-Id: Ieec47a3f9fb53d5ac4f975f13ae6b8efd323971d
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/include/Utils.hpp b/include/Utils.hpp
index cd44082..39e3794 100644
--- a/include/Utils.hpp
+++ b/include/Utils.hpp
@@ -17,6 +17,7 @@
#pragma once
#include <experimental/filesystem>
#include <nlohmann/json.hpp>
+#include <sdbusplus/exception.hpp>
bool findFiles(const std::experimental::filesystem::path &dirPath,
const std::string &matchString,
@@ -24,3 +25,19 @@
bool validateJson(const nlohmann::json &schemaFile,
const nlohmann::json &input);
+struct DBusInternalError final : public sdbusplus::exception_t
+{
+ const char *name() const noexcept override
+ {
+ return "org.freedesktop.DBus.Error.Failed";
+ };
+ const char *description() const noexcept override
+ {
+ return "internal error";
+ };
+ const char *what() const noexcept override
+ {
+ return "org.freedesktop.DBus.Error.Failed: "
+ "internal error";
+ };
+};
diff --git a/schemas/Pid.json b/schemas/Pid.json
new file mode 100644
index 0000000..aa262fb
--- /dev/null
+++ b/schemas/Pid.json
@@ -0,0 +1,83 @@
+{
+ "$schema": "http://json-schema.org/schema#",
+ "type": "object",
+ "properties": {
+ "Class": {
+ "type": "string"
+ },
+ "FFGainCoefficient": {
+ "type": "number"
+ },
+ "FFOffCoefficient": {
+ "type": "number"
+ },
+ "ICoefficient": {
+ "type": "number"
+ },
+ "ILimitMax": {
+ "type": "number"
+ },
+ "ILimitMin": {
+ "type": "number"
+ },
+ "Inputs": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "Name": {
+ "type": "string"
+ },
+ "OutLimitMax": {
+ "type": "number"
+ },
+ "OutLimitMin": {
+ "type": "number"
+ },
+ "Outputs": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "PCoefficient": {
+ "type": "number"
+ },
+ "SlewNeg": {
+ "type": "number"
+ },
+ "SlewPos": {
+ "type": "number"
+ },
+ "Type": {
+ "type": "string"
+ },
+ "Zones": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "SetPoint": {
+ "type": "number"
+ }
+ },
+ "required": [
+ "Class",
+ "FFGainCoefficient",
+ "FFOffCoefficient",
+ "ICoefficient",
+ "ILimitMax",
+ "ILimitMin",
+ "Inputs",
+ "Name",
+ "OutLimitMax",
+ "OutLimitMin",
+ "PCoefficient",
+ "SlewNeg",
+ "SlewPos",
+ "Type",
+ "Zones"
+ ]
+}
diff --git a/src/EntityManager.cpp b/src/EntityManager.cpp
index ec1b285..866baa7 100644
--- a/src/EntityManager.cpp
+++ b/src/EntityManager.cpp
@@ -72,7 +72,10 @@
static constexpr std::array<const char *, 1> SETTABLE_INTERFACES = {
"Thresholds"};
-
+using JsonVariantType =
+ sdbusplus::message::variant<std::vector<std::string>, std::string, int64_t,
+ uint64_t, double, int32_t, uint32_t, int16_t,
+ uint16_t, uint8_t, bool>;
using BasicVariantType =
sdbusplus::message::variant<std::string, int64_t, uint64_t, double, int32_t,
uint32_t, int16_t, uint16_t, uint8_t, bool>;
@@ -557,22 +560,25 @@
iface->register_property(propertyName, value);
return;
}
- iface->register_property(propertyName, value, [
- &systemConfiguration, jsonPointerString{std::string(jsonPointerString)}
- ](const PropertyType &newVal, PropertyType &val) {
- val = newVal;
- if (!setJsonFromPointer(jsonPointerString, val, systemConfiguration))
- {
- std::cerr << "error setting json field\n";
+ iface->register_property(
+ propertyName, value,
+ [&systemConfiguration,
+ jsonPointerString{std::string(jsonPointerString)}](
+ const PropertyType &newVal, PropertyType &val) {
+ val = newVal;
+ if (!setJsonFromPointer(jsonPointerString, val,
+ systemConfiguration))
+ {
+ std::cerr << "error setting json field\n";
+ return -1;
+ }
+ if (writeJsonFiles(systemConfiguration))
+ {
+ std::cerr << "error setting json file\n";
+ return 1;
+ }
return -1;
- }
- if (writeJsonFiles(systemConfiguration))
- {
- std::cerr << "error setting json file\n";
- return 1;
- }
- return -1;
- });
+ });
}
// adds simple json types to interface's properties
@@ -712,6 +718,112 @@
iface->initialize();
}
+void createAddObjectMethod(const std::string &jsonPointerPath,
+ const std::string &path,
+ nlohmann::json &systemConfiguration,
+ sdbusplus::asio::object_server &objServer)
+{
+ auto iface = objServer.add_interface(path, "xyz.openbmc_project.AddObject");
+
+ iface->register_method(
+ "AddObject",
+ [&systemConfiguration, &objServer,
+ jsonPointerPath{std::string(jsonPointerPath)},
+ path{std::string(path)}](
+ const boost::container::flat_map<std::string, JsonVariantType>
+ &data) {
+ nlohmann::json::json_pointer ptr(jsonPointerPath);
+ nlohmann::json &base = systemConfiguration[ptr];
+ auto findExposes = base.find("Exposes");
+
+ if (findExposes == base.end())
+ {
+ throw std::invalid_argument("Entity must have children.");
+ }
+
+ // this will throw invalid-argument to sdbusplus if invalid json
+ nlohmann::json newData{};
+ for (const auto &item : data)
+ {
+ nlohmann::json &newJson = newData[item.first];
+ mapbox::util::apply_visitor(
+ [&newJson](auto &&val) { newJson = std::move(val); },
+ item.second);
+ }
+
+ auto findName = newData.find("Name");
+ auto findType = newData.find("Type");
+ if (findName == newData.end() || findType == newData.end())
+ {
+ throw std::invalid_argument("AddObject missing Name or Type");
+ }
+ const std::string *type = findType->get_ptr<const std::string *>();
+ const std::string *name = findName->get_ptr<const std::string *>();
+ if (type == nullptr || name == nullptr)
+ {
+ throw std::invalid_argument("Type and Name must be a string.");
+ }
+
+ size_t lastIndex = 0;
+ // we add in the "exposes"
+ for (; lastIndex < findExposes->size(); lastIndex++)
+ {
+ if (findExposes->at(lastIndex)["Name"] == *name &&
+ findExposes->at(lastIndex)["Type"] == *type)
+ {
+ throw std::invalid_argument(
+ "Field already in JSON, not adding");
+ }
+ lastIndex++;
+ }
+
+ std::ifstream schemaFile(std::string(schemaDirectory) + "/" +
+ *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
+ if (!schemaFile.good())
+ {
+ throw std::invalid_argument(
+ "No schema avaliable, cannot validate.");
+ }
+ nlohmann::json schema =
+ nlohmann::json::parse(schemaFile, nullptr, false);
+ if (schema.is_discarded())
+ {
+ std::cerr << "Schema not legal" << *type << ".json\n";
+ throw DBusInternalError();
+ }
+ if (!validateJson(schema, newData))
+ {
+ throw std::invalid_argument("Data does not match schema");
+ }
+
+ if (!writeJsonFiles(systemConfiguration))
+ {
+ std::cerr << "Error writing json files\n";
+ throw DBusInternalError();
+ }
+ std::string dbusName = *name;
+
+ std::regex_replace(dbusName.begin(), dbusName.begin(),
+ dbusName.end(), ILLEGAL_DBUS_REGEX, "_");
+ auto iface = objServer.add_interface(
+ path + "/" + dbusName,
+ "xyz.openbmc_project.Configuration." + *type);
+ // permission is read-write, as since we just created it, must be
+ // runtime modifiable
+ populateInterfaceFromJson(
+ systemConfiguration,
+ jsonPointerPath + "/" + std::to_string(lastIndex), iface.get(),
+ newData, objServer,
+ sdbusplus::asio::PropertyPermission::readWrite);
+ // todo(james) generate patch
+ findExposes->push_back(newData);
+ });
+ iface->initialize();
+}
+
void postToDbus(const nlohmann::json &newConfiguration,
nlohmann::json &systemConfiguration,
sdbusplus::asio::object_server &objServer)
@@ -750,9 +862,13 @@
auto inventoryIface = objServer.add_interface(
boardName, "xyz.openbmc_project.Inventory.Item");
+
auto boardIface = objServer.add_interface(
boardName, "xyz.openbmc_project.Inventory.Item." + boardType);
+ createAddObjectMethod(jsonPointerPath, boardName, systemConfiguration,
+ objServer);
+
populateInterfaceFromJson(systemConfiguration, jsonPointerPath,
boardIface.get(), boardValues, objServer);
jsonPointerPath += "/";