EventService: Move subscription persistent data
This commit resolves https://github.com/openbmc/bmcweb/issues/168
Current store mechanism makes it very difficult to keep in sync with
the existing files, and has caused several bugs because the path it
uses different than the existing bmcweb_persistent_data.json, and it's
missing several error checks.
If there has old config in /var/lib/bmcweb/eventservice_config.json.
Restart bmcweb will move old config to bmcweb_presistent_data.json and
delete the old config.
Tested:
- Create new Subscription via POST
https://${bmc}/redfish/v1/EventService/Subscriptions/
The subscription is successfully created and GET succussfully.
Restart bmcweb or reboot.
The subscription will restore.
- Delete the Subscription via DELETE
https://${bmc}/redfish/v1/EventService/Subscriptions/${subscription_id}
The subscription is successfully delete.
bmcweb_persistent_data.json will delete subscription content.
- Modify EventService config via PATCH
https://{{bmc}}/redfish/v1/EventService
GET https://{{bmc}}/redfish/v1/EventService and the changes applied.
bmcweb_persistent_data.json will apply modification after PATCH.
Restart bmcweb or reboot
The config maintains the changed.
Signed-off-by: JunLin Chen <Jun-Lin.Chen@quantatw.com>
Change-Id: Ic29385ea8231ba976bbf415af2803df2d30cb10a
diff --git a/redfish-core/include/event_service_manager.hpp b/redfish-core/include/event_service_manager.hpp
index de40356..d89b789 100644
--- a/redfish-core/include/event_service_manager.hpp
+++ b/redfish-core/include/event_service_manager.hpp
@@ -24,7 +24,9 @@
#include <boost/asio/io_context.hpp>
#include <boost/container/flat_map.hpp>
#include <error_messages.hpp>
+#include <event_service_store.hpp>
#include <http_client.hpp>
+#include <persistent_data.hpp>
#include <random.hpp>
#include <server_sent_events.hpp>
#include <utils/json_utils.hpp>
@@ -40,7 +42,6 @@
using ReadingsObjType =
std::vector<std::tuple<std::string, std::string, double, int32_t>>;
-using EventServiceConfig = std::tuple<bool, uint32_t, uint32_t>;
static constexpr const char* eventFormatType = "Event";
static constexpr const char* metricReportFormatType = "MetricReport";
@@ -369,22 +370,9 @@
return true;
}
-class Subscription
+class Subscription : public persistent_data::UserSubscription
{
public:
- std::string id;
- std::string destinationUrl;
- std::string protocol;
- std::string retryPolicy;
- std::string customText;
- std::string eventFormatType;
- std::string subscriptionType;
- std::vector<std::string> registryMsgIds;
- std::vector<std::string> registryPrefixes;
- std::vector<std::string> resourceTypes;
- std::vector<nlohmann::json> httpHeaders; // key-value pair
- std::vector<std::string> metricReportDefinitions;
-
Subscription(const Subscription&) = delete;
Subscription& operator=(const Subscription&) = delete;
Subscription(Subscription&&) = delete;
@@ -600,13 +588,6 @@
std::shared_ptr<crow::ServerSentEvents> sseConn = nullptr;
};
-static constexpr const bool defaultEnabledState = true;
-static constexpr const uint32_t defaultRetryAttempts = 3;
-static constexpr const uint32_t defaultRetryInterval = 30;
-static constexpr const char* defaulEventFormatType = "Event";
-static constexpr const char* defaulSubscriptionType = "RedfishEvent";
-static constexpr const char* defaulRetryPolicy = "TerminateAfterRetries";
-
class EventServiceManager
{
private:
@@ -641,102 +622,30 @@
return handler;
}
- void loadDefaultConfig()
- {
- serviceEnabled = defaultEnabledState;
- retryAttempts = defaultRetryAttempts;
- retryTimeoutInterval = defaultRetryInterval;
- }
-
void initConfig()
{
- std::ifstream eventConfigFile(eventServiceFile);
- if (!eventConfigFile.good())
- {
- BMCWEB_LOG_DEBUG << "EventService config not exist";
- loadDefaultConfig();
- return;
- }
- auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
- if (jsonData.is_discarded())
- {
- BMCWEB_LOG_ERROR << "EventService config parse error.";
- loadDefaultConfig();
- return;
- }
+ loadOldBehavior();
- nlohmann::json jsonConfig;
- if (json_util::getValueFromJsonObject(jsonData, "Configuration",
- jsonConfig))
- {
- if (!json_util::getValueFromJsonObject(jsonConfig, "ServiceEnabled",
- serviceEnabled))
- {
- serviceEnabled = defaultEnabledState;
- }
- if (!json_util::getValueFromJsonObject(
- jsonConfig, "DeliveryRetryAttempts", retryAttempts))
- {
- retryAttempts = defaultRetryAttempts;
- }
- if (!json_util::getValueFromJsonObject(
- jsonConfig, "DeliveryRetryIntervalSeconds",
- retryTimeoutInterval))
- {
- retryTimeoutInterval = defaultRetryInterval;
- }
- }
- else
- {
- loadDefaultConfig();
- }
+ persistent_data::EventServiceConfig eventServiceConfig =
+ persistent_data::EventServiceStore::getInstance()
+ .getEventServiceConfig();
- nlohmann::json subscriptionsList;
- if (!json_util::getValueFromJsonObject(jsonData, "Subscriptions",
- subscriptionsList))
+ serviceEnabled = eventServiceConfig.enabled;
+ retryAttempts = eventServiceConfig.retryAttempts;
+ retryTimeoutInterval = eventServiceConfig.retryTimeoutInterval;
+
+ for (const auto& it : persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap)
{
- BMCWEB_LOG_DEBUG << "EventService: Subscriptions not exist.";
- return;
- }
+ std::shared_ptr<persistent_data::UserSubscription> newSub =
+ it.second;
- for (nlohmann::json& jsonObj : subscriptionsList)
- {
- std::string protocol;
- if (!json_util::getValueFromJsonObject(jsonObj, "Protocol",
- protocol))
- {
- BMCWEB_LOG_DEBUG << "Invalid subscription Protocol exist.";
- continue;
- }
-
- std::string subscriptionType;
- if (!json_util::getValueFromJsonObject(jsonObj, "SubscriptionType",
- subscriptionType))
- {
- subscriptionType = defaulSubscriptionType;
- }
- // SSE connections are initiated from client
- // and can't be re-established from server.
- if (subscriptionType == "SSE")
- {
- BMCWEB_LOG_DEBUG
- << "The subscription type is SSE, so skipping.";
- continue;
- }
-
- std::string destination;
- if (!json_util::getValueFromJsonObject(jsonObj, "Destination",
- destination))
- {
- BMCWEB_LOG_DEBUG << "Invalid subscription destination exist.";
- continue;
- }
std::string host;
std::string urlProto;
std::string port;
std::string path;
- bool status =
- validateAndSplitUrl(destination, urlProto, host, port, path);
+ bool status = validateAndSplitUrl(newSub->destinationUrl, urlProto,
+ host, port, path);
if (!status)
{
@@ -747,112 +656,140 @@
std::shared_ptr<Subscription> subValue =
std::make_shared<Subscription>(host, port, path, urlProto);
- subValue->destinationUrl = destination;
- subValue->protocol = protocol;
- subValue->subscriptionType = subscriptionType;
- if (!json_util::getValueFromJsonObject(
- jsonObj, "DeliveryRetryPolicy", subValue->retryPolicy))
- {
- subValue->retryPolicy = defaulRetryPolicy;
- }
- if (!json_util::getValueFromJsonObject(jsonObj, "EventFormatType",
- subValue->eventFormatType))
- {
- subValue->eventFormatType = defaulEventFormatType;
- }
- json_util::getValueFromJsonObject(jsonObj, "Context",
- subValue->customText);
- json_util::getValueFromJsonObject(jsonObj, "MessageIds",
- subValue->registryMsgIds);
- json_util::getValueFromJsonObject(jsonObj, "RegistryPrefixes",
- subValue->registryPrefixes);
- json_util::getValueFromJsonObject(jsonObj, "ResourceTypes",
- subValue->resourceTypes);
- json_util::getValueFromJsonObject(jsonObj, "HttpHeaders",
- subValue->httpHeaders);
- json_util::getValueFromJsonObject(
- jsonObj, "MetricReportDefinitions",
- subValue->metricReportDefinitions);
+ subValue->id = newSub->id;
+ subValue->destinationUrl = newSub->destinationUrl;
+ subValue->protocol = newSub->protocol;
+ subValue->retryPolicy = newSub->retryPolicy;
+ subValue->customText = newSub->customText;
+ subValue->eventFormatType = newSub->eventFormatType;
+ subValue->subscriptionType = newSub->subscriptionType;
+ subValue->registryMsgIds = newSub->registryMsgIds;
+ subValue->registryPrefixes = newSub->registryPrefixes;
+ subValue->resourceTypes = newSub->resourceTypes;
+ subValue->httpHeaders = newSub->httpHeaders;
+ subValue->metricReportDefinitions = newSub->metricReportDefinitions;
- std::string id = addSubscription(subValue, false);
- if (id.empty())
+ if (subValue->id.empty())
{
BMCWEB_LOG_ERROR << "Failed to add subscription";
}
+ subscriptionsMap.insert(std::pair(subValue->id, subValue));
+
+ updateNoOfSubscribersCount();
+
+#ifndef BMCWEB_ENABLE_REDFISH_DBUS_LOG_ENTRIES
+ if (lastEventTStr.empty())
+ {
+ cacheLastEventTimestamp();
+ }
+#endif
+ // Update retry configuration.
+ subValue->updateRetryConfig(retryAttempts, retryTimeoutInterval);
+ subValue->updateRetryPolicy();
}
return;
}
- void updateSubscriptionData()
+ void loadOldBehavior()
{
- // Persist the config and subscription data.
- nlohmann::json jsonData;
-
- nlohmann::json& configObj = jsonData["Configuration"];
- configObj["ServiceEnabled"] = serviceEnabled;
- configObj["DeliveryRetryAttempts"] = retryAttempts;
- configObj["DeliveryRetryIntervalSeconds"] = retryTimeoutInterval;
-
- nlohmann::json& subListArray = jsonData["Subscriptions"];
- subListArray = nlohmann::json::array();
-
- for (const auto& it : subscriptionsMap)
+ std::ifstream eventConfigFile(eventServiceFile);
+ if (!eventConfigFile.good())
{
- std::shared_ptr<Subscription> subValue = it.second;
- // Don't preserve SSE connections. Its initiated from
- // client side and can't be re-established from server.
- if (subValue->subscriptionType == "SSE")
+ BMCWEB_LOG_DEBUG << "Old eventService config not exist";
+ return;
+ }
+ auto jsonData = nlohmann::json::parse(eventConfigFile, nullptr, false);
+ if (jsonData.is_discarded())
+ {
+ BMCWEB_LOG_ERROR << "Old eventService config parse error.";
+ return;
+ }
+
+ for (const auto& item : jsonData.items())
+ {
+ if (item.key() == "Configuration")
{
- BMCWEB_LOG_DEBUG
- << "The subscription type is SSE, so skipping.";
- continue;
+ persistent_data::EventServiceStore::getInstance()
+ .getEventServiceConfig()
+ .fromJson(item.value());
+ }
+ else if (item.key() == "Subscriptions")
+ {
+ for (const auto& elem : item.value())
+ {
+ std::shared_ptr<persistent_data::UserSubscription>
+ newSubscription =
+ persistent_data::UserSubscription::fromJson(elem,
+ true);
+ if (newSubscription == nullptr)
+ {
+ BMCWEB_LOG_ERROR << "Problem reading subscription "
+ "from old persistent store";
+ continue;
+ }
+
+ std::uniform_int_distribution<uint32_t> dist(0);
+ bmcweb::OpenSSLGenerator gen;
+
+ std::string id;
+
+ int retry = 3;
+ while (retry)
+ {
+ id = std::to_string(dist(gen));
+ if (gen.error())
+ {
+ retry = 0;
+ break;
+ }
+ newSubscription->id = id;
+ auto inserted =
+ persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap.insert(
+ std::pair(id, newSubscription));
+ if (inserted.second)
+ {
+ break;
+ }
+ --retry;
+ }
+
+ if (retry <= 0)
+ {
+ BMCWEB_LOG_ERROR
+ << "Failed to generate random number from old "
+ "persistent store";
+ continue;
+ }
+ }
}
- nlohmann::json entry;
- entry["Context"] = subValue->customText;
- entry["DeliveryRetryPolicy"] = subValue->retryPolicy;
- entry["Destination"] = subValue->destinationUrl;
- entry["EventFormatType"] = subValue->eventFormatType;
- entry["HttpHeaders"] = subValue->httpHeaders;
- entry["MessageIds"] = subValue->registryMsgIds;
- entry["Protocol"] = subValue->protocol;
- entry["RegistryPrefixes"] = subValue->registryPrefixes;
- entry["ResourceTypes"] = subValue->resourceTypes;
- entry["SubscriptionType"] = subValue->subscriptionType;
- entry["MetricReportDefinitions"] =
- subValue->metricReportDefinitions;
-
- subListArray.push_back(entry);
- }
-
- const std::string tmpFile(std::string(eventServiceFile) + "_tmp");
- std::ofstream ofs(tmpFile, std::ios::out);
- const auto& writeData = jsonData.dump(
- 2, ' ', true, nlohmann::json::error_handler_t::replace);
- ofs << writeData;
- ofs.close();
-
- BMCWEB_LOG_DEBUG << "EventService config updated to file.";
- if (std::rename(tmpFile.c_str(), eventServiceFile) != 0)
- {
- BMCWEB_LOG_ERROR << "Error in renaming temporary file: "
- << tmpFile.c_str();
+ persistent_data::getConfig().writeData();
+ std::remove(eventServiceFile);
+ BMCWEB_LOG_DEBUG << "Remove old eventservice config";
}
}
- EventServiceConfig getEventServiceConfig()
+ void updateSubscriptionData()
{
- return {serviceEnabled, retryAttempts, retryTimeoutInterval};
+ persistent_data::EventServiceStore::getInstance()
+ .eventServiceConfig.enabled = serviceEnabled;
+ persistent_data::EventServiceStore::getInstance()
+ .eventServiceConfig.retryAttempts = retryAttempts;
+ persistent_data::EventServiceStore::getInstance()
+ .eventServiceConfig.retryTimeoutInterval = retryTimeoutInterval;
+
+ persistent_data::getConfig().writeData();
}
- void setEventServiceConfig(const EventServiceConfig& cfg)
+ void setEventServiceConfig(const persistent_data::EventServiceConfig& cfg)
{
bool updateConfig = false;
bool updateRetryCfg = false;
- if (serviceEnabled != std::get<0>(cfg))
+ if (serviceEnabled != cfg.enabled)
{
- serviceEnabled = std::get<0>(cfg);
+ serviceEnabled = cfg.enabled;
if (serviceEnabled && noOfMetricReportSubscribers)
{
registerMetricReportSignal();
@@ -864,16 +801,16 @@
updateConfig = true;
}
- if (retryAttempts != std::get<1>(cfg))
+ if (retryAttempts != cfg.retryAttempts)
{
- retryAttempts = std::get<1>(cfg);
+ retryAttempts = cfg.retryAttempts;
updateConfig = true;
updateRetryCfg = true;
}
- if (retryTimeoutInterval != std::get<2>(cfg))
+ if (retryTimeoutInterval != cfg.retryTimeoutInterval)
{
- retryTimeoutInterval = std::get<2>(cfg);
+ retryTimeoutInterval = cfg.retryTimeoutInterval;
updateConfig = true;
updateRetryCfg = true;
}
@@ -971,6 +908,23 @@
return std::string("");
}
+ std::shared_ptr<persistent_data::UserSubscription> newSub =
+ std::make_shared<persistent_data::UserSubscription>();
+ newSub->id = id;
+ newSub->destinationUrl = subValue->destinationUrl;
+ newSub->protocol = subValue->protocol;
+ newSub->retryPolicy = subValue->retryPolicy;
+ newSub->customText = subValue->customText;
+ newSub->eventFormatType = subValue->eventFormatType;
+ newSub->subscriptionType = subValue->subscriptionType;
+ newSub->registryMsgIds = subValue->registryMsgIds;
+ newSub->registryPrefixes = subValue->registryPrefixes;
+ newSub->resourceTypes = subValue->resourceTypes;
+ newSub->httpHeaders = subValue->httpHeaders;
+ newSub->metricReportDefinitions = subValue->metricReportDefinitions;
+ persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap.emplace(newSub->id, newSub);
+
updateNoOfSubscribersCount();
if (updateFile)
@@ -1007,6 +961,10 @@
if (obj != subscriptionsMap.end())
{
subscriptionsMap.erase(obj);
+ auto obj2 = persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap.find(id);
+ persistent_data::EventServiceStore::getInstance()
+ .subscriptionsConfigMap.erase(obj2);
updateNoOfSubscribersCount();
updateSubscriptionData();
}
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index 98c67c6..f1d6f50 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -58,16 +58,18 @@
"EventService.SubmitTestEvent"}}}}},
{"@odata.id", "/redfish/v1/EventService"}};
- const auto& [enabled, retryAttempts, retryTimeoutInterval] =
- EventServiceManager::getInstance().getEventServiceConfig();
+ const persistent_data::EventServiceConfig eventServiceConfig =
+ persistent_data::EventServiceStore::getInstance()
+ .getEventServiceConfig();
asyncResp->res.jsonValue["Status"]["State"] =
- (enabled ? "Enabled" : "Disabled");
- asyncResp->res.jsonValue["ServiceEnabled"] = enabled;
+ (eventServiceConfig.enabled ? "Enabled" : "Disabled");
+ asyncResp->res.jsonValue["ServiceEnabled"] =
+ eventServiceConfig.enabled;
asyncResp->res.jsonValue["DeliveryRetryAttempts"] =
- retryAttempts;
+ eventServiceConfig.retryAttempts;
asyncResp->res.jsonValue["DeliveryRetryIntervalSeconds"] =
- retryTimeoutInterval;
+ eventServiceConfig.retryTimeoutInterval;
asyncResp->res.jsonValue["EventFormatTypes"] =
supportedEvtFormatTypes;
asyncResp->res.jsonValue["RegistryPrefixes"] =
@@ -103,12 +105,13 @@
return;
}
- auto [enabled, retryCount, retryTimeoutInterval] =
- EventServiceManager::getInstance().getEventServiceConfig();
+ persistent_data::EventServiceConfig eventServiceConfig =
+ persistent_data::EventServiceStore::getInstance()
+ .getEventServiceConfig();
if (serviceEnabled)
{
- enabled = *serviceEnabled;
+ eventServiceConfig.enabled = *serviceEnabled;
}
if (retryAttemps)
@@ -122,7 +125,7 @@
}
else
{
- retryCount = *retryAttemps;
+ eventServiceConfig.retryAttempts = *retryAttemps;
}
}
@@ -137,12 +140,13 @@
}
else
{
- retryTimeoutInterval = *retryInterval;
+ eventServiceConfig.retryTimeoutInterval =
+ *retryInterval;
}
}
EventServiceManager::getInstance().setEventServiceConfig(
- std::make_tuple(enabled, retryCount, retryTimeoutInterval));
+ eventServiceConfig);
});
}