blob: adc1c808ccbe7733bc61f1cbe6c786a614df7c00 [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>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
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
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* crashdumpObject = "com.intel.crashdump";
40constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* crashdumpInterface = "com.intel.crashdump";
42constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070043 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070048constexpr char const* crashdumpTelemetryInterface =
49 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070050
Jason M. Bills4851d452019-03-28 11:27:48 -070051namespace message_registries
52{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053static const Message* getMessageFromRegistry(
54 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070055 const boost::beast::span<const MessageEntry> registry)
56{
57 boost::beast::span<const MessageEntry>::const_iterator messageIt =
58 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050059 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070060 return !std::strcmp(messageEntry.first,
61 messageKey.c_str());
62 });
63 if (messageIt != registry.cend())
64 {
65 return &messageIt->second;
66 }
67
68 return nullptr;
69}
70
Gunnar Mills1214b7e2020-06-04 10:11:30 -050071static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070072{
73 // Redfish MessageIds are in the form
74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
75 // the right Message
76 std::vector<std::string> fields;
77 fields.reserve(4);
78 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 std::string& registryName = fields[0];
80 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070081
82 // Find the right registry and check it for the MessageKey
83 if (std::string(base::header.registryPrefix) == registryName)
84 {
85 return getMessageFromRegistry(
86 messageKey, boost::beast::span<const MessageEntry>(base::registry));
87 }
88 if (std::string(openbmc::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey,
92 boost::beast::span<const MessageEntry>(openbmc::registry));
93 }
94 return nullptr;
95}
96} // namespace message_registries
97
James Feistf6150402019-01-08 10:36:20 -080098namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070099
Andrew Geisslercb92c032018-08-17 07:56:14 -0700100using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500101 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
102 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700103
104using GetManagedObjectsType = boost::container::flat_map<
105 sdbusplus::message::object_path,
106 boost::container::flat_map<std::string, GetManagedPropertyType>>;
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
Ed Tanousd4d25792020-09-29 15:15:03 -0700110 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114 {
115 return "Critical";
116 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700117 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700120 {
121 return "OK";
122 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700123 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700124 {
125 return "Warning";
126 }
127 return "";
128}
129
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500130static int getJournalMetadata(sd_journal* journal,
131 const std::string_view& field,
132 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700133{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700135 size_t length = 0;
136 int ret = 0;
137 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700138 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 if (ret < 0)
141 {
142 return ret;
143 }
Ed Tanous39e77502019-03-04 17:35:53 -0800144 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 // Only use the content after the "=" character.
146 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
147 return ret;
148}
149
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150static int getJournalMetadata(sd_journal* journal,
151 const std::string_view& field, const int& base,
152 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700153{
154 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800155 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 // Get the metadata from the requested field of the journal entry
157 ret = getJournalMetadata(journal, field, metadata);
158 if (ret < 0)
159 {
160 return ret;
161 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000162 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700163 return ret;
164}
165
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800167{
168 int ret = 0;
169 uint64_t timestamp = 0;
170 ret = sd_journal_get_realtime_usec(journal, &timestamp);
171 if (ret < 0)
172 {
173 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
174 << strerror(-ret);
175 return false;
176 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500177 entryTimestamp = crow::utility::getDateTime(
178 static_cast<std::time_t>(timestamp / 1000 / 1000));
179 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800180}
181
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182static bool getSkipParam(crow::Response& res, const crow::Request& req,
183 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700184{
James Feist5a7e8772020-07-22 09:08:38 -0700185 boost::urls::url_view::params_type::iterator it =
186 req.urlParams.find("$skip");
187 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700188 {
James Feist5a7e8772020-07-22 09:08:38 -0700189 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500190 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700191 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
192 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
194
195 messages::queryParameterValueTypeError(res, std::string(skipParam),
196 "$skip");
197 return false;
198 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 }
200 return true;
201}
202
Ed Tanous271584a2019-07-09 16:24:22 -0700203static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204static bool getTopParam(crow::Response& res, const crow::Request& req,
205 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
James Feist5a7e8772020-07-22 09:08:38 -0700207 boost::urls::url_view::params_type::iterator it =
208 req.urlParams.find("$top");
209 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700210 {
James Feist5a7e8772020-07-22 09:08:38 -0700211 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700213 top = std::strtoul(topParam.c_str(), &ptr, 10);
214 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 {
216 messages::queryParameterValueTypeError(res, std::string(topParam),
217 "$top");
218 return false;
219 }
Ed Tanous271584a2019-07-09 16:24:22 -0700220 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 {
222
223 messages::queryParameterOutOfRange(
224 res, std::to_string(top), "$top",
225 "1-" + std::to_string(maxEntriesPerPage));
226 return false;
227 }
228 }
229 return true;
230}
231
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700233 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700234{
235 int ret = 0;
236 static uint64_t prevTs = 0;
237 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700238 if (firstEntry)
239 {
240 prevTs = 0;
241 }
242
Jason M. Bills16428a12018-11-02 12:42:29 -0700243 // Get the entry timestamp
244 uint64_t curTs = 0;
245 ret = sd_journal_get_realtime_usec(journal, &curTs);
246 if (ret < 0)
247 {
248 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
249 << strerror(-ret);
250 return false;
251 }
252 // If the timestamp isn't unique, increment the index
253 if (curTs == prevTs)
254 {
255 index++;
256 }
257 else
258 {
259 // Otherwise, reset it
260 index = 0;
261 }
262 // Save the timestamp
263 prevTs = curTs;
264
265 entryID = std::to_string(curTs);
266 if (index > 0)
267 {
268 entryID += "_" + std::to_string(index);
269 }
270 return true;
271}
272
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500273static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700274 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700275{
Ed Tanous271584a2019-07-09 16:24:22 -0700276 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700277 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700278 if (firstEntry)
279 {
280 prevTs = 0;
281 }
282
Jason M. Bills95820182019-04-22 16:25:34 -0700283 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700284 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700285 std::tm timeStruct = {};
286 std::istringstream entryStream(logEntry);
287 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
288 {
289 curTs = std::mktime(&timeStruct);
290 }
291 // If the timestamp isn't unique, increment the index
292 if (curTs == prevTs)
293 {
294 index++;
295 }
296 else
297 {
298 // Otherwise, reset it
299 index = 0;
300 }
301 // Save the timestamp
302 prevTs = curTs;
303
304 entryID = std::to_string(curTs);
305 if (index > 0)
306 {
307 entryID += "_" + std::to_string(index);
308 }
309 return true;
310}
311
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500312static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
313 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700314{
315 if (entryID.empty())
316 {
317 return false;
318 }
319 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800320 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700321
322 auto underscorePos = tsStr.find("_");
323 if (underscorePos != tsStr.npos)
324 {
325 // Timestamp has an index
326 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800327 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700328 indexStr.remove_prefix(underscorePos + 1);
329 std::size_t pos;
330 try
331 {
Ed Tanous39e77502019-03-04 17:35:53 -0800332 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700333 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500334 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700335 {
336 messages::resourceMissingAtURI(res, entryID);
337 return false;
338 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 {
341 messages::resourceMissingAtURI(res, entryID);
342 return false;
343 }
344 if (pos != indexStr.size())
345 {
346 messages::resourceMissingAtURI(res, entryID);
347 return false;
348 }
349 }
350 // Timestamp has no index
351 std::size_t pos;
352 try
353 {
Ed Tanous39e77502019-03-04 17:35:53 -0800354 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700355 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700357 {
358 messages::resourceMissingAtURI(res, entryID);
359 return false;
360 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500361 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700362 {
363 messages::resourceMissingAtURI(res, entryID);
364 return false;
365 }
366 if (pos != tsStr.size())
367 {
368 messages::resourceMissingAtURI(res, entryID);
369 return false;
370 }
371 return true;
372}
373
Jason M. Bills95820182019-04-22 16:25:34 -0700374static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500375 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700376{
377 static const std::filesystem::path redfishLogDir = "/var/log";
378 static const std::string redfishLogFilename = "redfish";
379
380 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500381 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700382 std::filesystem::directory_iterator(redfishLogDir))
383 {
384 // If we find a redfish log file, save the path
385 std::string filename = dirEnt.path().filename();
386 if (boost::starts_with(filename, redfishLogFilename))
387 {
388 redfishLogFiles.emplace_back(redfishLogDir / filename);
389 }
390 }
391 // As the log files rotate, they are appended with a ".#" that is higher for
392 // the older logs. Since we don't expect more than 10 log files, we
393 // can just sort the list to get them in order from newest to oldest
394 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
395
396 return !redfishLogFiles.empty();
397}
398
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500399inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
400 const std::string& dumpType)
401{
402 std::string dumpPath;
403 if (dumpType == "BMC")
404 {
405 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
406 }
407 else if (dumpType == "System")
408 {
409 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
410 }
411 else
412 {
413 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
414 messages::internalError(asyncResp->res);
415 return;
416 }
417
418 crow::connections::systemBus->async_method_call(
419 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
420 GetManagedObjectsType& resp) {
421 if (ec)
422 {
423 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
424 messages::internalError(asyncResp->res);
425 return;
426 }
427
428 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
429 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500430 std::string dumpEntryPath =
431 "/xyz/openbmc_project/dump/" +
432 std::string(boost::algorithm::to_lower_copy(dumpType)) +
433 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434
435 for (auto& object : resp)
436 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500437 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500438 {
439 continue;
440 }
441 std::time_t timestamp;
442 uint64_t size = 0;
443 entriesArray.push_back({});
444 nlohmann::json& thisEntry = entriesArray.back();
445 const std::string& path =
446 static_cast<const std::string&>(object.first);
Ed Tanousf23b7292020-10-15 09:41:17 -0700447 std::size_t lastPos = path.rfind('/');
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500448 if (lastPos == std::string::npos)
449 {
450 continue;
451 }
452 std::string entryID = path.substr(lastPos + 1);
453
454 for (auto& interfaceMap : object.second)
455 {
456 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
457 {
458
459 for (auto& propertyMap : interfaceMap.second)
460 {
461 if (propertyMap.first == "Size")
462 {
463 auto sizePtr =
464 std::get_if<uint64_t>(&propertyMap.second);
465 if (sizePtr == nullptr)
466 {
467 messages::internalError(asyncResp->res);
468 break;
469 }
470 size = *sizePtr;
471 break;
472 }
473 }
474 }
475 else if (interfaceMap.first ==
476 "xyz.openbmc_project.Time.EpochTime")
477 {
478
479 for (auto& propertyMap : interfaceMap.second)
480 {
481 if (propertyMap.first == "Elapsed")
482 {
483 const uint64_t* usecsTimeStamp =
484 std::get_if<uint64_t>(&propertyMap.second);
485 if (usecsTimeStamp == nullptr)
486 {
487 messages::internalError(asyncResp->res);
488 break;
489 }
490 timestamp =
491 static_cast<std::time_t>(*usecsTimeStamp);
492 break;
493 }
494 }
495 }
496 }
497
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500498 thisEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500499 thisEntry["@odata.id"] = dumpPath + entryID;
500 thisEntry["Id"] = entryID;
501 thisEntry["EntryType"] = "Event";
502 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
503 thisEntry["Name"] = dumpType + " Dump Entry";
504
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500505 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500506
507 if (dumpType == "BMC")
508 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500509 thisEntry["DiagnosticDataType"] = "Manager";
510 thisEntry["AdditionalDataURI"] =
511 "/redfish/v1/Managers/bmc/LogServices/Dump/"
512 "attachment/" +
513 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500514 }
515 else if (dumpType == "System")
516 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500517 thisEntry["DiagnosticDataType"] = "OEM";
518 thisEntry["OEMDiagnosticDataType"] = "System";
519 thisEntry["AdditionalDataURI"] =
520 "/redfish/v1/Systems/system/LogServices/Dump/"
521 "attachment/" +
522 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 }
524 }
525 asyncResp->res.jsonValue["Members@odata.count"] =
526 entriesArray.size();
527 },
528 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
529 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
530}
531
532inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
533 const std::string& entryID,
534 const std::string& dumpType)
535{
536 std::string dumpPath;
537 if (dumpType == "BMC")
538 {
539 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
540 }
541 else if (dumpType == "System")
542 {
543 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
544 }
545 else
546 {
547 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
548 messages::internalError(asyncResp->res);
549 return;
550 }
551
552 crow::connections::systemBus->async_method_call(
553 [asyncResp, entryID, dumpPath, dumpType](
554 const boost::system::error_code ec, GetManagedObjectsType& resp) {
555 if (ec)
556 {
557 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
558 messages::internalError(asyncResp->res);
559 return;
560 }
561
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500562 bool foundDumpEntry = false;
563 std::string dumpEntryPath =
564 "/xyz/openbmc_project/dump/" +
565 std::string(boost::algorithm::to_lower_copy(dumpType)) +
566 "/entry/";
567
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500568 for (auto& objectPath : resp)
569 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500570 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500571 {
572 continue;
573 }
574
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500575 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 std::time_t timestamp;
577 uint64_t size = 0;
578
579 for (auto& interfaceMap : objectPath.second)
580 {
581 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
582 {
583 for (auto& propertyMap : interfaceMap.second)
584 {
585 if (propertyMap.first == "Size")
586 {
587 auto sizePtr =
588 std::get_if<uint64_t>(&propertyMap.second);
589 if (sizePtr == nullptr)
590 {
591 messages::internalError(asyncResp->res);
592 break;
593 }
594 size = *sizePtr;
595 break;
596 }
597 }
598 }
599 else if (interfaceMap.first ==
600 "xyz.openbmc_project.Time.EpochTime")
601 {
602 for (auto& propertyMap : interfaceMap.second)
603 {
604 if (propertyMap.first == "Elapsed")
605 {
606 const uint64_t* usecsTimeStamp =
607 std::get_if<uint64_t>(&propertyMap.second);
608 if (usecsTimeStamp == nullptr)
609 {
610 messages::internalError(asyncResp->res);
611 break;
612 }
613 timestamp =
614 static_cast<std::time_t>(*usecsTimeStamp);
615 break;
616 }
617 }
618 }
619 }
620
621 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500622 "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500623 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
624 asyncResp->res.jsonValue["Id"] = entryID;
625 asyncResp->res.jsonValue["EntryType"] = "Event";
626 asyncResp->res.jsonValue["Created"] =
627 crow::utility::getDateTime(timestamp);
628 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
629
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500630 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500631
632 if (dumpType == "BMC")
633 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500634 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
635 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500636 "/redfish/v1/Managers/bmc/LogServices/Dump/"
637 "attachment/" +
638 entryID;
639 }
640 else if (dumpType == "System")
641 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500642 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
643 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500644 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500645 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500646 "/redfish/v1/Systems/system/LogServices/Dump/"
647 "attachment/" +
648 entryID;
649 }
650 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500651 if (foundDumpEntry == false)
652 {
653 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
654 messages::internalError(asyncResp->res);
655 return;
656 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500657 },
658 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
659 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
660}
661
Stanley Chu98782562020-11-04 16:10:24 +0800662inline void deleteDumpEntry(const std::shared_ptr<AsyncResp>& asyncResp,
663 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500664 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500665{
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500666 auto respHandler = [asyncResp](const boost::system::error_code ec) {
667 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
668 if (ec)
669 {
670 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
671 << ec;
672 messages::internalError(asyncResp->res);
673 return;
674 }
675 };
676 crow::connections::systemBus->async_method_call(
677 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500678 "/xyz/openbmc_project/dump/" +
679 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
680 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500681 "xyz.openbmc_project.Object.Delete", "Delete");
682}
683
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500684inline void createDumpTaskCallback(const crow::Request& req,
Ed Tanousb5a76932020-09-29 16:16:58 -0700685 const std::shared_ptr<AsyncResp>& asyncResp,
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500686 const uint32_t& dumpId,
687 const std::string& dumpPath,
688 const std::string& dumpType)
689{
690 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500691 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500692 boost::system::error_code err, sdbusplus::message::message& m,
693 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000694 if (err)
695 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500696 BMCWEB_LOG_ERROR << "Error in creating a dump";
697 taskData->state = "Cancelled";
698 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000699 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500700 std::vector<std::pair<
701 std::string,
702 std::vector<std::pair<std::string, std::variant<std::string>>>>>
703 interfacesList;
704
705 sdbusplus::message::object_path objPath;
706
707 m.read(objPath, interfacesList);
708
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500709 if (objPath.str ==
710 "/xyz/openbmc_project/dump/" +
711 std::string(boost::algorithm::to_lower_copy(dumpType)) +
712 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500713 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500714 nlohmann::json retMessage = messages::success();
715 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500716
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500717 std::string headerLoc =
718 "Location: " + dumpPath + std::to_string(dumpId);
719 taskData->payload->httpHeaders.emplace_back(
720 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500721
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500722 taskData->state = "Completed";
723 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500724 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500725 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 },
727 "type='signal',interface='org.freedesktop.DBus."
728 "ObjectManager',"
729 "member='InterfacesAdded', "
730 "path='/xyz/openbmc_project/dump'");
731
732 task->startTimer(std::chrono::minutes(3));
733 task->populateResp(asyncResp->res);
734 task->payload.emplace(req);
735}
736
737inline void createDump(crow::Response& res, const crow::Request& req,
738 const std::string& dumpType)
739{
740 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
741
742 std::string dumpPath;
743 if (dumpType == "BMC")
744 {
745 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
746 }
747 else if (dumpType == "System")
748 {
749 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
750 }
751 else
752 {
753 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
754 messages::internalError(asyncResp->res);
755 return;
756 }
757
758 std::optional<std::string> diagnosticDataType;
759 std::optional<std::string> oemDiagnosticDataType;
760
761 if (!redfish::json_util::readJson(
762 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
763 "OEMDiagnosticDataType", oemDiagnosticDataType))
764 {
765 return;
766 }
767
768 if (dumpType == "System")
769 {
770 if (!oemDiagnosticDataType || !diagnosticDataType)
771 {
772 BMCWEB_LOG_ERROR << "CreateDump action parameter "
773 "'DiagnosticDataType'/"
774 "'OEMDiagnosticDataType' value not found!";
775 messages::actionParameterMissing(
776 asyncResp->res, "CollectDiagnosticData",
777 "DiagnosticDataType & OEMDiagnosticDataType");
778 return;
779 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700780 if ((*oemDiagnosticDataType != "System") ||
781 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500782 {
783 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
784 messages::invalidObject(asyncResp->res,
785 "System Dump creation parameters");
786 return;
787 }
788 }
789 else if (dumpType == "BMC")
790 {
791 if (!diagnosticDataType)
792 {
793 BMCWEB_LOG_ERROR << "CreateDump action parameter "
794 "'DiagnosticDataType' not found!";
795 messages::actionParameterMissing(
796 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
797 return;
798 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700799 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500800 {
801 BMCWEB_LOG_ERROR
802 << "Wrong parameter value passed for 'DiagnosticDataType'";
803 messages::invalidObject(asyncResp->res,
804 "BMC Dump creation parameters");
805 return;
806 }
807 }
808
809 crow::connections::systemBus->async_method_call(
810 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
811 const uint32_t& dumpId) {
812 if (ec)
813 {
814 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
815 messages::internalError(asyncResp->res);
816 return;
817 }
818 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
819
820 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
821 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500822 "xyz.openbmc_project.Dump.Manager",
823 "/xyz/openbmc_project/dump/" +
824 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500825 "xyz.openbmc_project.Dump.Create", "CreateDump");
826}
827
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500828inline void clearDump(crow::Response& res, const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500829{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500830 std::string dumpTypeLowerCopy =
831 std::string(boost::algorithm::to_lower_copy(dumpType));
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500832 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
833 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500834 [asyncResp, dumpType](const boost::system::error_code ec,
835 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500836 if (ec)
837 {
838 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
839 messages::internalError(asyncResp->res);
840 return;
841 }
842
843 for (const std::string& path : subTreePaths)
844 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700845 std::size_t pos = path.rfind('/');
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500846 if (pos != std::string::npos)
847 {
848 std::string logID = path.substr(pos + 1);
Stanley Chu98782562020-11-04 16:10:24 +0800849 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500850 }
851 }
852 },
853 "xyz.openbmc_project.ObjectMapper",
854 "/xyz/openbmc_project/object_mapper",
855 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500856 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
857 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
858 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500859}
860
Ed Tanous2c70f802020-09-28 14:29:23 -0700861static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500862 const std::vector<std::pair<std::string, VariantType>>& params,
863 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700864{
865 for (auto property : params)
866 {
867 if (property.first == "Timestamp")
868 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500869 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500870 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700871 if (value != nullptr)
872 {
873 timestamp = *value;
874 }
875 }
876 else if (property.first == "Filename")
877 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500879 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700880 if (value != nullptr)
881 {
882 filename = *value;
883 }
884 }
885 else if (property.first == "Log")
886 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500887 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500888 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700889 if (value != nullptr)
890 {
891 logfile = *value;
892 }
893 }
894 }
895}
896
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500897constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800898class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700899{
900 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700901 SystemLogServiceCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800902 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800903 {
904 entityPrivileges = {
905 {boost::beast::http::verb::get, {{"Login"}}},
906 {boost::beast::http::verb::head, {{"Login"}}},
907 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
908 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
909 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
910 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
911 }
912
913 private:
914 /**
915 * Functions triggers appropriate requests on DBus
916 */
Ed Tanouscb13a392020-07-25 19:02:03 +0000917 void doGet(crow::Response& res, const crow::Request&,
918 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800919 {
920 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800921 // Collections don't include the static data added by SubRoute because
922 // it has a duplicate entry for members
923 asyncResp->res.jsonValue["@odata.type"] =
924 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800925 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800926 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800927 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
928 asyncResp->res.jsonValue["Description"] =
929 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500930 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800931 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800932 logServiceArray.push_back(
933 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500934#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600935 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500936 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600937#endif
938
Jason M. Billsd53dd412019-02-12 17:16:22 -0800939#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
940 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500941 {{"@odata.id",
942 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800943#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800944 asyncResp->res.jsonValue["Members@odata.count"] =
945 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800946
947 crow::connections::systemBus->async_method_call(
948 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500949 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800950 if (ec)
951 {
952 BMCWEB_LOG_ERROR << ec;
953 return;
954 }
955
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500956 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800957 {
958 if (pathStr.find("PostCode") != std::string::npos)
959 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000960 nlohmann::json& logServiceArrayLocal =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800961 asyncResp->res.jsonValue["Members"];
Ed Tanous23a21a12020-07-25 04:45:05 +0000962 logServiceArrayLocal.push_back(
ZhikuiRena3316fc2020-01-29 14:58:08 -0800963 {{"@odata.id", "/redfish/v1/Systems/system/"
964 "LogServices/PostCodes"}});
965 asyncResp->res.jsonValue["Members@odata.count"] =
Ed Tanous23a21a12020-07-25 04:45:05 +0000966 logServiceArrayLocal.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800967 return;
968 }
969 }
970 },
971 "xyz.openbmc_project.ObjectMapper",
972 "/xyz/openbmc_project/object_mapper",
973 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500974 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800975 }
976};
977
978class EventLogService : public Node
979{
980 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700981 EventLogService(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800982 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800983 {
984 entityPrivileges = {
985 {boost::beast::http::verb::get, {{"Login"}}},
986 {boost::beast::http::verb::head, {{"Login"}}},
987 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
988 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
989 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
990 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
991 }
992
993 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000994 void doGet(crow::Response& res, const crow::Request&,
995 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800996 {
997 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
998
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800999 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001000 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001001 asyncResp->res.jsonValue["@odata.type"] =
1002 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001003 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1004 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001005 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001006 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1007 asyncResp->res.jsonValue["Entries"] = {
1008 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001009 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001010 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1011
1012 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1013 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001014 }
1015};
1016
Tim Lee1f56a3a2019-10-09 10:17:57 +08001017class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001018{
1019 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001020 JournalEventLogClear(App& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001021 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1022 "LogService.ClearLog/")
1023 {
1024 entityPrivileges = {
1025 {boost::beast::http::verb::get, {{"Login"}}},
1026 {boost::beast::http::verb::head, {{"Login"}}},
1027 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1028 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1029 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1030 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1031 }
1032
1033 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001034 void doPost(crow::Response& res, const crow::Request&,
1035 const std::vector<std::string>&) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001036 {
1037 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1038
1039 // Clear the EventLog by deleting the log files
1040 std::vector<std::filesystem::path> redfishLogFiles;
1041 if (getRedfishLogFiles(redfishLogFiles))
1042 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001043 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001044 {
1045 std::error_code ec;
1046 std::filesystem::remove(file, ec);
1047 }
1048 }
1049
1050 // Reload rsyslog so it knows to start new log files
1051 crow::connections::systemBus->async_method_call(
1052 [asyncResp](const boost::system::error_code ec) {
1053 if (ec)
1054 {
1055 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1056 messages::internalError(asyncResp->res);
1057 return;
1058 }
1059
1060 messages::success(asyncResp->res);
1061 },
1062 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1063 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1064 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001065 }
1066};
1067
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001068static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001069 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001070 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001071{
Jason M. Bills95820182019-04-22 16:25:34 -07001072 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001073 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001074 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001075 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001076 {
1077 return 1;
1078 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001079 std::string timestamp = logEntry.substr(0, space);
1080 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001081 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001082 if (entryStart == std::string::npos)
1083 {
1084 return 1;
1085 }
1086 std::string_view entry(logEntry);
1087 entry.remove_prefix(entryStart);
1088 // Use split to separate the entry into its fields
1089 std::vector<std::string> logEntryFields;
1090 boost::split(logEntryFields, entry, boost::is_any_of(","),
1091 boost::token_compress_on);
1092 // We need at least a MessageId to be valid
1093 if (logEntryFields.size() < 1)
1094 {
1095 return 1;
1096 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001097 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001098
Jason M. Bills4851d452019-03-28 11:27:48 -07001099 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001100 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001101 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001102
Jason M. Bills4851d452019-03-28 11:27:48 -07001103 std::string msg;
1104 std::string severity;
1105 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001106 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001107 msg = message->message;
1108 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001109 }
1110
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001111 // Get the MessageArgs from the log if there are any
1112 boost::beast::span<std::string> messageArgs;
1113 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001114 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001115 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001116 // If the first string is empty, assume there are no MessageArgs
1117 std::size_t messageArgsSize = 0;
1118 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001119 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001120 messageArgsSize = logEntryFields.size() - 1;
1121 }
1122
Ed Tanous23a21a12020-07-25 04:45:05 +00001123 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001124
1125 // Fill the MessageArgs into the Message
1126 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001127 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001128 {
1129 std::string argStr = "%" + std::to_string(++i);
1130 size_t argPos = msg.find(argStr);
1131 if (argPos != std::string::npos)
1132 {
1133 msg.replace(argPos, argStr.length(), messageArg);
1134 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001135 }
1136 }
1137
Jason M. Bills95820182019-04-22 16:25:34 -07001138 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1139 // format which matches the Redfish format except for the fractional seconds
1140 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001141 std::size_t dot = timestamp.find_first_of('.');
1142 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001143 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001144 {
Jason M. Bills95820182019-04-22 16:25:34 -07001145 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001146 }
1147
1148 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001149 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001150 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001151 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001152 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001153 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001154 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001155 {"Id", logEntryID},
1156 {"Message", std::move(msg)},
1157 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001158 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001159 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001160 {"Severity", std::move(severity)},
1161 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001162 return 0;
1163}
1164
Anthony Wilson27062602019-04-22 02:10:09 -05001165class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001166{
1167 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001168 JournalEventLogEntryCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001169 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001170 {
1171 entityPrivileges = {
1172 {boost::beast::http::verb::get, {{"Login"}}},
1173 {boost::beast::http::verb::head, {{"Login"}}},
1174 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1175 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1176 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1177 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1178 }
1179
1180 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001181 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001182 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001183 {
1184 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001185 uint64_t skip = 0;
1186 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001187 if (!getSkipParam(asyncResp->res, req, skip))
1188 {
1189 return;
1190 }
1191 if (!getTopParam(asyncResp->res, req, top))
1192 {
1193 return;
1194 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001195 // Collections don't include the static data added by SubRoute because
1196 // it has a duplicate entry for members
1197 asyncResp->res.jsonValue["@odata.type"] =
1198 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001199 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001200 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001201 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1202 asyncResp->res.jsonValue["Description"] =
1203 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001204
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001205 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001206 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001207 // Go through the log files and create a unique ID for each entry
1208 std::vector<std::filesystem::path> redfishLogFiles;
1209 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001210 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001211 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001212
1213 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001214 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1215 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001216 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001217 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001218 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001219 {
1220 continue;
1221 }
1222
Jason M. Billse85d6b12019-07-29 17:01:15 -07001223 // Reset the unique ID on the first entry
1224 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001225 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 {
Jason M. Bills95820182019-04-22 16:25:34 -07001227 entryCount++;
1228 // Handle paging using skip (number of entries to skip from the
1229 // start) and top (number of entries to display)
1230 if (entryCount <= skip || entryCount > skip + top)
1231 {
1232 continue;
1233 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001234
Jason M. Bills95820182019-04-22 16:25:34 -07001235 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001236 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001237 {
1238 continue;
1239 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001240
Jason M. Billse85d6b12019-07-29 17:01:15 -07001241 if (firstEntry)
1242 {
1243 firstEntry = false;
1244 }
1245
Jason M. Bills95820182019-04-22 16:25:34 -07001246 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001247 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001248 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1249 {
1250 messages::internalError(asyncResp->res);
1251 return;
1252 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001253 }
1254 }
1255 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1256 if (skip + top < entryCount)
1257 {
1258 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001259 "/redfish/v1/Systems/system/LogServices/EventLog/"
1260 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001261 std::to_string(skip + top);
1262 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001263 }
1264};
1265
Jason M. Bills897967d2019-07-29 17:05:30 -07001266class JournalEventLogEntry : public Node
1267{
1268 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001269 JournalEventLogEntry(App& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001270 Node(app,
1271 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1272 std::string())
1273 {
1274 entityPrivileges = {
1275 {boost::beast::http::verb::get, {{"Login"}}},
1276 {boost::beast::http::verb::head, {{"Login"}}},
1277 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1278 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1279 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1280 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1281 }
1282
1283 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001284 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001285 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001286 {
1287 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1288 if (params.size() != 1)
1289 {
1290 messages::internalError(asyncResp->res);
1291 return;
1292 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001293 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001294
1295 // Go through the log files and check the unique ID for each entry to
1296 // find the target entry
1297 std::vector<std::filesystem::path> redfishLogFiles;
1298 getRedfishLogFiles(redfishLogFiles);
1299 std::string logEntry;
1300
1301 // Oldest logs are in the last file, so start there and loop backwards
1302 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1303 it++)
1304 {
1305 std::ifstream logStream(*it);
1306 if (!logStream.is_open())
1307 {
1308 continue;
1309 }
1310
1311 // Reset the unique ID on the first entry
1312 bool firstEntry = true;
1313 while (std::getline(logStream, logEntry))
1314 {
1315 std::string idStr;
1316 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1317 {
1318 continue;
1319 }
1320
1321 if (firstEntry)
1322 {
1323 firstEntry = false;
1324 }
1325
1326 if (idStr == targetID)
1327 {
1328 if (fillEventLogEntryJson(idStr, logEntry,
1329 asyncResp->res.jsonValue) != 0)
1330 {
1331 messages::internalError(asyncResp->res);
1332 return;
1333 }
1334 return;
1335 }
1336 }
1337 }
1338 // Requested ID was not found
1339 messages::resourceMissingAtURI(asyncResp->res, targetID);
1340 }
1341};
1342
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001343class DBusEventLogEntryCollection : public Node
1344{
1345 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001346 DBusEventLogEntryCollection(App& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001347 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1348 {
1349 entityPrivileges = {
1350 {boost::beast::http::verb::get, {{"Login"}}},
1351 {boost::beast::http::verb::head, {{"Login"}}},
1352 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1353 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1354 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1355 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1356 }
1357
1358 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001359 void doGet(crow::Response& res, const crow::Request&,
1360 const std::vector<std::string>&) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001361 {
1362 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1363
1364 // Collections don't include the static data added by SubRoute because
1365 // it has a duplicate entry for members
1366 asyncResp->res.jsonValue["@odata.type"] =
1367 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001368 asyncResp->res.jsonValue["@odata.id"] =
1369 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1370 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1371 asyncResp->res.jsonValue["Description"] =
1372 "Collection of System Event Log Entries";
1373
Andrew Geisslercb92c032018-08-17 07:56:14 -07001374 // DBus implementation of EventLog/Entries
1375 // Make call to Logging Service to find all log entry objects
1376 crow::connections::systemBus->async_method_call(
1377 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001378 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001379 if (ec)
1380 {
1381 // TODO Handle for specific error code
1382 BMCWEB_LOG_ERROR
1383 << "getLogEntriesIfaceData resp_handler got error "
1384 << ec;
1385 messages::internalError(asyncResp->res);
1386 return;
1387 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001388 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001389 asyncResp->res.jsonValue["Members"];
1390 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001391 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001392 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001393 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001394 {
1395 if (interfaceMap.first !=
1396 "xyz.openbmc_project.Logging.Entry")
1397 {
1398 BMCWEB_LOG_DEBUG << "Bailing early on "
1399 << interfaceMap.first;
1400 continue;
1401 }
1402 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001403 nlohmann::json& thisEntry = entriesArray.back();
1404 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001405 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001406 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001407 std::string* severity = nullptr;
1408 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001409
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001410 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001411 {
1412 if (propertyMap.first == "Id")
1413 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001414 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001415 if (id == nullptr)
1416 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001417 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001418 }
1419 }
1420 else if (propertyMap.first == "Timestamp")
1421 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001422 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001423 std::get_if<uint64_t>(&propertyMap.second);
1424 if (millisTimeStamp == nullptr)
1425 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001426 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001427 }
George Liuebd45902020-08-26 14:21:10 +08001428 else
1429 {
1430 timestamp = crow::utility::getTimestamp(
1431 *millisTimeStamp);
1432 }
George Liud139c232020-08-18 18:48:57 +08001433 }
1434 else if (propertyMap.first == "UpdateTimestamp")
1435 {
1436 const uint64_t* millisTimeStamp =
1437 std::get_if<uint64_t>(&propertyMap.second);
1438 if (millisTimeStamp == nullptr)
1439 {
1440 messages::internalError(asyncResp->res);
1441 }
George Liuebd45902020-08-26 14:21:10 +08001442 else
1443 {
1444 updateTimestamp =
1445 crow::utility::getTimestamp(
1446 *millisTimeStamp);
1447 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001448 }
1449 else if (propertyMap.first == "Severity")
1450 {
1451 severity = std::get_if<std::string>(
1452 &propertyMap.second);
1453 if (severity == nullptr)
1454 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001455 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001456 }
1457 }
1458 else if (propertyMap.first == "Message")
1459 {
1460 message = std::get_if<std::string>(
1461 &propertyMap.second);
1462 if (message == nullptr)
1463 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001464 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001465 }
1466 }
1467 }
1468 thisEntry = {
George Liud139c232020-08-18 18:48:57 +08001469 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001470 {"@odata.id",
1471 "/redfish/v1/Systems/system/LogServices/EventLog/"
1472 "Entries/" +
1473 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001474 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001475 {"Id", std::to_string(*id)},
1476 {"Message", *message},
1477 {"EntryType", "Event"},
1478 {"Severity",
1479 translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001480 {"Created", crow::utility::getDateTime(timestamp)},
1481 {"Modified",
1482 crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001483 }
1484 }
1485 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001486 [](const nlohmann::json& left,
1487 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001488 return (left["Id"] <= right["Id"]);
1489 });
1490 asyncResp->res.jsonValue["Members@odata.count"] =
1491 entriesArray.size();
1492 },
1493 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1494 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001495 }
1496};
1497
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001498class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001499{
1500 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001501 DBusEventLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001502 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001503 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1504 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001505 {
1506 entityPrivileges = {
1507 {boost::beast::http::verb::get, {{"Login"}}},
1508 {boost::beast::http::verb::head, {{"Login"}}},
1509 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1510 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1511 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1512 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1513 }
1514
1515 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001516 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001517 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001518 {
1519 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001520 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001521 {
1522 messages::internalError(asyncResp->res);
1523 return;
1524 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001525 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001526
Andrew Geisslercb92c032018-08-17 07:56:14 -07001527 // DBus implementation of EventLog/Entries
1528 // Make call to Logging Service to find all log entry objects
1529 crow::connections::systemBus->async_method_call(
1530 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001531 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001532 if (ec)
1533 {
1534 BMCWEB_LOG_ERROR
1535 << "EventLogEntry (DBus) resp_handler got error " << ec;
1536 messages::internalError(asyncResp->res);
1537 return;
1538 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001539 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001540 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001541 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001542 std::string* severity = nullptr;
1543 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001544
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001545 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001546 {
1547 if (propertyMap.first == "Id")
1548 {
1549 id = std::get_if<uint32_t>(&propertyMap.second);
1550 if (id == nullptr)
1551 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001552 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001553 }
1554 }
1555 else if (propertyMap.first == "Timestamp")
1556 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001557 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001558 std::get_if<uint64_t>(&propertyMap.second);
1559 if (millisTimeStamp == nullptr)
1560 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001561 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001562 }
George Liuebd45902020-08-26 14:21:10 +08001563 else
1564 {
1565 timestamp =
1566 crow::utility::getTimestamp(*millisTimeStamp);
1567 }
George Liud139c232020-08-18 18:48:57 +08001568 }
1569 else if (propertyMap.first == "UpdateTimestamp")
1570 {
1571 const uint64_t* millisTimeStamp =
1572 std::get_if<uint64_t>(&propertyMap.second);
1573 if (millisTimeStamp == nullptr)
1574 {
1575 messages::internalError(asyncResp->res);
1576 }
George Liuebd45902020-08-26 14:21:10 +08001577 else
1578 {
1579 updateTimestamp =
1580 crow::utility::getTimestamp(*millisTimeStamp);
1581 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001582 }
1583 else if (propertyMap.first == "Severity")
1584 {
1585 severity =
1586 std::get_if<std::string>(&propertyMap.second);
1587 if (severity == nullptr)
1588 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001589 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001590 }
1591 }
1592 else if (propertyMap.first == "Message")
1593 {
1594 message = std::get_if<std::string>(&propertyMap.second);
1595 if (message == nullptr)
1596 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001597 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001598 }
1599 }
1600 }
Ed Tanous271584a2019-07-09 16:24:22 -07001601 if (id == nullptr || message == nullptr || severity == nullptr)
1602 {
1603 return;
1604 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001605 asyncResp->res.jsonValue = {
George Liud139c232020-08-18 18:48:57 +08001606 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001607 {"@odata.id",
1608 "/redfish/v1/Systems/system/LogServices/EventLog/"
1609 "Entries/" +
1610 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001611 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001612 {"Id", std::to_string(*id)},
1613 {"Message", *message},
1614 {"EntryType", "Event"},
1615 {"Severity", translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001616 {"Created", crow::utility::getDateTime(timestamp)},
1617 {"Modified", crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001618 },
1619 "xyz.openbmc_project.Logging",
1620 "/xyz/openbmc_project/logging/entry/" + entryID,
1621 "org.freedesktop.DBus.Properties", "GetAll",
1622 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001623 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001624
Ed Tanouscb13a392020-07-25 19:02:03 +00001625 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001626 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001627 {
1628
1629 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1630
1631 auto asyncResp = std::make_shared<AsyncResp>(res);
1632
1633 if (params.size() != 1)
1634 {
1635 messages::internalError(asyncResp->res);
1636 return;
1637 }
1638 std::string entryID = params[0];
1639
1640 dbus::utility::escapePathForDbus(entryID);
1641
1642 // Process response from Logging service.
1643 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1644 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1645 if (ec)
1646 {
1647 // TODO Handle for specific error code
1648 BMCWEB_LOG_ERROR
1649 << "EventLogEntry (DBus) doDelete respHandler got error "
1650 << ec;
1651 asyncResp->res.result(
1652 boost::beast::http::status::internal_server_error);
1653 return;
1654 }
1655
1656 asyncResp->res.result(boost::beast::http::status::ok);
1657 };
1658
1659 // Make call to Logging service to request Delete Log
1660 crow::connections::systemBus->async_method_call(
1661 respHandler, "xyz.openbmc_project.Logging",
1662 "/xyz/openbmc_project/logging/entry/" + entryID,
1663 "xyz.openbmc_project.Object.Delete", "Delete");
1664 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001665};
1666
1667class BMCLogServiceCollection : public Node
1668{
1669 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001670 BMCLogServiceCollection(App& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001671 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001672 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001673 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001674 {boost::beast::http::verb::get, {{"Login"}}},
1675 {boost::beast::http::verb::head, {{"Login"}}},
1676 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1677 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1678 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1679 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001680 }
1681
1682 private:
1683 /**
1684 * Functions triggers appropriate requests on DBus
1685 */
Ed Tanouscb13a392020-07-25 19:02:03 +00001686 void doGet(crow::Response& res, const crow::Request&,
1687 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001688 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001689 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001690 // Collections don't include the static data added by SubRoute because
1691 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001692 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001693 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001694 asyncResp->res.jsonValue["@odata.id"] =
1695 "/redfish/v1/Managers/bmc/LogServices";
1696 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1697 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001698 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001699 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001700 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001701#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1702 logServiceArray.push_back(
1703 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1704#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001705#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1706 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001707 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001708#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001709 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001710 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001711 }
1712};
1713
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001714class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001715{
1716 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001717 BMCJournalLogService(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001718 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001719 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001720 entityPrivileges = {
1721 {boost::beast::http::verb::get, {{"Login"}}},
1722 {boost::beast::http::verb::head, {{"Login"}}},
1723 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1724 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1725 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1726 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1727 }
1728
1729 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001730 void doGet(crow::Response& res, const crow::Request&,
1731 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001732 {
1733 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001734 asyncResp->res.jsonValue["@odata.type"] =
1735 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001736 asyncResp->res.jsonValue["@odata.id"] =
1737 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001738 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1739 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1740 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001741 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001742 asyncResp->res.jsonValue["Entries"] = {
1743 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001744 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001745 }
1746};
1747
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001748static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1749 sd_journal* journal,
1750 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001751{
1752 // Get the Log Entry contents
1753 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001754
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001755 std::string message;
1756 std::string_view syslogID;
1757 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1758 if (ret < 0)
1759 {
1760 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1761 << strerror(-ret);
1762 }
1763 if (!syslogID.empty())
1764 {
1765 message += std::string(syslogID) + ": ";
1766 }
1767
Ed Tanous39e77502019-03-04 17:35:53 -08001768 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001769 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001770 if (ret < 0)
1771 {
1772 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1773 return 1;
1774 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001775 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001776
1777 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001778 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001779 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001780 if (ret < 0)
1781 {
1782 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001783 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001784
1785 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001786 std::string entryTimeStr;
1787 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001788 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001789 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001790 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001791
1792 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001793 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001794 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001795 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1796 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001797 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001798 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001799 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001800 {"EntryType", "Oem"},
1801 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001802 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001803 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001804 {"Created", std::move(entryTimeStr)}};
1805 return 0;
1806}
1807
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001808class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001809{
1810 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001811 BMCJournalLogEntryCollection(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001812 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001813 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001814 entityPrivileges = {
1815 {boost::beast::http::verb::get, {{"Login"}}},
1816 {boost::beast::http::verb::head, {{"Login"}}},
1817 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1818 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1819 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1820 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1821 }
1822
1823 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001824 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001825 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001826 {
1827 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001828 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001829 uint64_t skip = 0;
1830 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001831 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001832 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001833 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001834 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001835 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001836 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001837 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001838 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001839 // Collections don't include the static data added by SubRoute because
1840 // it has a duplicate entry for members
1841 asyncResp->res.jsonValue["@odata.type"] =
1842 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001843 asyncResp->res.jsonValue["@odata.id"] =
1844 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001845 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001846 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001847 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1848 asyncResp->res.jsonValue["Description"] =
1849 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001850 asyncResp->res.jsonValue["@odata.id"] =
1851 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001852 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001853 logEntryArray = nlohmann::json::array();
1854
1855 // Go through the journal and use the timestamp to create a unique ID
1856 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001857 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001858 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1859 if (ret < 0)
1860 {
1861 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001862 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001863 return;
1864 }
1865 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1866 journalTmp, sd_journal_close);
1867 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001868 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001869 // Reset the unique ID on the first entry
1870 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001871 SD_JOURNAL_FOREACH(journal.get())
1872 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001873 entryCount++;
1874 // Handle paging using skip (number of entries to skip from the
1875 // start) and top (number of entries to display)
1876 if (entryCount <= skip || entryCount > skip + top)
1877 {
1878 continue;
1879 }
1880
Jason M. Bills16428a12018-11-02 12:42:29 -07001881 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001882 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001883 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001884 continue;
1885 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001886
Jason M. Billse85d6b12019-07-29 17:01:15 -07001887 if (firstEntry)
1888 {
1889 firstEntry = false;
1890 }
1891
Jason M. Billse1f26342018-07-18 12:12:00 -07001892 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001893 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001894 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1895 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001896 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001897 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001898 return;
1899 }
1900 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001901 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1902 if (skip + top < entryCount)
1903 {
1904 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001905 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001906 std::to_string(skip + top);
1907 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001908 }
1909};
1910
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001911class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001912{
1913 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001914 BMCJournalLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001915 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001916 std::string())
1917 {
1918 entityPrivileges = {
1919 {boost::beast::http::verb::get, {{"Login"}}},
1920 {boost::beast::http::verb::head, {{"Login"}}},
1921 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1922 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1923 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1924 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1925 }
1926
1927 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001928 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001929 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001930 {
1931 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1932 if (params.size() != 1)
1933 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001934 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001935 return;
1936 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001937 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001938 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001939 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001940 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001941 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001942 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001943 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001944 }
1945
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001946 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001947 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1948 if (ret < 0)
1949 {
1950 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001951 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001952 return;
1953 }
1954 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1955 journalTmp, sd_journal_close);
1956 journalTmp = nullptr;
1957 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001958 // tracking the unique ID
1959 std::string idStr;
1960 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001961 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301962 if (ret < 0)
1963 {
1964 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1965 << strerror(-ret);
1966 messages::internalError(asyncResp->res);
1967 return;
1968 }
Ed Tanous271584a2019-07-09 16:24:22 -07001969 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001970 {
1971 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001972 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1973 {
1974 messages::internalError(asyncResp->res);
1975 return;
1976 }
1977 if (firstEntry)
1978 {
1979 firstEntry = false;
1980 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001981 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001982 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001983 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001984 {
1985 messages::resourceMissingAtURI(asyncResp->res, entryID);
1986 return;
1987 }
1988
1989 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1990 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001991 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001992 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001993 return;
1994 }
1995 }
1996};
1997
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001998class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001999{
2000 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002001 BMCDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002002 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002003 {
2004 entityPrivileges = {
2005 {boost::beast::http::verb::get, {{"Login"}}},
2006 {boost::beast::http::verb::head, {{"Login"}}},
2007 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2008 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2009 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2010 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2011 }
2012
2013 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002014 void doGet(crow::Response& res, const crow::Request&,
2015 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002016 {
2017 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2018
2019 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002020 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002021 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002022 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002023 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2024 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2025 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002026 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002027 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002028 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2029 asyncResp->res.jsonValue["Actions"] = {
2030 {"#LogService.ClearLog",
2031 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2032 "Actions/LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002033 {"#LogService.CollectDiagnosticData",
2034 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2035 "Actions/LogService.CollectDiagnosticData"}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002036 }
2037};
2038
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002039class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002040{
2041 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002042 BMCDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002043 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002044 {
2045 entityPrivileges = {
2046 {boost::beast::http::verb::get, {{"Login"}}},
2047 {boost::beast::http::verb::head, {{"Login"}}},
2048 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2049 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2050 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2051 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2052 }
2053
2054 private:
2055 /**
2056 * Functions triggers appropriate requests on DBus
2057 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002058 void doGet(crow::Response& res, const crow::Request&,
2059 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002060 {
2061 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2062
2063 asyncResp->res.jsonValue["@odata.type"] =
2064 "#LogEntryCollection.LogEntryCollection";
2065 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002066 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2067 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002068 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002069 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002070
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002071 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002072 }
2073};
2074
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002075class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002076{
2077 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002078 BMCDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002079 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002080 std::string())
2081 {
2082 entityPrivileges = {
2083 {boost::beast::http::verb::get, {{"Login"}}},
2084 {boost::beast::http::verb::head, {{"Login"}}},
2085 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2086 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2087 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2088 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2089 }
2090
2091 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002092 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002093 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002094 {
2095 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2096 if (params.size() != 1)
2097 {
2098 messages::internalError(asyncResp->res);
2099 return;
2100 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002101 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002102 }
2103
Ed Tanouscb13a392020-07-25 19:02:03 +00002104 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002105 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002106 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002107 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002108 if (params.size() != 1)
2109 {
2110 messages::internalError(asyncResp->res);
2111 return;
2112 }
Stanley Chu98782562020-11-04 16:10:24 +08002113 deleteDumpEntry(asyncResp, params[0], "bmc");
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002114 }
2115};
raviteja-bc9bb6862020-02-03 11:53:32 -06002116
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002117class BMCDumpCreate : public Node
2118{
2119 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002120 BMCDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002121 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002122 "Actions/"
2123 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002124 {
2125 entityPrivileges = {
2126 {boost::beast::http::verb::get, {{"Login"}}},
2127 {boost::beast::http::verb::head, {{"Login"}}},
2128 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2129 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2130 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2131 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2132 }
2133
2134 private:
2135 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002136 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002137 {
2138 createDump(res, req, "BMC");
2139 }
2140};
2141
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002142class BMCDumpClear : public Node
2143{
2144 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002145 BMCDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002146 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2147 "Actions/"
2148 "LogService.ClearLog/")
2149 {
2150 entityPrivileges = {
2151 {boost::beast::http::verb::get, {{"Login"}}},
2152 {boost::beast::http::verb::head, {{"Login"}}},
2153 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2154 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2155 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2156 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2157 }
2158
2159 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002160 void doPost(crow::Response& res, const crow::Request&,
2161 const std::vector<std::string>&) override
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002162 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002163 clearDump(res, "BMC");
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002164 }
2165};
2166
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002167class SystemDumpService : public Node
2168{
2169 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002170 SystemDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002171 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2172 {
2173 entityPrivileges = {
2174 {boost::beast::http::verb::get, {{"Login"}}},
2175 {boost::beast::http::verb::head, {{"Login"}}},
2176 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2177 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2178 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2179 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2180 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002181
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002182 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002183 void doGet(crow::Response& res, const crow::Request&,
2184 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002185 {
2186 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002187
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002188 asyncResp->res.jsonValue["@odata.id"] =
2189 "/redfish/v1/Systems/system/LogServices/Dump";
2190 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002191 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002192 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2193 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2194 asyncResp->res.jsonValue["Id"] = "Dump";
2195 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2196 asyncResp->res.jsonValue["Entries"] = {
2197 {"@odata.id",
2198 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2199 asyncResp->res.jsonValue["Actions"] = {
2200 {"#LogService.ClearLog",
2201 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2202 "LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002203 {"#LogService.CollectDiagnosticData",
2204 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2205 "LogService.CollectDiagnosticData"}}}};
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002206 }
2207};
2208
2209class SystemDumpEntryCollection : public Node
2210{
2211 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002212 SystemDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002213 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2214 {
2215 entityPrivileges = {
2216 {boost::beast::http::verb::get, {{"Login"}}},
2217 {boost::beast::http::verb::head, {{"Login"}}},
2218 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2219 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2220 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2221 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2222 }
2223
2224 private:
2225 /**
2226 * Functions triggers appropriate requests on DBus
2227 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002228 void doGet(crow::Response& res, const crow::Request&,
2229 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002230 {
2231 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2232
2233 asyncResp->res.jsonValue["@odata.type"] =
2234 "#LogEntryCollection.LogEntryCollection";
2235 asyncResp->res.jsonValue["@odata.id"] =
2236 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2237 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2238 asyncResp->res.jsonValue["Description"] =
2239 "Collection of System Dump Entries";
2240
2241 getDumpEntryCollection(asyncResp, "System");
2242 }
2243};
2244
2245class SystemDumpEntry : public Node
2246{
2247 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002248 SystemDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002249 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2250 std::string())
2251 {
2252 entityPrivileges = {
2253 {boost::beast::http::verb::get, {{"Login"}}},
2254 {boost::beast::http::verb::head, {{"Login"}}},
2255 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2256 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2257 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2258 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2259 }
2260
2261 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002262 void doGet(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002263 const std::vector<std::string>& params) override
2264 {
2265 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2266 if (params.size() != 1)
2267 {
2268 messages::internalError(asyncResp->res);
2269 return;
2270 }
2271 getDumpEntryById(asyncResp, params[0], "System");
2272 }
2273
Ed Tanouscb13a392020-07-25 19:02:03 +00002274 void doDelete(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002275 const std::vector<std::string>& params) override
2276 {
2277 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2278 if (params.size() != 1)
2279 {
2280 messages::internalError(asyncResp->res);
2281 return;
2282 }
Stanley Chu98782562020-11-04 16:10:24 +08002283 deleteDumpEntry(asyncResp, params[0], "system");
raviteja-bc9bb6862020-02-03 11:53:32 -06002284 }
2285};
2286
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002287class SystemDumpCreate : public Node
2288{
2289 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002290 SystemDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002291 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002292 "Actions/"
2293 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002294 {
2295 entityPrivileges = {
2296 {boost::beast::http::verb::get, {{"Login"}}},
2297 {boost::beast::http::verb::head, {{"Login"}}},
2298 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2299 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2300 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2301 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2302 }
2303
2304 private:
2305 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002306 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002307 {
2308 createDump(res, req, "System");
2309 }
2310};
2311
raviteja-b013487e2020-03-03 03:20:48 -06002312class SystemDumpClear : public Node
2313{
2314 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002315 SystemDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002316 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002317 "Actions/"
2318 "LogService.ClearLog/")
2319 {
2320 entityPrivileges = {
2321 {boost::beast::http::verb::get, {{"Login"}}},
2322 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002323 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2324 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2325 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002326 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2327 }
2328
2329 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002330 void doPost(crow::Response& res, const crow::Request&,
2331 const std::vector<std::string>&) override
raviteja-b013487e2020-03-03 03:20:48 -06002332 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002333 clearDump(res, "System");
raviteja-b013487e2020-03-03 03:20:48 -06002334 }
2335};
2336
Jason M. Bills424c4172019-03-21 13:50:33 -07002337class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002338{
2339 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002340 CrashdumpService(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002341 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002342 {
AppaRao Puli39460282020-04-07 17:03:04 +05302343 // Note: Deviated from redfish privilege registry for GET & HEAD
2344 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002345 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302346 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2347 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002348 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2349 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2350 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2351 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002352 }
2353
2354 private:
2355 /**
2356 * Functions triggers appropriate requests on DBus
2357 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002358 void doGet(crow::Response& res, const crow::Request&,
2359 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002360 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002361 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002362 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002363 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002364 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002365 asyncResp->res.jsonValue["@odata.type"] =
2366 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002367 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2368 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2369 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002370 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2371 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002372 asyncResp->res.jsonValue["Entries"] = {
2373 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002374 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002375 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002376 {"#LogService.ClearLog",
2377 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2378 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002379 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002380 {{"#Crashdump.OnDemand",
2381 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002382 "Actions/Oem/Crashdump.OnDemand"}}},
2383 {"#Crashdump.Telemetry",
2384 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2385 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002386
2387#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002388 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002389 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002390 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2391 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002392#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002393 }
2394};
2395
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002396class CrashdumpClear : public Node
2397{
2398 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002399 CrashdumpClear(App& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002400 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2401 "LogService.ClearLog/")
2402 {
AppaRao Puli39460282020-04-07 17:03:04 +05302403 // Note: Deviated from redfish privilege registry for GET & HEAD
2404 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002405 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302406 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2407 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002408 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2409 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2410 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2411 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2412 }
2413
2414 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002415 void doPost(crow::Response& res, const crow::Request&,
2416 const std::vector<std::string>&) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002417 {
2418 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2419
2420 crow::connections::systemBus->async_method_call(
2421 [asyncResp](const boost::system::error_code ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002422 const std::string&) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002423 if (ec)
2424 {
2425 messages::internalError(asyncResp->res);
2426 return;
2427 }
2428 messages::success(asyncResp->res);
2429 },
2430 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2431 }
2432};
2433
Ed Tanousb5a76932020-09-29 16:16:58 -07002434static void logCrashdumpEntry(const std::shared_ptr<AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002435 const std::string& logID,
2436 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002437{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002438 auto getStoredLogCallback =
2439 [asyncResp, logID, &logEntryJson](
2440 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002441 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002442 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002443 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002444 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2445 if (ec.value() ==
2446 boost::system::linux_error::bad_request_descriptor)
2447 {
2448 messages::resourceNotFound(asyncResp->res, "LogEntry",
2449 logID);
2450 }
2451 else
2452 {
2453 messages::internalError(asyncResp->res);
2454 }
2455 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002456 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002457
Johnathan Mantey043a0532020-03-10 17:15:28 -07002458 std::string timestamp{};
2459 std::string filename{};
2460 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002461 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002462
2463 if (filename.empty() || timestamp.empty())
2464 {
2465 messages::resourceMissingAtURI(asyncResp->res, logID);
2466 return;
2467 }
2468
2469 std::string crashdumpURI =
2470 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2471 logID + "/" + filename;
2472 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2473 {"@odata.id", "/redfish/v1/Systems/system/"
2474 "LogServices/Crashdump/Entries/" +
2475 logID},
2476 {"Name", "CPU Crashdump"},
2477 {"Id", logID},
2478 {"EntryType", "Oem"},
2479 {"OemRecordFormat", "Crashdump URI"},
2480 {"Message", std::move(crashdumpURI)},
2481 {"Created", std::move(timestamp)}};
2482 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002483 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002484 std::move(getStoredLogCallback), crashdumpObject,
2485 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002486 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002487}
2488
Jason M. Bills424c4172019-03-21 13:50:33 -07002489class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002490{
2491 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002492 CrashdumpEntryCollection(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002493 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002494 {
AppaRao Puli39460282020-04-07 17:03:04 +05302495 // Note: Deviated from redfish privilege registry for GET & HEAD
2496 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002497 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302498 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2499 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002500 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2501 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2502 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2503 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002504 }
2505
2506 private:
2507 /**
2508 * Functions triggers appropriate requests on DBus
2509 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002510 void doGet(crow::Response& res, const crow::Request&,
2511 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002512 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002513 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002514 // Collections don't include the static data added by SubRoute because
2515 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002516 auto getLogEntriesCallback = [asyncResp](
2517 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002518 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002519 if (ec)
2520 {
2521 if (ec.value() !=
2522 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002523 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002524 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2525 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002526 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002527 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002528 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002529 }
2530 asyncResp->res.jsonValue["@odata.type"] =
2531 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002532 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002533 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002534 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002535 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002536 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002537 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002538 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002539 std::vector<std::string> logIDs;
2540 // Get the list of log entries and build up an empty array big
2541 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002542 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002543 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002544 // Get the log ID
Ed Tanousf23b7292020-10-15 09:41:17 -07002545 std::size_t lastPos = objpath.rfind('/');
Jason M. Billse855dd22019-10-08 11:37:48 -07002546 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002547 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002548 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002549 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002550 logIDs.emplace_back(objpath.substr(lastPos + 1));
2551
2552 // Add a space for the log entry to the array
2553 logEntryArray.push_back({});
2554 }
2555 // Now go through and set up async calls to fill in the entries
2556 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002557 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002558 {
2559 // Add the log entry to the array
2560 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002561 }
2562 asyncResp->res.jsonValue["Members@odata.count"] =
2563 logEntryArray.size();
2564 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002565 crow::connections::systemBus->async_method_call(
2566 std::move(getLogEntriesCallback),
2567 "xyz.openbmc_project.ObjectMapper",
2568 "/xyz/openbmc_project/object_mapper",
2569 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002570 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002571 }
2572};
2573
Jason M. Bills424c4172019-03-21 13:50:33 -07002574class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002575{
2576 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002577 CrashdumpEntry(App& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002578 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002579 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002580 std::string())
2581 {
AppaRao Puli39460282020-04-07 17:03:04 +05302582 // Note: Deviated from redfish privilege registry for GET & HEAD
2583 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002584 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302585 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2586 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002587 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2588 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2589 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2590 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002591 }
2592
2593 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002594 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002595 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002596 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002597 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002598 if (params.size() != 1)
2599 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002600 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002601 return;
2602 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002603 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002604 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2605 }
2606};
2607
2608class CrashdumpFile : public Node
2609{
2610 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002611 CrashdumpFile(App& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002612 Node(app,
2613 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2614 "<str>/",
2615 std::string(), std::string())
2616 {
AppaRao Puli39460282020-04-07 17:03:04 +05302617 // Note: Deviated from redfish privilege registry for GET & HEAD
2618 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002619 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302620 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2621 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002622 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2623 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2624 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2625 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2626 }
2627
2628 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002629 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002630 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002631 {
2632 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2633 if (params.size() != 2)
2634 {
2635 messages::internalError(asyncResp->res);
2636 return;
2637 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002638 const std::string& logID = params[0];
2639 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002640
Johnathan Mantey043a0532020-03-10 17:15:28 -07002641 auto getStoredLogCallback =
2642 [asyncResp, logID, fileName](
2643 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002644 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002645 if (ec)
2646 {
2647 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2648 << ec.message();
2649 messages::internalError(asyncResp->res);
2650 return;
2651 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002652
Johnathan Mantey043a0532020-03-10 17:15:28 -07002653 std::string dbusFilename{};
2654 std::string dbusTimestamp{};
2655 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002656
Ed Tanous2c70f802020-09-28 14:29:23 -07002657 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002658 dbusFilepath);
2659
2660 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2661 dbusFilepath.empty())
2662 {
2663 messages::resourceMissingAtURI(asyncResp->res, fileName);
2664 return;
2665 }
2666
2667 // Verify the file name parameter is correct
2668 if (fileName != dbusFilename)
2669 {
2670 messages::resourceMissingAtURI(asyncResp->res, fileName);
2671 return;
2672 }
2673
2674 if (!std::filesystem::exists(dbusFilepath))
2675 {
2676 messages::resourceMissingAtURI(asyncResp->res, fileName);
2677 return;
2678 }
2679 std::ifstream ifs(dbusFilepath, std::ios::in |
2680 std::ios::binary |
2681 std::ios::ate);
2682 std::ifstream::pos_type fileSize = ifs.tellg();
2683 if (fileSize < 0)
2684 {
2685 messages::generalError(asyncResp->res);
2686 return;
2687 }
2688 ifs.seekg(0, std::ios::beg);
2689
2690 auto crashData = std::make_unique<char[]>(
2691 static_cast<unsigned int>(fileSize));
2692
2693 ifs.read(crashData.get(), static_cast<int>(fileSize));
2694
2695 // The cast to std::string is intentional in order to use the
2696 // assign() that applies move mechanics
2697 asyncResp->res.body().assign(
2698 static_cast<std::string>(crashData.get()));
2699
2700 // Configure this to be a file download when accessed from
2701 // a browser
2702 asyncResp->res.addHeader("Content-Disposition", "attachment");
2703 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002704 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002705 std::move(getStoredLogCallback), crashdumpObject,
2706 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002707 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002708 }
2709};
2710
Jason M. Bills424c4172019-03-21 13:50:33 -07002711class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002712{
2713 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002714 OnDemandCrashdump(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002715 Node(app,
2716 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2717 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002718 {
AppaRao Puli39460282020-04-07 17:03:04 +05302719 // Note: Deviated from redfish privilege registry for GET & HEAD
2720 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002721 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302722 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2723 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2724 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2725 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2726 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2727 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002728 }
2729
2730 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002731 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002732 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002733 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002734 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002735
James Feistfe306722020-03-12 16:32:08 -07002736 auto generateonDemandLogCallback = [asyncResp,
2737 req](const boost::system::error_code
2738 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002739 const std::string&) {
James Feist46229572020-02-19 15:11:58 -08002740 if (ec)
2741 {
2742 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002743 {
James Feist46229572020-02-19 15:11:58 -08002744 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002745 }
James Feist46229572020-02-19 15:11:58 -08002746 else if (ec.value() ==
2747 boost::system::errc::device_or_resource_busy)
2748 {
2749 messages::serviceTemporarilyUnavailable(asyncResp->res,
2750 "60");
2751 }
2752 else
2753 {
2754 messages::internalError(asyncResp->res);
2755 }
2756 return;
2757 }
2758 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002759 [](boost::system::error_code err, sdbusplus::message::message&,
2760 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002761 if (!err)
2762 {
James Feiste5d50062020-05-11 17:29:00 -07002763 taskData->messages.emplace_back(
2764 messages::taskCompletedOK(
2765 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002766 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002767 }
James Feist32898ce2020-03-10 16:16:52 -07002768 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002769 },
James Feist46229572020-02-19 15:11:58 -08002770 "type='signal',interface='org.freedesktop.DBus.Properties',"
2771 "member='PropertiesChanged',arg0namespace='com.intel."
2772 "crashdump'");
2773 task->startTimer(std::chrono::minutes(5));
2774 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002775 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002776 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002777 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002778 std::move(generateonDemandLogCallback), crashdumpObject,
2779 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002780 }
2781};
2782
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002783class TelemetryCrashdump : public Node
2784{
2785 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002786 TelemetryCrashdump(App& app) :
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002787 Node(app,
2788 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2789 "Crashdump.Telemetry/")
2790 {
2791 // Note: Deviated from redfish privilege registry for GET & HEAD
2792 // method for security reasons.
2793 entityPrivileges = {
2794 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2795 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2796 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2797 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2798 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2799 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2800 }
2801
2802 private:
2803 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002804 const std::vector<std::string>&) override
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002805 {
2806 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2807
2808 auto generateTelemetryLogCallback = [asyncResp, req](
2809 const boost::system::error_code
2810 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002811 const std::string&) {
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002812 if (ec)
2813 {
2814 if (ec.value() == boost::system::errc::operation_not_supported)
2815 {
2816 messages::resourceInStandby(asyncResp->res);
2817 }
2818 else if (ec.value() ==
2819 boost::system::errc::device_or_resource_busy)
2820 {
2821 messages::serviceTemporarilyUnavailable(asyncResp->res,
2822 "60");
2823 }
2824 else
2825 {
2826 messages::internalError(asyncResp->res);
2827 }
2828 return;
2829 }
2830 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2831 [](boost::system::error_code err, sdbusplus::message::message&,
2832 const std::shared_ptr<task::TaskData>& taskData) {
2833 if (!err)
2834 {
2835 taskData->messages.emplace_back(
2836 messages::taskCompletedOK(
2837 std::to_string(taskData->index)));
2838 taskData->state = "Completed";
2839 }
2840 return task::completed;
2841 },
2842 "type='signal',interface='org.freedesktop.DBus.Properties',"
2843 "member='PropertiesChanged',arg0namespace='com.intel."
2844 "crashdump'");
2845 task->startTimer(std::chrono::minutes(5));
2846 task->populateResp(asyncResp->res);
2847 task->payload.emplace(req);
2848 };
2849 crow::connections::systemBus->async_method_call(
2850 std::move(generateTelemetryLogCallback), crashdumpObject,
2851 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2852 }
2853};
2854
Jason M. Billse1f26342018-07-18 12:12:00 -07002855class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002856{
2857 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002858 SendRawPECI(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002859 Node(app,
2860 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2861 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002862 {
AppaRao Puli39460282020-04-07 17:03:04 +05302863 // Note: Deviated from redfish privilege registry for GET & HEAD
2864 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002865 entityPrivileges = {
2866 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2867 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2868 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2869 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2870 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2871 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2872 }
2873
2874 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002875 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002876 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002877 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002878 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002879 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002880
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002881 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002882 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002883 return;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002884 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002885 uint32_t idx = 0;
2886 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002887 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002888 if (cmd.size() < 3)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002889 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002890 std::string s("[");
2891 for (auto const& val : cmd)
2892 {
2893 if (val != *cmd.begin())
2894 {
2895 s += ",";
2896 }
2897 s += std::to_string(val);
2898 }
2899 s += "]";
2900 messages::actionParameterValueFormatError(
2901 res, s, "PECICommands[" + std::to_string(idx) + "]",
2902 "SendRawPeci");
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002903 return;
2904 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002905 idx++;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002906 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002907 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002908 auto sendRawPECICallback =
2909 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002910 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002911 if (ec)
2912 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002913 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002914 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002915 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002916 return;
2917 }
2918 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2919 {"PECIResponse", resp}};
2920 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002921 // Call the SendRawPECI command with the provided data
2922 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002923 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002924 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002925 }
2926};
2927
Andrew Geisslercb92c032018-08-17 07:56:14 -07002928/**
2929 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2930 */
2931class DBusLogServiceActionsClear : public Node
2932{
2933 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002934 DBusLogServiceActionsClear(App& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002935 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002936 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002937 {
2938 entityPrivileges = {
2939 {boost::beast::http::verb::get, {{"Login"}}},
2940 {boost::beast::http::verb::head, {{"Login"}}},
2941 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2942 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2943 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2944 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2945 }
2946
2947 private:
2948 /**
2949 * Function handles POST method request.
2950 * The Clear Log actions does not require any parameter.The action deletes
2951 * all entries found in the Entries collection for this Log Service.
2952 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002953 void doPost(crow::Response& res, const crow::Request&,
2954 const std::vector<std::string>&) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002955 {
2956 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2957
2958 auto asyncResp = std::make_shared<AsyncResp>(res);
2959 // Process response from Logging service.
Ed Tanous2c70f802020-09-28 14:29:23 -07002960 auto respHandler = [asyncResp](const boost::system::error_code ec) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07002961 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2962 if (ec)
2963 {
2964 // TODO Handle for specific error code
2965 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2966 asyncResp->res.result(
2967 boost::beast::http::status::internal_server_error);
2968 return;
2969 }
2970
2971 asyncResp->res.result(boost::beast::http::status::no_content);
2972 };
2973
2974 // Make call to Logging service to request Clear Log
2975 crow::connections::systemBus->async_method_call(
Ed Tanous2c70f802020-09-28 14:29:23 -07002976 respHandler, "xyz.openbmc_project.Logging",
Andrew Geisslercb92c032018-08-17 07:56:14 -07002977 "/xyz/openbmc_project/logging",
2978 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2979 }
2980};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002981
2982/****************************************************
2983 * Redfish PostCode interfaces
2984 * using DBUS interface: getPostCodesTS
2985 ******************************************************/
2986class PostCodesLogService : public Node
2987{
2988 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002989 PostCodesLogService(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002990 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2991 {
2992 entityPrivileges = {
2993 {boost::beast::http::verb::get, {{"Login"}}},
2994 {boost::beast::http::verb::head, {{"Login"}}},
2995 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2996 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2997 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2998 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2999 }
3000
3001 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003002 void doGet(crow::Response& res, const crow::Request&,
3003 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003004 {
3005 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3006
3007 asyncResp->res.jsonValue = {
3008 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
3009 {"@odata.type", "#LogService.v1_1_0.LogService"},
ZhikuiRena3316fc2020-01-29 14:58:08 -08003010 {"Name", "POST Code Log Service"},
3011 {"Description", "POST Code Log Service"},
3012 {"Id", "BIOS POST Code Log"},
3013 {"OverWritePolicy", "WrapsWhenFull"},
3014 {"Entries",
3015 {{"@odata.id",
3016 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3017 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3018 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3019 "Actions/LogService.ClearLog"}};
3020 }
3021};
3022
3023class PostCodesClear : public Node
3024{
3025 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003026 PostCodesClear(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003027 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3028 "LogService.ClearLog/")
3029 {
3030 entityPrivileges = {
3031 {boost::beast::http::verb::get, {{"Login"}}},
3032 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303033 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3034 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3035 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3036 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003037 }
3038
3039 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003040 void doPost(crow::Response& res, const crow::Request&,
3041 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003042 {
3043 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3044
3045 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3046 // Make call to post-code service to request clear all
3047 crow::connections::systemBus->async_method_call(
3048 [asyncResp](const boost::system::error_code ec) {
3049 if (ec)
3050 {
3051 // TODO Handle for specific error code
3052 BMCWEB_LOG_ERROR
3053 << "doClearPostCodes resp_handler got error " << ec;
3054 asyncResp->res.result(
3055 boost::beast::http::status::internal_server_error);
3056 messages::internalError(asyncResp->res);
3057 return;
3058 }
3059 },
3060 "xyz.openbmc_project.State.Boot.PostCode",
3061 "/xyz/openbmc_project/State/Boot/PostCode",
3062 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3063 }
3064};
3065
3066static void fillPostCodeEntry(
Ed Tanousb5a76932020-09-29 16:16:58 -07003067 const std::shared_ptr<AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003068 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003069 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3070 const uint64_t skip = 0, const uint64_t top = 0)
3071{
3072 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003073 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003074 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003075
3076 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003077 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003078
3079 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003080 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003081 {
3082 currentCodeIndex++;
3083 std::string postcodeEntryID =
3084 "B" + std::to_string(bootIndex) + "-" +
3085 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3086
3087 uint64_t usecSinceEpoch = code.first;
3088 uint64_t usTimeOffset = 0;
3089
3090 if (1 == currentCodeIndex)
3091 { // already incremented
3092 firstCodeTimeUs = code.first;
3093 }
3094 else
3095 {
3096 usTimeOffset = code.first - firstCodeTimeUs;
3097 }
3098
3099 // skip if no specific codeIndex is specified and currentCodeIndex does
3100 // not fall between top and skip
3101 if ((codeIndex == 0) &&
3102 (currentCodeIndex <= skip || currentCodeIndex > top))
3103 {
3104 continue;
3105 }
3106
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003107 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003108 // currentIndex
3109 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3110 {
3111 // This is done for simplicity. 1st entry is needed to calculate
3112 // time offset. To improve efficiency, one can get to the entry
3113 // directly (possibly with flatmap's nth method)
3114 continue;
3115 }
3116
3117 // currentCodeIndex is within top and skip or equal to specified code
3118 // index
3119
3120 // Get the Created time from the timestamp
3121 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003122 entryTimeStr = crow::utility::getDateTime(
3123 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003124
3125 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3126 std::ostringstream hexCode;
3127 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3128 << code.second;
3129 std::ostringstream timeOffsetStr;
3130 // Set Fixed -Point Notation
3131 timeOffsetStr << std::fixed;
3132 // Set precision to 4 digits
3133 timeOffsetStr << std::setprecision(4);
3134 // Add double to stream
3135 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3136 std::vector<std::string> messageArgs = {
3137 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3138
3139 // Get MessageArgs template from message registry
3140 std::string msg;
3141 if (message != nullptr)
3142 {
3143 msg = message->message;
3144
3145 // fill in this post code value
3146 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003147 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003148 {
3149 std::string argStr = "%" + std::to_string(++i);
3150 size_t argPos = msg.find(argStr);
3151 if (argPos != std::string::npos)
3152 {
3153 msg.replace(argPos, argStr.length(), messageArg);
3154 }
3155 }
3156 }
3157
Tim Leed4342a92020-04-27 11:47:58 +08003158 // Get Severity template from message registry
3159 std::string severity;
3160 if (message != nullptr)
3161 {
3162 severity = message->severity;
3163 }
3164
ZhikuiRena3316fc2020-01-29 14:58:08 -08003165 // add to AsyncResp
3166 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003167 nlohmann::json& bmcLogEntry = logEntryArray.back();
Gunnar Mills743e9a12020-10-26 12:44:53 -05003168 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3169 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3170 "PostCodes/Entries/" +
3171 postcodeEntryID},
3172 {"Name", "POST Code Log Entry"},
3173 {"Id", postcodeEntryID},
3174 {"Message", std::move(msg)},
3175 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3176 {"MessageArgs", std::move(messageArgs)},
3177 {"EntryType", "Event"},
3178 {"Severity", std::move(severity)},
3179 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003180 }
3181}
3182
Ed Tanousb5a76932020-09-29 16:16:58 -07003183static void getPostCodeForEntry(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003184 const uint16_t bootIndex,
3185 const uint64_t codeIndex)
3186{
3187 crow::connections::systemBus->async_method_call(
3188 [aResp, bootIndex, codeIndex](
3189 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003190 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003191 if (ec)
3192 {
3193 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3194 messages::internalError(aResp->res);
3195 return;
3196 }
3197
3198 // skip the empty postcode boots
3199 if (postcode.empty())
3200 {
3201 return;
3202 }
3203
3204 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3205
3206 aResp->res.jsonValue["Members@odata.count"] =
3207 aResp->res.jsonValue["Members"].size();
3208 },
3209 "xyz.openbmc_project.State.Boot.PostCode",
3210 "/xyz/openbmc_project/State/Boot/PostCode",
3211 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3212 bootIndex);
3213}
3214
Ed Tanousb5a76932020-09-29 16:16:58 -07003215static void getPostCodeForBoot(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003216 const uint16_t bootIndex,
3217 const uint16_t bootCount,
3218 const uint64_t entryCount, const uint64_t skip,
3219 const uint64_t top)
3220{
3221 crow::connections::systemBus->async_method_call(
3222 [aResp, bootIndex, bootCount, entryCount, skip,
3223 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003224 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003225 if (ec)
3226 {
3227 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3228 messages::internalError(aResp->res);
3229 return;
3230 }
3231
3232 uint64_t endCount = entryCount;
3233 if (!postcode.empty())
3234 {
3235 endCount = entryCount + postcode.size();
3236
3237 if ((skip < endCount) && ((top + skip) > entryCount))
3238 {
3239 uint64_t thisBootSkip =
3240 std::max(skip, entryCount) - entryCount;
3241 uint64_t thisBootTop =
3242 std::min(top + skip, endCount) - entryCount;
3243
3244 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3245 thisBootSkip, thisBootTop);
3246 }
3247 aResp->res.jsonValue["Members@odata.count"] = endCount;
3248 }
3249
3250 // continue to previous bootIndex
3251 if (bootIndex < bootCount)
3252 {
3253 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3254 bootCount, endCount, skip, top);
3255 }
3256 else
3257 {
3258 aResp->res.jsonValue["Members@odata.nextLink"] =
3259 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3260 "Entries?$skip=" +
3261 std::to_string(skip + top);
3262 }
3263 },
3264 "xyz.openbmc_project.State.Boot.PostCode",
3265 "/xyz/openbmc_project/State/Boot/PostCode",
3266 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3267 bootIndex);
3268}
3269
Ed Tanousb5a76932020-09-29 16:16:58 -07003270static void getCurrentBootNumber(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003271 const uint64_t skip, const uint64_t top)
3272{
3273 uint64_t entryCount = 0;
3274 crow::connections::systemBus->async_method_call(
3275 [aResp, entryCount, skip,
3276 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003277 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003278 if (ec)
3279 {
3280 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3281 messages::internalError(aResp->res);
3282 return;
3283 }
3284 auto pVal = std::get_if<uint16_t>(&bootCount);
3285 if (pVal)
3286 {
3287 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3288 }
3289 else
3290 {
3291 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3292 }
3293 },
3294 "xyz.openbmc_project.State.Boot.PostCode",
3295 "/xyz/openbmc_project/State/Boot/PostCode",
3296 "org.freedesktop.DBus.Properties", "Get",
3297 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3298}
3299
3300class PostCodesEntryCollection : public Node
3301{
3302 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003303 PostCodesEntryCollection(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003304 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3305 {
3306 entityPrivileges = {
3307 {boost::beast::http::verb::get, {{"Login"}}},
3308 {boost::beast::http::verb::head, {{"Login"}}},
3309 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3310 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3311 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3312 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3313 }
3314
3315 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003316 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00003317 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003318 {
3319 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3320
3321 asyncResp->res.jsonValue["@odata.type"] =
3322 "#LogEntryCollection.LogEntryCollection";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003323 asyncResp->res.jsonValue["@odata.id"] =
3324 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3325 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3326 asyncResp->res.jsonValue["Description"] =
3327 "Collection of POST Code Log Entries";
3328 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3329 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3330
3331 uint64_t skip = 0;
3332 uint64_t top = maxEntriesPerPage; // Show max entries by default
3333 if (!getSkipParam(asyncResp->res, req, skip))
3334 {
3335 return;
3336 }
3337 if (!getTopParam(asyncResp->res, req, top))
3338 {
3339 return;
3340 }
3341 getCurrentBootNumber(asyncResp, skip, top);
3342 }
3343};
3344
3345class PostCodesEntry : public Node
3346{
3347 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003348 PostCodesEntry(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003349 Node(app,
3350 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3351 std::string())
3352 {
3353 entityPrivileges = {
3354 {boost::beast::http::verb::get, {{"Login"}}},
3355 {boost::beast::http::verb::head, {{"Login"}}},
3356 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3357 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3358 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3359 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3360 }
3361
3362 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003363 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003364 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003365 {
3366 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3367 if (params.size() != 1)
3368 {
3369 messages::internalError(asyncResp->res);
3370 return;
3371 }
3372
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003373 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003374
3375 size_t bootPos = targetID.find('B');
3376 if (bootPos == std::string::npos)
3377 {
3378 // Requested ID was not found
3379 messages::resourceMissingAtURI(asyncResp->res, targetID);
3380 return;
3381 }
3382 std::string_view bootIndexStr(targetID);
3383 bootIndexStr.remove_prefix(bootPos + 1);
3384 uint16_t bootIndex = 0;
3385 uint64_t codeIndex = 0;
3386 size_t dashPos = bootIndexStr.find('-');
3387
3388 if (dashPos == std::string::npos)
3389 {
3390 return;
3391 }
3392 std::string_view codeIndexStr(bootIndexStr);
3393 bootIndexStr.remove_suffix(dashPos);
3394 codeIndexStr.remove_prefix(dashPos + 1);
3395
3396 bootIndex = static_cast<uint16_t>(
Ed Tanous23a21a12020-07-25 04:45:05 +00003397 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3398 codeIndex = strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003399 if (bootIndex == 0 || codeIndex == 0)
3400 {
3401 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3402 << params[0];
3403 }
3404
3405 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003406 asyncResp->res.jsonValue["@odata.id"] =
3407 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3408 "Entries";
3409 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3410 asyncResp->res.jsonValue["Description"] =
3411 "Collection of POST Code Log Entries";
3412 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3413 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3414
3415 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3416 }
3417};
3418
Ed Tanous1da66f72018-07-27 16:13:37 -07003419} // namespace redfish