blob: 2284e235374917be2f5a4cc73db9f9c0cfafb65f [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"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070039constexpr char const *crashdumpObject = "com.intel.crashdump";
40constexpr char const *crashdumpPath = "/com/intel/crashdump";
41constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
42constexpr char const *crashdumpInterface = "com.intel.crashdump";
43constexpr char const *deleteAllInterface =
44 "xyz.openbmc_project.Collection.DeleteAll";
45constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070047constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070049
Jason M. Bills4851d452019-03-28 11:27:48 -070050namespace message_registries
51{
52static const Message *getMessageFromRegistry(
53 const std::string &messageKey,
54 const boost::beast::span<const MessageEntry> registry)
55{
56 boost::beast::span<const MessageEntry>::const_iterator messageIt =
57 std::find_if(registry.cbegin(), registry.cend(),
58 [&messageKey](const MessageEntry &messageEntry) {
59 return !std::strcmp(messageEntry.first,
60 messageKey.c_str());
61 });
62 if (messageIt != registry.cend())
63 {
64 return &messageIt->second;
65 }
66
67 return nullptr;
68}
69
70static const Message *getMessage(const std::string_view &messageID)
71{
72 // Redfish MessageIds are in the form
73 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
74 // the right Message
75 std::vector<std::string> fields;
76 fields.reserve(4);
77 boost::split(fields, messageID, boost::is_any_of("."));
78 std::string &registryName = fields[0];
79 std::string &messageKey = fields[3];
80
81 // Find the right registry and check it for the MessageKey
82 if (std::string(base::header.registryPrefix) == registryName)
83 {
84 return getMessageFromRegistry(
85 messageKey, boost::beast::span<const MessageEntry>(base::registry));
86 }
87 if (std::string(openbmc::header.registryPrefix) == registryName)
88 {
89 return getMessageFromRegistry(
90 messageKey,
91 boost::beast::span<const MessageEntry>(openbmc::registry));
92 }
93 return nullptr;
94}
95} // namespace message_registries
96
James Feistf6150402019-01-08 10:36:20 -080097namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070098
Andrew Geisslercb92c032018-08-17 07:56:14 -070099using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500100 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700102
103using GetManagedObjectsType = boost::container::flat_map<
104 sdbusplus::message::object_path,
105 boost::container::flat_map<std::string, GetManagedPropertyType>>;
106
107inline std::string translateSeverityDbusToRedfish(const std::string &s)
108{
109 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
114 {
115 return "Critical";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
118 {
119 return "OK";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
126 {
127 return "Critical";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
134 {
135 return "OK";
136 }
137 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
138 {
139 return "Warning";
140 }
141 return "";
142}
143
raviteja-bc9bb6862020-02-03 11:53:32 -0600144inline void deleteSystemDumpEntry(crow::Response &res,
145 const std::string &entryID)
146{
147 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
148
149 auto respHandler = [asyncResp](const boost::system::error_code ec) {
150 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
151 if (ec)
152 {
153 BMCWEB_LOG_ERROR
154 << "System Dump (DBus) doDelete respHandler got error " << ec;
155 asyncResp->res.result(
156 boost::beast::http::status::internal_server_error);
157 return;
158 }
raviteja-bc9bb6862020-02-03 11:53:32 -0600159 };
160 crow::connections::systemBus->async_method_call(
161 respHandler, "xyz.openbmc_project.Dump.Manager",
162 "/xyz/openbmc_project/dump/entry/" + entryID,
163 "xyz.openbmc_project.Object.Delete", "Delete");
164}
165
Jason M. Bills16428a12018-11-02 12:42:29 -0700166static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800167 const std::string_view &field,
168 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700169{
170 const char *data = nullptr;
171 size_t length = 0;
172 int ret = 0;
173 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700174 ret = sd_journal_get_data(journal, field.data(),
175 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700176 if (ret < 0)
177 {
178 return ret;
179 }
Ed Tanous39e77502019-03-04 17:35:53 -0800180 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700181 // Only use the content after the "=" character.
182 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
183 return ret;
184}
185
186static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800187 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700188 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189{
190 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800191 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700192 // Get the metadata from the requested field of the journal entry
193 ret = getJournalMetadata(journal, field, metadata);
194 if (ret < 0)
195 {
196 return ret;
197 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000198 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 return ret;
200}
201
ZhikuiRena3316fc2020-01-29 14:58:08 -0800202static bool getTimestampStr(const uint64_t usecSinceEpoch,
203 std::string &entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700204{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800205 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Jason M. Bills16428a12018-11-02 12:42:29 -0700206 struct tm *loctime = localtime(&t);
207 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700208 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700209 {
210 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
211 }
212 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800213 std::string_view t1(entryTime);
214 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 if (t1.size() > 2 && t2.size() > 2)
216 {
217 t1.remove_suffix(2);
218 t2.remove_prefix(t2.size() - 2);
219 }
Ed Tanous39e77502019-03-04 17:35:53 -0800220 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 return true;
222}
223
ZhikuiRena3316fc2020-01-29 14:58:08 -0800224static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
225{
226 int ret = 0;
227 uint64_t timestamp = 0;
228 ret = sd_journal_get_realtime_usec(journal, &timestamp);
229 if (ret < 0)
230 {
231 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
232 << strerror(-ret);
233 return false;
234 }
235 return getTimestampStr(timestamp, entryTimestamp);
236}
237
Jason M. Bills16428a12018-11-02 12:42:29 -0700238static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700239 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700240{
241 char *skipParam = req.urlParams.get("$skip");
242 if (skipParam != nullptr)
243 {
244 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700245 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 if (*skipParam == '\0' || *ptr != '\0')
247 {
248
249 messages::queryParameterValueTypeError(res, std::string(skipParam),
250 "$skip");
251 return false;
252 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700253 }
254 return true;
255}
256
Ed Tanous271584a2019-07-09 16:24:22 -0700257static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700258static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700259 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700260{
261 char *topParam = req.urlParams.get("$top");
262 if (topParam != nullptr)
263 {
264 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700265 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700266 if (*topParam == '\0' || *ptr != '\0')
267 {
268 messages::queryParameterValueTypeError(res, std::string(topParam),
269 "$top");
270 return false;
271 }
Ed Tanous271584a2019-07-09 16:24:22 -0700272 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700273 {
274
275 messages::queryParameterOutOfRange(
276 res, std::to_string(top), "$top",
277 "1-" + std::to_string(maxEntriesPerPage));
278 return false;
279 }
280 }
281 return true;
282}
283
Jason M. Billse85d6b12019-07-29 17:01:15 -0700284static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
285 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700286{
287 int ret = 0;
288 static uint64_t prevTs = 0;
289 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700290 if (firstEntry)
291 {
292 prevTs = 0;
293 }
294
Jason M. Bills16428a12018-11-02 12:42:29 -0700295 // Get the entry timestamp
296 uint64_t curTs = 0;
297 ret = sd_journal_get_realtime_usec(journal, &curTs);
298 if (ret < 0)
299 {
300 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
301 << strerror(-ret);
302 return false;
303 }
304 // If the timestamp isn't unique, increment the index
305 if (curTs == prevTs)
306 {
307 index++;
308 }
309 else
310 {
311 // Otherwise, reset it
312 index = 0;
313 }
314 // Save the timestamp
315 prevTs = curTs;
316
317 entryID = std::to_string(curTs);
318 if (index > 0)
319 {
320 entryID += "_" + std::to_string(index);
321 }
322 return true;
323}
324
Jason M. Billse85d6b12019-07-29 17:01:15 -0700325static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
326 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700327{
Ed Tanous271584a2019-07-09 16:24:22 -0700328 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700329 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700330 if (firstEntry)
331 {
332 prevTs = 0;
333 }
334
Jason M. Bills95820182019-04-22 16:25:34 -0700335 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700336 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700337 std::tm timeStruct = {};
338 std::istringstream entryStream(logEntry);
339 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
340 {
341 curTs = std::mktime(&timeStruct);
342 }
343 // If the timestamp isn't unique, increment the index
344 if (curTs == prevTs)
345 {
346 index++;
347 }
348 else
349 {
350 // Otherwise, reset it
351 index = 0;
352 }
353 // Save the timestamp
354 prevTs = curTs;
355
356 entryID = std::to_string(curTs);
357 if (index > 0)
358 {
359 entryID += "_" + std::to_string(index);
360 }
361 return true;
362}
363
Jason M. Bills16428a12018-11-02 12:42:29 -0700364static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700365 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700366{
367 if (entryID.empty())
368 {
369 return false;
370 }
371 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800372 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700373
374 auto underscorePos = tsStr.find("_");
375 if (underscorePos != tsStr.npos)
376 {
377 // Timestamp has an index
378 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800379 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700380 indexStr.remove_prefix(underscorePos + 1);
381 std::size_t pos;
382 try
383 {
Ed Tanous39e77502019-03-04 17:35:53 -0800384 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700385 }
Ed Tanous271584a2019-07-09 16:24:22 -0700386 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 {
388 messages::resourceMissingAtURI(res, entryID);
389 return false;
390 }
Ed Tanous271584a2019-07-09 16:24:22 -0700391 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700392 {
393 messages::resourceMissingAtURI(res, entryID);
394 return false;
395 }
396 if (pos != indexStr.size())
397 {
398 messages::resourceMissingAtURI(res, entryID);
399 return false;
400 }
401 }
402 // Timestamp has no index
403 std::size_t pos;
404 try
405 {
Ed Tanous39e77502019-03-04 17:35:53 -0800406 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700407 }
Ed Tanous271584a2019-07-09 16:24:22 -0700408 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700409 {
410 messages::resourceMissingAtURI(res, entryID);
411 return false;
412 }
Ed Tanous271584a2019-07-09 16:24:22 -0700413 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700414 {
415 messages::resourceMissingAtURI(res, entryID);
416 return false;
417 }
418 if (pos != tsStr.size())
419 {
420 messages::resourceMissingAtURI(res, entryID);
421 return false;
422 }
423 return true;
424}
425
Jason M. Bills95820182019-04-22 16:25:34 -0700426static bool
427 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
428{
429 static const std::filesystem::path redfishLogDir = "/var/log";
430 static const std::string redfishLogFilename = "redfish";
431
432 // Loop through the directory looking for redfish log files
433 for (const std::filesystem::directory_entry &dirEnt :
434 std::filesystem::directory_iterator(redfishLogDir))
435 {
436 // If we find a redfish log file, save the path
437 std::string filename = dirEnt.path().filename();
438 if (boost::starts_with(filename, redfishLogFilename))
439 {
440 redfishLogFiles.emplace_back(redfishLogDir / filename);
441 }
442 }
443 // As the log files rotate, they are appended with a ".#" that is higher for
444 // the older logs. Since we don't expect more than 10 log files, we
445 // can just sort the list to get them in order from newest to oldest
446 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
447
448 return !redfishLogFiles.empty();
449}
450
Johnathan Mantey043a0532020-03-10 17:15:28 -0700451static void ParseCrashdumpParameters(
452 const std::vector<std::pair<std::string, VariantType>> &params,
453 std::string &filename, std::string &timestamp, std::string &logfile)
454{
455 for (auto property : params)
456 {
457 if (property.first == "Timestamp")
458 {
459 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500460 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700461 if (value != nullptr)
462 {
463 timestamp = *value;
464 }
465 }
466 else if (property.first == "Filename")
467 {
468 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500469 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700470 if (value != nullptr)
471 {
472 filename = *value;
473 }
474 }
475 else if (property.first == "Log")
476 {
477 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500478 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700479 if (value != nullptr)
480 {
481 logfile = *value;
482 }
483 }
484 }
485}
486
ZhikuiRena3316fc2020-01-29 14:58:08 -0800487constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800488class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700489{
490 public:
491 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800492 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800493 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800494 {
495 entityPrivileges = {
496 {boost::beast::http::verb::get, {{"Login"}}},
497 {boost::beast::http::verb::head, {{"Login"}}},
498 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
499 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
500 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
501 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
502 }
503
504 private:
505 /**
506 * Functions triggers appropriate requests on DBus
507 */
508 void doGet(crow::Response &res, const crow::Request &req,
509 const std::vector<std::string> &params) override
510 {
511 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800512 // Collections don't include the static data added by SubRoute because
513 // it has a duplicate entry for members
514 asyncResp->res.jsonValue["@odata.type"] =
515 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800516 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800517 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800518 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
519 asyncResp->res.jsonValue["Description"] =
520 "Collection of LogServices for this Computer System";
521 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
522 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800523 logServiceArray.push_back(
524 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600525#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
526 logServiceArray.push_back(
527 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
528#endif
529
Jason M. Billsd53dd412019-02-12 17:16:22 -0800530#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
531 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500532 {{"@odata.id",
533 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800534#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800535 asyncResp->res.jsonValue["Members@odata.count"] =
536 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800537
538 crow::connections::systemBus->async_method_call(
539 [asyncResp](const boost::system::error_code ec,
540 const std::vector<std::string> &subtreePath) {
541 if (ec)
542 {
543 BMCWEB_LOG_ERROR << ec;
544 return;
545 }
546
547 for (auto &pathStr : subtreePath)
548 {
549 if (pathStr.find("PostCode") != std::string::npos)
550 {
551 nlohmann::json &logServiceArray =
552 asyncResp->res.jsonValue["Members"];
553 logServiceArray.push_back(
554 {{"@odata.id", "/redfish/v1/Systems/system/"
555 "LogServices/PostCodes"}});
556 asyncResp->res.jsonValue["Members@odata.count"] =
557 logServiceArray.size();
558 return;
559 }
560 }
561 },
562 "xyz.openbmc_project.ObjectMapper",
563 "/xyz/openbmc_project/object_mapper",
564 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
565 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800566 }
567};
568
569class EventLogService : public Node
570{
571 public:
572 template <typename CrowApp>
573 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800574 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800575 {
576 entityPrivileges = {
577 {boost::beast::http::verb::get, {{"Login"}}},
578 {boost::beast::http::verb::head, {{"Login"}}},
579 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
580 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
581 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
582 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
583 }
584
585 private:
586 void doGet(crow::Response &res, const crow::Request &req,
587 const std::vector<std::string> &params) override
588 {
589 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
590
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800591 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800592 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800593 asyncResp->res.jsonValue["@odata.type"] =
594 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595 asyncResp->res.jsonValue["Name"] = "Event Log Service";
596 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500597 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800598 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
599 asyncResp->res.jsonValue["Entries"] = {
600 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800601 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500602 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
603
604 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
605 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700606 }
607};
608
Tim Lee1f56a3a2019-10-09 10:17:57 +0800609class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700610{
611 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800612 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700613 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
614 "LogService.ClearLog/")
615 {
616 entityPrivileges = {
617 {boost::beast::http::verb::get, {{"Login"}}},
618 {boost::beast::http::verb::head, {{"Login"}}},
619 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
620 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
621 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
622 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
623 }
624
625 private:
626 void doPost(crow::Response &res, const crow::Request &req,
627 const std::vector<std::string> &params) override
628 {
629 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
630
631 // Clear the EventLog by deleting the log files
632 std::vector<std::filesystem::path> redfishLogFiles;
633 if (getRedfishLogFiles(redfishLogFiles))
634 {
635 for (const std::filesystem::path &file : redfishLogFiles)
636 {
637 std::error_code ec;
638 std::filesystem::remove(file, ec);
639 }
640 }
641
642 // Reload rsyslog so it knows to start new log files
643 crow::connections::systemBus->async_method_call(
644 [asyncResp](const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
648 messages::internalError(asyncResp->res);
649 return;
650 }
651
652 messages::success(asyncResp->res);
653 },
654 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
655 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
656 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800657 }
658};
659
Jason M. Bills95820182019-04-22 16:25:34 -0700660static int fillEventLogEntryJson(const std::string &logEntryID,
661 const std::string logEntry,
662 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800663{
Jason M. Bills95820182019-04-22 16:25:34 -0700664 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700665 // First get the Timestamp
666 size_t space = logEntry.find_first_of(" ");
667 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700668 {
669 return 1;
670 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700671 std::string timestamp = logEntry.substr(0, space);
672 // Then get the log contents
673 size_t entryStart = logEntry.find_first_not_of(" ", space);
674 if (entryStart == std::string::npos)
675 {
676 return 1;
677 }
678 std::string_view entry(logEntry);
679 entry.remove_prefix(entryStart);
680 // Use split to separate the entry into its fields
681 std::vector<std::string> logEntryFields;
682 boost::split(logEntryFields, entry, boost::is_any_of(","),
683 boost::token_compress_on);
684 // We need at least a MessageId to be valid
685 if (logEntryFields.size() < 1)
686 {
687 return 1;
688 }
689 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700690
Jason M. Bills4851d452019-03-28 11:27:48 -0700691 // Get the Message from the MessageRegistry
692 const message_registries::Message *message =
693 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800694
Jason M. Bills4851d452019-03-28 11:27:48 -0700695 std::string msg;
696 std::string severity;
697 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800698 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700699 msg = message->message;
700 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800701 }
702
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700703 // Get the MessageArgs from the log if there are any
704 boost::beast::span<std::string> messageArgs;
705 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700706 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700707 std::string &messageArgsStart = logEntryFields[1];
708 // If the first string is empty, assume there are no MessageArgs
709 std::size_t messageArgsSize = 0;
710 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700711 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700712 messageArgsSize = logEntryFields.size() - 1;
713 }
714
715 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
716
717 // Fill the MessageArgs into the Message
718 int i = 0;
719 for (const std::string &messageArg : messageArgs)
720 {
721 std::string argStr = "%" + std::to_string(++i);
722 size_t argPos = msg.find(argStr);
723 if (argPos != std::string::npos)
724 {
725 msg.replace(argPos, argStr.length(), messageArg);
726 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700727 }
728 }
729
Jason M. Bills95820182019-04-22 16:25:34 -0700730 // Get the Created time from the timestamp. The log timestamp is in RFC3339
731 // format which matches the Redfish format except for the fractional seconds
732 // between the '.' and the '+', so just remove them.
733 std::size_t dot = timestamp.find_first_of(".");
734 std::size_t plus = timestamp.find_first_of("+");
735 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800736 {
Jason M. Bills95820182019-04-22 16:25:34 -0700737 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800738 }
739
740 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700741 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700742 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800743 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700744 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700745 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800746 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700747 {"Id", logEntryID},
748 {"Message", std::move(msg)},
749 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800750 {"MessageArgs", std::move(messageArgs)},
751 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700752 {"Severity", std::move(severity)},
753 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800754 return 0;
755}
756
Anthony Wilson27062602019-04-22 02:10:09 -0500757class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800758{
759 public:
760 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500761 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800762 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800763 {
764 entityPrivileges = {
765 {boost::beast::http::verb::get, {{"Login"}}},
766 {boost::beast::http::verb::head, {{"Login"}}},
767 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
768 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
769 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
770 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
771 }
772
773 private:
774 void doGet(crow::Response &res, const crow::Request &req,
775 const std::vector<std::string> &params) override
776 {
777 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700778 uint64_t skip = 0;
779 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800780 if (!getSkipParam(asyncResp->res, req, skip))
781 {
782 return;
783 }
784 if (!getTopParam(asyncResp->res, req, top))
785 {
786 return;
787 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800788 // Collections don't include the static data added by SubRoute because
789 // it has a duplicate entry for members
790 asyncResp->res.jsonValue["@odata.type"] =
791 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800792 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800793 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800794 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
795 asyncResp->res.jsonValue["Description"] =
796 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700797
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800798 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
799 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700800 // Go through the log files and create a unique ID for each entry
801 std::vector<std::filesystem::path> redfishLogFiles;
802 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000803 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700804 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700805
806 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700807 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
808 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800809 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700810 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700811 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800812 {
813 continue;
814 }
815
Jason M. Billse85d6b12019-07-29 17:01:15 -0700816 // Reset the unique ID on the first entry
817 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700818 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800819 {
Jason M. Bills95820182019-04-22 16:25:34 -0700820 entryCount++;
821 // Handle paging using skip (number of entries to skip from the
822 // start) and top (number of entries to display)
823 if (entryCount <= skip || entryCount > skip + top)
824 {
825 continue;
826 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800827
Jason M. Bills95820182019-04-22 16:25:34 -0700828 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700829 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700830 {
831 continue;
832 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800833
Jason M. Billse85d6b12019-07-29 17:01:15 -0700834 if (firstEntry)
835 {
836 firstEntry = false;
837 }
838
Jason M. Bills95820182019-04-22 16:25:34 -0700839 logEntryArray.push_back({});
840 nlohmann::json &bmcLogEntry = logEntryArray.back();
841 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
842 {
843 messages::internalError(asyncResp->res);
844 return;
845 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800846 }
847 }
848 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
849 if (skip + top < entryCount)
850 {
851 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700852 "/redfish/v1/Systems/system/LogServices/EventLog/"
853 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800854 std::to_string(skip + top);
855 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500856 }
857};
858
Jason M. Bills897967d2019-07-29 17:05:30 -0700859class JournalEventLogEntry : public Node
860{
861 public:
862 JournalEventLogEntry(CrowApp &app) :
863 Node(app,
864 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
865 std::string())
866 {
867 entityPrivileges = {
868 {boost::beast::http::verb::get, {{"Login"}}},
869 {boost::beast::http::verb::head, {{"Login"}}},
870 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
871 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
872 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
874 }
875
876 private:
877 void doGet(crow::Response &res, const crow::Request &req,
878 const std::vector<std::string> &params) override
879 {
880 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
881 if (params.size() != 1)
882 {
883 messages::internalError(asyncResp->res);
884 return;
885 }
886 const std::string &targetID = params[0];
887
888 // Go through the log files and check the unique ID for each entry to
889 // find the target entry
890 std::vector<std::filesystem::path> redfishLogFiles;
891 getRedfishLogFiles(redfishLogFiles);
892 std::string logEntry;
893
894 // Oldest logs are in the last file, so start there and loop backwards
895 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
896 it++)
897 {
898 std::ifstream logStream(*it);
899 if (!logStream.is_open())
900 {
901 continue;
902 }
903
904 // Reset the unique ID on the first entry
905 bool firstEntry = true;
906 while (std::getline(logStream, logEntry))
907 {
908 std::string idStr;
909 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
910 {
911 continue;
912 }
913
914 if (firstEntry)
915 {
916 firstEntry = false;
917 }
918
919 if (idStr == targetID)
920 {
921 if (fillEventLogEntryJson(idStr, logEntry,
922 asyncResp->res.jsonValue) != 0)
923 {
924 messages::internalError(asyncResp->res);
925 return;
926 }
927 return;
928 }
929 }
930 }
931 // Requested ID was not found
932 messages::resourceMissingAtURI(asyncResp->res, targetID);
933 }
934};
935
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500936class DBusEventLogEntryCollection : public Node
937{
938 public:
939 template <typename CrowApp>
940 DBusEventLogEntryCollection(CrowApp &app) :
941 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
942 {
943 entityPrivileges = {
944 {boost::beast::http::verb::get, {{"Login"}}},
945 {boost::beast::http::verb::head, {{"Login"}}},
946 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
947 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
948 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
949 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
950 }
951
952 private:
953 void doGet(crow::Response &res, const crow::Request &req,
954 const std::vector<std::string> &params) override
955 {
956 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
957
958 // Collections don't include the static data added by SubRoute because
959 // it has a duplicate entry for members
960 asyncResp->res.jsonValue["@odata.type"] =
961 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500962 asyncResp->res.jsonValue["@odata.id"] =
963 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
964 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
965 asyncResp->res.jsonValue["Description"] =
966 "Collection of System Event Log Entries";
967
Andrew Geisslercb92c032018-08-17 07:56:14 -0700968 // DBus implementation of EventLog/Entries
969 // Make call to Logging Service to find all log entry objects
970 crow::connections::systemBus->async_method_call(
971 [asyncResp](const boost::system::error_code ec,
972 GetManagedObjectsType &resp) {
973 if (ec)
974 {
975 // TODO Handle for specific error code
976 BMCWEB_LOG_ERROR
977 << "getLogEntriesIfaceData resp_handler got error "
978 << ec;
979 messages::internalError(asyncResp->res);
980 return;
981 }
982 nlohmann::json &entriesArray =
983 asyncResp->res.jsonValue["Members"];
984 entriesArray = nlohmann::json::array();
985 for (auto &objectPath : resp)
986 {
987 for (auto &interfaceMap : objectPath.second)
988 {
989 if (interfaceMap.first !=
990 "xyz.openbmc_project.Logging.Entry")
991 {
992 BMCWEB_LOG_DEBUG << "Bailing early on "
993 << interfaceMap.first;
994 continue;
995 }
996 entriesArray.push_back({});
997 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700998 uint32_t *id = nullptr;
999 std::time_t timestamp{};
1000 std::string *severity = nullptr;
1001 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001002 for (auto &propertyMap : interfaceMap.second)
1003 {
1004 if (propertyMap.first == "Id")
1005 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001006 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001007 if (id == nullptr)
1008 {
1009 messages::propertyMissing(asyncResp->res,
1010 "Id");
1011 }
1012 }
1013 else if (propertyMap.first == "Timestamp")
1014 {
1015 const uint64_t *millisTimeStamp =
1016 std::get_if<uint64_t>(&propertyMap.second);
1017 if (millisTimeStamp == nullptr)
1018 {
1019 messages::propertyMissing(asyncResp->res,
1020 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001021 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001022 }
1023 // Retrieve Created property with format:
1024 // yyyy-mm-ddThh:mm:ss
1025 std::chrono::milliseconds chronoTimeStamp(
1026 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001027 timestamp = std::chrono::duration_cast<
1028 std::chrono::duration<int>>(
1029 chronoTimeStamp)
1030 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001031 }
1032 else if (propertyMap.first == "Severity")
1033 {
1034 severity = std::get_if<std::string>(
1035 &propertyMap.second);
1036 if (severity == nullptr)
1037 {
1038 messages::propertyMissing(asyncResp->res,
1039 "Severity");
1040 }
1041 }
1042 else if (propertyMap.first == "Message")
1043 {
1044 message = std::get_if<std::string>(
1045 &propertyMap.second);
1046 if (message == nullptr)
1047 {
1048 messages::propertyMissing(asyncResp->res,
1049 "Message");
1050 }
1051 }
1052 }
1053 thisEntry = {
1054 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001055 {"@odata.id",
1056 "/redfish/v1/Systems/system/LogServices/EventLog/"
1057 "Entries/" +
1058 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001059 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001060 {"Id", std::to_string(*id)},
1061 {"Message", *message},
1062 {"EntryType", "Event"},
1063 {"Severity",
1064 translateSeverityDbusToRedfish(*severity)},
1065 {"Created", crow::utility::getDateTime(timestamp)}};
1066 }
1067 }
1068 std::sort(entriesArray.begin(), entriesArray.end(),
1069 [](const nlohmann::json &left,
1070 const nlohmann::json &right) {
1071 return (left["Id"] <= right["Id"]);
1072 });
1073 asyncResp->res.jsonValue["Members@odata.count"] =
1074 entriesArray.size();
1075 },
1076 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1077 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001078 }
1079};
1080
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001081class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001082{
1083 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001084 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001086 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1087 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088 {
1089 entityPrivileges = {
1090 {boost::beast::http::verb::get, {{"Login"}}},
1091 {boost::beast::http::verb::head, {{"Login"}}},
1092 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1093 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1094 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1095 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1096 }
1097
1098 private:
1099 void doGet(crow::Response &res, const crow::Request &req,
1100 const std::vector<std::string> &params) override
1101 {
1102 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001103 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001104 {
1105 messages::internalError(asyncResp->res);
1106 return;
1107 }
Ed Tanous029573d2019-02-01 10:57:49 -08001108 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001109
Andrew Geisslercb92c032018-08-17 07:56:14 -07001110 // DBus implementation of EventLog/Entries
1111 // Make call to Logging Service to find all log entry objects
1112 crow::connections::systemBus->async_method_call(
1113 [asyncResp, entryID](const boost::system::error_code ec,
1114 GetManagedPropertyType &resp) {
1115 if (ec)
1116 {
1117 BMCWEB_LOG_ERROR
1118 << "EventLogEntry (DBus) resp_handler got error " << ec;
1119 messages::internalError(asyncResp->res);
1120 return;
1121 }
Ed Tanous66664f22019-10-11 13:05:49 -07001122 uint32_t *id = nullptr;
1123 std::time_t timestamp{};
1124 std::string *severity = nullptr;
1125 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001126 for (auto &propertyMap : resp)
1127 {
1128 if (propertyMap.first == "Id")
1129 {
1130 id = std::get_if<uint32_t>(&propertyMap.second);
1131 if (id == nullptr)
1132 {
1133 messages::propertyMissing(asyncResp->res, "Id");
1134 }
1135 }
1136 else if (propertyMap.first == "Timestamp")
1137 {
1138 const uint64_t *millisTimeStamp =
1139 std::get_if<uint64_t>(&propertyMap.second);
1140 if (millisTimeStamp == nullptr)
1141 {
1142 messages::propertyMissing(asyncResp->res,
1143 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001144 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001145 }
1146 // Retrieve Created property with format:
1147 // yyyy-mm-ddThh:mm:ss
1148 std::chrono::milliseconds chronoTimeStamp(
1149 *millisTimeStamp);
1150 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001151 std::chrono::duration_cast<
1152 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001153 .count();
1154 }
1155 else if (propertyMap.first == "Severity")
1156 {
1157 severity =
1158 std::get_if<std::string>(&propertyMap.second);
1159 if (severity == nullptr)
1160 {
1161 messages::propertyMissing(asyncResp->res,
1162 "Severity");
1163 }
1164 }
1165 else if (propertyMap.first == "Message")
1166 {
1167 message = std::get_if<std::string>(&propertyMap.second);
1168 if (message == nullptr)
1169 {
1170 messages::propertyMissing(asyncResp->res,
1171 "Message");
1172 }
1173 }
1174 }
Ed Tanous271584a2019-07-09 16:24:22 -07001175 if (id == nullptr || message == nullptr || severity == nullptr)
1176 {
1177 return;
1178 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001179 asyncResp->res.jsonValue = {
1180 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001181 {"@odata.id",
1182 "/redfish/v1/Systems/system/LogServices/EventLog/"
1183 "Entries/" +
1184 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001185 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001186 {"Id", std::to_string(*id)},
1187 {"Message", *message},
1188 {"EntryType", "Event"},
1189 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001190 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001191 },
1192 "xyz.openbmc_project.Logging",
1193 "/xyz/openbmc_project/logging/entry/" + entryID,
1194 "org.freedesktop.DBus.Properties", "GetAll",
1195 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001197
1198 void doDelete(crow::Response &res, const crow::Request &req,
1199 const std::vector<std::string> &params) override
1200 {
1201
1202 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1203
1204 auto asyncResp = std::make_shared<AsyncResp>(res);
1205
1206 if (params.size() != 1)
1207 {
1208 messages::internalError(asyncResp->res);
1209 return;
1210 }
1211 std::string entryID = params[0];
1212
1213 dbus::utility::escapePathForDbus(entryID);
1214
1215 // Process response from Logging service.
1216 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1217 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1218 if (ec)
1219 {
1220 // TODO Handle for specific error code
1221 BMCWEB_LOG_ERROR
1222 << "EventLogEntry (DBus) doDelete respHandler got error "
1223 << ec;
1224 asyncResp->res.result(
1225 boost::beast::http::status::internal_server_error);
1226 return;
1227 }
1228
1229 asyncResp->res.result(boost::beast::http::status::ok);
1230 };
1231
1232 // Make call to Logging service to request Delete Log
1233 crow::connections::systemBus->async_method_call(
1234 respHandler, "xyz.openbmc_project.Logging",
1235 "/xyz/openbmc_project/logging/entry/" + entryID,
1236 "xyz.openbmc_project.Object.Delete", "Delete");
1237 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001238};
1239
1240class BMCLogServiceCollection : public Node
1241{
1242 public:
1243 template <typename CrowApp>
1244 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001245 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001246 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001247 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001248 {boost::beast::http::verb::get, {{"Login"}}},
1249 {boost::beast::http::verb::head, {{"Login"}}},
1250 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1251 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1252 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1253 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001254 }
1255
1256 private:
1257 /**
1258 * Functions triggers appropriate requests on DBus
1259 */
1260 void doGet(crow::Response &res, const crow::Request &req,
1261 const std::vector<std::string> &params) override
1262 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001263 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001264 // Collections don't include the static data added by SubRoute because
1265 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001266 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001267 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 asyncResp->res.jsonValue["@odata.id"] =
1269 "/redfish/v1/Managers/bmc/LogServices";
1270 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1271 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001272 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001273 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1274 logServiceArray = nlohmann::json::array();
1275#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1276 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001277 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001278#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001279 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001280 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001281 }
1282};
1283
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001284class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001285{
1286 public:
1287 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001288 BMCJournalLogService(CrowApp &app) :
1289 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001290 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001291 entityPrivileges = {
1292 {boost::beast::http::verb::get, {{"Login"}}},
1293 {boost::beast::http::verb::head, {{"Login"}}},
1294 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1295 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1296 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1297 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1298 }
1299
1300 private:
1301 void doGet(crow::Response &res, const crow::Request &req,
1302 const std::vector<std::string> &params) override
1303 {
1304 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001305 asyncResp->res.jsonValue["@odata.type"] =
1306 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001307 asyncResp->res.jsonValue["@odata.id"] =
1308 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001309 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1310 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1311 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001312 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001313 asyncResp->res.jsonValue["Entries"] = {
1314 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001315 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001316 }
1317};
1318
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001319static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1320 sd_journal *journal,
1321 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001322{
1323 // Get the Log Entry contents
1324 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001325
Ed Tanous39e77502019-03-04 17:35:53 -08001326 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001327 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001328 if (ret < 0)
1329 {
1330 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1331 return 1;
1332 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001333
1334 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001335 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001336 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001337 if (ret < 0)
1338 {
1339 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001340 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001341
1342 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001343 std::string entryTimeStr;
1344 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001345 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001346 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001347 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001348
1349 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001350 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001351 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001352 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1353 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001354 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001355 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001356 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001357 {"EntryType", "Oem"},
1358 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001359 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001360 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001361 {"Created", std::move(entryTimeStr)}};
1362 return 0;
1363}
1364
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001365class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001366{
1367 public:
1368 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001369 BMCJournalLogEntryCollection(CrowApp &app) :
1370 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001371 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001372 entityPrivileges = {
1373 {boost::beast::http::verb::get, {{"Login"}}},
1374 {boost::beast::http::verb::head, {{"Login"}}},
1375 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1376 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1377 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1378 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1379 }
1380
1381 private:
1382 void doGet(crow::Response &res, const crow::Request &req,
1383 const std::vector<std::string> &params) override
1384 {
1385 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001386 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001387 uint64_t skip = 0;
1388 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001389 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001390 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001391 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001392 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001393 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001394 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001395 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001396 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001397 // Collections don't include the static data added by SubRoute because
1398 // it has a duplicate entry for members
1399 asyncResp->res.jsonValue["@odata.type"] =
1400 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001401 asyncResp->res.jsonValue["@odata.id"] =
1402 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001403 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001404 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001405 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1406 asyncResp->res.jsonValue["Description"] =
1407 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001408 asyncResp->res.jsonValue["@odata.id"] =
1409 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001410 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1411 logEntryArray = nlohmann::json::array();
1412
1413 // Go through the journal and use the timestamp to create a unique ID
1414 // for each entry
1415 sd_journal *journalTmp = nullptr;
1416 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1417 if (ret < 0)
1418 {
1419 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001420 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001421 return;
1422 }
1423 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1424 journalTmp, sd_journal_close);
1425 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001426 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001427 // Reset the unique ID on the first entry
1428 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001429 SD_JOURNAL_FOREACH(journal.get())
1430 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001431 entryCount++;
1432 // Handle paging using skip (number of entries to skip from the
1433 // start) and top (number of entries to display)
1434 if (entryCount <= skip || entryCount > skip + top)
1435 {
1436 continue;
1437 }
1438
Jason M. Bills16428a12018-11-02 12:42:29 -07001439 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001440 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001441 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001442 continue;
1443 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001444
Jason M. Billse85d6b12019-07-29 17:01:15 -07001445 if (firstEntry)
1446 {
1447 firstEntry = false;
1448 }
1449
Jason M. Billse1f26342018-07-18 12:12:00 -07001450 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001451 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1452 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1453 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001454 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001455 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001456 return;
1457 }
1458 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001459 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1460 if (skip + top < entryCount)
1461 {
1462 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001463 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001464 std::to_string(skip + top);
1465 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001466 }
1467};
1468
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001469class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001470{
1471 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001472 BMCJournalLogEntry(CrowApp &app) :
1473 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001474 std::string())
1475 {
1476 entityPrivileges = {
1477 {boost::beast::http::verb::get, {{"Login"}}},
1478 {boost::beast::http::verb::head, {{"Login"}}},
1479 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1480 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1481 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1483 }
1484
1485 private:
1486 void doGet(crow::Response &res, const crow::Request &req,
1487 const std::vector<std::string> &params) override
1488 {
1489 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1490 if (params.size() != 1)
1491 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001492 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001493 return;
1494 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001495 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001496 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001497 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001498 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001499 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001500 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001501 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 }
1503
1504 sd_journal *journalTmp = nullptr;
1505 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1506 if (ret < 0)
1507 {
1508 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001509 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001510 return;
1511 }
1512 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1513 journalTmp, sd_journal_close);
1514 journalTmp = nullptr;
1515 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001516 // tracking the unique ID
1517 std::string idStr;
1518 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001519 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301520 if (ret < 0)
1521 {
1522 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1523 << strerror(-ret);
1524 messages::internalError(asyncResp->res);
1525 return;
1526 }
Ed Tanous271584a2019-07-09 16:24:22 -07001527 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001528 {
1529 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001530 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1531 {
1532 messages::internalError(asyncResp->res);
1533 return;
1534 }
1535 if (firstEntry)
1536 {
1537 firstEntry = false;
1538 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001539 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001540 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001541 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001542 {
1543 messages::resourceMissingAtURI(asyncResp->res, entryID);
1544 return;
1545 }
1546
1547 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1548 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001549 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001550 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001551 return;
1552 }
1553 }
1554};
1555
raviteja-bc9bb6862020-02-03 11:53:32 -06001556class SystemDumpService : public Node
1557{
1558 public:
1559 template <typename CrowApp>
1560 SystemDumpService(CrowApp &app) :
1561 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1562 {
1563 entityPrivileges = {
1564 {boost::beast::http::verb::get, {{"Login"}}},
1565 {boost::beast::http::verb::head, {{"Login"}}},
1566 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1567 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1568 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1569 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1570 }
1571
1572 private:
1573 void doGet(crow::Response &res, const crow::Request &req,
1574 const std::vector<std::string> &params) override
1575 {
1576 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1577
1578 asyncResp->res.jsonValue["@odata.id"] =
1579 "/redfish/v1/Systems/system/LogServices/System";
1580 asyncResp->res.jsonValue["@odata.type"] =
1581 "#LogService.v1_1_0.LogService";
1582 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1583 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1584 asyncResp->res.jsonValue["Id"] = "System";
1585 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1586 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1587 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1588
1589 asyncResp->res.jsonValue["Entries"] = {
1590 {"@odata.id",
1591 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1592 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1593 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1594 "Actions/LogService.ClearLog"}};
1595 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1596 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1597 "Actions/LogService.CreateLog"}};
1598 }
1599};
1600
1601class SystemDumpEntryCollection : public Node
1602{
1603 public:
1604 template <typename CrowApp>
1605 SystemDumpEntryCollection(CrowApp &app) :
1606 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1607 {
1608 entityPrivileges = {
1609 {boost::beast::http::verb::get, {{"Login"}}},
1610 {boost::beast::http::verb::head, {{"Login"}}},
1611 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1612 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1613 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1614 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1615 }
1616
1617 private:
1618 /**
1619 * Functions triggers appropriate requests on DBus
1620 */
1621 void doGet(crow::Response &res, const crow::Request &req,
1622 const std::vector<std::string> &params) override
1623 {
1624 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1625
1626 asyncResp->res.jsonValue["@odata.type"] =
1627 "#LogEntryCollection.LogEntryCollection";
1628 asyncResp->res.jsonValue["@odata.id"] =
1629 "/redfish/v1/Systems/system/LogServices/System/Entries";
1630 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1631 asyncResp->res.jsonValue["Description"] =
1632 "Collection of System Dump Entries";
1633
1634 crow::connections::systemBus->async_method_call(
1635 [asyncResp](const boost::system::error_code ec,
1636 const crow::openbmc_mapper::GetSubTreeType &resp) {
1637 if (ec)
1638 {
1639 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1640 messages::internalError(asyncResp->res);
1641 return;
1642 }
1643
1644 nlohmann::json &logArray = asyncResp->res.jsonValue["Members"];
1645 logArray = nlohmann::json::array();
1646 for (auto &object : resp)
1647 {
1648 const std::string &path =
1649 static_cast<const std::string &>(object.first);
1650 std::size_t lastPos = path.rfind("/");
1651 if (lastPos == std::string::npos)
1652 {
1653 continue;
1654 }
1655 std::string logID = path.substr(lastPos + 1);
1656 logArray.push_back(
1657 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1658 "System/Entries/" +
1659 logID}});
1660 }
1661 asyncResp->res.jsonValue["Members@odata.count"] =
1662 logArray.size();
1663 },
1664 "xyz.openbmc_project.ObjectMapper",
1665 "/xyz/openbmc_project/object_mapper",
1666 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1667 "/xyz/openbmc_project/dump", 0,
1668 std::array<const char *, 1>{
1669 "xyz.openbmc_project.Dump.Entry.System"});
1670 }
1671};
1672
1673class SystemDumpEntry : public Node
1674{
1675 public:
1676 SystemDumpEntry(CrowApp &app) :
1677 Node(app,
1678 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1679 std::string())
1680 {
1681 entityPrivileges = {
1682 {boost::beast::http::verb::get, {{"Login"}}},
1683 {boost::beast::http::verb::head, {{"Login"}}},
1684 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1685 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1686 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1687 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1688 }
1689
1690 private:
1691 void doGet(crow::Response &res, const crow::Request &req,
1692 const std::vector<std::string> &params) override
1693 {
1694 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1695 if (params.size() != 1)
1696 {
1697 messages::internalError(asyncResp->res);
1698 return;
1699 }
1700 const std::string &entryID = params[0];
1701 crow::connections::systemBus->async_method_call(
1702 [asyncResp, entryID](const boost::system::error_code ec,
1703 GetManagedObjectsType &resp) {
1704 if (ec)
1705 {
1706 BMCWEB_LOG_ERROR
1707 << "SystemDumpEntry resp_handler got error " << ec;
1708 messages::internalError(asyncResp->res);
1709 return;
1710 }
1711
1712 for (auto &objectPath : resp)
1713 {
1714 if (objectPath.first.str.find(
1715 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1716 std::string::npos)
1717 {
1718 continue;
1719 }
1720
1721 bool foundSystemDumpEntry = false;
1722 for (auto &interfaceMap : objectPath.second)
1723 {
1724 if (interfaceMap.first ==
1725 "xyz.openbmc_project.Dump.Entry.System")
1726 {
1727 foundSystemDumpEntry = true;
1728 break;
1729 }
1730 }
1731 if (foundSystemDumpEntry == false)
1732 {
1733 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1734 messages::internalError(asyncResp->res);
1735 return;
1736 }
1737
1738 std::string timestamp{};
1739 uint64_t size = 0;
1740
1741 for (auto &interfaceMap : objectPath.second)
1742 {
1743 if (interfaceMap.first ==
1744 "xyz.openbmc_project.Dump.Entry")
1745 {
1746 for (auto &propertyMap : interfaceMap.second)
1747 {
1748 if (propertyMap.first == "Size")
1749 {
1750 auto sizePtr = std::get_if<uint64_t>(
1751 &propertyMap.second);
1752 if (sizePtr == nullptr)
1753 {
1754 messages::propertyMissing(
1755 asyncResp->res, "Size");
1756 break;
1757 }
1758 size = *sizePtr;
1759 break;
1760 }
1761 }
1762 }
1763 else if (interfaceMap.first ==
1764 "xyz.openbmc_project.Time.EpochTime")
1765 {
1766 for (auto &propertyMap : interfaceMap.second)
1767 {
1768 if (propertyMap.first == "Elapsed")
1769 {
1770 const uint64_t *usecsTimeStamp =
1771 std::get_if<uint64_t>(
1772 &propertyMap.second);
1773 if (usecsTimeStamp == nullptr)
1774 {
1775 messages::propertyMissing(
1776 asyncResp->res, "Elapsed");
1777 break;
1778 }
1779 getTimestampStr(*usecsTimeStamp, timestamp);
1780 break;
1781 }
1782 }
1783 }
1784 }
1785 asyncResp->res.jsonValue = {
1786 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1787 {"@odata.id",
1788 "/redfish/v1/Systems/system/LogServices/System/"
1789 "Entries/" +
1790 entryID},
1791 {"Name", "System Dump Entry"},
1792 {"Id", entryID},
1793 {"SizeInB", size},
1794 {"EntryType", "Dump"},
1795 {"EntryCode", "User generated dump"},
1796 {"Created", timestamp}};
1797
1798 asyncResp->res
1799 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1800 {"target",
1801 "/redfish/v1/Systems/system/LogServices/System/"
1802 "Entries/" +
1803 entryID + "/Actions/LogEntry.DownloadLog"}};
1804 }
1805 },
1806 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1807 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1808 }
1809
1810 void doDelete(crow::Response &res, const crow::Request &req,
1811 const std::vector<std::string> &params) override
1812 {
1813 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1814
1815 auto asyncResp = std::make_shared<AsyncResp>(res);
1816
1817 if (params.size() != 1)
1818 {
1819 messages::internalError(asyncResp->res);
1820 return;
1821 }
1822 std::string entryID = params[0];
1823
1824 crow::connections::systemBus->async_method_call(
1825 [asyncResp,
1826 entryID](const boost::system::error_code ec,
1827 const crow::openbmc_mapper::GetSubTreeType &resp) {
1828 if (ec)
1829 {
1830 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1831 messages::internalError(asyncResp->res);
1832 return;
1833 }
1834
1835 for (auto &object : resp)
1836 {
1837 const std::string &path =
1838 static_cast<const std::string &>(object.first);
1839
1840 std::size_t pos = path.rfind(
1841 "/xyz/openbmc_project/dump/entry/" + entryID);
1842 if (pos != std::string::npos)
1843 {
1844 deleteSystemDumpEntry(asyncResp->res, entryID);
1845 return;
1846 }
1847 }
1848 },
1849 "xyz.openbmc_project.ObjectMapper",
1850 "/xyz/openbmc_project/object_mapper",
1851 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1852 "/xyz/openbmc_project/dump", 0,
1853 std::array<const char *, 1>{
1854 "xyz.openbmc_project.Dump.Entry.System"});
1855 }
1856};
1857
raviteja-b06578432020-02-03 12:50:42 -06001858class SystemDumpEntryDownload : public Node
1859{
1860 public:
1861 SystemDumpEntryDownload(CrowApp &app) :
1862 Node(app,
1863 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
1864 "Actions/"
1865 "LogEntry.DownloadLog/",
1866 std::string())
1867 {
1868 entityPrivileges = {
1869 {boost::beast::http::verb::get, {{"Login"}}},
1870 {boost::beast::http::verb::head, {{"Login"}}},
1871 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1872 }
1873
1874 private:
1875 void doPost(crow::Response &res, const crow::Request &req,
1876 const std::vector<std::string> &params) override
1877 {
1878 if (params.size() != 1)
1879 {
1880 messages::internalError(res);
1881 return;
1882 }
1883 const std::string &entryID = params[0];
1884 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
1885 }
1886};
1887
raviteja-b013487e2020-03-03 03:20:48 -06001888class SystemDumpClear : public Node
1889{
1890 public:
1891 SystemDumpClear(CrowApp &app) :
1892 Node(app, "/redfish/v1/Systems/system/LogServices/System/"
1893 "Actions/"
1894 "LogService.ClearLog/")
1895 {
1896 entityPrivileges = {
1897 {boost::beast::http::verb::get, {{"Login"}}},
1898 {boost::beast::http::verb::head, {{"Login"}}},
1899 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1900 }
1901
1902 private:
1903 void doPost(crow::Response &res, const crow::Request &req,
1904 const std::vector<std::string> &params) override
1905 {
1906
1907 auto asyncResp = std::make_shared<AsyncResp>(res);
1908 crow::connections::systemBus->async_method_call(
1909 [asyncResp](const boost::system::error_code ec,
1910 const std::vector<std::string> &dumpList) {
1911 if (ec)
1912 {
1913 messages::internalError(asyncResp->res);
1914 return;
1915 }
1916
1917 for (const std::string &objectPath : dumpList)
1918 {
1919 std::size_t pos = objectPath.rfind("/");
1920 if (pos != std::string::npos)
1921 {
1922 std::string logID = objectPath.substr(pos + 1);
1923 deleteSystemDumpEntry(asyncResp->res, logID);
1924 }
1925 }
1926 },
1927 "xyz.openbmc_project.ObjectMapper",
1928 "/xyz/openbmc_project/object_mapper",
1929 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1930 "/xyz/openbmc_project/dump", 0,
1931 std::array<const char *, 1>{
1932 "xyz.openbmc_project.Dump.Entry.System"});
1933 }
1934};
1935
Jason M. Bills424c4172019-03-21 13:50:33 -07001936class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001937{
1938 public:
1939 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001940 CrashdumpService(CrowApp &app) :
1941 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001942 {
AppaRao Puli39460282020-04-07 17:03:04 +05301943 // Note: Deviated from redfish privilege registry for GET & HEAD
1944 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001945 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301946 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1947 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001948 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1949 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1950 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1951 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001952 }
1953
1954 private:
1955 /**
1956 * Functions triggers appropriate requests on DBus
1957 */
1958 void doGet(crow::Response &res, const crow::Request &req,
1959 const std::vector<std::string> &params) override
1960 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001961 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001962 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001963 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001964 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001965 asyncResp->res.jsonValue["@odata.type"] =
1966 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001967 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1968 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1969 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001970 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1971 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001972 asyncResp->res.jsonValue["Entries"] = {
1973 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001974 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001975 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001976 {"#LogService.ClearLog",
1977 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1978 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001979 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001980 {{"#Crashdump.OnDemand",
1981 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1982 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001983
1984#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001985 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001986 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001987 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1988 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001989#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001990 }
1991};
1992
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001993class CrashdumpClear : public Node
1994{
1995 public:
1996 CrashdumpClear(CrowApp &app) :
1997 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1998 "LogService.ClearLog/")
1999 {
AppaRao Puli39460282020-04-07 17:03:04 +05302000 // Note: Deviated from redfish privilege registry for GET & HEAD
2001 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002002 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302003 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2004 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002005 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2006 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2007 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2008 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2009 }
2010
2011 private:
2012 void doPost(crow::Response &res, const crow::Request &req,
2013 const std::vector<std::string> &params) override
2014 {
2015 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2016
2017 crow::connections::systemBus->async_method_call(
2018 [asyncResp](const boost::system::error_code ec,
2019 const std::string &resp) {
2020 if (ec)
2021 {
2022 messages::internalError(asyncResp->res);
2023 return;
2024 }
2025 messages::success(asyncResp->res);
2026 },
2027 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2028 }
2029};
2030
Jason M. Billse855dd22019-10-08 11:37:48 -07002031static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
2032 const std::string &logID,
2033 nlohmann::json &logEntryJson)
2034{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002035 auto getStoredLogCallback =
2036 [asyncResp, logID, &logEntryJson](
2037 const boost::system::error_code ec,
2038 const std::vector<std::pair<std::string, VariantType>> &params) {
2039 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002040 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002041 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2042 if (ec.value() ==
2043 boost::system::linux_error::bad_request_descriptor)
2044 {
2045 messages::resourceNotFound(asyncResp->res, "LogEntry",
2046 logID);
2047 }
2048 else
2049 {
2050 messages::internalError(asyncResp->res);
2051 }
2052 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002053 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002054
Johnathan Mantey043a0532020-03-10 17:15:28 -07002055 std::string timestamp{};
2056 std::string filename{};
2057 std::string logfile{};
2058 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2059
2060 if (filename.empty() || timestamp.empty())
2061 {
2062 messages::resourceMissingAtURI(asyncResp->res, logID);
2063 return;
2064 }
2065
2066 std::string crashdumpURI =
2067 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2068 logID + "/" + filename;
2069 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2070 {"@odata.id", "/redfish/v1/Systems/system/"
2071 "LogServices/Crashdump/Entries/" +
2072 logID},
2073 {"Name", "CPU Crashdump"},
2074 {"Id", logID},
2075 {"EntryType", "Oem"},
2076 {"OemRecordFormat", "Crashdump URI"},
2077 {"Message", std::move(crashdumpURI)},
2078 {"Created", std::move(timestamp)}};
2079 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002080 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002081 std::move(getStoredLogCallback), crashdumpObject,
2082 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002083 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002084}
2085
Jason M. Bills424c4172019-03-21 13:50:33 -07002086class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002087{
2088 public:
2089 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07002090 CrashdumpEntryCollection(CrowApp &app) :
2091 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002092 {
AppaRao Puli39460282020-04-07 17:03:04 +05302093 // Note: Deviated from redfish privilege registry for GET & HEAD
2094 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002095 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302096 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2097 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002098 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2099 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2100 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2101 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002102 }
2103
2104 private:
2105 /**
2106 * Functions triggers appropriate requests on DBus
2107 */
2108 void doGet(crow::Response &res, const crow::Request &req,
2109 const std::vector<std::string> &params) override
2110 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002111 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002112 // Collections don't include the static data added by SubRoute because
2113 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002114 auto getLogEntriesCallback = [asyncResp](
2115 const boost::system::error_code ec,
2116 const std::vector<std::string> &resp) {
2117 if (ec)
2118 {
2119 if (ec.value() !=
2120 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002121 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002122 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2123 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002124 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002125 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002126 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002127 }
2128 asyncResp->res.jsonValue["@odata.type"] =
2129 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002130 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002131 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002132 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002133 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002134 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002135 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
2136 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002137 std::vector<std::string> logIDs;
2138 // Get the list of log entries and build up an empty array big
2139 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07002140 for (const std::string &objpath : resp)
2141 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002142 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002143 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002144 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002145 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002146 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002147 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002148 logIDs.emplace_back(objpath.substr(lastPos + 1));
2149
2150 // Add a space for the log entry to the array
2151 logEntryArray.push_back({});
2152 }
2153 // Now go through and set up async calls to fill in the entries
2154 size_t index = 0;
2155 for (const std::string &logID : logIDs)
2156 {
2157 // Add the log entry to the array
2158 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002159 }
2160 asyncResp->res.jsonValue["Members@odata.count"] =
2161 logEntryArray.size();
2162 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002163 crow::connections::systemBus->async_method_call(
2164 std::move(getLogEntriesCallback),
2165 "xyz.openbmc_project.ObjectMapper",
2166 "/xyz/openbmc_project/object_mapper",
2167 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002168 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002169 }
2170};
2171
Jason M. Bills424c4172019-03-21 13:50:33 -07002172class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002173{
2174 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002175 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002176 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002177 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002178 std::string())
2179 {
AppaRao Puli39460282020-04-07 17:03:04 +05302180 // Note: Deviated from redfish privilege registry for GET & HEAD
2181 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002182 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302183 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2184 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002185 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2186 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2187 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2188 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002189 }
2190
2191 private:
2192 void doGet(crow::Response &res, const crow::Request &req,
2193 const std::vector<std::string> &params) override
2194 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002195 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002196 if (params.size() != 1)
2197 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002198 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002199 return;
2200 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002201 const std::string &logID = params[0];
2202 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2203 }
2204};
2205
2206class CrashdumpFile : public Node
2207{
2208 public:
2209 CrashdumpFile(CrowApp &app) :
2210 Node(app,
2211 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2212 "<str>/",
2213 std::string(), std::string())
2214 {
AppaRao Puli39460282020-04-07 17:03:04 +05302215 // Note: Deviated from redfish privilege registry for GET & HEAD
2216 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002217 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302218 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2219 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002220 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2221 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2222 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2223 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2224 }
2225
2226 private:
2227 void doGet(crow::Response &res, const crow::Request &req,
2228 const std::vector<std::string> &params) override
2229 {
2230 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2231 if (params.size() != 2)
2232 {
2233 messages::internalError(asyncResp->res);
2234 return;
2235 }
2236 const std::string &logID = params[0];
2237 const std::string &fileName = params[1];
2238
Johnathan Mantey043a0532020-03-10 17:15:28 -07002239 auto getStoredLogCallback =
2240 [asyncResp, logID, fileName](
2241 const boost::system::error_code ec,
2242 const std::vector<std::pair<std::string, VariantType>> &resp) {
2243 if (ec)
2244 {
2245 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2246 << ec.message();
2247 messages::internalError(asyncResp->res);
2248 return;
2249 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002250
Johnathan Mantey043a0532020-03-10 17:15:28 -07002251 std::string dbusFilename{};
2252 std::string dbusTimestamp{};
2253 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002254
Johnathan Mantey043a0532020-03-10 17:15:28 -07002255 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2256 dbusFilepath);
2257
2258 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2259 dbusFilepath.empty())
2260 {
2261 messages::resourceMissingAtURI(asyncResp->res, fileName);
2262 return;
2263 }
2264
2265 // Verify the file name parameter is correct
2266 if (fileName != dbusFilename)
2267 {
2268 messages::resourceMissingAtURI(asyncResp->res, fileName);
2269 return;
2270 }
2271
2272 if (!std::filesystem::exists(dbusFilepath))
2273 {
2274 messages::resourceMissingAtURI(asyncResp->res, fileName);
2275 return;
2276 }
2277 std::ifstream ifs(dbusFilepath, std::ios::in |
2278 std::ios::binary |
2279 std::ios::ate);
2280 std::ifstream::pos_type fileSize = ifs.tellg();
2281 if (fileSize < 0)
2282 {
2283 messages::generalError(asyncResp->res);
2284 return;
2285 }
2286 ifs.seekg(0, std::ios::beg);
2287
2288 auto crashData = std::make_unique<char[]>(
2289 static_cast<unsigned int>(fileSize));
2290
2291 ifs.read(crashData.get(), static_cast<int>(fileSize));
2292
2293 // The cast to std::string is intentional in order to use the
2294 // assign() that applies move mechanics
2295 asyncResp->res.body().assign(
2296 static_cast<std::string>(crashData.get()));
2297
2298 // Configure this to be a file download when accessed from
2299 // a browser
2300 asyncResp->res.addHeader("Content-Disposition", "attachment");
2301 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002302 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002303 std::move(getStoredLogCallback), crashdumpObject,
2304 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002305 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002306 }
2307};
2308
Jason M. Bills424c4172019-03-21 13:50:33 -07002309class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002310{
2311 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002312 OnDemandCrashdump(CrowApp &app) :
2313 Node(app,
2314 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2315 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002316 {
AppaRao Puli39460282020-04-07 17:03:04 +05302317 // Note: Deviated from redfish privilege registry for GET & HEAD
2318 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002319 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302320 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2321 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2322 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2323 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2324 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2325 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002326 }
2327
2328 private:
2329 void doPost(crow::Response &res, const crow::Request &req,
2330 const std::vector<std::string> &params) override
2331 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002332 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002333
James Feistfe306722020-03-12 16:32:08 -07002334 auto generateonDemandLogCallback = [asyncResp,
2335 req](const boost::system::error_code
2336 ec,
2337 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08002338 if (ec)
2339 {
2340 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002341 {
James Feist46229572020-02-19 15:11:58 -08002342 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002343 }
James Feist46229572020-02-19 15:11:58 -08002344 else if (ec.value() ==
2345 boost::system::errc::device_or_resource_busy)
2346 {
2347 messages::serviceTemporarilyUnavailable(asyncResp->res,
2348 "60");
2349 }
2350 else
2351 {
2352 messages::internalError(asyncResp->res);
2353 }
2354 return;
2355 }
2356 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08002357 [](boost::system::error_code err, sdbusplus::message::message &,
2358 const std::shared_ptr<task::TaskData> &taskData) {
2359 if (!err)
2360 {
James Feiste5d50062020-05-11 17:29:00 -07002361 taskData->messages.emplace_back(
2362 messages::taskCompletedOK(
2363 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002364 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002365 }
James Feist32898ce2020-03-10 16:16:52 -07002366 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002367 },
James Feist46229572020-02-19 15:11:58 -08002368 "type='signal',interface='org.freedesktop.DBus.Properties',"
2369 "member='PropertiesChanged',arg0namespace='com.intel."
2370 "crashdump'");
2371 task->startTimer(std::chrono::minutes(5));
2372 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002373 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002374 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002375 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002376 std::move(generateonDemandLogCallback), crashdumpObject,
2377 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002378 }
2379};
2380
Jason M. Billse1f26342018-07-18 12:12:00 -07002381class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002382{
2383 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07002384 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002385 Node(app,
2386 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2387 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002388 {
AppaRao Puli39460282020-04-07 17:03:04 +05302389 // Note: Deviated from redfish privilege registry for GET & HEAD
2390 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002391 entityPrivileges = {
2392 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2393 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2394 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2395 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2396 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2397 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2398 }
2399
2400 private:
2401 void doPost(crow::Response &res, const crow::Request &req,
2402 const std::vector<std::string> &params) override
2403 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002404 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002405 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002406
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002407 nlohmann::json reqJson =
2408 nlohmann::json::parse(req.body, nullptr, false);
2409 if (reqJson.find("PECICommands") != reqJson.end())
2410 {
2411 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2412 {
2413 return;
2414 }
2415 uint32_t idx = 0;
2416 for (auto const &cmd : peciCommands)
2417 {
2418 if (cmd.size() < 3)
2419 {
2420 std::string s("[");
2421 for (auto const &val : cmd)
2422 {
2423 if (val != *cmd.begin())
2424 {
2425 s += ",";
2426 }
2427 s += std::to_string(val);
2428 }
2429 s += "]";
2430 messages::actionParameterValueFormatError(
2431 res, s, "PECICommands[" + std::to_string(idx) + "]",
2432 "SendRawPeci");
2433 return;
2434 }
2435 idx++;
2436 }
2437 }
2438 else
2439 {
2440 /* This interface is deprecated */
2441 uint8_t clientAddress = 0;
2442 uint8_t readLength = 0;
2443 std::vector<uint8_t> peciCommand;
2444 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2445 "ReadLength", readLength, "PECICommand",
2446 peciCommand))
2447 {
2448 return;
2449 }
2450 peciCommands.push_back({clientAddress, 0, readLength});
2451 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2452 peciCommand.end());
2453 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002454 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002455 auto sendRawPECICallback =
2456 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002457 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002458 if (ec)
2459 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002460 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002461 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002462 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002463 return;
2464 }
2465 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2466 {"PECIResponse", resp}};
2467 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002468 // Call the SendRawPECI command with the provided data
2469 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002470 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002471 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002472 }
2473};
2474
Andrew Geisslercb92c032018-08-17 07:56:14 -07002475/**
2476 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2477 */
2478class DBusLogServiceActionsClear : public Node
2479{
2480 public:
2481 DBusLogServiceActionsClear(CrowApp &app) :
2482 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002483 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002484 {
2485 entityPrivileges = {
2486 {boost::beast::http::verb::get, {{"Login"}}},
2487 {boost::beast::http::verb::head, {{"Login"}}},
2488 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2489 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2490 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2491 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2492 }
2493
2494 private:
2495 /**
2496 * Function handles POST method request.
2497 * The Clear Log actions does not require any parameter.The action deletes
2498 * all entries found in the Entries collection for this Log Service.
2499 */
2500 void doPost(crow::Response &res, const crow::Request &req,
2501 const std::vector<std::string> &params) override
2502 {
2503 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2504
2505 auto asyncResp = std::make_shared<AsyncResp>(res);
2506 // Process response from Logging service.
2507 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2508 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2509 if (ec)
2510 {
2511 // TODO Handle for specific error code
2512 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2513 asyncResp->res.result(
2514 boost::beast::http::status::internal_server_error);
2515 return;
2516 }
2517
2518 asyncResp->res.result(boost::beast::http::status::no_content);
2519 };
2520
2521 // Make call to Logging service to request Clear Log
2522 crow::connections::systemBus->async_method_call(
2523 resp_handler, "xyz.openbmc_project.Logging",
2524 "/xyz/openbmc_project/logging",
2525 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2526 }
2527};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002528
2529/****************************************************
2530 * Redfish PostCode interfaces
2531 * using DBUS interface: getPostCodesTS
2532 ******************************************************/
2533class PostCodesLogService : public Node
2534{
2535 public:
2536 PostCodesLogService(CrowApp &app) :
2537 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2538 {
2539 entityPrivileges = {
2540 {boost::beast::http::verb::get, {{"Login"}}},
2541 {boost::beast::http::verb::head, {{"Login"}}},
2542 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2543 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2544 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2545 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2546 }
2547
2548 private:
2549 void doGet(crow::Response &res, const crow::Request &req,
2550 const std::vector<std::string> &params) override
2551 {
2552 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2553
2554 asyncResp->res.jsonValue = {
2555 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2556 {"@odata.type", "#LogService.v1_1_0.LogService"},
2557 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2558 {"Name", "POST Code Log Service"},
2559 {"Description", "POST Code Log Service"},
2560 {"Id", "BIOS POST Code Log"},
2561 {"OverWritePolicy", "WrapsWhenFull"},
2562 {"Entries",
2563 {{"@odata.id",
2564 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2565 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2566 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2567 "Actions/LogService.ClearLog"}};
2568 }
2569};
2570
2571class PostCodesClear : public Node
2572{
2573 public:
2574 PostCodesClear(CrowApp &app) :
2575 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2576 "LogService.ClearLog/")
2577 {
2578 entityPrivileges = {
2579 {boost::beast::http::verb::get, {{"Login"}}},
2580 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302581 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2582 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2583 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2584 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002585 }
2586
2587 private:
2588 void doPost(crow::Response &res, const crow::Request &req,
2589 const std::vector<std::string> &params) override
2590 {
2591 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2592
2593 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2594 // Make call to post-code service to request clear all
2595 crow::connections::systemBus->async_method_call(
2596 [asyncResp](const boost::system::error_code ec) {
2597 if (ec)
2598 {
2599 // TODO Handle for specific error code
2600 BMCWEB_LOG_ERROR
2601 << "doClearPostCodes resp_handler got error " << ec;
2602 asyncResp->res.result(
2603 boost::beast::http::status::internal_server_error);
2604 messages::internalError(asyncResp->res);
2605 return;
2606 }
2607 },
2608 "xyz.openbmc_project.State.Boot.PostCode",
2609 "/xyz/openbmc_project/State/Boot/PostCode",
2610 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2611 }
2612};
2613
2614static void fillPostCodeEntry(
2615 std::shared_ptr<AsyncResp> aResp,
2616 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2617 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2618 const uint64_t skip = 0, const uint64_t top = 0)
2619{
2620 // Get the Message from the MessageRegistry
2621 const message_registries::Message *message =
2622 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2623 std::string severity;
2624 if (message != nullptr)
2625 {
2626 severity = message->severity;
2627 }
2628
2629 uint64_t currentCodeIndex = 0;
2630 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2631
2632 uint64_t firstCodeTimeUs = 0;
2633 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2634 {
2635 currentCodeIndex++;
2636 std::string postcodeEntryID =
2637 "B" + std::to_string(bootIndex) + "-" +
2638 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2639
2640 uint64_t usecSinceEpoch = code.first;
2641 uint64_t usTimeOffset = 0;
2642
2643 if (1 == currentCodeIndex)
2644 { // already incremented
2645 firstCodeTimeUs = code.first;
2646 }
2647 else
2648 {
2649 usTimeOffset = code.first - firstCodeTimeUs;
2650 }
2651
2652 // skip if no specific codeIndex is specified and currentCodeIndex does
2653 // not fall between top and skip
2654 if ((codeIndex == 0) &&
2655 (currentCodeIndex <= skip || currentCodeIndex > top))
2656 {
2657 continue;
2658 }
2659
2660 // skip if a sepcific codeIndex is specified and does not match the
2661 // currentIndex
2662 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2663 {
2664 // This is done for simplicity. 1st entry is needed to calculate
2665 // time offset. To improve efficiency, one can get to the entry
2666 // directly (possibly with flatmap's nth method)
2667 continue;
2668 }
2669
2670 // currentCodeIndex is within top and skip or equal to specified code
2671 // index
2672
2673 // Get the Created time from the timestamp
2674 std::string entryTimeStr;
2675 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2676 {
2677 continue;
2678 }
2679
2680 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2681 std::ostringstream hexCode;
2682 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2683 << code.second;
2684 std::ostringstream timeOffsetStr;
2685 // Set Fixed -Point Notation
2686 timeOffsetStr << std::fixed;
2687 // Set precision to 4 digits
2688 timeOffsetStr << std::setprecision(4);
2689 // Add double to stream
2690 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2691 std::vector<std::string> messageArgs = {
2692 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2693
2694 // Get MessageArgs template from message registry
2695 std::string msg;
2696 if (message != nullptr)
2697 {
2698 msg = message->message;
2699
2700 // fill in this post code value
2701 int i = 0;
2702 for (const std::string &messageArg : messageArgs)
2703 {
2704 std::string argStr = "%" + std::to_string(++i);
2705 size_t argPos = msg.find(argStr);
2706 if (argPos != std::string::npos)
2707 {
2708 msg.replace(argPos, argStr.length(), messageArg);
2709 }
2710 }
2711 }
2712
2713 // add to AsyncResp
2714 logEntryArray.push_back({});
2715 nlohmann::json &bmcLogEntry = logEntryArray.back();
2716 bmcLogEntry = {
2717 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2718 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2719 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2720 "PostCodes/Entries/" +
2721 postcodeEntryID},
2722 {"Name", "POST Code Log Entry"},
2723 {"Id", postcodeEntryID},
2724 {"Message", std::move(msg)},
2725 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2726 {"MessageArgs", std::move(messageArgs)},
2727 {"EntryType", "Event"},
2728 {"Severity", std::move(severity)},
2729 {"Created", std::move(entryTimeStr)}};
2730 }
2731}
2732
2733static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2734 const uint16_t bootIndex,
2735 const uint64_t codeIndex)
2736{
2737 crow::connections::systemBus->async_method_call(
2738 [aResp, bootIndex, codeIndex](
2739 const boost::system::error_code ec,
2740 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2741 if (ec)
2742 {
2743 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2744 messages::internalError(aResp->res);
2745 return;
2746 }
2747
2748 // skip the empty postcode boots
2749 if (postcode.empty())
2750 {
2751 return;
2752 }
2753
2754 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2755
2756 aResp->res.jsonValue["Members@odata.count"] =
2757 aResp->res.jsonValue["Members"].size();
2758 },
2759 "xyz.openbmc_project.State.Boot.PostCode",
2760 "/xyz/openbmc_project/State/Boot/PostCode",
2761 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2762 bootIndex);
2763}
2764
2765static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2766 const uint16_t bootIndex,
2767 const uint16_t bootCount,
2768 const uint64_t entryCount, const uint64_t skip,
2769 const uint64_t top)
2770{
2771 crow::connections::systemBus->async_method_call(
2772 [aResp, bootIndex, bootCount, entryCount, skip,
2773 top](const boost::system::error_code ec,
2774 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2775 if (ec)
2776 {
2777 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2778 messages::internalError(aResp->res);
2779 return;
2780 }
2781
2782 uint64_t endCount = entryCount;
2783 if (!postcode.empty())
2784 {
2785 endCount = entryCount + postcode.size();
2786
2787 if ((skip < endCount) && ((top + skip) > entryCount))
2788 {
2789 uint64_t thisBootSkip =
2790 std::max(skip, entryCount) - entryCount;
2791 uint64_t thisBootTop =
2792 std::min(top + skip, endCount) - entryCount;
2793
2794 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2795 thisBootSkip, thisBootTop);
2796 }
2797 aResp->res.jsonValue["Members@odata.count"] = endCount;
2798 }
2799
2800 // continue to previous bootIndex
2801 if (bootIndex < bootCount)
2802 {
2803 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2804 bootCount, endCount, skip, top);
2805 }
2806 else
2807 {
2808 aResp->res.jsonValue["Members@odata.nextLink"] =
2809 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2810 "Entries?$skip=" +
2811 std::to_string(skip + top);
2812 }
2813 },
2814 "xyz.openbmc_project.State.Boot.PostCode",
2815 "/xyz/openbmc_project/State/Boot/PostCode",
2816 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2817 bootIndex);
2818}
2819
2820static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2821 const uint64_t skip, const uint64_t top)
2822{
2823 uint64_t entryCount = 0;
2824 crow::connections::systemBus->async_method_call(
2825 [aResp, entryCount, skip,
2826 top](const boost::system::error_code ec,
2827 const std::variant<uint16_t> &bootCount) {
2828 if (ec)
2829 {
2830 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2831 messages::internalError(aResp->res);
2832 return;
2833 }
2834 auto pVal = std::get_if<uint16_t>(&bootCount);
2835 if (pVal)
2836 {
2837 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2838 }
2839 else
2840 {
2841 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2842 }
2843 },
2844 "xyz.openbmc_project.State.Boot.PostCode",
2845 "/xyz/openbmc_project/State/Boot/PostCode",
2846 "org.freedesktop.DBus.Properties", "Get",
2847 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2848}
2849
2850class PostCodesEntryCollection : public Node
2851{
2852 public:
2853 template <typename CrowApp>
2854 PostCodesEntryCollection(CrowApp &app) :
2855 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2856 {
2857 entityPrivileges = {
2858 {boost::beast::http::verb::get, {{"Login"}}},
2859 {boost::beast::http::verb::head, {{"Login"}}},
2860 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2861 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2862 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2863 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2864 }
2865
2866 private:
2867 void doGet(crow::Response &res, const crow::Request &req,
2868 const std::vector<std::string> &params) override
2869 {
2870 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2871
2872 asyncResp->res.jsonValue["@odata.type"] =
2873 "#LogEntryCollection.LogEntryCollection";
2874 asyncResp->res.jsonValue["@odata.context"] =
2875 "/redfish/v1/"
2876 "$metadata#LogEntryCollection.LogEntryCollection";
2877 asyncResp->res.jsonValue["@odata.id"] =
2878 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2879 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2880 asyncResp->res.jsonValue["Description"] =
2881 "Collection of POST Code Log Entries";
2882 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2883 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2884
2885 uint64_t skip = 0;
2886 uint64_t top = maxEntriesPerPage; // Show max entries by default
2887 if (!getSkipParam(asyncResp->res, req, skip))
2888 {
2889 return;
2890 }
2891 if (!getTopParam(asyncResp->res, req, top))
2892 {
2893 return;
2894 }
2895 getCurrentBootNumber(asyncResp, skip, top);
2896 }
2897};
2898
2899class PostCodesEntry : public Node
2900{
2901 public:
2902 PostCodesEntry(CrowApp &app) :
2903 Node(app,
2904 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2905 std::string())
2906 {
2907 entityPrivileges = {
2908 {boost::beast::http::verb::get, {{"Login"}}},
2909 {boost::beast::http::verb::head, {{"Login"}}},
2910 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2911 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2912 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2913 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2914 }
2915
2916 private:
2917 void doGet(crow::Response &res, const crow::Request &req,
2918 const std::vector<std::string> &params) override
2919 {
2920 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2921 if (params.size() != 1)
2922 {
2923 messages::internalError(asyncResp->res);
2924 return;
2925 }
2926
2927 const std::string &targetID = params[0];
2928
2929 size_t bootPos = targetID.find('B');
2930 if (bootPos == std::string::npos)
2931 {
2932 // Requested ID was not found
2933 messages::resourceMissingAtURI(asyncResp->res, targetID);
2934 return;
2935 }
2936 std::string_view bootIndexStr(targetID);
2937 bootIndexStr.remove_prefix(bootPos + 1);
2938 uint16_t bootIndex = 0;
2939 uint64_t codeIndex = 0;
2940 size_t dashPos = bootIndexStr.find('-');
2941
2942 if (dashPos == std::string::npos)
2943 {
2944 return;
2945 }
2946 std::string_view codeIndexStr(bootIndexStr);
2947 bootIndexStr.remove_suffix(dashPos);
2948 codeIndexStr.remove_prefix(dashPos + 1);
2949
2950 bootIndex = static_cast<uint16_t>(
2951 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2952 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2953 if (bootIndex == 0 || codeIndex == 0)
2954 {
2955 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2956 << params[0];
2957 }
2958
2959 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2960 asyncResp->res.jsonValue["@odata.context"] =
2961 "/redfish/v1/$metadata#LogEntry.LogEntry";
2962 asyncResp->res.jsonValue["@odata.id"] =
2963 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2964 "Entries";
2965 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2966 asyncResp->res.jsonValue["Description"] =
2967 "Collection of POST Code Log Entries";
2968 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2969 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2970
2971 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2972 }
2973};
2974
Ed Tanous1da66f72018-07-27 16:13:37 -07002975} // namespace redfish