Add Journal EventLog to Manager
In order to get access to the EventLog on multi-host platforms,
add Journal EventLog to Manager.
This implementation is based on the discussion we had on
patch 76319 [1].
TLDR: On multi-host, we technically would have to split the event log
on a per host node basis, so that each host node has its own
specific event log.
However, this is currently not supported so we had to decide,
whether we put it on a specific ComputerSystem, or refactor the current
implementation of the EventLog, to allow for the EventLog LogService to
be part of the Managers resource.
We chose the latter one, because a), it is not clear on which
ComputerSystem to put the EventLog, as long as we aren't splitting the
event log per host node, and b), if that particular
ComputerSystem is not existing at runtime, there would be no access to
the EventLog at all.
This feature can be enabled with the redfish-eventlog-location meson
option. By default it is set to 'systems', which translates to the
EventLog being under the Systems resource.
To enable the EventLog under the Managers resource set
```
-Dredfish-eventlog-location=managers
```
This in turn, disables the EventLog under the ComputerSystem resource.
Tested: Redfish validation succeeded for both ComputerSystem and
Managers tree.
```
curl command:
curl -w "@curl-format.txt" -c cjar -b cjar -k -X GET 'https://'"${BMC}"':4443/redfish/v1/'"$ROUTE"'' \
-H 'X-Auth-Token: '"$BMCWEB_SESSION_TOKEN"''
GET /redfish/v1/Managers/bmc/LogServices
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices",
"@odata.type": "#LogServiceCollection.LogServiceCollection",
"Description": "Collection of LogServices for this Manager",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/Journal"
},
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog"
}
],
"Members@odata.count": 2,
"Name": "Open BMC Log Services Collection"
}
GET /redfish/v1/Managers/bmc/LogServices/EventLog
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog",
"@odata.type": "#LogService.v1_2_0.LogService",
"Actions": {
"#LogService.ClearLog": {
"target": "/redfish/v1/Managers/bmc/LogServices/EventLog/Actions/LogService.ClearLog"
}
},
"DateTime": "2025-09-24T15:22:36+00:00",
"DateTimeLocalOffset": "+00:00",
"Description": "Manager Event Log Service",
"Entries": {
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog/Entries"
},
"Id": "EventLog",
"Name": "Event Log Service",
"OverWritePolicy": "WrapsWhenFull"
}
GET /redfish/v1/Managers/bmc/LogServices/EventLog/Entries
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog/Entries",
"@odata.type": "#LogEntryCollection.LogEntryCollection",
"Description": "Collection of Manager Event Log Entries",
"Members": [
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog/Entries/1730009576",
"@odata.type": "#LogEntry.v1_9_0.LogEntry",
"Created": "2024-10-27T06:12:56+00:00",
"EntryType": "Event",
"Id": "1730009576",
"Message": "Host system DC power is off",
"MessageArgs": [],
"MessageId": "OpenBMC.0.1.DCPowerOff",
"Name": "Manager Event Log Entry",
"Severity": "OK"
},
...
],
"Members@odata.count": 2820,
"Members@odata.nextLink": "/redfish/v1/Managers/bmc/LogServices/EventLog/Entries?$skip=1000",
"Name": "Manager Event Log Entries"
}
GET /redfish/v1/Managers/bmc/LogServices/EventLog/Entries/1730009576
{
"@odata.id": "/redfish/v1/Managers/bmc/LogServices/EventLog/Entries/1730009576",
"@odata.type": "#LogEntry.v1_9_0.LogEntry",
"Created": "2024-10-27T06:12:56+00:00",
"EntryType": "Event",
"Id": "1730009576",
"Message": "Host system DC power is off",
"MessageArgs": [],
"MessageId": "OpenBMC.0.1.DCPowerOff",
"Name": "Manager Event Log Entry",
"Severity": "OK"
}
```
ClearLog action:
Log files are being successfully deleted from /var/log
[1] https://gerrit.openbmc.org/c/openbmc/bmcweb/+/76319
Change-Id: If5b4fe10151b6bfd28a1b49c41f8cfcec1b9132c
Signed-off-by: Oliver Brewka <oliver.brewka@9elements.com>
diff --git a/config/meson.build b/config/meson.build
index 7e4b6f8..172daba 100644
--- a/config/meson.build
+++ b/config/meson.build
@@ -50,6 +50,7 @@
'mutual-tls-common-name-parsing-default',
'redfish-manager-uri-name',
'redfish-system-uri-name',
+ 'redfish-eventlog-location',
]
int_options = ['http-body-limit', 'watchdog-timeout-seconds']
diff --git a/docs/Redfish.md b/docs/Redfish.md
index 1d6d34c..46357b6 100644
--- a/docs/Redfish.md
+++ b/docs/Redfish.md
@@ -655,6 +655,12 @@
These two implementations do not work together, so choosing one will disable the
other.
+By default, the EventLog Entries LogService resides under the System resource.
+However, it is possible to change the location to the Manager resource with the
+`-DBMCWEB_REDFISH_EVENTLOG_LOCATION=managers` option flag. Apart from the
+different redfish parent resource, EventLog Entries LogService under Manager
+functions the same like the default implementation under the System resource.
+
#### LogServiceCollection
- Description
diff --git a/meson.options b/meson.options
index 0268c66..d598026 100644
--- a/meson.options
+++ b/meson.options
@@ -140,6 +140,20 @@
/redfish/v1/Systems/system/LogServices/HostLogger''',
)
+# BMCWEB_REDFISH_EVENTLOG_LOCATION
+option(
+ 'redfish-eventlog-location',
+ type: 'combo',
+ choices: ['systems', 'managers'],
+ value: 'systems',
+ description: '''Set which Redfish resource enables event log service
+ transactions through Redfish. By default, this option
+ is set to systems. In that case paths are under
+ /redfish/v1/Systems/<redfish-system-uri-name>/LogServices/EventLog
+ Change to managers, for paths to be under
+ /redfish/v1/Managers/<redfish-manager-uri-name>/LogServices/EventLog''',
+)
+
# BMCWEB_REDFISH_PROVISIONING_FEATURE
option(
'redfish-provisioning-feature',
diff --git a/redfish-core/lib/log_services.hpp b/redfish-core/lib/log_services.hpp
index 3538f76..9ee2ed6 100644
--- a/redfish-core/lib/log_services.hpp
+++ b/redfish-core/lib/log_services.hpp
@@ -932,11 +932,17 @@
"Collection of LogServices for this Computer System";
nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
logServiceArray = nlohmann::json::array();
- nlohmann::json::object_t eventLog;
- eventLog["@odata.id"] =
- std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
- BMCWEB_REDFISH_SYSTEM_URI_NAME);
- logServiceArray.emplace_back(std::move(eventLog));
+
+ if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "systems" &&
+ !BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
+ {
+ nlohmann::json::object_t eventLog;
+ eventLog["@odata.id"] =
+ std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
+ BMCWEB_REDFISH_SYSTEM_URI_NAME);
+ logServiceArray.emplace_back(std::move(eventLog));
+ }
+
if constexpr (BMCWEB_REDFISH_DUMP_LOG)
{
nlohmann::json::object_t dumpLog;
@@ -1036,6 +1042,15 @@
logServiceArray.emplace_back(std::move(journal));
}
+ if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "managers")
+ {
+ nlohmann::json::object_t eventLog;
+ eventLog["@odata.id"] =
+ boost::urls::format("/redfish/v1/Managers/{}/LogServices/EventLog",
+ BMCWEB_REDFISH_MANAGER_URI_NAME);
+ logServiceArray.emplace_back(std::move(eventLog));
+ }
+
asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
if constexpr (BMCWEB_REDFISH_DUMP_LOG)
@@ -1112,6 +1127,25 @@
asyncResp, eventlog_utils::LogServiceParent::Systems);
}
+inline void handleManagersEventLogServiceGet(
+
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& managerId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "Manager", managerId);
+ return;
+ }
+ eventlog_utils::handleSystemsAndManagersEventLogServiceGet(
+ asyncResp, eventlog_utils::LogServiceParent::Managers);
+}
+
inline void getDumpServiceInfo(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& dumpType)
@@ -2173,4 +2207,11 @@
std::bind_front(handleSystemsEventLogServiceGet, std::ref(app)));
}
+inline void requestRoutesManagersEventLogService(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/EventLog/")
+ .privileges(redfish::privileges::getLogService)
+ .methods(boost::beast::http::verb::get)(
+ std::bind_front(handleManagersEventLogServiceGet, std::ref(app)));
+}
} // namespace redfish
diff --git a/redfish-core/lib/manager_logservices_journal_eventlog.hpp b/redfish-core/lib/manager_logservices_journal_eventlog.hpp
new file mode 100644
index 0000000..38ca86b
--- /dev/null
+++ b/redfish-core/lib/manager_logservices_journal_eventlog.hpp
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+// SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
+#pragma once
+
+#include "app.hpp"
+#include "async_resp.hpp"
+#include "error_messages.hpp"
+#include "http_request.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "utils/eventlog_utils.hpp"
+#include "utils/query_param.hpp"
+
+#include <boost/beast/http/field.hpp>
+#include <boost/beast/http/status.hpp>
+#include <boost/beast/http/verb.hpp>
+#include <boost/system/linux_error.hpp>
+#include <boost/url/format.hpp>
+#include <boost/url/url.hpp>
+#include <sdbusplus/message.hpp>
+#include <sdbusplus/message/native_types.hpp>
+#include <sdbusplus/unpack_properties.hpp>
+
+#include <string>
+
+namespace redfish
+{
+
+inline void handleManagersLogServiceEventLogLogEntryCollection(
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& managerId)
+{
+ query_param::QueryCapabilities capabilities = {
+ .canDelegateTop = true,
+ .canDelegateSkip = true,
+ };
+ query_param::Query delegatedQuery;
+ if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
+ delegatedQuery, capabilities))
+ {
+ return;
+ }
+ if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "Manager", managerId);
+ return;
+ }
+
+ eventlog_utils::
+ handleSystemsAndManagersLogServiceEventLogLogEntryCollection(
+ asyncResp, delegatedQuery,
+ eventlog_utils::LogServiceParent::Managers);
+}
+
+inline void handleManagersJournalEventLogEntry(
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& managerId, const std::string& param)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "Manager", managerId);
+ return;
+ }
+ eventlog_utils::handleSystemsAndManagersLogServiceEventLogEntriesGet(
+ asyncResp, param, eventlog_utils::LogServiceParent::Managers);
+}
+
+inline void handleManagersJournalEventLogClear(
+
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& managerId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+ if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
+ {
+ messages::resourceNotFound(asyncResp->res, "Manager", managerId);
+ return;
+ }
+ eventlog_utils::handleSystemsAndManagersLogServicesEventLogActionsClearPost(
+ asyncResp);
+}
+
+inline void requestRoutesManagersJournalEventLog(App& app)
+{
+ BMCWEB_ROUTE(app,
+ "/redfish/v1/Managers/<str>/LogServices/EventLog/Entries/")
+ .privileges(redfish::privileges::getLogEntryCollection)
+ .methods(boost::beast::http::verb::get)(std::bind_front(
+ handleManagersLogServiceEventLogLogEntryCollection, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app, "/redfish/v1/Managers/<str>/LogServices/EventLog/Entries/<str>/")
+ .privileges(redfish::privileges::getLogEntry)
+ .methods(boost::beast::http::verb::get)(
+ std::bind_front(handleManagersJournalEventLogEntry, std::ref(app)));
+
+ BMCWEB_ROUTE(
+ app,
+ "/redfish/v1/Managers/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
+ .privileges(redfish::privileges::
+ postLogServiceSubOverComputerSystemLogServiceCollection)
+ .methods(boost::beast::http::verb::post)(
+ std::bind_front(handleManagersJournalEventLogClear, std::ref(app)));
+}
+} // namespace redfish
diff --git a/redfish-core/src/redfish.cpp b/redfish-core/src/redfish.cpp
index 67bd25e..45ad5cf 100644
--- a/redfish-core/src/redfish.cpp
+++ b/redfish-core/src/redfish.cpp
@@ -23,6 +23,7 @@
#include "log_services.hpp"
#include "manager_diagnostic_data.hpp"
#include "manager_logservices_journal.hpp"
+#include "manager_logservices_journal_eventlog.hpp"
#include "managers.hpp"
#include "memory.hpp"
#include "message_registries.hpp"
@@ -115,10 +116,34 @@
requestRoutesCableCollection(app);
requestRoutesSystemsLogServiceCollection(app);
- requestRoutesSystemsEventLogService(app);
+ requestRoutesManagersLogServiceCollection(app);
requestRoutesSystemsLogServicesPostCode(app);
+ if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "systems")
+ {
+ requestRoutesSystemsEventLogService(app);
+ if constexpr (BMCWEB_REDFISH_DBUS_LOG)
+ {
+ requestRoutesDBusLogServiceActionsClear(app);
+ requestRoutesDBusEventLogEntryCollection(app);
+ requestRoutesDBusEventLogEntry(app);
+ requestRoutesDBusEventLogEntryDownload(app);
+ }
+ else
+ {
+ requestRoutesJournalEventLogEntryCollection(app);
+ requestRoutesJournalEventLogEntry(app);
+ requestRoutesJournalEventLogClear(app);
+ }
+ }
+
+ if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "managers")
+ {
+ requestRoutesManagersEventLogService(app);
+ requestRoutesManagersJournalEventLog(app);
+ }
+
if constexpr (BMCWEB_REDFISH_DUMP_LOG)
{
requestRoutesSystemDumpService(app);
@@ -140,8 +165,6 @@
requestRoutesFaultLogDumpClear(app);
}
- requestRoutesManagersLogServiceCollection(app);
-
if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
{
requestRoutesBMCJournalLogService(app);
@@ -171,20 +194,6 @@
requestNBDVirtualMediaRoutes(app);
}
- if constexpr (BMCWEB_REDFISH_DBUS_LOG)
- {
- requestRoutesDBusLogServiceActionsClear(app);
- requestRoutesDBusEventLogEntryCollection(app);
- requestRoutesDBusEventLogEntry(app);
- requestRoutesDBusEventLogEntryDownload(app);
- }
- else
- {
- requestRoutesJournalEventLogEntryCollection(app);
- requestRoutesJournalEventLogEntry(app);
- requestRoutesJournalEventLogClear(app);
- }
-
if constexpr (BMCWEB_REDFISH_HOST_LOGGER)
{
requestRoutesSystemsLogServiceHostlogger(app);