Add a class to handle ExternalStorer file writes.
Signed-off-by: Kasun Athukorala <kasunath@google.com>
Change-Id: Ic1092a6a1da3375f595774018abfecd08a3cb7d8
diff --git a/src/rde/external_storer_file.cpp b/src/rde/external_storer_file.cpp
new file mode 100644
index 0000000..0768134
--- /dev/null
+++ b/src/rde/external_storer_file.cpp
@@ -0,0 +1,180 @@
+#include "rde/external_storer_file.hpp"
+
+#include <fmt/format.h>
+
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_io.hpp>
+
+#include <fstream>
+#include <string_view>
+
+namespace bios_bmc_smm_error_logger
+{
+namespace rde
+{
+
+bool ExternalStorerFileWriter::createFolder(const std::string& folderPath) const
+{
+ std::filesystem::path path(folderPath);
+ if (!std::filesystem::is_directory(path))
+ {
+ if (!std::filesystem::create_directories(path))
+ {
+ fmt::print(stderr, "Failed to create a folder at {}\n", folderPath);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ExternalStorerFileWriter::createFile(const std::string& folderPath,
+ const nlohmann::json& jsonPdr) const
+{
+ if (!createFolder(folderPath))
+ {
+ return false;
+ }
+ std::filesystem::path path(folderPath);
+ path /= "index.json";
+ // If the file already exist, overwrite it.
+ std::ofstream output(path);
+ output << jsonPdr;
+ output.close();
+ return true;
+}
+
+ExternalStorerFileInterface::ExternalStorerFileInterface(
+ std::string_view rootPath,
+ std::unique_ptr<FileHandlerInterface> fileHandler) :
+ rootPath(rootPath),
+ fileHandler(std::move(fileHandler)), logServiceId("")
+{}
+
+bool ExternalStorerFileInterface::publishJson(std::string_view jsonStr)
+{
+ nlohmann::json jsonDecoded;
+ try
+ {
+ jsonDecoded = nlohmann::json::parse(jsonStr);
+ }
+ catch (nlohmann::json::parse_error& e)
+ {
+ fmt::print(stderr, "JSON parse error: \n{}\n", e.what());
+ return false;
+ }
+
+ // We need to know the type to determine how to process the decoded JSON
+ // output.
+ if (!jsonDecoded.contains("@odata.type"))
+ {
+ fmt::print(stderr, "@odata.type field doesn't exist in:\n {}\n",
+ jsonDecoded.dump(4));
+ return false;
+ }
+
+ auto schemaType = getSchemaType(jsonDecoded);
+ if (schemaType == JsonPdrType::logEntry)
+ {
+ return processLogEntry(jsonDecoded);
+ }
+ if (schemaType == JsonPdrType::logService)
+ {
+ return processLogService(jsonDecoded);
+ }
+ return processOtherTypes(jsonDecoded);
+}
+
+JsonPdrType ExternalStorerFileInterface::getSchemaType(
+ const nlohmann::json& jsonSchema) const
+{
+ auto logEntryFound =
+ std::string(jsonSchema["@odata.type"]).find("LogEntry");
+ if (logEntryFound != std::string::npos)
+ {
+ return JsonPdrType::logEntry;
+ }
+
+ auto logServiceFound =
+ std::string(jsonSchema["@odata.type"]).find("LogService");
+ if (logServiceFound != std::string::npos)
+ {
+ return JsonPdrType::logService;
+ }
+
+ return JsonPdrType::other;
+}
+
+bool ExternalStorerFileInterface::processLogEntry(nlohmann::json& logEntry)
+{
+ // TODO: Add policies for LogEntry retention.
+ // https://github.com/openbmc/bios-bmc-smm-error-logger/issues/1.
+ if (logServiceId.empty())
+ {
+ fmt::print(stderr, "First need a LogService PDR with a new UUID.\n");
+ return false;
+ }
+
+ std::string id = boost::uuids::to_string(randomGen());
+ std::string path = "/redfish/v1/Systems/system/LogServices/" +
+ logServiceId + "/Entries/" + id;
+
+ // Populate the "Id" with the UUID we generated.
+ logEntry["Id"] = id;
+ // Remove the @odata.id from the JSON since ExternalStorer will fill it for
+ // a client.
+ logEntry.erase("@odata.id");
+
+ return createFile(path, logEntry);
+}
+
+bool ExternalStorerFileInterface::processLogService(
+ const nlohmann::json& logService)
+{
+ if (!logService.contains("@odata.id"))
+ {
+ fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
+ logService.dump(4));
+ return false;
+ }
+
+ if (!logService.contains("Id"))
+ {
+ fmt::print(stderr, "Id field doesn't exist in:\n {}\n",
+ logService.dump(4));
+ return false;
+ }
+
+ logServiceId = logService["Id"].get<std::string>();
+
+ if (!createFile(logService["@odata.id"].get<std::string>(), logService))
+ {
+ fmt::print(stderr, "Failed to create LogService index file for:\n{}\n",
+ logService.dump(4));
+ return false;
+ }
+ // ExternalStorer needs a .../Entries/index.json file with no data.
+ nlohmann::json jEmpty = "{}"_json;
+ return createFile(logService["@odata.id"].get<std::string>() + "/Entries",
+ jEmpty);
+}
+
+bool ExternalStorerFileInterface::processOtherTypes(
+ const nlohmann::json& jsonPdr) const
+{
+ if (!jsonPdr.contains("@odata.id"))
+ {
+ fmt::print(stderr, "@odata.id field doesn't exist in:\n {}\n",
+ jsonPdr.dump(4));
+ return false;
+ }
+ return createFile(jsonPdr["@odata.id"].get<std::string>(), jsonPdr);
+}
+
+bool ExternalStorerFileInterface::createFile(
+ const std::string& subPath, const nlohmann::json& jsonPdr) const
+{
+ return fileHandler->createFile(rootPath + subPath, jsonPdr);
+}
+
+} // namespace rde
+} // namespace bios_bmc_smm_error_logger