Add Persistent Heartbeat subscription properties
This adds Heartbeat parameters to subscriptions so that the future
heartbeat implementation can use those parameters specified by the
schema [1][2][3].
- SendHeartbeat
- HeartbeatIntervalMinutes
Tested:
1. POST Subscription
- Create a subscription (e.g. via Redfish-Event-Listener) or like
```
curl -k -H "Content-Type: application/json" -X POST https://${bmc}/redfish/v1/EventService/Subscriptions \
-d '{
"Context": "Public",
"DeliveryRetryPolicy": "TerminateAfterRetries",
"Destination": "https://DESTINATION-IPADDR/Redfish-Evt-Listener",
"EventFormatType": "Event",
"HeartbeatIntervalMinutes": 2,
"HttpHeaders": [],
"MessageIds": [],
"MetricReportDefinitions": [],
"Protocol": "Redfish",
"RegistryPrefixes": [],
"ResourceTypes": [],
"SendHeartbeat": true,
"SubscriptionType": "RedfishEvent",
"VerifyCertificate": true
}'
```
2. GET the subscription and check the content
```
SUBID=<id>
curl -k -X GET https://${bmc}/redfish/v1/EventService/Subscriptions/${SUBID}
```
3. PATCH Subscription
- PATCH with various SendHeartbeat & HeartbeatIntervalMinutes
For example,
```
curl -k -X PATCH https://${bmc}/redfish/v1/EventService/Subscriptions/${SUBID} \
-H "Content-Type: application/json" \
-d '{"SendHeartbeat":true, "HeartbeatIntervalMinutes":10}'
```
- Restart bmcweb or reboot BMC
- Get the subscription data and see whether the heartbeat properties are
persistent.
4. Redfish Validator Service passes
[1] https://github.com/openbmc/bmcweb/blob/d109e2b60f7bb367dc8115475c6cb86bca6e1914/redfish-core/schema/dmtf/json-schema/EventDestination.v1_15_0.json#L356
[2] https://github.com/openbmc/bmcweb/blob/d109e2b60f7bb367dc8115475c6cb86bca6e1914/redfish-core/schema/dmtf/json-schema/EventDestination.v1_15_0.json#L222
[3] https://www.dmtf.org/sites/default/files/standards/documents/DSP2046_2022.3.html
Change-Id: I9e7feadb2e851ca320147df2231f65ece58ddf25
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
diff --git a/Redfish.md b/Redfish.md
index bc41ae5..cd5b726 100644
--- a/Redfish.md
+++ b/Redfish.md
@@ -492,9 +492,11 @@
- Destination
- EventTypes
- Context
+- HeartbeatIntervalMinutes
- OriginResources
- RegistryPrefixes
- Protocol
+- SendHeartbeat
### /redfish/v1/JsonSchemas/
diff --git a/include/event_service_store.hpp b/include/event_service_store.hpp
index b0a1d97..ffbb389 100644
--- a/include/event_service_store.hpp
+++ b/include/event_service_store.hpp
@@ -7,6 +7,7 @@
#include <boost/url/url.hpp>
#include <nlohmann/json.hpp>
+#include <limits>
#include <memory>
#include <string>
#include <vector>
@@ -22,6 +23,10 @@
std::string protocol;
bool verifyCertificate = true;
std::string retryPolicy;
+ bool sendHeartbeat = false;
+ // This value of hbIntervalMinutes is just a reasonable default value and
+ // most clients will update it if sendHeartbeat is turned on
+ uint64_t hbIntervalMinutes = 10;
std::string customText;
std::string eventFormatType;
std::string subscriptionType;
@@ -93,6 +98,25 @@
}
subvalue.retryPolicy = *value;
}
+ else if (element.first == "SendHeartbeat")
+ {
+ const bool* value = element.second.get_ptr<const bool*>();
+ if (value == nullptr)
+ {
+ continue;
+ }
+ subvalue.sendHeartbeat = *value;
+ }
+ else if (element.first == "HeartbeatIntervalMinutes")
+ {
+ const uint64_t* value =
+ element.second.get_ptr<const uint64_t*>();
+ if (value == nullptr || *value < 1 || *value > 65535)
+ {
+ continue;
+ }
+ subvalue.hbIntervalMinutes = *value;
+ }
else if (element.first == "Context")
{
const std::string* value =
diff --git a/include/persistent_data.hpp b/include/persistent_data.hpp
index afcdb14..0cdedcd 100644
--- a/include/persistent_data.hpp
+++ b/include/persistent_data.hpp
@@ -326,6 +326,9 @@
subscription["Id"] = subValue.id;
subscription["Context"] = subValue.customText;
subscription["DeliveryRetryPolicy"] = subValue.retryPolicy;
+ subscription["SendHeartbeat"] = subValue.sendHeartbeat;
+ subscription["HeartbeatIntervalMinutes"] =
+ subValue.hbIntervalMinutes;
subscription["Destination"] = subValue.destinationUrl;
subscription["EventFormatType"] = subValue.eventFormatType;
subscription["HttpHeaders"] = std::move(headers);
diff --git a/redfish-core/lib/event_service.hpp b/redfish-core/lib/event_service.hpp
index d415496..04ca735 100644
--- a/redfish-core/lib/event_service.hpp
+++ b/redfish-core/lib/event_service.hpp
@@ -34,6 +34,7 @@
#include <charconv>
#include <memory>
+#include <optional>
#include <ranges>
#include <span>
#include <string>
@@ -299,6 +300,8 @@
std::optional<std::string> subscriptionType;
std::optional<std::string> eventFormatType2;
std::optional<std::string> retryPolicy;
+ std::optional<bool> sendHeartbeat;
+ std::optional<uint64_t> hbIntervalMinutes;
std::optional<std::vector<std::string>> msgIds;
std::optional<std::vector<std::string>> regPrefixes;
std::optional<std::vector<std::string>> originResources;
@@ -312,6 +315,7 @@
"DeliveryRetryPolicy", retryPolicy, //
"Destination", destUrl, //
"EventFormatType", eventFormatType2, //
+ "HeartbeatIntervalMinutes", hbIntervalMinutes, //
"HttpHeaders", headers, //
"MessageIds", msgIds, //
"MetricReportDefinitions", mrdJsonArray, //
@@ -319,6 +323,7 @@
"Protocol", protocol, //
"RegistryPrefixes", regPrefixes, //
"ResourceTypes", resTypes, //
+ "SendHeartbeat", sendHeartbeat, //
"SubscriptionType", subscriptionType, //
"VerifyCertificate", verifyCertificate //
))
@@ -403,6 +408,18 @@
"RetryPolicy", "Protocol");
return;
}
+ if (sendHeartbeat)
+ {
+ messages::propertyValueConflict(
+ asyncResp->res, "SendHeartbeat", "Protocol");
+ return;
+ }
+ if (hbIntervalMinutes)
+ {
+ messages::propertyValueConflict(
+ asyncResp->res, "HeartbeatIntervalMinutes", "Protocol");
+ return;
+ }
if (msgIds)
{
messages::propertyValueConflict(asyncResp->res,
@@ -646,6 +663,21 @@
// Default "TerminateAfterRetries"
subValue->userSub->retryPolicy = "TerminateAfterRetries";
}
+ if (sendHeartbeat)
+ {
+ subValue->userSub->sendHeartbeat = *sendHeartbeat;
+ }
+ if (hbIntervalMinutes)
+ {
+ if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
+ {
+ messages::propertyValueOutOfRange(
+ asyncResp->res, *hbIntervalMinutes,
+ "HeartbeatIntervalMinutes");
+ return;
+ }
+ subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
+ }
if (mrdJsonArray)
{
@@ -730,6 +762,8 @@
jVal["MessageIds"] = userSub.registryMsgIds;
jVal["DeliveryRetryPolicy"] = userSub.retryPolicy;
+ jVal["SendHeartbeat"] = userSub.sendHeartbeat;
+ jVal["HeartbeatIntervalMinutes"] = userSub.hbIntervalMinutes;
jVal["VerifyCertificate"] = userSub.verifyCertificate;
nlohmann::json::array_t mrdJsonArray;
@@ -766,6 +800,8 @@
std::optional<std::string> context;
std::optional<std::string> retryPolicy;
+ std::optional<bool> sendHeartbeat;
+ std::optional<uint64_t> hbIntervalMinutes;
std::optional<bool> verifyCertificate;
std::optional<std::vector<nlohmann::json::object_t>> headers;
@@ -773,7 +809,9 @@
req, asyncResp->res, //
"Context", context, //
"DeliveryRetryPolicy", retryPolicy, //
+ "HeartbeatIntervalMinutes", hbIntervalMinutes, //
"HttpHeaders", headers, //
+ "SendHeartbeat", sendHeartbeat, //
"VerifyCertificate", verifyCertificate //
))
{
@@ -821,6 +859,22 @@
subValue->userSub->retryPolicy = *retryPolicy;
}
+ if (sendHeartbeat)
+ {
+ subValue->userSub->sendHeartbeat = *sendHeartbeat;
+ }
+ if (hbIntervalMinutes)
+ {
+ if (*hbIntervalMinutes < 1 || *hbIntervalMinutes > 65535)
+ {
+ messages::propertyValueOutOfRange(
+ asyncResp->res, *hbIntervalMinutes,
+ "HeartbeatIntervalMinutes");
+ return;
+ }
+ subValue->userSub->hbIntervalMinutes = *hbIntervalMinutes;
+ }
+
if (verifyCertificate)
{
subValue->userSub->verifyCertificate = *verifyCertificate;