blob: c74bfa2986ac1d10f5ed83b037e1981d1a5593f5 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070022
Jason M. Billse1f26342018-07-18 12:12:00 -070023#include <systemd/sd-journal.h>
24
Jason M. Bills4851d452019-03-28 11:27:48 -070025#include <boost/algorithm/string/split.hpp>
26#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070027#include <boost/container/flat_map.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070028#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070029#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070030#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080031#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070032
33namespace redfish
34{
35
Jason M. Bills424c4172019-03-21 13:50:33 -070036constexpr char const *CrashdumpObject = "com.intel.crashdump";
37constexpr char const *CrashdumpPath = "/com/intel/crashdump";
38constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
39constexpr char const *CrashdumpInterface = "com.intel.crashdump";
40constexpr char const *CrashdumpOnDemandInterface =
41 "com.intel.crashdump.OnDemand";
42constexpr char const *CrashdumpRawPECIInterface =
43 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070044
Jason M. Bills4851d452019-03-28 11:27:48 -070045namespace message_registries
46{
47static const Message *getMessageFromRegistry(
48 const std::string &messageKey,
49 const boost::beast::span<const MessageEntry> registry)
50{
51 boost::beast::span<const MessageEntry>::const_iterator messageIt =
52 std::find_if(registry.cbegin(), registry.cend(),
53 [&messageKey](const MessageEntry &messageEntry) {
54 return !std::strcmp(messageEntry.first,
55 messageKey.c_str());
56 });
57 if (messageIt != registry.cend())
58 {
59 return &messageIt->second;
60 }
61
62 return nullptr;
63}
64
65static const Message *getMessage(const std::string_view &messageID)
66{
67 // Redfish MessageIds are in the form
68 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
69 // the right Message
70 std::vector<std::string> fields;
71 fields.reserve(4);
72 boost::split(fields, messageID, boost::is_any_of("."));
73 std::string &registryName = fields[0];
74 std::string &messageKey = fields[3];
75
76 // Find the right registry and check it for the MessageKey
77 if (std::string(base::header.registryPrefix) == registryName)
78 {
79 return getMessageFromRegistry(
80 messageKey, boost::beast::span<const MessageEntry>(base::registry));
81 }
82 if (std::string(openbmc::header.registryPrefix) == registryName)
83 {
84 return getMessageFromRegistry(
85 messageKey,
86 boost::beast::span<const MessageEntry>(openbmc::registry));
87 }
88 return nullptr;
89}
90} // namespace message_registries
91
James Feistf6150402019-01-08 10:36:20 -080092namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070093
Andrew Geisslercb92c032018-08-17 07:56:14 -070094using GetManagedPropertyType = boost::container::flat_map<
95 std::string,
96 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
97 int32_t, uint32_t, int64_t, uint64_t, double>>;
98
99using GetManagedObjectsType = boost::container::flat_map<
100 sdbusplus::message::object_path,
101 boost::container::flat_map<std::string, GetManagedPropertyType>>;
102
103inline std::string translateSeverityDbusToRedfish(const std::string &s)
104{
105 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
106 {
107 return "Critical";
108 }
109 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
114 {
115 return "OK";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
118 {
119 return "Critical";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
126 {
127 return "OK";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
134 {
135 return "Warning";
136 }
137 return "";
138}
139
Jason M. Bills16428a12018-11-02 12:42:29 -0700140static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800141 const std::string_view &field,
142 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700143{
144 const char *data = nullptr;
145 size_t length = 0;
146 int ret = 0;
147 // Get the metadata from the requested field of the journal entry
Ed Tanousb01bf292019-03-25 19:25:26 +0000148 ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
149 &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 if (ret < 0)
151 {
152 return ret;
153 }
Ed Tanous39e77502019-03-04 17:35:53 -0800154 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700155 // Only use the content after the "=" character.
156 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
157 return ret;
158}
159
160static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800161 const std::string_view &field, const int &base,
Jason M. Bills16428a12018-11-02 12:42:29 -0700162 int &contents)
163{
164 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800165 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700166 // Get the metadata from the requested field of the journal entry
167 ret = getJournalMetadata(journal, field, metadata);
168 if (ret < 0)
169 {
170 return ret;
171 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000172 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700173 return ret;
174}
175
176static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
177{
178 int ret = 0;
179 uint64_t timestamp = 0;
180 ret = sd_journal_get_realtime_usec(journal, &timestamp);
181 if (ret < 0)
182 {
183 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
184 << strerror(-ret);
185 return false;
186 }
187 time_t t =
188 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
189 struct tm *loctime = localtime(&t);
190 char entryTime[64] = {};
191 if (NULL != loctime)
192 {
193 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
194 }
195 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800196 std::string_view t1(entryTime);
197 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700198 if (t1.size() > 2 && t2.size() > 2)
199 {
200 t1.remove_suffix(2);
201 t2.remove_prefix(t2.size() - 2);
202 }
Ed Tanous39e77502019-03-04 17:35:53 -0800203 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700204 return true;
205}
206
207static bool getSkipParam(crow::Response &res, const crow::Request &req,
208 long &skip)
209{
210 char *skipParam = req.urlParams.get("$skip");
211 if (skipParam != nullptr)
212 {
213 char *ptr = nullptr;
214 skip = std::strtol(skipParam, &ptr, 10);
215 if (*skipParam == '\0' || *ptr != '\0')
216 {
217
218 messages::queryParameterValueTypeError(res, std::string(skipParam),
219 "$skip");
220 return false;
221 }
222 if (skip < 0)
223 {
224
225 messages::queryParameterOutOfRange(res, std::to_string(skip),
226 "$skip", "greater than 0");
227 return false;
228 }
229 }
230 return true;
231}
232
233static constexpr const long maxEntriesPerPage = 1000;
234static bool getTopParam(crow::Response &res, const crow::Request &req,
235 long &top)
236{
237 char *topParam = req.urlParams.get("$top");
238 if (topParam != nullptr)
239 {
240 char *ptr = nullptr;
241 top = std::strtol(topParam, &ptr, 10);
242 if (*topParam == '\0' || *ptr != '\0')
243 {
244 messages::queryParameterValueTypeError(res, std::string(topParam),
245 "$top");
246 return false;
247 }
248 if (top < 1 || top > maxEntriesPerPage)
249 {
250
251 messages::queryParameterOutOfRange(
252 res, std::to_string(top), "$top",
253 "1-" + std::to_string(maxEntriesPerPage));
254 return false;
255 }
256 }
257 return true;
258}
259
260static bool getUniqueEntryID(sd_journal *journal, std::string &entryID)
261{
262 int ret = 0;
263 static uint64_t prevTs = 0;
264 static int index = 0;
265 // Get the entry timestamp
266 uint64_t curTs = 0;
267 ret = sd_journal_get_realtime_usec(journal, &curTs);
268 if (ret < 0)
269 {
270 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
271 << strerror(-ret);
272 return false;
273 }
274 // If the timestamp isn't unique, increment the index
275 if (curTs == prevTs)
276 {
277 index++;
278 }
279 else
280 {
281 // Otherwise, reset it
282 index = 0;
283 }
284 // Save the timestamp
285 prevTs = curTs;
286
287 entryID = std::to_string(curTs);
288 if (index > 0)
289 {
290 entryID += "_" + std::to_string(index);
291 }
292 return true;
293}
294
Jason M. Bills95820182019-04-22 16:25:34 -0700295static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID)
296{
297 static uint64_t prevTs = 0;
298 static int index = 0;
299 // Get the entry timestamp
300 uint64_t curTs = 0;
301 std::tm timeStruct = {};
302 std::istringstream entryStream(logEntry);
303 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
304 {
305 curTs = std::mktime(&timeStruct);
306 }
307 // If the timestamp isn't unique, increment the index
308 if (curTs == prevTs)
309 {
310 index++;
311 }
312 else
313 {
314 // Otherwise, reset it
315 index = 0;
316 }
317 // Save the timestamp
318 prevTs = curTs;
319
320 entryID = std::to_string(curTs);
321 if (index > 0)
322 {
323 entryID += "_" + std::to_string(index);
324 }
325 return true;
326}
327
Jason M. Bills16428a12018-11-02 12:42:29 -0700328static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
329 uint64_t &timestamp, uint16_t &index)
330{
331 if (entryID.empty())
332 {
333 return false;
334 }
335 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800336 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700337
338 auto underscorePos = tsStr.find("_");
339 if (underscorePos != tsStr.npos)
340 {
341 // Timestamp has an index
342 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800343 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700344 indexStr.remove_prefix(underscorePos + 1);
345 std::size_t pos;
346 try
347 {
Ed Tanous39e77502019-03-04 17:35:53 -0800348 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700349 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000350 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700351 {
352 messages::resourceMissingAtURI(res, entryID);
353 return false;
354 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000355 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700356 {
357 messages::resourceMissingAtURI(res, entryID);
358 return false;
359 }
360 if (pos != indexStr.size())
361 {
362 messages::resourceMissingAtURI(res, entryID);
363 return false;
364 }
365 }
366 // Timestamp has no index
367 std::size_t pos;
368 try
369 {
Ed Tanous39e77502019-03-04 17:35:53 -0800370 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700371 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000372 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700373 {
374 messages::resourceMissingAtURI(res, entryID);
375 return false;
376 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000377 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700378 {
379 messages::resourceMissingAtURI(res, entryID);
380 return false;
381 }
382 if (pos != tsStr.size())
383 {
384 messages::resourceMissingAtURI(res, entryID);
385 return false;
386 }
387 return true;
388}
389
Jason M. Bills95820182019-04-22 16:25:34 -0700390static bool
391 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
392{
393 static const std::filesystem::path redfishLogDir = "/var/log";
394 static const std::string redfishLogFilename = "redfish";
395
396 // Loop through the directory looking for redfish log files
397 for (const std::filesystem::directory_entry &dirEnt :
398 std::filesystem::directory_iterator(redfishLogDir))
399 {
400 // If we find a redfish log file, save the path
401 std::string filename = dirEnt.path().filename();
402 if (boost::starts_with(filename, redfishLogFilename))
403 {
404 redfishLogFiles.emplace_back(redfishLogDir / filename);
405 }
406 }
407 // As the log files rotate, they are appended with a ".#" that is higher for
408 // the older logs. Since we don't expect more than 10 log files, we
409 // can just sort the list to get them in order from newest to oldest
410 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
411
412 return !redfishLogFiles.empty();
413}
414
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800415class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700416{
417 public:
418 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800419 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800420 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800421 {
422 entityPrivileges = {
423 {boost::beast::http::verb::get, {{"Login"}}},
424 {boost::beast::http::verb::head, {{"Login"}}},
425 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
426 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
427 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
428 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
429 }
430
431 private:
432 /**
433 * Functions triggers appropriate requests on DBus
434 */
435 void doGet(crow::Response &res, const crow::Request &req,
436 const std::vector<std::string> &params) override
437 {
438 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800439 // Collections don't include the static data added by SubRoute because
440 // it has a duplicate entry for members
441 asyncResp->res.jsonValue["@odata.type"] =
442 "#LogServiceCollection.LogServiceCollection";
443 asyncResp->res.jsonValue["@odata.context"] =
444 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
445 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800446 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800447 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
448 asyncResp->res.jsonValue["Description"] =
449 "Collection of LogServices for this Computer System";
450 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
451 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800452 logServiceArray.push_back(
453 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800454#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
455 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500456 {{"@odata.id",
457 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800458#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800459 asyncResp->res.jsonValue["Members@odata.count"] =
460 logServiceArray.size();
461 }
462};
463
464class EventLogService : public Node
465{
466 public:
467 template <typename CrowApp>
468 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800469 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800470 {
471 entityPrivileges = {
472 {boost::beast::http::verb::get, {{"Login"}}},
473 {boost::beast::http::verb::head, {{"Login"}}},
474 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
475 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
476 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
477 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
478 }
479
480 private:
481 void doGet(crow::Response &res, const crow::Request &req,
482 const std::vector<std::string> &params) override
483 {
484 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
485
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800486 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800487 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800488 asyncResp->res.jsonValue["@odata.type"] =
489 "#LogService.v1_1_0.LogService";
490 asyncResp->res.jsonValue["@odata.context"] =
491 "/redfish/v1/$metadata#LogService.LogService";
492 asyncResp->res.jsonValue["Name"] = "Event Log Service";
493 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
494 asyncResp->res.jsonValue["Id"] = "Event Log";
495 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
496 asyncResp->res.jsonValue["Entries"] = {
497 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800498 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500499 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
500
501 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
502 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700503 }
504};
505
506class EventLogClear : public Node
507{
508 public:
509 EventLogClear(CrowApp &app) :
510 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
511 "LogService.ClearLog/")
512 {
513 entityPrivileges = {
514 {boost::beast::http::verb::get, {{"Login"}}},
515 {boost::beast::http::verb::head, {{"Login"}}},
516 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
517 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
518 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
519 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
520 }
521
522 private:
523 void doPost(crow::Response &res, const crow::Request &req,
524 const std::vector<std::string> &params) override
525 {
526 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
527
528 // Clear the EventLog by deleting the log files
529 std::vector<std::filesystem::path> redfishLogFiles;
530 if (getRedfishLogFiles(redfishLogFiles))
531 {
532 for (const std::filesystem::path &file : redfishLogFiles)
533 {
534 std::error_code ec;
535 std::filesystem::remove(file, ec);
536 }
537 }
538
539 // Reload rsyslog so it knows to start new log files
540 crow::connections::systemBus->async_method_call(
541 [asyncResp](const boost::system::error_code ec) {
542 if (ec)
543 {
544 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
545 messages::internalError(asyncResp->res);
546 return;
547 }
548
549 messages::success(asyncResp->res);
550 },
551 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
552 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
553 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800554 }
555};
556
Jason M. Bills95820182019-04-22 16:25:34 -0700557static int fillEventLogEntryJson(const std::string &logEntryID,
558 const std::string logEntry,
559 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800560{
Jason M. Bills95820182019-04-22 16:25:34 -0700561 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700562 // First get the Timestamp
563 size_t space = logEntry.find_first_of(" ");
564 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700565 {
566 return 1;
567 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700568 std::string timestamp = logEntry.substr(0, space);
569 // Then get the log contents
570 size_t entryStart = logEntry.find_first_not_of(" ", space);
571 if (entryStart == std::string::npos)
572 {
573 return 1;
574 }
575 std::string_view entry(logEntry);
576 entry.remove_prefix(entryStart);
577 // Use split to separate the entry into its fields
578 std::vector<std::string> logEntryFields;
579 boost::split(logEntryFields, entry, boost::is_any_of(","),
580 boost::token_compress_on);
581 // We need at least a MessageId to be valid
582 if (logEntryFields.size() < 1)
583 {
584 return 1;
585 }
586 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700587
Jason M. Bills4851d452019-03-28 11:27:48 -0700588 // Get the Message from the MessageRegistry
589 const message_registries::Message *message =
590 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800591
Jason M. Bills4851d452019-03-28 11:27:48 -0700592 std::string msg;
593 std::string severity;
594 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700596 msg = message->message;
597 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800598 }
599
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700600 // Get the MessageArgs from the log if there are any
601 boost::beast::span<std::string> messageArgs;
602 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700603 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700604 std::string &messageArgsStart = logEntryFields[1];
605 // If the first string is empty, assume there are no MessageArgs
606 std::size_t messageArgsSize = 0;
607 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700608 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700609 messageArgsSize = logEntryFields.size() - 1;
610 }
611
612 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
613
614 // Fill the MessageArgs into the Message
615 int i = 0;
616 for (const std::string &messageArg : messageArgs)
617 {
618 std::string argStr = "%" + std::to_string(++i);
619 size_t argPos = msg.find(argStr);
620 if (argPos != std::string::npos)
621 {
622 msg.replace(argPos, argStr.length(), messageArg);
623 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700624 }
625 }
626
Jason M. Bills95820182019-04-22 16:25:34 -0700627 // Get the Created time from the timestamp. The log timestamp is in RFC3339
628 // format which matches the Redfish format except for the fractional seconds
629 // between the '.' and the '+', so just remove them.
630 std::size_t dot = timestamp.find_first_of(".");
631 std::size_t plus = timestamp.find_first_of("+");
632 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800633 {
Jason M. Bills95820182019-04-22 16:25:34 -0700634 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800635 }
636
637 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700638 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700639 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800640 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800641 {"@odata.id",
Jason M. Bills95820182019-04-22 16:25:34 -0700642 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/#" +
643 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800644 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700645 {"Id", logEntryID},
646 {"Message", std::move(msg)},
647 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800648 {"MessageArgs", std::move(messageArgs)},
649 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700650 {"Severity", std::move(severity)},
651 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800652 return 0;
653}
654
Anthony Wilson27062602019-04-22 02:10:09 -0500655class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800656{
657 public:
658 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500659 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800660 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800661 {
662 entityPrivileges = {
663 {boost::beast::http::verb::get, {{"Login"}}},
664 {boost::beast::http::verb::head, {{"Login"}}},
665 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
666 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
667 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
668 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
669 }
670
671 private:
672 void doGet(crow::Response &res, const crow::Request &req,
673 const std::vector<std::string> &params) override
674 {
675 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
676 long skip = 0;
677 long top = maxEntriesPerPage; // Show max entries by default
678 if (!getSkipParam(asyncResp->res, req, skip))
679 {
680 return;
681 }
682 if (!getTopParam(asyncResp->res, req, top))
683 {
684 return;
685 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800686 // Collections don't include the static data added by SubRoute because
687 // it has a duplicate entry for members
688 asyncResp->res.jsonValue["@odata.type"] =
689 "#LogEntryCollection.LogEntryCollection";
690 asyncResp->res.jsonValue["@odata.context"] =
691 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
692 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800693 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800694 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
695 asyncResp->res.jsonValue["Description"] =
696 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700697
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800698 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
699 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700700 // Go through the log files and create a unique ID for each entry
701 std::vector<std::filesystem::path> redfishLogFiles;
702 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000703 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700704 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700705
706 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700707 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
708 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800709 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700710 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700711 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800712 {
713 continue;
714 }
715
Jason M. Bills95820182019-04-22 16:25:34 -0700716 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800717 {
Jason M. Bills95820182019-04-22 16:25:34 -0700718 entryCount++;
719 // Handle paging using skip (number of entries to skip from the
720 // start) and top (number of entries to display)
721 if (entryCount <= skip || entryCount > skip + top)
722 {
723 continue;
724 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800725
Jason M. Bills95820182019-04-22 16:25:34 -0700726 std::string idStr;
727 if (!getUniqueEntryID(logEntry, idStr))
728 {
729 continue;
730 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800731
Jason M. Bills95820182019-04-22 16:25:34 -0700732 logEntryArray.push_back({});
733 nlohmann::json &bmcLogEntry = logEntryArray.back();
734 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
735 {
736 messages::internalError(asyncResp->res);
737 return;
738 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800739 }
740 }
741 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
742 if (skip + top < entryCount)
743 {
744 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700745 "/redfish/v1/Systems/system/LogServices/EventLog/"
746 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800747 std::to_string(skip + top);
748 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500749 }
750};
751
752class DBusEventLogEntryCollection : public Node
753{
754 public:
755 template <typename CrowApp>
756 DBusEventLogEntryCollection(CrowApp &app) :
757 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
758 {
759 entityPrivileges = {
760 {boost::beast::http::verb::get, {{"Login"}}},
761 {boost::beast::http::verb::head, {{"Login"}}},
762 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
763 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
764 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
765 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
766 }
767
768 private:
769 void doGet(crow::Response &res, const crow::Request &req,
770 const std::vector<std::string> &params) override
771 {
772 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
773
774 // Collections don't include the static data added by SubRoute because
775 // it has a duplicate entry for members
776 asyncResp->res.jsonValue["@odata.type"] =
777 "#LogEntryCollection.LogEntryCollection";
778 asyncResp->res.jsonValue["@odata.context"] =
779 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
780 asyncResp->res.jsonValue["@odata.id"] =
781 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
782 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
783 asyncResp->res.jsonValue["Description"] =
784 "Collection of System Event Log Entries";
785
Andrew Geisslercb92c032018-08-17 07:56:14 -0700786 // DBus implementation of EventLog/Entries
787 // Make call to Logging Service to find all log entry objects
788 crow::connections::systemBus->async_method_call(
789 [asyncResp](const boost::system::error_code ec,
790 GetManagedObjectsType &resp) {
791 if (ec)
792 {
793 // TODO Handle for specific error code
794 BMCWEB_LOG_ERROR
795 << "getLogEntriesIfaceData resp_handler got error "
796 << ec;
797 messages::internalError(asyncResp->res);
798 return;
799 }
800 nlohmann::json &entriesArray =
801 asyncResp->res.jsonValue["Members"];
802 entriesArray = nlohmann::json::array();
803 for (auto &objectPath : resp)
804 {
805 for (auto &interfaceMap : objectPath.second)
806 {
807 if (interfaceMap.first !=
808 "xyz.openbmc_project.Logging.Entry")
809 {
810 BMCWEB_LOG_DEBUG << "Bailing early on "
811 << interfaceMap.first;
812 continue;
813 }
814 entriesArray.push_back({});
815 nlohmann::json &thisEntry = entriesArray.back();
816 uint32_t *id;
817 std::time_t timestamp;
818 std::string *severity, *message;
819 bool *resolved;
820 for (auto &propertyMap : interfaceMap.second)
821 {
822 if (propertyMap.first == "Id")
823 {
824 id = sdbusplus::message::variant_ns::get_if<
825 uint32_t>(&propertyMap.second);
826 if (id == nullptr)
827 {
828 messages::propertyMissing(asyncResp->res,
829 "Id");
830 }
831 }
832 else if (propertyMap.first == "Timestamp")
833 {
834 const uint64_t *millisTimeStamp =
835 std::get_if<uint64_t>(&propertyMap.second);
836 if (millisTimeStamp == nullptr)
837 {
838 messages::propertyMissing(asyncResp->res,
839 "Timestamp");
840 }
841 // Retrieve Created property with format:
842 // yyyy-mm-ddThh:mm:ss
843 std::chrono::milliseconds chronoTimeStamp(
844 *millisTimeStamp);
845 timestamp =
846 std::chrono::duration_cast<
847 std::chrono::seconds>(chronoTimeStamp)
848 .count();
849 }
850 else if (propertyMap.first == "Severity")
851 {
852 severity = std::get_if<std::string>(
853 &propertyMap.second);
854 if (severity == nullptr)
855 {
856 messages::propertyMissing(asyncResp->res,
857 "Severity");
858 }
859 }
860 else if (propertyMap.first == "Message")
861 {
862 message = std::get_if<std::string>(
863 &propertyMap.second);
864 if (message == nullptr)
865 {
866 messages::propertyMissing(asyncResp->res,
867 "Message");
868 }
869 }
870 }
871 thisEntry = {
872 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
873 {"@odata.context", "/redfish/v1/"
874 "$metadata#LogEntry.LogEntry"},
875 {"@odata.id",
876 "/redfish/v1/Systems/system/LogServices/EventLog/"
877 "Entries/" +
878 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500879 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700880 {"Id", std::to_string(*id)},
881 {"Message", *message},
882 {"EntryType", "Event"},
883 {"Severity",
884 translateSeverityDbusToRedfish(*severity)},
885 {"Created", crow::utility::getDateTime(timestamp)}};
886 }
887 }
888 std::sort(entriesArray.begin(), entriesArray.end(),
889 [](const nlohmann::json &left,
890 const nlohmann::json &right) {
891 return (left["Id"] <= right["Id"]);
892 });
893 asyncResp->res.jsonValue["Members@odata.count"] =
894 entriesArray.size();
895 },
896 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
897 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800898 }
899};
900
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500901class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800902{
903 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500904 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800905 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800906 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
907 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800908 {
909 entityPrivileges = {
910 {boost::beast::http::verb::get, {{"Login"}}},
911 {boost::beast::http::verb::head, {{"Login"}}},
912 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
913 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
914 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
915 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
916 }
917
918 private:
919 void doGet(crow::Response &res, const crow::Request &req,
920 const std::vector<std::string> &params) override
921 {
922 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -0800923 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800924 {
925 messages::internalError(asyncResp->res);
926 return;
927 }
Ed Tanous029573d2019-02-01 10:57:49 -0800928 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -0700929
Andrew Geisslercb92c032018-08-17 07:56:14 -0700930 // DBus implementation of EventLog/Entries
931 // Make call to Logging Service to find all log entry objects
932 crow::connections::systemBus->async_method_call(
933 [asyncResp, entryID](const boost::system::error_code ec,
934 GetManagedPropertyType &resp) {
935 if (ec)
936 {
937 BMCWEB_LOG_ERROR
938 << "EventLogEntry (DBus) resp_handler got error " << ec;
939 messages::internalError(asyncResp->res);
940 return;
941 }
942 uint32_t *id;
943 std::time_t timestamp;
944 std::string *severity, *message;
945 bool *resolved;
946 for (auto &propertyMap : resp)
947 {
948 if (propertyMap.first == "Id")
949 {
950 id = std::get_if<uint32_t>(&propertyMap.second);
951 if (id == nullptr)
952 {
953 messages::propertyMissing(asyncResp->res, "Id");
954 }
955 }
956 else if (propertyMap.first == "Timestamp")
957 {
958 const uint64_t *millisTimeStamp =
959 std::get_if<uint64_t>(&propertyMap.second);
960 if (millisTimeStamp == nullptr)
961 {
962 messages::propertyMissing(asyncResp->res,
963 "Timestamp");
964 }
965 // Retrieve Created property with format:
966 // yyyy-mm-ddThh:mm:ss
967 std::chrono::milliseconds chronoTimeStamp(
968 *millisTimeStamp);
969 timestamp =
970 std::chrono::duration_cast<std::chrono::seconds>(
971 chronoTimeStamp)
972 .count();
973 }
974 else if (propertyMap.first == "Severity")
975 {
976 severity =
977 std::get_if<std::string>(&propertyMap.second);
978 if (severity == nullptr)
979 {
980 messages::propertyMissing(asyncResp->res,
981 "Severity");
982 }
983 }
984 else if (propertyMap.first == "Message")
985 {
986 message = std::get_if<std::string>(&propertyMap.second);
987 if (message == nullptr)
988 {
989 messages::propertyMissing(asyncResp->res,
990 "Message");
991 }
992 }
993 }
994 asyncResp->res.jsonValue = {
995 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
996 {"@odata.context", "/redfish/v1/"
997 "$metadata#LogEntry.LogEntry"},
998 {"@odata.id",
999 "/redfish/v1/Systems/system/LogServices/EventLog/"
1000 "Entries/" +
1001 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001002 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001003 {"Id", std::to_string(*id)},
1004 {"Message", *message},
1005 {"EntryType", "Event"},
1006 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001007 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001008 },
1009 "xyz.openbmc_project.Logging",
1010 "/xyz/openbmc_project/logging/entry/" + entryID,
1011 "org.freedesktop.DBus.Properties", "GetAll",
1012 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001013 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001014
1015 void doDelete(crow::Response &res, const crow::Request &req,
1016 const std::vector<std::string> &params) override
1017 {
1018
1019 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1020
1021 auto asyncResp = std::make_shared<AsyncResp>(res);
1022
1023 if (params.size() != 1)
1024 {
1025 messages::internalError(asyncResp->res);
1026 return;
1027 }
1028 std::string entryID = params[0];
1029
1030 dbus::utility::escapePathForDbus(entryID);
1031
1032 // Process response from Logging service.
1033 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1034 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1035 if (ec)
1036 {
1037 // TODO Handle for specific error code
1038 BMCWEB_LOG_ERROR
1039 << "EventLogEntry (DBus) doDelete respHandler got error "
1040 << ec;
1041 asyncResp->res.result(
1042 boost::beast::http::status::internal_server_error);
1043 return;
1044 }
1045
1046 asyncResp->res.result(boost::beast::http::status::ok);
1047 };
1048
1049 // Make call to Logging service to request Delete Log
1050 crow::connections::systemBus->async_method_call(
1051 respHandler, "xyz.openbmc_project.Logging",
1052 "/xyz/openbmc_project/logging/entry/" + entryID,
1053 "xyz.openbmc_project.Object.Delete", "Delete");
1054 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001055};
1056
1057class BMCLogServiceCollection : public Node
1058{
1059 public:
1060 template <typename CrowApp>
1061 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001062 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001063 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001064 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001065 {boost::beast::http::verb::get, {{"Login"}}},
1066 {boost::beast::http::verb::head, {{"Login"}}},
1067 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1068 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1069 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1070 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001071 }
1072
1073 private:
1074 /**
1075 * Functions triggers appropriate requests on DBus
1076 */
1077 void doGet(crow::Response &res, const crow::Request &req,
1078 const std::vector<std::string> &params) override
1079 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001080 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001081 // Collections don't include the static data added by SubRoute because
1082 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001083 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001084 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001085 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001086 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001087 asyncResp->res.jsonValue["@odata.id"] =
1088 "/redfish/v1/Managers/bmc/LogServices";
1089 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1090 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001091 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001092 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1093 logServiceArray = nlohmann::json::array();
1094#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1095 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001096 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001097#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001098 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001099 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001100 }
1101};
1102
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001104{
1105 public:
1106 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001107 BMCJournalLogService(CrowApp &app) :
1108 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001109 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001110 entityPrivileges = {
1111 {boost::beast::http::verb::get, {{"Login"}}},
1112 {boost::beast::http::verb::head, {{"Login"}}},
1113 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1114 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1115 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1116 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1117 }
1118
1119 private:
1120 void doGet(crow::Response &res, const crow::Request &req,
1121 const std::vector<std::string> &params) override
1122 {
1123 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001124 asyncResp->res.jsonValue["@odata.type"] =
1125 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001126 asyncResp->res.jsonValue["@odata.id"] =
1127 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001128 asyncResp->res.jsonValue["@odata.context"] =
1129 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001130 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1131 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1132 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001133 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001134 asyncResp->res.jsonValue["Entries"] = {
1135 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001136 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001137 }
1138};
1139
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1141 sd_journal *journal,
1142 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001143{
1144 // Get the Log Entry contents
1145 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001146
Ed Tanous39e77502019-03-04 17:35:53 -08001147 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001148 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001149 if (ret < 0)
1150 {
1151 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1152 return 1;
1153 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001154
1155 // Get the severity from the PRIORITY field
Jason M. Billse1f26342018-07-18 12:12:00 -07001156 int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001157 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001158 if (ret < 0)
1159 {
1160 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
1161 return 1;
1162 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001163
1164 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001165 std::string entryTimeStr;
1166 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001167 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001168 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001169 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001170
1171 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001172 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001173 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001174 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001175 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1176 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001177 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001178 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001179 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001180 {"EntryType", "Oem"},
1181 {"Severity",
1182 severity <= 2 ? "Critical"
1183 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
Ed Tanous086be232019-05-23 11:47:09 -07001184 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001185 {"Created", std::move(entryTimeStr)}};
1186 return 0;
1187}
1188
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001189class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001190{
1191 public:
1192 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001193 BMCJournalLogEntryCollection(CrowApp &app) :
1194 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001195 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001196 entityPrivileges = {
1197 {boost::beast::http::verb::get, {{"Login"}}},
1198 {boost::beast::http::verb::head, {{"Login"}}},
1199 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1200 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1201 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1202 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1203 }
1204
1205 private:
1206 void doGet(crow::Response &res, const crow::Request &req,
1207 const std::vector<std::string> &params) override
1208 {
1209 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001210 static constexpr const long maxEntriesPerPage = 1000;
1211 long skip = 0;
1212 long top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001213 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001214 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001215 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001216 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001217 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001218 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001219 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001220 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001221 // Collections don't include the static data added by SubRoute because
1222 // it has a duplicate entry for members
1223 asyncResp->res.jsonValue["@odata.type"] =
1224 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001225 asyncResp->res.jsonValue["@odata.id"] =
1226 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001227 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001228 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001229 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001230 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001231 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1232 asyncResp->res.jsonValue["Description"] =
1233 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001234 asyncResp->res.jsonValue["@odata.id"] =
1235 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001236 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1237 logEntryArray = nlohmann::json::array();
1238
1239 // Go through the journal and use the timestamp to create a unique ID
1240 // for each entry
1241 sd_journal *journalTmp = nullptr;
1242 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1243 if (ret < 0)
1244 {
1245 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001246 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001247 return;
1248 }
1249 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1250 journalTmp, sd_journal_close);
1251 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001252 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001253 SD_JOURNAL_FOREACH(journal.get())
1254 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001255 entryCount++;
1256 // Handle paging using skip (number of entries to skip from the
1257 // start) and top (number of entries to display)
1258 if (entryCount <= skip || entryCount > skip + top)
1259 {
1260 continue;
1261 }
1262
Jason M. Bills16428a12018-11-02 12:42:29 -07001263 std::string idStr;
1264 if (!getUniqueEntryID(journal.get(), idStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001265 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001266 continue;
1267 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001268
Jason M. Billse1f26342018-07-18 12:12:00 -07001269 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001270 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1271 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1272 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001273 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001274 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001275 return;
1276 }
1277 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001278 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1279 if (skip + top < entryCount)
1280 {
1281 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001282 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001283 std::to_string(skip + top);
1284 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001285 }
1286};
1287
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001288class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001289{
1290 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001291 BMCJournalLogEntry(CrowApp &app) :
1292 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001293 std::string())
1294 {
1295 entityPrivileges = {
1296 {boost::beast::http::verb::get, {{"Login"}}},
1297 {boost::beast::http::verb::head, {{"Login"}}},
1298 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1299 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1300 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1301 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1302 }
1303
1304 private:
1305 void doGet(crow::Response &res, const crow::Request &req,
1306 const std::vector<std::string> &params) override
1307 {
1308 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1309 if (params.size() != 1)
1310 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001311 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001312 return;
1313 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001314 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001315 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001316 uint64_t ts = 0;
1317 uint16_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001318 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001319 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001320 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001321 }
1322
1323 sd_journal *journalTmp = nullptr;
1324 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1325 if (ret < 0)
1326 {
1327 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001328 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001329 return;
1330 }
1331 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1332 journalTmp, sd_journal_close);
1333 journalTmp = nullptr;
1334 // Go to the timestamp in the log and move to the entry at the index
1335 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1336 for (int i = 0; i <= index; i++)
1337 {
1338 sd_journal_next(journal.get());
1339 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001340 // Confirm that the entry ID matches what was requested
1341 std::string idStr;
1342 if (!getUniqueEntryID(journal.get(), idStr) || idStr != entryID)
1343 {
1344 messages::resourceMissingAtURI(asyncResp->res, entryID);
1345 return;
1346 }
1347
1348 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1349 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001350 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001351 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001352 return;
1353 }
1354 }
1355};
1356
Jason M. Bills424c4172019-03-21 13:50:33 -07001357class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001358{
1359 public:
1360 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001361 CrashdumpService(CrowApp &app) :
1362 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001363 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001364 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001365 {boost::beast::http::verb::get, {{"Login"}}},
1366 {boost::beast::http::verb::head, {{"Login"}}},
1367 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1368 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1369 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1370 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001371 }
1372
1373 private:
1374 /**
1375 * Functions triggers appropriate requests on DBus
1376 */
1377 void doGet(crow::Response &res, const crow::Request &req,
1378 const std::vector<std::string> &params) override
1379 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001380 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001381 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001382 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001383 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001384 asyncResp->res.jsonValue["@odata.type"] =
1385 "#LogService.v1_1_0.LogService";
1386 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001387 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Bills424c4172019-03-21 13:50:33 -07001388 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1389 asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1390 asyncResp->res.jsonValue["Id"] = "Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001391 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1392 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001393 asyncResp->res.jsonValue["Entries"] = {
1394 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001395 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001396 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001397 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001398 {{"#Crashdump.OnDemand",
1399 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1400 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001401
1402#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001403 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001404 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001405 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1406 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001407#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001408 }
1409};
1410
Jason M. Bills424c4172019-03-21 13:50:33 -07001411class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001412{
1413 public:
1414 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001415 CrashdumpEntryCollection(CrowApp &app) :
1416 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001417 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001418 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001419 {boost::beast::http::verb::get, {{"Login"}}},
1420 {boost::beast::http::verb::head, {{"Login"}}},
1421 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1422 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1423 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1424 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001425 }
1426
1427 private:
1428 /**
1429 * Functions triggers appropriate requests on DBus
1430 */
1431 void doGet(crow::Response &res, const crow::Request &req,
1432 const std::vector<std::string> &params) override
1433 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001434 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001435 // Collections don't include the static data added by SubRoute because
1436 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001437 auto getLogEntriesCallback = [asyncResp](
1438 const boost::system::error_code ec,
1439 const std::vector<std::string> &resp) {
1440 if (ec)
1441 {
1442 if (ec.value() !=
1443 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001444 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001445 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1446 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001447 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001448 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001449 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001450 }
1451 asyncResp->res.jsonValue["@odata.type"] =
1452 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001453 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001454 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001455 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001456 "/redfish/v1/"
1457 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001458 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001459 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001460 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001461 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1462 logEntryArray = nlohmann::json::array();
1463 for (const std::string &objpath : resp)
1464 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001465 // Don't list the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001466 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001467 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001468 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001469 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001470 std::size_t lastPos = objpath.rfind("/");
1471 if (lastPos != std::string::npos)
1472 {
1473 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001474 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Bills424c4172019-03-21 13:50:33 -07001475 "Crashdump/Entries/" +
Jason M. Billse1f26342018-07-18 12:12:00 -07001476 objpath.substr(lastPos + 1)}});
1477 }
1478 }
1479 asyncResp->res.jsonValue["Members@odata.count"] =
1480 logEntryArray.size();
1481 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001482 crow::connections::systemBus->async_method_call(
1483 std::move(getLogEntriesCallback),
1484 "xyz.openbmc_project.ObjectMapper",
1485 "/xyz/openbmc_project/object_mapper",
1486 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001487 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001488 }
1489};
1490
Jason M. Bills424c4172019-03-21 13:50:33 -07001491std::string getLogCreatedTime(const nlohmann::json &Crashdump)
Ed Tanous1da66f72018-07-27 16:13:37 -07001492{
Jason M. Bills424c4172019-03-21 13:50:33 -07001493 nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1494 if (cdIt != Crashdump.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001495 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001496 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1497 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001498 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001499 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1500 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001501 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001502 const std::string *logTime =
1503 tsIt->get_ptr<const std::string *>();
1504 if (logTime != nullptr)
1505 {
1506 return *logTime;
1507 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001508 }
1509 }
1510 }
1511 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1512
1513 return std::string();
1514}
1515
Jason M. Bills424c4172019-03-21 13:50:33 -07001516class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001517{
1518 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001519 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001520 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001521 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001522 std::string())
1523 {
1524 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001525 {boost::beast::http::verb::get, {{"Login"}}},
1526 {boost::beast::http::verb::head, {{"Login"}}},
1527 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1528 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1529 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1530 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001531 }
1532
1533 private:
1534 void doGet(crow::Response &res, const crow::Request &req,
1535 const std::vector<std::string> &params) override
1536 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001537 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001538 if (params.size() != 1)
1539 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001540 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001541 return;
1542 }
Ed Tanousb01bf292019-03-25 19:25:26 +00001543 const uint8_t logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001544 auto getStoredLogCallback = [asyncResp, logId](
1545 const boost::system::error_code ec,
1546 const std::variant<std::string> &resp) {
1547 if (ec)
1548 {
1549 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1550 messages::internalError(asyncResp->res);
1551 return;
1552 }
1553 const std::string *log = std::get_if<std::string>(&resp);
1554 if (log == nullptr)
1555 {
1556 messages::internalError(asyncResp->res);
1557 return;
1558 }
1559 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1560 if (j.is_discarded())
1561 {
1562 messages::internalError(asyncResp->res);
1563 return;
1564 }
1565 std::string t = getLogCreatedTime(j);
1566 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001567 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001568 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1569 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001570 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001571 std::to_string(logId)},
Jason M. Bills424c4172019-03-21 13:50:33 -07001572 {"Name", "CPU Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001573 {"Id", logId},
1574 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001575 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001576 {"Oem", {{"Intel", std::move(j)}}},
1577 {"Created", std::move(t)}};
1578 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001579 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001580 std::move(getStoredLogCallback), CrashdumpObject,
1581 CrashdumpPath + std::string("/") + std::to_string(logId),
1582 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1583 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001584 }
1585};
1586
Jason M. Bills424c4172019-03-21 13:50:33 -07001587class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001588{
1589 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001590 OnDemandCrashdump(CrowApp &app) :
1591 Node(app,
1592 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1593 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001594 {
1595 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001596 {boost::beast::http::verb::get, {{"Login"}}},
1597 {boost::beast::http::verb::head, {{"Login"}}},
1598 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1599 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1600 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1601 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001602 }
1603
1604 private:
1605 void doPost(crow::Response &res, const crow::Request &req,
1606 const std::vector<std::string> &params) override
1607 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001608 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001609 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001610
Jason M. Bills48e46392019-02-13 12:58:37 -08001611 // Only allow one OnDemand Log request at a time
1612 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001613 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001614 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001615 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001616 return;
1617 }
1618 // Make this static so it survives outside this method
1619 static boost::asio::deadline_timer timeout(*req.ioService);
1620
1621 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001622 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001623 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001624 if (ec)
1625 {
1626 // operation_aborted is expected if timer is canceled before
1627 // completion.
1628 if (ec != boost::asio::error::operation_aborted)
1629 {
1630 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1631 }
1632 return;
1633 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001634 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001635
Jason M. Billsf12894f2018-10-09 12:45:45 -07001636 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001637 });
1638
Jason M. Bills48e46392019-02-13 12:58:37 -08001639 auto onDemandLogMatcherCallback = [asyncResp](
1640 sdbusplus::message::message &m) {
1641 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
Ed Tanous1da66f72018-07-27 16:13:37 -07001642 boost::system::error_code ec;
1643 timeout.cancel(ec);
1644 if (ec)
1645 {
1646 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
1647 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001648 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -07001649 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -08001650 std::string, boost::container::flat_map<
1651 std::string, std::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001652 interfacesAdded;
1653 m.read(objPath, interfacesAdded);
Ed Tanousabf2add2019-01-22 16:40:12 -08001654 const std::string *log = std::get_if<std::string>(
Jason M. Bills424c4172019-03-21 13:50:33 -07001655 &interfacesAdded[CrashdumpInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -07001656 if (log == nullptr)
1657 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001658 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001659 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001660 // match object inside which this lambda is executing. Once it
1661 // is set to nullptr, the match object will be destroyed and the
1662 // lambda will lose its context, including res, so it needs to
1663 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001664 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001665 return;
1666 }
1667 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1668 if (j.is_discarded())
1669 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001670 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001671 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001672 // match object inside which this lambda is executing. Once it
1673 // is set to nullptr, the match object will be destroyed and the
1674 // lambda will lose its context, including res, so it needs to
1675 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001676 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001677 return;
1678 }
1679 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -07001680 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001681 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001682 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001683 {"Name", "CPU Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001684 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001685 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001686 {"Oem", {{"Intel", std::move(j)}}},
1687 {"Created", std::move(t)}};
Jason M. Bills48e46392019-02-13 12:58:37 -08001688 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001689 // match object inside which this lambda is executing. Once it is
1690 // set to nullptr, the match object will be destroyed and the lambda
1691 // will lose its context, including res, so it needs to be the last
1692 // thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001693 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001694 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001695 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001696 *crow::connections::systemBus,
1697 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001698 sdbusplus::bus::match::rules::argNpath(0,
1699 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001700 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001701
Jason M. Bills48e46392019-02-13 12:58:37 -08001702 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001703 [asyncResp](const boost::system::error_code ec,
1704 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001705 if (ec)
1706 {
1707 if (ec.value() ==
1708 boost::system::errc::operation_not_supported)
1709 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001710 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001711 }
1712 else
1713 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001714 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001715 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001716 boost::system::error_code timeoutec;
1717 timeout.cancel(timeoutec);
1718 if (timeoutec)
1719 {
1720 BMCWEB_LOG_ERROR << "error canceling timer "
1721 << timeoutec;
1722 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001723 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001724 return;
1725 }
1726 };
1727 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001728 std::move(generateonDemandLogCallback), CrashdumpObject,
1729 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001730 }
1731};
1732
Jason M. Billse1f26342018-07-18 12:12:00 -07001733class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001734{
1735 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001736 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001737 Node(app,
1738 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1739 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001740 {
1741 entityPrivileges = {
1742 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1743 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1744 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1745 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1746 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1747 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1748 }
1749
1750 private:
1751 void doPost(crow::Response &res, const crow::Request &req,
1752 const std::vector<std::string> &params) override
1753 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001754 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001755 uint8_t clientAddress = 0;
1756 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001757 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001758 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1759 "ReadLength", readLength, "PECICommand",
1760 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001761 {
Ed Tanousb1556422018-10-16 14:09:17 -07001762 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001763 }
Ed Tanousb1556422018-10-16 14:09:17 -07001764
Ed Tanous1da66f72018-07-27 16:13:37 -07001765 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001766 auto sendRawPECICallback =
1767 [asyncResp](const boost::system::error_code ec,
1768 const std::vector<uint8_t> &resp) {
1769 if (ec)
1770 {
1771 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1772 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001773 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001774 return;
1775 }
1776 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1777 {"PECIResponse", resp}};
1778 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001779 // Call the SendRawPECI command with the provided data
1780 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001781 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1782 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001783 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001784 }
1785};
1786
Andrew Geisslercb92c032018-08-17 07:56:14 -07001787/**
1788 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1789 */
1790class DBusLogServiceActionsClear : public Node
1791{
1792 public:
1793 DBusLogServiceActionsClear(CrowApp &app) :
1794 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1795 "LogService.Reset")
1796 {
1797 entityPrivileges = {
1798 {boost::beast::http::verb::get, {{"Login"}}},
1799 {boost::beast::http::verb::head, {{"Login"}}},
1800 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1801 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1802 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1803 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1804 }
1805
1806 private:
1807 /**
1808 * Function handles POST method request.
1809 * The Clear Log actions does not require any parameter.The action deletes
1810 * all entries found in the Entries collection for this Log Service.
1811 */
1812 void doPost(crow::Response &res, const crow::Request &req,
1813 const std::vector<std::string> &params) override
1814 {
1815 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1816
1817 auto asyncResp = std::make_shared<AsyncResp>(res);
1818 // Process response from Logging service.
1819 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1820 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1821 if (ec)
1822 {
1823 // TODO Handle for specific error code
1824 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1825 asyncResp->res.result(
1826 boost::beast::http::status::internal_server_error);
1827 return;
1828 }
1829
1830 asyncResp->res.result(boost::beast::http::status::no_content);
1831 };
1832
1833 // Make call to Logging service to request Clear Log
1834 crow::connections::systemBus->async_method_call(
1835 resp_handler, "xyz.openbmc_project.Logging",
1836 "/xyz/openbmc_project/logging",
1837 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1838 }
1839};
Ed Tanous1da66f72018-07-27 16:13:37 -07001840} // namespace redfish