blob: 1198455650f9f9be73687f2e3a11286f30e4b3e8 [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();
430
431 for (auto& object : resp)
432 {
433 bool foundDumpEntry = false;
434 for (auto& interfaceMap : object.second)
435 {
436 if (interfaceMap.first ==
437 ("xyz.openbmc_project.Dump.Entry." + dumpType))
438 {
439 foundDumpEntry = true;
440 break;
441 }
442 }
443
444 if (foundDumpEntry == false)
445 {
446 continue;
447 }
448 std::time_t timestamp;
449 uint64_t size = 0;
450 entriesArray.push_back({});
451 nlohmann::json& thisEntry = entriesArray.back();
452 const std::string& path =
453 static_cast<const std::string&>(object.first);
454 std::size_t lastPos = path.rfind("/");
455 if (lastPos == std::string::npos)
456 {
457 continue;
458 }
459 std::string entryID = path.substr(lastPos + 1);
460
461 for (auto& interfaceMap : object.second)
462 {
463 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
464 {
465
466 for (auto& propertyMap : interfaceMap.second)
467 {
468 if (propertyMap.first == "Size")
469 {
470 auto sizePtr =
471 std::get_if<uint64_t>(&propertyMap.second);
472 if (sizePtr == nullptr)
473 {
474 messages::internalError(asyncResp->res);
475 break;
476 }
477 size = *sizePtr;
478 break;
479 }
480 }
481 }
482 else if (interfaceMap.first ==
483 "xyz.openbmc_project.Time.EpochTime")
484 {
485
486 for (auto& propertyMap : interfaceMap.second)
487 {
488 if (propertyMap.first == "Elapsed")
489 {
490 const uint64_t* usecsTimeStamp =
491 std::get_if<uint64_t>(&propertyMap.second);
492 if (usecsTimeStamp == nullptr)
493 {
494 messages::internalError(asyncResp->res);
495 break;
496 }
497 timestamp =
498 static_cast<std::time_t>(*usecsTimeStamp);
499 break;
500 }
501 }
502 }
503 }
504
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500505 thisEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500506 thisEntry["@odata.id"] = dumpPath + entryID;
507 thisEntry["Id"] = entryID;
508 thisEntry["EntryType"] = "Event";
509 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
510 thisEntry["Name"] = dumpType + " Dump Entry";
511
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500512 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500513
514 if (dumpType == "BMC")
515 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500516 thisEntry["DiagnosticDataType"] = "Manager";
517 thisEntry["AdditionalDataURI"] =
518 "/redfish/v1/Managers/bmc/LogServices/Dump/"
519 "attachment/" +
520 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500521 }
522 else if (dumpType == "System")
523 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500524 thisEntry["DiagnosticDataType"] = "OEM";
525 thisEntry["OEMDiagnosticDataType"] = "System";
526 thisEntry["AdditionalDataURI"] =
527 "/redfish/v1/Systems/system/LogServices/Dump/"
528 "attachment/" +
529 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500530 }
531 }
532 asyncResp->res.jsonValue["Members@odata.count"] =
533 entriesArray.size();
534 },
535 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
536 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
537}
538
539inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
540 const std::string& entryID,
541 const std::string& dumpType)
542{
543 std::string dumpPath;
544 if (dumpType == "BMC")
545 {
546 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
547 }
548 else if (dumpType == "System")
549 {
550 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
551 }
552 else
553 {
554 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
555 messages::internalError(asyncResp->res);
556 return;
557 }
558
559 crow::connections::systemBus->async_method_call(
560 [asyncResp, entryID, dumpPath, dumpType](
561 const boost::system::error_code ec, GetManagedObjectsType& resp) {
562 if (ec)
563 {
564 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
565 messages::internalError(asyncResp->res);
566 return;
567 }
568
569 for (auto& objectPath : resp)
570 {
571 if (objectPath.first.str.find(
572 "/xyz/openbmc_project/dump/entry/" + entryID) ==
573 std::string::npos)
574 {
575 continue;
576 }
577
578 bool foundDumpEntry = false;
579 for (auto& interfaceMap : objectPath.second)
580 {
581 if (interfaceMap.first ==
582 ("xyz.openbmc_project.Dump.Entry." + dumpType))
583 {
584 foundDumpEntry = true;
585 break;
586 }
587 }
588 if (foundDumpEntry == false)
589 {
590 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
591 messages::internalError(asyncResp->res);
592 return;
593 }
594
595 std::time_t timestamp;
596 uint64_t size = 0;
597
598 for (auto& interfaceMap : objectPath.second)
599 {
600 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
601 {
602 for (auto& propertyMap : interfaceMap.second)
603 {
604 if (propertyMap.first == "Size")
605 {
606 auto sizePtr =
607 std::get_if<uint64_t>(&propertyMap.second);
608 if (sizePtr == nullptr)
609 {
610 messages::internalError(asyncResp->res);
611 break;
612 }
613 size = *sizePtr;
614 break;
615 }
616 }
617 }
618 else if (interfaceMap.first ==
619 "xyz.openbmc_project.Time.EpochTime")
620 {
621 for (auto& propertyMap : interfaceMap.second)
622 {
623 if (propertyMap.first == "Elapsed")
624 {
625 const uint64_t* usecsTimeStamp =
626 std::get_if<uint64_t>(&propertyMap.second);
627 if (usecsTimeStamp == nullptr)
628 {
629 messages::internalError(asyncResp->res);
630 break;
631 }
632 timestamp =
633 static_cast<std::time_t>(*usecsTimeStamp);
634 break;
635 }
636 }
637 }
638 }
639
640 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500641 "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500642 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
643 asyncResp->res.jsonValue["Id"] = entryID;
644 asyncResp->res.jsonValue["EntryType"] = "Event";
645 asyncResp->res.jsonValue["Created"] =
646 crow::utility::getDateTime(timestamp);
647 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
648
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500649 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500650
651 if (dumpType == "BMC")
652 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500653 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
654 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 "/redfish/v1/Managers/bmc/LogServices/Dump/"
656 "attachment/" +
657 entryID;
658 }
659 else if (dumpType == "System")
660 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500661 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
662 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500664 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500665 "/redfish/v1/Systems/system/LogServices/Dump/"
666 "attachment/" +
667 entryID;
668 }
669 }
670 },
671 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
672 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
673}
674
675inline void deleteDumpEntry(crow::Response& res, const std::string& entryID)
676{
677 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
678
679 auto respHandler = [asyncResp](const boost::system::error_code ec) {
680 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
681 if (ec)
682 {
683 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
684 << ec;
685 messages::internalError(asyncResp->res);
686 return;
687 }
688 };
689 crow::connections::systemBus->async_method_call(
690 respHandler, "xyz.openbmc_project.Dump.Manager",
691 "/xyz/openbmc_project/dump/entry/" + entryID,
692 "xyz.openbmc_project.Object.Delete", "Delete");
693}
694
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500695inline void createDumpTaskCallback(const crow::Request& req,
696 std::shared_ptr<AsyncResp> asyncResp,
697 const uint32_t& dumpId,
698 const std::string& dumpPath,
699 const std::string& dumpType)
700{
701 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500702 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500703 boost::system::error_code err, sdbusplus::message::message& m,
704 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000705 if (err)
706 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500707 BMCWEB_LOG_ERROR << "Error in creating a dump";
708 taskData->state = "Cancelled";
709 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000710 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500711 std::vector<std::pair<
712 std::string,
713 std::vector<std::pair<std::string, std::variant<std::string>>>>>
714 interfacesList;
715
716 sdbusplus::message::object_path objPath;
717
718 m.read(objPath, interfacesList);
719
720 for (auto& interface : interfacesList)
721 {
722 if (interface.first ==
723 ("xyz.openbmc_project.Dump.Entry." + dumpType))
724 {
725 nlohmann::json retMessage = messages::success();
726 taskData->messages.emplace_back(retMessage);
727
728 std::string headerLoc =
729 "Location: " + dumpPath + std::to_string(dumpId);
730 taskData->payload->httpHeaders.emplace_back(
731 std::move(headerLoc));
732
733 taskData->state = "Completed";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500734 break;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500735 }
736 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500737 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500738 },
739 "type='signal',interface='org.freedesktop.DBus."
740 "ObjectManager',"
741 "member='InterfacesAdded', "
742 "path='/xyz/openbmc_project/dump'");
743
744 task->startTimer(std::chrono::minutes(3));
745 task->populateResp(asyncResp->res);
746 task->payload.emplace(req);
747}
748
749inline void createDump(crow::Response& res, const crow::Request& req,
750 const std::string& dumpType)
751{
752 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
753
754 std::string dumpPath;
755 if (dumpType == "BMC")
756 {
757 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
758 }
759 else if (dumpType == "System")
760 {
761 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
762 }
763 else
764 {
765 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
766 messages::internalError(asyncResp->res);
767 return;
768 }
769
770 std::optional<std::string> diagnosticDataType;
771 std::optional<std::string> oemDiagnosticDataType;
772
773 if (!redfish::json_util::readJson(
774 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
775 "OEMDiagnosticDataType", oemDiagnosticDataType))
776 {
777 return;
778 }
779
780 if (dumpType == "System")
781 {
782 if (!oemDiagnosticDataType || !diagnosticDataType)
783 {
784 BMCWEB_LOG_ERROR << "CreateDump action parameter "
785 "'DiagnosticDataType'/"
786 "'OEMDiagnosticDataType' value not found!";
787 messages::actionParameterMissing(
788 asyncResp->res, "CollectDiagnosticData",
789 "DiagnosticDataType & OEMDiagnosticDataType");
790 return;
791 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700792 if ((*oemDiagnosticDataType != "System") ||
793 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500794 {
795 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
796 messages::invalidObject(asyncResp->res,
797 "System Dump creation parameters");
798 return;
799 }
800 }
801 else if (dumpType == "BMC")
802 {
803 if (!diagnosticDataType)
804 {
805 BMCWEB_LOG_ERROR << "CreateDump action parameter "
806 "'DiagnosticDataType' not found!";
807 messages::actionParameterMissing(
808 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
809 return;
810 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700811 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500812 {
813 BMCWEB_LOG_ERROR
814 << "Wrong parameter value passed for 'DiagnosticDataType'";
815 messages::invalidObject(asyncResp->res,
816 "BMC Dump creation parameters");
817 return;
818 }
819 }
820
821 crow::connections::systemBus->async_method_call(
822 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
823 const uint32_t& dumpId) {
824 if (ec)
825 {
826 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
827 messages::internalError(asyncResp->res);
828 return;
829 }
830 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
831
832 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
833 },
834 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
835 "xyz.openbmc_project.Dump.Create", "CreateDump");
836}
837
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500838inline void clearDump(crow::Response& res, const std::string& dumpInterface)
839{
840 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
841 crow::connections::systemBus->async_method_call(
842 [asyncResp](const boost::system::error_code ec,
843 const std::vector<std::string>& subTreePaths) {
844 if (ec)
845 {
846 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
847 messages::internalError(asyncResp->res);
848 return;
849 }
850
851 for (const std::string& path : subTreePaths)
852 {
853 std::size_t pos = path.rfind("/");
854 if (pos != std::string::npos)
855 {
856 std::string logID = path.substr(pos + 1);
857 deleteDumpEntry(asyncResp->res, logID);
858 }
859 }
860 },
861 "xyz.openbmc_project.ObjectMapper",
862 "/xyz/openbmc_project/object_mapper",
863 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
864 "/xyz/openbmc_project/dump", 0,
865 std::array<std::string, 1>{dumpInterface});
866}
867
Ed Tanous2c70f802020-09-28 14:29:23 -0700868static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500869 const std::vector<std::pair<std::string, VariantType>>& params,
870 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700871{
872 for (auto property : params)
873 {
874 if (property.first == "Timestamp")
875 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500876 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500877 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700878 if (value != nullptr)
879 {
880 timestamp = *value;
881 }
882 }
883 else if (property.first == "Filename")
884 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500885 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500886 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700887 if (value != nullptr)
888 {
889 filename = *value;
890 }
891 }
892 else if (property.first == "Log")
893 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500894 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500895 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700896 if (value != nullptr)
897 {
898 logfile = *value;
899 }
900 }
901 }
902}
903
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500904constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800905class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700906{
907 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700908 SystemLogServiceCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800909 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800910 {
911 entityPrivileges = {
912 {boost::beast::http::verb::get, {{"Login"}}},
913 {boost::beast::http::verb::head, {{"Login"}}},
914 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
915 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
916 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
917 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
918 }
919
920 private:
921 /**
922 * Functions triggers appropriate requests on DBus
923 */
Ed Tanouscb13a392020-07-25 19:02:03 +0000924 void doGet(crow::Response& res, const crow::Request&,
925 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800926 {
927 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800928 // Collections don't include the static data added by SubRoute because
929 // it has a duplicate entry for members
930 asyncResp->res.jsonValue["@odata.type"] =
931 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800932 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800933 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800934 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
935 asyncResp->res.jsonValue["Description"] =
936 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500937 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800938 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800939 logServiceArray.push_back(
940 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500941#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600942 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500943 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600944#endif
945
Jason M. Billsd53dd412019-02-12 17:16:22 -0800946#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
947 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500948 {{"@odata.id",
949 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800950#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800951 asyncResp->res.jsonValue["Members@odata.count"] =
952 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800953
954 crow::connections::systemBus->async_method_call(
955 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500956 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800957 if (ec)
958 {
959 BMCWEB_LOG_ERROR << ec;
960 return;
961 }
962
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500963 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800964 {
965 if (pathStr.find("PostCode") != std::string::npos)
966 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000967 nlohmann::json& logServiceArrayLocal =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800968 asyncResp->res.jsonValue["Members"];
Ed Tanous23a21a12020-07-25 04:45:05 +0000969 logServiceArrayLocal.push_back(
ZhikuiRena3316fc2020-01-29 14:58:08 -0800970 {{"@odata.id", "/redfish/v1/Systems/system/"
971 "LogServices/PostCodes"}});
972 asyncResp->res.jsonValue["Members@odata.count"] =
Ed Tanous23a21a12020-07-25 04:45:05 +0000973 logServiceArrayLocal.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800974 return;
975 }
976 }
977 },
978 "xyz.openbmc_project.ObjectMapper",
979 "/xyz/openbmc_project/object_mapper",
980 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500981 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800982 }
983};
984
985class EventLogService : public Node
986{
987 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700988 EventLogService(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800989 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800990 {
991 entityPrivileges = {
992 {boost::beast::http::verb::get, {{"Login"}}},
993 {boost::beast::http::verb::head, {{"Login"}}},
994 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
995 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
996 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
997 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
998 }
999
1000 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001001 void doGet(crow::Response& res, const crow::Request&,
1002 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001003 {
1004 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1005
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001006 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001007 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001008 asyncResp->res.jsonValue["@odata.type"] =
1009 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001010 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1011 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001012 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001013 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1014 asyncResp->res.jsonValue["Entries"] = {
1015 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001016 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001017 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1018
1019 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1020 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001021 }
1022};
1023
Tim Lee1f56a3a2019-10-09 10:17:57 +08001024class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001025{
1026 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001027 JournalEventLogClear(App& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001028 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1029 "LogService.ClearLog/")
1030 {
1031 entityPrivileges = {
1032 {boost::beast::http::verb::get, {{"Login"}}},
1033 {boost::beast::http::verb::head, {{"Login"}}},
1034 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1035 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1036 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1037 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1038 }
1039
1040 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001041 void doPost(crow::Response& res, const crow::Request&,
1042 const std::vector<std::string>&) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001043 {
1044 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1045
1046 // Clear the EventLog by deleting the log files
1047 std::vector<std::filesystem::path> redfishLogFiles;
1048 if (getRedfishLogFiles(redfishLogFiles))
1049 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001050 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001051 {
1052 std::error_code ec;
1053 std::filesystem::remove(file, ec);
1054 }
1055 }
1056
1057 // Reload rsyslog so it knows to start new log files
1058 crow::connections::systemBus->async_method_call(
1059 [asyncResp](const boost::system::error_code ec) {
1060 if (ec)
1061 {
1062 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1063 messages::internalError(asyncResp->res);
1064 return;
1065 }
1066
1067 messages::success(asyncResp->res);
1068 },
1069 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1070 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1071 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001072 }
1073};
1074
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001075static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -07001076 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001077 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001078{
Jason M. Bills95820182019-04-22 16:25:34 -07001079 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001080 // First get the Timestamp
1081 size_t space = logEntry.find_first_of(" ");
1082 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001083 {
1084 return 1;
1085 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001086 std::string timestamp = logEntry.substr(0, space);
1087 // Then get the log contents
1088 size_t entryStart = logEntry.find_first_not_of(" ", space);
1089 if (entryStart == std::string::npos)
1090 {
1091 return 1;
1092 }
1093 std::string_view entry(logEntry);
1094 entry.remove_prefix(entryStart);
1095 // Use split to separate the entry into its fields
1096 std::vector<std::string> logEntryFields;
1097 boost::split(logEntryFields, entry, boost::is_any_of(","),
1098 boost::token_compress_on);
1099 // We need at least a MessageId to be valid
1100 if (logEntryFields.size() < 1)
1101 {
1102 return 1;
1103 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001104 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001105
Jason M. Bills4851d452019-03-28 11:27:48 -07001106 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001107 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001108 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001109
Jason M. Bills4851d452019-03-28 11:27:48 -07001110 std::string msg;
1111 std::string severity;
1112 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001113 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001114 msg = message->message;
1115 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001116 }
1117
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001118 // Get the MessageArgs from the log if there are any
1119 boost::beast::span<std::string> messageArgs;
1120 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001121 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001122 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001123 // If the first string is empty, assume there are no MessageArgs
1124 std::size_t messageArgsSize = 0;
1125 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001126 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001127 messageArgsSize = logEntryFields.size() - 1;
1128 }
1129
Ed Tanous23a21a12020-07-25 04:45:05 +00001130 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001131
1132 // Fill the MessageArgs into the Message
1133 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001134 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001135 {
1136 std::string argStr = "%" + std::to_string(++i);
1137 size_t argPos = msg.find(argStr);
1138 if (argPos != std::string::npos)
1139 {
1140 msg.replace(argPos, argStr.length(), messageArg);
1141 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001142 }
1143 }
1144
Jason M. Bills95820182019-04-22 16:25:34 -07001145 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1146 // format which matches the Redfish format except for the fractional seconds
1147 // between the '.' and the '+', so just remove them.
1148 std::size_t dot = timestamp.find_first_of(".");
1149 std::size_t plus = timestamp.find_first_of("+");
1150 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001151 {
Jason M. Bills95820182019-04-22 16:25:34 -07001152 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001153 }
1154
1155 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001156 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001157 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001158 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001159 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001160 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001161 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001162 {"Id", logEntryID},
1163 {"Message", std::move(msg)},
1164 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001165 {"MessageArgs", std::move(messageArgs)},
1166 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001167 {"Severity", std::move(severity)},
1168 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001169 return 0;
1170}
1171
Anthony Wilson27062602019-04-22 02:10:09 -05001172class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001173{
1174 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001175 JournalEventLogEntryCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001176 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001177 {
1178 entityPrivileges = {
1179 {boost::beast::http::verb::get, {{"Login"}}},
1180 {boost::beast::http::verb::head, {{"Login"}}},
1181 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1182 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1183 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1184 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1185 }
1186
1187 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001188 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001189 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001190 {
1191 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001192 uint64_t skip = 0;
1193 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001194 if (!getSkipParam(asyncResp->res, req, skip))
1195 {
1196 return;
1197 }
1198 if (!getTopParam(asyncResp->res, req, top))
1199 {
1200 return;
1201 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001202 // Collections don't include the static data added by SubRoute because
1203 // it has a duplicate entry for members
1204 asyncResp->res.jsonValue["@odata.type"] =
1205 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001206 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001207 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001208 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1209 asyncResp->res.jsonValue["Description"] =
1210 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001211
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001212 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001213 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001214 // Go through the log files and create a unique ID for each entry
1215 std::vector<std::filesystem::path> redfishLogFiles;
1216 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001217 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001218 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001219
1220 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001221 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1222 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001223 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001224 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001225 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 {
1227 continue;
1228 }
1229
Jason M. Billse85d6b12019-07-29 17:01:15 -07001230 // Reset the unique ID on the first entry
1231 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001232 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001233 {
Jason M. Bills95820182019-04-22 16:25:34 -07001234 entryCount++;
1235 // Handle paging using skip (number of entries to skip from the
1236 // start) and top (number of entries to display)
1237 if (entryCount <= skip || entryCount > skip + top)
1238 {
1239 continue;
1240 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001241
Jason M. Bills95820182019-04-22 16:25:34 -07001242 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001243 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001244 {
1245 continue;
1246 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001247
Jason M. Billse85d6b12019-07-29 17:01:15 -07001248 if (firstEntry)
1249 {
1250 firstEntry = false;
1251 }
1252
Jason M. Bills95820182019-04-22 16:25:34 -07001253 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001254 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001255 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1256 {
1257 messages::internalError(asyncResp->res);
1258 return;
1259 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001260 }
1261 }
1262 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1263 if (skip + top < entryCount)
1264 {
1265 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001266 "/redfish/v1/Systems/system/LogServices/EventLog/"
1267 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001268 std::to_string(skip + top);
1269 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001270 }
1271};
1272
Jason M. Bills897967d2019-07-29 17:05:30 -07001273class JournalEventLogEntry : public Node
1274{
1275 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001276 JournalEventLogEntry(App& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001277 Node(app,
1278 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1279 std::string())
1280 {
1281 entityPrivileges = {
1282 {boost::beast::http::verb::get, {{"Login"}}},
1283 {boost::beast::http::verb::head, {{"Login"}}},
1284 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1285 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1286 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1287 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1288 }
1289
1290 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001291 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001292 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001293 {
1294 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1295 if (params.size() != 1)
1296 {
1297 messages::internalError(asyncResp->res);
1298 return;
1299 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001300 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001301
1302 // Go through the log files and check the unique ID for each entry to
1303 // find the target entry
1304 std::vector<std::filesystem::path> redfishLogFiles;
1305 getRedfishLogFiles(redfishLogFiles);
1306 std::string logEntry;
1307
1308 // Oldest logs are in the last file, so start there and loop backwards
1309 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1310 it++)
1311 {
1312 std::ifstream logStream(*it);
1313 if (!logStream.is_open())
1314 {
1315 continue;
1316 }
1317
1318 // Reset the unique ID on the first entry
1319 bool firstEntry = true;
1320 while (std::getline(logStream, logEntry))
1321 {
1322 std::string idStr;
1323 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1324 {
1325 continue;
1326 }
1327
1328 if (firstEntry)
1329 {
1330 firstEntry = false;
1331 }
1332
1333 if (idStr == targetID)
1334 {
1335 if (fillEventLogEntryJson(idStr, logEntry,
1336 asyncResp->res.jsonValue) != 0)
1337 {
1338 messages::internalError(asyncResp->res);
1339 return;
1340 }
1341 return;
1342 }
1343 }
1344 }
1345 // Requested ID was not found
1346 messages::resourceMissingAtURI(asyncResp->res, targetID);
1347 }
1348};
1349
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001350class DBusEventLogEntryCollection : public Node
1351{
1352 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001353 DBusEventLogEntryCollection(App& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001354 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1355 {
1356 entityPrivileges = {
1357 {boost::beast::http::verb::get, {{"Login"}}},
1358 {boost::beast::http::verb::head, {{"Login"}}},
1359 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1360 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1361 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1362 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1363 }
1364
1365 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001366 void doGet(crow::Response& res, const crow::Request&,
1367 const std::vector<std::string>&) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001368 {
1369 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1370
1371 // Collections don't include the static data added by SubRoute because
1372 // it has a duplicate entry for members
1373 asyncResp->res.jsonValue["@odata.type"] =
1374 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001375 asyncResp->res.jsonValue["@odata.id"] =
1376 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1377 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1378 asyncResp->res.jsonValue["Description"] =
1379 "Collection of System Event Log Entries";
1380
Andrew Geisslercb92c032018-08-17 07:56:14 -07001381 // DBus implementation of EventLog/Entries
1382 // Make call to Logging Service to find all log entry objects
1383 crow::connections::systemBus->async_method_call(
1384 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001385 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001386 if (ec)
1387 {
1388 // TODO Handle for specific error code
1389 BMCWEB_LOG_ERROR
1390 << "getLogEntriesIfaceData resp_handler got error "
1391 << ec;
1392 messages::internalError(asyncResp->res);
1393 return;
1394 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001395 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001396 asyncResp->res.jsonValue["Members"];
1397 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001398 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001399 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001400 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001401 {
1402 if (interfaceMap.first !=
1403 "xyz.openbmc_project.Logging.Entry")
1404 {
1405 BMCWEB_LOG_DEBUG << "Bailing early on "
1406 << interfaceMap.first;
1407 continue;
1408 }
1409 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001410 nlohmann::json& thisEntry = entriesArray.back();
1411 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001412 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001413 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001414 std::string* severity = nullptr;
1415 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001416
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001417 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001418 {
1419 if (propertyMap.first == "Id")
1420 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001421 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001422 if (id == nullptr)
1423 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001424 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001425 }
1426 }
1427 else if (propertyMap.first == "Timestamp")
1428 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001429 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001430 std::get_if<uint64_t>(&propertyMap.second);
1431 if (millisTimeStamp == nullptr)
1432 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001433 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001434 }
George Liuebd45902020-08-26 14:21:10 +08001435 else
1436 {
1437 timestamp = crow::utility::getTimestamp(
1438 *millisTimeStamp);
1439 }
George Liud139c232020-08-18 18:48:57 +08001440 }
1441 else if (propertyMap.first == "UpdateTimestamp")
1442 {
1443 const uint64_t* millisTimeStamp =
1444 std::get_if<uint64_t>(&propertyMap.second);
1445 if (millisTimeStamp == nullptr)
1446 {
1447 messages::internalError(asyncResp->res);
1448 }
George Liuebd45902020-08-26 14:21:10 +08001449 else
1450 {
1451 updateTimestamp =
1452 crow::utility::getTimestamp(
1453 *millisTimeStamp);
1454 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001455 }
1456 else if (propertyMap.first == "Severity")
1457 {
1458 severity = std::get_if<std::string>(
1459 &propertyMap.second);
1460 if (severity == nullptr)
1461 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001462 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001463 }
1464 }
1465 else if (propertyMap.first == "Message")
1466 {
1467 message = std::get_if<std::string>(
1468 &propertyMap.second);
1469 if (message == nullptr)
1470 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001471 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001472 }
1473 }
1474 }
1475 thisEntry = {
George Liud139c232020-08-18 18:48:57 +08001476 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001477 {"@odata.id",
1478 "/redfish/v1/Systems/system/LogServices/EventLog/"
1479 "Entries/" +
1480 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001481 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001482 {"Id", std::to_string(*id)},
1483 {"Message", *message},
1484 {"EntryType", "Event"},
1485 {"Severity",
1486 translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001487 {"Created", crow::utility::getDateTime(timestamp)},
1488 {"Modified",
1489 crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001490 }
1491 }
1492 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001493 [](const nlohmann::json& left,
1494 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001495 return (left["Id"] <= right["Id"]);
1496 });
1497 asyncResp->res.jsonValue["Members@odata.count"] =
1498 entriesArray.size();
1499 },
1500 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1501 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001502 }
1503};
1504
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001505class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001506{
1507 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001508 DBusEventLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001509 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001510 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1511 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001512 {
1513 entityPrivileges = {
1514 {boost::beast::http::verb::get, {{"Login"}}},
1515 {boost::beast::http::verb::head, {{"Login"}}},
1516 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1517 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1518 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1519 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1520 }
1521
1522 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001523 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001524 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001525 {
1526 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001527 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001528 {
1529 messages::internalError(asyncResp->res);
1530 return;
1531 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001532 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001533
Andrew Geisslercb92c032018-08-17 07:56:14 -07001534 // DBus implementation of EventLog/Entries
1535 // Make call to Logging Service to find all log entry objects
1536 crow::connections::systemBus->async_method_call(
1537 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001538 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001539 if (ec)
1540 {
1541 BMCWEB_LOG_ERROR
1542 << "EventLogEntry (DBus) resp_handler got error " << ec;
1543 messages::internalError(asyncResp->res);
1544 return;
1545 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001546 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001547 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001548 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001549 std::string* severity = nullptr;
1550 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001551
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001552 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001553 {
1554 if (propertyMap.first == "Id")
1555 {
1556 id = std::get_if<uint32_t>(&propertyMap.second);
1557 if (id == nullptr)
1558 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001559 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001560 }
1561 }
1562 else if (propertyMap.first == "Timestamp")
1563 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001564 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001565 std::get_if<uint64_t>(&propertyMap.second);
1566 if (millisTimeStamp == nullptr)
1567 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001568 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001569 }
George Liuebd45902020-08-26 14:21:10 +08001570 else
1571 {
1572 timestamp =
1573 crow::utility::getTimestamp(*millisTimeStamp);
1574 }
George Liud139c232020-08-18 18:48:57 +08001575 }
1576 else if (propertyMap.first == "UpdateTimestamp")
1577 {
1578 const uint64_t* millisTimeStamp =
1579 std::get_if<uint64_t>(&propertyMap.second);
1580 if (millisTimeStamp == nullptr)
1581 {
1582 messages::internalError(asyncResp->res);
1583 }
George Liuebd45902020-08-26 14:21:10 +08001584 else
1585 {
1586 updateTimestamp =
1587 crow::utility::getTimestamp(*millisTimeStamp);
1588 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001589 }
1590 else if (propertyMap.first == "Severity")
1591 {
1592 severity =
1593 std::get_if<std::string>(&propertyMap.second);
1594 if (severity == nullptr)
1595 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001596 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001597 }
1598 }
1599 else if (propertyMap.first == "Message")
1600 {
1601 message = std::get_if<std::string>(&propertyMap.second);
1602 if (message == nullptr)
1603 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001604 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001605 }
1606 }
1607 }
Ed Tanous271584a2019-07-09 16:24:22 -07001608 if (id == nullptr || message == nullptr || severity == nullptr)
1609 {
1610 return;
1611 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001612 asyncResp->res.jsonValue = {
George Liud139c232020-08-18 18:48:57 +08001613 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001614 {"@odata.id",
1615 "/redfish/v1/Systems/system/LogServices/EventLog/"
1616 "Entries/" +
1617 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001618 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001619 {"Id", std::to_string(*id)},
1620 {"Message", *message},
1621 {"EntryType", "Event"},
1622 {"Severity", translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001623 {"Created", crow::utility::getDateTime(timestamp)},
1624 {"Modified", crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001625 },
1626 "xyz.openbmc_project.Logging",
1627 "/xyz/openbmc_project/logging/entry/" + entryID,
1628 "org.freedesktop.DBus.Properties", "GetAll",
1629 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001630 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001631
Ed Tanouscb13a392020-07-25 19:02:03 +00001632 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001633 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001634 {
1635
1636 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1637
1638 auto asyncResp = std::make_shared<AsyncResp>(res);
1639
1640 if (params.size() != 1)
1641 {
1642 messages::internalError(asyncResp->res);
1643 return;
1644 }
1645 std::string entryID = params[0];
1646
1647 dbus::utility::escapePathForDbus(entryID);
1648
1649 // Process response from Logging service.
1650 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1651 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1652 if (ec)
1653 {
1654 // TODO Handle for specific error code
1655 BMCWEB_LOG_ERROR
1656 << "EventLogEntry (DBus) doDelete respHandler got error "
1657 << ec;
1658 asyncResp->res.result(
1659 boost::beast::http::status::internal_server_error);
1660 return;
1661 }
1662
1663 asyncResp->res.result(boost::beast::http::status::ok);
1664 };
1665
1666 // Make call to Logging service to request Delete Log
1667 crow::connections::systemBus->async_method_call(
1668 respHandler, "xyz.openbmc_project.Logging",
1669 "/xyz/openbmc_project/logging/entry/" + entryID,
1670 "xyz.openbmc_project.Object.Delete", "Delete");
1671 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001672};
1673
1674class BMCLogServiceCollection : public Node
1675{
1676 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001677 BMCLogServiceCollection(App& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001678 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001679 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001680 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001681 {boost::beast::http::verb::get, {{"Login"}}},
1682 {boost::beast::http::verb::head, {{"Login"}}},
1683 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1684 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1685 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1686 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001687 }
1688
1689 private:
1690 /**
1691 * Functions triggers appropriate requests on DBus
1692 */
Ed Tanouscb13a392020-07-25 19:02:03 +00001693 void doGet(crow::Response& res, const crow::Request&,
1694 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001695 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001696 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001697 // Collections don't include the static data added by SubRoute because
1698 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001699 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001700 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001701 asyncResp->res.jsonValue["@odata.id"] =
1702 "/redfish/v1/Managers/bmc/LogServices";
1703 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1704 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001705 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001706 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001707 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001708#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1709 logServiceArray.push_back(
1710 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1711#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001712#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1713 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001714 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001715#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001716 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001717 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001718 }
1719};
1720
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001721class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001722{
1723 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001724 BMCJournalLogService(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001725 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001726 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001727 entityPrivileges = {
1728 {boost::beast::http::verb::get, {{"Login"}}},
1729 {boost::beast::http::verb::head, {{"Login"}}},
1730 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1731 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1732 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1733 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1734 }
1735
1736 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001737 void doGet(crow::Response& res, const crow::Request&,
1738 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001739 {
1740 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001741 asyncResp->res.jsonValue["@odata.type"] =
1742 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001743 asyncResp->res.jsonValue["@odata.id"] =
1744 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001745 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1746 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1747 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001748 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001749 asyncResp->res.jsonValue["Entries"] = {
1750 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001751 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001752 }
1753};
1754
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001755static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1756 sd_journal* journal,
1757 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001758{
1759 // Get the Log Entry contents
1760 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001761
Ed Tanous39e77502019-03-04 17:35:53 -08001762 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001763 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001764 if (ret < 0)
1765 {
1766 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1767 return 1;
1768 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001769
1770 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001771 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001772 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001773 if (ret < 0)
1774 {
1775 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001776 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001777
1778 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001779 std::string entryTimeStr;
1780 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001781 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001782 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001783 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001784
1785 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001786 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001787 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001788 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1789 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001790 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001791 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001792 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001793 {"EntryType", "Oem"},
1794 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001795 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001796 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001797 {"Created", std::move(entryTimeStr)}};
1798 return 0;
1799}
1800
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001801class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001802{
1803 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001804 BMCJournalLogEntryCollection(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001805 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001806 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001807 entityPrivileges = {
1808 {boost::beast::http::verb::get, {{"Login"}}},
1809 {boost::beast::http::verb::head, {{"Login"}}},
1810 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1811 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1812 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1813 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1814 }
1815
1816 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001817 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001818 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001819 {
1820 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001821 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001822 uint64_t skip = 0;
1823 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001824 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001825 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001826 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001827 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001828 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001829 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001830 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001831 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001832 // Collections don't include the static data added by SubRoute because
1833 // it has a duplicate entry for members
1834 asyncResp->res.jsonValue["@odata.type"] =
1835 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001836 asyncResp->res.jsonValue["@odata.id"] =
1837 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001838 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001839 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001840 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1841 asyncResp->res.jsonValue["Description"] =
1842 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001843 asyncResp->res.jsonValue["@odata.id"] =
1844 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001845 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001846 logEntryArray = nlohmann::json::array();
1847
1848 // Go through the journal and use the timestamp to create a unique ID
1849 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001850 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001851 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1852 if (ret < 0)
1853 {
1854 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001855 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001856 return;
1857 }
1858 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1859 journalTmp, sd_journal_close);
1860 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001861 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001862 // Reset the unique ID on the first entry
1863 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001864 SD_JOURNAL_FOREACH(journal.get())
1865 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001866 entryCount++;
1867 // Handle paging using skip (number of entries to skip from the
1868 // start) and top (number of entries to display)
1869 if (entryCount <= skip || entryCount > skip + top)
1870 {
1871 continue;
1872 }
1873
Jason M. Bills16428a12018-11-02 12:42:29 -07001874 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001875 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001876 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001877 continue;
1878 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001879
Jason M. Billse85d6b12019-07-29 17:01:15 -07001880 if (firstEntry)
1881 {
1882 firstEntry = false;
1883 }
1884
Jason M. Billse1f26342018-07-18 12:12:00 -07001885 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001886 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001887 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1888 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001889 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001890 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 return;
1892 }
1893 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001894 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1895 if (skip + top < entryCount)
1896 {
1897 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001898 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001899 std::to_string(skip + top);
1900 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001901 }
1902};
1903
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001904class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001905{
1906 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001907 BMCJournalLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001908 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001909 std::string())
1910 {
1911 entityPrivileges = {
1912 {boost::beast::http::verb::get, {{"Login"}}},
1913 {boost::beast::http::verb::head, {{"Login"}}},
1914 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1915 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1916 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1917 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1918 }
1919
1920 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001921 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001922 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001923 {
1924 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1925 if (params.size() != 1)
1926 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001927 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001928 return;
1929 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001930 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001931 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001932 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001933 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001934 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001935 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001936 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001937 }
1938
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001939 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001940 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1941 if (ret < 0)
1942 {
1943 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001944 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001945 return;
1946 }
1947 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1948 journalTmp, sd_journal_close);
1949 journalTmp = nullptr;
1950 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001951 // tracking the unique ID
1952 std::string idStr;
1953 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301955 if (ret < 0)
1956 {
1957 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1958 << strerror(-ret);
1959 messages::internalError(asyncResp->res);
1960 return;
1961 }
Ed Tanous271584a2019-07-09 16:24:22 -07001962 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001963 {
1964 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001965 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1966 {
1967 messages::internalError(asyncResp->res);
1968 return;
1969 }
1970 if (firstEntry)
1971 {
1972 firstEntry = false;
1973 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001974 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001975 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001976 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001977 {
1978 messages::resourceMissingAtURI(asyncResp->res, entryID);
1979 return;
1980 }
1981
1982 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1983 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001984 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001985 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001986 return;
1987 }
1988 }
1989};
1990
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001991class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001992{
1993 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001994 BMCDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001995 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001996 {
1997 entityPrivileges = {
1998 {boost::beast::http::verb::get, {{"Login"}}},
1999 {boost::beast::http::verb::head, {{"Login"}}},
2000 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2001 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2002 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2003 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2004 }
2005
2006 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002007 void doGet(crow::Response& res, const crow::Request&,
2008 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002009 {
2010 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2011
2012 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002013 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002014 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002015 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002016 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2017 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2018 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002019 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002020 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002021 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2022 asyncResp->res.jsonValue["Actions"] = {
2023 {"#LogService.ClearLog",
2024 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2025 "Actions/LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002026 {"#LogService.CollectDiagnosticData",
2027 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2028 "Actions/LogService.CollectDiagnosticData"}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002029 }
2030};
2031
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002032class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002033{
2034 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002035 BMCDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002036 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002037 {
2038 entityPrivileges = {
2039 {boost::beast::http::verb::get, {{"Login"}}},
2040 {boost::beast::http::verb::head, {{"Login"}}},
2041 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2042 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2043 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2044 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2045 }
2046
2047 private:
2048 /**
2049 * Functions triggers appropriate requests on DBus
2050 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002051 void doGet(crow::Response& res, const crow::Request&,
2052 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002053 {
2054 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2055
2056 asyncResp->res.jsonValue["@odata.type"] =
2057 "#LogEntryCollection.LogEntryCollection";
2058 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002059 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2060 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002061 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002062 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002063
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002064 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002065 }
2066};
2067
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002068class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002069{
2070 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002071 BMCDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002072 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002073 std::string())
2074 {
2075 entityPrivileges = {
2076 {boost::beast::http::verb::get, {{"Login"}}},
2077 {boost::beast::http::verb::head, {{"Login"}}},
2078 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2079 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2080 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2081 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2082 }
2083
2084 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002085 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002086 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002087 {
2088 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2089 if (params.size() != 1)
2090 {
2091 messages::internalError(asyncResp->res);
2092 return;
2093 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002094 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002095 }
2096
Ed Tanouscb13a392020-07-25 19:02:03 +00002097 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002098 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002099 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002100 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002101 if (params.size() != 1)
2102 {
2103 messages::internalError(asyncResp->res);
2104 return;
2105 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002106 deleteDumpEntry(asyncResp->res, params[0]);
2107 }
2108};
raviteja-bc9bb6862020-02-03 11:53:32 -06002109
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002110class BMCDumpCreate : public Node
2111{
2112 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002113 BMCDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002114 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002115 "Actions/"
2116 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002117 {
2118 entityPrivileges = {
2119 {boost::beast::http::verb::get, {{"Login"}}},
2120 {boost::beast::http::verb::head, {{"Login"}}},
2121 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2122 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2123 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2124 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2125 }
2126
2127 private:
2128 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002129 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002130 {
2131 createDump(res, req, "BMC");
2132 }
2133};
2134
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002135class BMCDumpClear : public Node
2136{
2137 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002138 BMCDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002139 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2140 "Actions/"
2141 "LogService.ClearLog/")
2142 {
2143 entityPrivileges = {
2144 {boost::beast::http::verb::get, {{"Login"}}},
2145 {boost::beast::http::verb::head, {{"Login"}}},
2146 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2147 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2148 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2149 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2150 }
2151
2152 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002153 void doPost(crow::Response& res, const crow::Request&,
2154 const std::vector<std::string>&) override
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002155 {
2156 clearDump(res, "xyz.openbmc_project.Dump.Entry.BMC");
2157 }
2158};
2159
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002160class SystemDumpService : public Node
2161{
2162 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002163 SystemDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002164 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2165 {
2166 entityPrivileges = {
2167 {boost::beast::http::verb::get, {{"Login"}}},
2168 {boost::beast::http::verb::head, {{"Login"}}},
2169 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2170 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2171 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2172 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2173 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002174
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002175 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002176 void doGet(crow::Response& res, const crow::Request&,
2177 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002178 {
2179 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002180
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002181 asyncResp->res.jsonValue["@odata.id"] =
2182 "/redfish/v1/Systems/system/LogServices/Dump";
2183 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002184 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002185 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2186 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2187 asyncResp->res.jsonValue["Id"] = "Dump";
2188 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2189 asyncResp->res.jsonValue["Entries"] = {
2190 {"@odata.id",
2191 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2192 asyncResp->res.jsonValue["Actions"] = {
2193 {"#LogService.ClearLog",
2194 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2195 "LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002196 {"#LogService.CollectDiagnosticData",
2197 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2198 "LogService.CollectDiagnosticData"}}}};
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002199 }
2200};
2201
2202class SystemDumpEntryCollection : public Node
2203{
2204 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002205 SystemDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002206 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2207 {
2208 entityPrivileges = {
2209 {boost::beast::http::verb::get, {{"Login"}}},
2210 {boost::beast::http::verb::head, {{"Login"}}},
2211 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2212 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2213 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2214 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2215 }
2216
2217 private:
2218 /**
2219 * Functions triggers appropriate requests on DBus
2220 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002221 void doGet(crow::Response& res, const crow::Request&,
2222 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002223 {
2224 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2225
2226 asyncResp->res.jsonValue["@odata.type"] =
2227 "#LogEntryCollection.LogEntryCollection";
2228 asyncResp->res.jsonValue["@odata.id"] =
2229 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2230 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2231 asyncResp->res.jsonValue["Description"] =
2232 "Collection of System Dump Entries";
2233
2234 getDumpEntryCollection(asyncResp, "System");
2235 }
2236};
2237
2238class SystemDumpEntry : public Node
2239{
2240 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002241 SystemDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002242 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2243 std::string())
2244 {
2245 entityPrivileges = {
2246 {boost::beast::http::verb::get, {{"Login"}}},
2247 {boost::beast::http::verb::head, {{"Login"}}},
2248 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2249 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2250 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2251 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2252 }
2253
2254 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002255 void doGet(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002256 const std::vector<std::string>& params) override
2257 {
2258 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2259 if (params.size() != 1)
2260 {
2261 messages::internalError(asyncResp->res);
2262 return;
2263 }
2264 getDumpEntryById(asyncResp, params[0], "System");
2265 }
2266
Ed Tanouscb13a392020-07-25 19:02:03 +00002267 void doDelete(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002268 const std::vector<std::string>& params) override
2269 {
2270 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2271 if (params.size() != 1)
2272 {
2273 messages::internalError(asyncResp->res);
2274 return;
2275 }
2276 deleteDumpEntry(asyncResp->res, params[0]);
raviteja-bc9bb6862020-02-03 11:53:32 -06002277 }
2278};
2279
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002280class SystemDumpCreate : public Node
2281{
2282 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002283 SystemDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002284 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002285 "Actions/"
2286 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002287 {
2288 entityPrivileges = {
2289 {boost::beast::http::verb::get, {{"Login"}}},
2290 {boost::beast::http::verb::head, {{"Login"}}},
2291 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2292 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2293 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2294 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2295 }
2296
2297 private:
2298 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002299 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002300 {
2301 createDump(res, req, "System");
2302 }
2303};
2304
raviteja-b013487e2020-03-03 03:20:48 -06002305class SystemDumpClear : public Node
2306{
2307 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002308 SystemDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002309 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002310 "Actions/"
2311 "LogService.ClearLog/")
2312 {
2313 entityPrivileges = {
2314 {boost::beast::http::verb::get, {{"Login"}}},
2315 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002316 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2317 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2318 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002319 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2320 }
2321
2322 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002323 void doPost(crow::Response& res, const crow::Request&,
2324 const std::vector<std::string>&) override
raviteja-b013487e2020-03-03 03:20:48 -06002325 {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002326 clearDump(res, "xyz.openbmc_project.Dump.Entry.System");
raviteja-b013487e2020-03-03 03:20:48 -06002327 }
2328};
2329
Jason M. Bills424c4172019-03-21 13:50:33 -07002330class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002331{
2332 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002333 CrashdumpService(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002334 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002335 {
AppaRao Puli39460282020-04-07 17:03:04 +05302336 // Note: Deviated from redfish privilege registry for GET & HEAD
2337 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002338 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302339 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2340 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002341 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2342 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2343 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2344 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002345 }
2346
2347 private:
2348 /**
2349 * Functions triggers appropriate requests on DBus
2350 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002351 void doGet(crow::Response& res, const crow::Request&,
2352 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002353 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002354 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002355 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002356 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002357 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002358 asyncResp->res.jsonValue["@odata.type"] =
2359 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002360 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2361 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2362 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002363 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2364 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002365 asyncResp->res.jsonValue["Entries"] = {
2366 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002367 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002368 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002369 {"#LogService.ClearLog",
2370 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2371 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002372 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002373 {{"#Crashdump.OnDemand",
2374 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002375 "Actions/Oem/Crashdump.OnDemand"}}},
2376 {"#Crashdump.Telemetry",
2377 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2378 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002379
2380#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002381 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002382 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002383 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2384 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002385#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002386 }
2387};
2388
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002389class CrashdumpClear : public Node
2390{
2391 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002392 CrashdumpClear(App& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002393 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2394 "LogService.ClearLog/")
2395 {
AppaRao Puli39460282020-04-07 17:03:04 +05302396 // Note: Deviated from redfish privilege registry for GET & HEAD
2397 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002398 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302399 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2400 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002401 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2402 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2403 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2404 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2405 }
2406
2407 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002408 void doPost(crow::Response& res, const crow::Request&,
2409 const std::vector<std::string>&) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002410 {
2411 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2412
2413 crow::connections::systemBus->async_method_call(
2414 [asyncResp](const boost::system::error_code ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002415 const std::string&) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002416 if (ec)
2417 {
2418 messages::internalError(asyncResp->res);
2419 return;
2420 }
2421 messages::success(asyncResp->res);
2422 },
2423 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2424 }
2425};
2426
Jason M. Billse855dd22019-10-08 11:37:48 -07002427static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002428 const std::string& logID,
2429 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002430{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002431 auto getStoredLogCallback =
2432 [asyncResp, logID, &logEntryJson](
2433 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002434 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002435 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002436 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002437 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2438 if (ec.value() ==
2439 boost::system::linux_error::bad_request_descriptor)
2440 {
2441 messages::resourceNotFound(asyncResp->res, "LogEntry",
2442 logID);
2443 }
2444 else
2445 {
2446 messages::internalError(asyncResp->res);
2447 }
2448 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002449 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002450
Johnathan Mantey043a0532020-03-10 17:15:28 -07002451 std::string timestamp{};
2452 std::string filename{};
2453 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002454 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002455
2456 if (filename.empty() || timestamp.empty())
2457 {
2458 messages::resourceMissingAtURI(asyncResp->res, logID);
2459 return;
2460 }
2461
2462 std::string crashdumpURI =
2463 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2464 logID + "/" + filename;
2465 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2466 {"@odata.id", "/redfish/v1/Systems/system/"
2467 "LogServices/Crashdump/Entries/" +
2468 logID},
2469 {"Name", "CPU Crashdump"},
2470 {"Id", logID},
2471 {"EntryType", "Oem"},
2472 {"OemRecordFormat", "Crashdump URI"},
2473 {"Message", std::move(crashdumpURI)},
2474 {"Created", std::move(timestamp)}};
2475 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002476 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002477 std::move(getStoredLogCallback), crashdumpObject,
2478 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002479 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002480}
2481
Jason M. Bills424c4172019-03-21 13:50:33 -07002482class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002483{
2484 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002485 CrashdumpEntryCollection(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002486 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002487 {
AppaRao Puli39460282020-04-07 17:03:04 +05302488 // Note: Deviated from redfish privilege registry for GET & HEAD
2489 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002490 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302491 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2492 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002493 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2494 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2495 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2496 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002497 }
2498
2499 private:
2500 /**
2501 * Functions triggers appropriate requests on DBus
2502 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002503 void doGet(crow::Response& res, const crow::Request&,
2504 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002505 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002506 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002507 // Collections don't include the static data added by SubRoute because
2508 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002509 auto getLogEntriesCallback = [asyncResp](
2510 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002511 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002512 if (ec)
2513 {
2514 if (ec.value() !=
2515 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002516 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002517 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2518 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002519 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002520 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002521 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002522 }
2523 asyncResp->res.jsonValue["@odata.type"] =
2524 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002525 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002526 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002527 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002528 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002529 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002530 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002531 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002532 std::vector<std::string> logIDs;
2533 // Get the list of log entries and build up an empty array big
2534 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002535 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002536 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002537 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002538 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002539 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002540 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002541 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002542 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002543 logIDs.emplace_back(objpath.substr(lastPos + 1));
2544
2545 // Add a space for the log entry to the array
2546 logEntryArray.push_back({});
2547 }
2548 // Now go through and set up async calls to fill in the entries
2549 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002550 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002551 {
2552 // Add the log entry to the array
2553 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002554 }
2555 asyncResp->res.jsonValue["Members@odata.count"] =
2556 logEntryArray.size();
2557 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002558 crow::connections::systemBus->async_method_call(
2559 std::move(getLogEntriesCallback),
2560 "xyz.openbmc_project.ObjectMapper",
2561 "/xyz/openbmc_project/object_mapper",
2562 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002563 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002564 }
2565};
2566
Jason M. Bills424c4172019-03-21 13:50:33 -07002567class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002568{
2569 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002570 CrashdumpEntry(App& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002571 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002572 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002573 std::string())
2574 {
AppaRao Puli39460282020-04-07 17:03:04 +05302575 // Note: Deviated from redfish privilege registry for GET & HEAD
2576 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002577 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302578 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2579 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002580 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2581 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2582 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2583 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002584 }
2585
2586 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002587 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002588 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002589 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002590 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002591 if (params.size() != 1)
2592 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002593 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002594 return;
2595 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002596 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002597 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2598 }
2599};
2600
2601class CrashdumpFile : public Node
2602{
2603 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002604 CrashdumpFile(App& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002605 Node(app,
2606 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2607 "<str>/",
2608 std::string(), std::string())
2609 {
AppaRao Puli39460282020-04-07 17:03:04 +05302610 // Note: Deviated from redfish privilege registry for GET & HEAD
2611 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002612 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302613 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2614 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002615 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2616 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2617 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2618 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2619 }
2620
2621 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002622 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002623 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002624 {
2625 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2626 if (params.size() != 2)
2627 {
2628 messages::internalError(asyncResp->res);
2629 return;
2630 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002631 const std::string& logID = params[0];
2632 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002633
Johnathan Mantey043a0532020-03-10 17:15:28 -07002634 auto getStoredLogCallback =
2635 [asyncResp, logID, fileName](
2636 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002637 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002638 if (ec)
2639 {
2640 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2641 << ec.message();
2642 messages::internalError(asyncResp->res);
2643 return;
2644 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002645
Johnathan Mantey043a0532020-03-10 17:15:28 -07002646 std::string dbusFilename{};
2647 std::string dbusTimestamp{};
2648 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002649
Ed Tanous2c70f802020-09-28 14:29:23 -07002650 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002651 dbusFilepath);
2652
2653 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2654 dbusFilepath.empty())
2655 {
2656 messages::resourceMissingAtURI(asyncResp->res, fileName);
2657 return;
2658 }
2659
2660 // Verify the file name parameter is correct
2661 if (fileName != dbusFilename)
2662 {
2663 messages::resourceMissingAtURI(asyncResp->res, fileName);
2664 return;
2665 }
2666
2667 if (!std::filesystem::exists(dbusFilepath))
2668 {
2669 messages::resourceMissingAtURI(asyncResp->res, fileName);
2670 return;
2671 }
2672 std::ifstream ifs(dbusFilepath, std::ios::in |
2673 std::ios::binary |
2674 std::ios::ate);
2675 std::ifstream::pos_type fileSize = ifs.tellg();
2676 if (fileSize < 0)
2677 {
2678 messages::generalError(asyncResp->res);
2679 return;
2680 }
2681 ifs.seekg(0, std::ios::beg);
2682
2683 auto crashData = std::make_unique<char[]>(
2684 static_cast<unsigned int>(fileSize));
2685
2686 ifs.read(crashData.get(), static_cast<int>(fileSize));
2687
2688 // The cast to std::string is intentional in order to use the
2689 // assign() that applies move mechanics
2690 asyncResp->res.body().assign(
2691 static_cast<std::string>(crashData.get()));
2692
2693 // Configure this to be a file download when accessed from
2694 // a browser
2695 asyncResp->res.addHeader("Content-Disposition", "attachment");
2696 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002697 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002698 std::move(getStoredLogCallback), crashdumpObject,
2699 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002700 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002701 }
2702};
2703
Jason M. Bills424c4172019-03-21 13:50:33 -07002704class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002705{
2706 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002707 OnDemandCrashdump(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002708 Node(app,
2709 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2710 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002711 {
AppaRao Puli39460282020-04-07 17:03:04 +05302712 // Note: Deviated from redfish privilege registry for GET & HEAD
2713 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002714 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302715 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2716 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2717 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2718 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2719 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2720 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002721 }
2722
2723 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002724 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002725 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002726 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002727 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002728
James Feistfe306722020-03-12 16:32:08 -07002729 auto generateonDemandLogCallback = [asyncResp,
2730 req](const boost::system::error_code
2731 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002732 const std::string&) {
James Feist46229572020-02-19 15:11:58 -08002733 if (ec)
2734 {
2735 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002736 {
James Feist46229572020-02-19 15:11:58 -08002737 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002738 }
James Feist46229572020-02-19 15:11:58 -08002739 else if (ec.value() ==
2740 boost::system::errc::device_or_resource_busy)
2741 {
2742 messages::serviceTemporarilyUnavailable(asyncResp->res,
2743 "60");
2744 }
2745 else
2746 {
2747 messages::internalError(asyncResp->res);
2748 }
2749 return;
2750 }
2751 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002752 [](boost::system::error_code err, sdbusplus::message::message&,
2753 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002754 if (!err)
2755 {
James Feiste5d50062020-05-11 17:29:00 -07002756 taskData->messages.emplace_back(
2757 messages::taskCompletedOK(
2758 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002759 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002760 }
James Feist32898ce2020-03-10 16:16:52 -07002761 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002762 },
James Feist46229572020-02-19 15:11:58 -08002763 "type='signal',interface='org.freedesktop.DBus.Properties',"
2764 "member='PropertiesChanged',arg0namespace='com.intel."
2765 "crashdump'");
2766 task->startTimer(std::chrono::minutes(5));
2767 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002768 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002769 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002770 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002771 std::move(generateonDemandLogCallback), crashdumpObject,
2772 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002773 }
2774};
2775
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002776class TelemetryCrashdump : public Node
2777{
2778 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002779 TelemetryCrashdump(App& app) :
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002780 Node(app,
2781 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2782 "Crashdump.Telemetry/")
2783 {
2784 // Note: Deviated from redfish privilege registry for GET & HEAD
2785 // method for security reasons.
2786 entityPrivileges = {
2787 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2788 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2789 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2790 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2791 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2792 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2793 }
2794
2795 private:
2796 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002797 const std::vector<std::string>&) override
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002798 {
2799 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2800
2801 auto generateTelemetryLogCallback = [asyncResp, req](
2802 const boost::system::error_code
2803 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002804 const std::string&) {
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002805 if (ec)
2806 {
2807 if (ec.value() == boost::system::errc::operation_not_supported)
2808 {
2809 messages::resourceInStandby(asyncResp->res);
2810 }
2811 else if (ec.value() ==
2812 boost::system::errc::device_or_resource_busy)
2813 {
2814 messages::serviceTemporarilyUnavailable(asyncResp->res,
2815 "60");
2816 }
2817 else
2818 {
2819 messages::internalError(asyncResp->res);
2820 }
2821 return;
2822 }
2823 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2824 [](boost::system::error_code err, sdbusplus::message::message&,
2825 const std::shared_ptr<task::TaskData>& taskData) {
2826 if (!err)
2827 {
2828 taskData->messages.emplace_back(
2829 messages::taskCompletedOK(
2830 std::to_string(taskData->index)));
2831 taskData->state = "Completed";
2832 }
2833 return task::completed;
2834 },
2835 "type='signal',interface='org.freedesktop.DBus.Properties',"
2836 "member='PropertiesChanged',arg0namespace='com.intel."
2837 "crashdump'");
2838 task->startTimer(std::chrono::minutes(5));
2839 task->populateResp(asyncResp->res);
2840 task->payload.emplace(req);
2841 };
2842 crow::connections::systemBus->async_method_call(
2843 std::move(generateTelemetryLogCallback), crashdumpObject,
2844 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2845 }
2846};
2847
Jason M. Billse1f26342018-07-18 12:12:00 -07002848class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002849{
2850 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002851 SendRawPECI(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002852 Node(app,
2853 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2854 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002855 {
AppaRao Puli39460282020-04-07 17:03:04 +05302856 // Note: Deviated from redfish privilege registry for GET & HEAD
2857 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002858 entityPrivileges = {
2859 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2860 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2861 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2862 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2863 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2864 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2865 }
2866
2867 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002868 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002869 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002870 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002871 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002872 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002873
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002874 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002875 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002876 return;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002877 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002878 uint32_t idx = 0;
2879 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002880 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002881 if (cmd.size() < 3)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002882 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002883 std::string s("[");
2884 for (auto const& val : cmd)
2885 {
2886 if (val != *cmd.begin())
2887 {
2888 s += ",";
2889 }
2890 s += std::to_string(val);
2891 }
2892 s += "]";
2893 messages::actionParameterValueFormatError(
2894 res, s, "PECICommands[" + std::to_string(idx) + "]",
2895 "SendRawPeci");
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002896 return;
2897 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002898 idx++;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002899 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002900 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002901 auto sendRawPECICallback =
2902 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002903 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002904 if (ec)
2905 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002906 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002907 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002908 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002909 return;
2910 }
2911 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2912 {"PECIResponse", resp}};
2913 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002914 // Call the SendRawPECI command with the provided data
2915 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002916 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002917 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002918 }
2919};
2920
Andrew Geisslercb92c032018-08-17 07:56:14 -07002921/**
2922 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2923 */
2924class DBusLogServiceActionsClear : public Node
2925{
2926 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002927 DBusLogServiceActionsClear(App& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002928 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002929 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002930 {
2931 entityPrivileges = {
2932 {boost::beast::http::verb::get, {{"Login"}}},
2933 {boost::beast::http::verb::head, {{"Login"}}},
2934 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2935 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2936 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2937 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2938 }
2939
2940 private:
2941 /**
2942 * Function handles POST method request.
2943 * The Clear Log actions does not require any parameter.The action deletes
2944 * all entries found in the Entries collection for this Log Service.
2945 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002946 void doPost(crow::Response& res, const crow::Request&,
2947 const std::vector<std::string>&) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002948 {
2949 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2950
2951 auto asyncResp = std::make_shared<AsyncResp>(res);
2952 // Process response from Logging service.
Ed Tanous2c70f802020-09-28 14:29:23 -07002953 auto respHandler = [asyncResp](const boost::system::error_code ec) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07002954 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2955 if (ec)
2956 {
2957 // TODO Handle for specific error code
2958 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2959 asyncResp->res.result(
2960 boost::beast::http::status::internal_server_error);
2961 return;
2962 }
2963
2964 asyncResp->res.result(boost::beast::http::status::no_content);
2965 };
2966
2967 // Make call to Logging service to request Clear Log
2968 crow::connections::systemBus->async_method_call(
Ed Tanous2c70f802020-09-28 14:29:23 -07002969 respHandler, "xyz.openbmc_project.Logging",
Andrew Geisslercb92c032018-08-17 07:56:14 -07002970 "/xyz/openbmc_project/logging",
2971 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2972 }
2973};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002974
2975/****************************************************
2976 * Redfish PostCode interfaces
2977 * using DBUS interface: getPostCodesTS
2978 ******************************************************/
2979class PostCodesLogService : public Node
2980{
2981 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002982 PostCodesLogService(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002983 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2984 {
2985 entityPrivileges = {
2986 {boost::beast::http::verb::get, {{"Login"}}},
2987 {boost::beast::http::verb::head, {{"Login"}}},
2988 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2989 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2990 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2991 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2992 }
2993
2994 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002995 void doGet(crow::Response& res, const crow::Request&,
2996 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002997 {
2998 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2999
3000 asyncResp->res.jsonValue = {
3001 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
3002 {"@odata.type", "#LogService.v1_1_0.LogService"},
3003 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
3004 {"Name", "POST Code Log Service"},
3005 {"Description", "POST Code Log Service"},
3006 {"Id", "BIOS POST Code Log"},
3007 {"OverWritePolicy", "WrapsWhenFull"},
3008 {"Entries",
3009 {{"@odata.id",
3010 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3011 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3012 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3013 "Actions/LogService.ClearLog"}};
3014 }
3015};
3016
3017class PostCodesClear : public Node
3018{
3019 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003020 PostCodesClear(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003021 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3022 "LogService.ClearLog/")
3023 {
3024 entityPrivileges = {
3025 {boost::beast::http::verb::get, {{"Login"}}},
3026 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303027 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3028 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3029 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3030 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003031 }
3032
3033 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003034 void doPost(crow::Response& res, const crow::Request&,
3035 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003036 {
3037 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3038
3039 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3040 // Make call to post-code service to request clear all
3041 crow::connections::systemBus->async_method_call(
3042 [asyncResp](const boost::system::error_code ec) {
3043 if (ec)
3044 {
3045 // TODO Handle for specific error code
3046 BMCWEB_LOG_ERROR
3047 << "doClearPostCodes resp_handler got error " << ec;
3048 asyncResp->res.result(
3049 boost::beast::http::status::internal_server_error);
3050 messages::internalError(asyncResp->res);
3051 return;
3052 }
3053 },
3054 "xyz.openbmc_project.State.Boot.PostCode",
3055 "/xyz/openbmc_project/State/Boot/PostCode",
3056 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3057 }
3058};
3059
3060static void fillPostCodeEntry(
3061 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003062 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003063 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3064 const uint64_t skip = 0, const uint64_t top = 0)
3065{
3066 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003067 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003068 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003069
3070 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003071 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003072
3073 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003074 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003075 {
3076 currentCodeIndex++;
3077 std::string postcodeEntryID =
3078 "B" + std::to_string(bootIndex) + "-" +
3079 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3080
3081 uint64_t usecSinceEpoch = code.first;
3082 uint64_t usTimeOffset = 0;
3083
3084 if (1 == currentCodeIndex)
3085 { // already incremented
3086 firstCodeTimeUs = code.first;
3087 }
3088 else
3089 {
3090 usTimeOffset = code.first - firstCodeTimeUs;
3091 }
3092
3093 // skip if no specific codeIndex is specified and currentCodeIndex does
3094 // not fall between top and skip
3095 if ((codeIndex == 0) &&
3096 (currentCodeIndex <= skip || currentCodeIndex > top))
3097 {
3098 continue;
3099 }
3100
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003101 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003102 // currentIndex
3103 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3104 {
3105 // This is done for simplicity. 1st entry is needed to calculate
3106 // time offset. To improve efficiency, one can get to the entry
3107 // directly (possibly with flatmap's nth method)
3108 continue;
3109 }
3110
3111 // currentCodeIndex is within top and skip or equal to specified code
3112 // index
3113
3114 // Get the Created time from the timestamp
3115 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003116 entryTimeStr = crow::utility::getDateTime(
3117 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003118
3119 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3120 std::ostringstream hexCode;
3121 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3122 << code.second;
3123 std::ostringstream timeOffsetStr;
3124 // Set Fixed -Point Notation
3125 timeOffsetStr << std::fixed;
3126 // Set precision to 4 digits
3127 timeOffsetStr << std::setprecision(4);
3128 // Add double to stream
3129 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3130 std::vector<std::string> messageArgs = {
3131 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3132
3133 // Get MessageArgs template from message registry
3134 std::string msg;
3135 if (message != nullptr)
3136 {
3137 msg = message->message;
3138
3139 // fill in this post code value
3140 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003141 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003142 {
3143 std::string argStr = "%" + std::to_string(++i);
3144 size_t argPos = msg.find(argStr);
3145 if (argPos != std::string::npos)
3146 {
3147 msg.replace(argPos, argStr.length(), messageArg);
3148 }
3149 }
3150 }
3151
Tim Leed4342a92020-04-27 11:47:58 +08003152 // Get Severity template from message registry
3153 std::string severity;
3154 if (message != nullptr)
3155 {
3156 severity = message->severity;
3157 }
3158
ZhikuiRena3316fc2020-01-29 14:58:08 -08003159 // add to AsyncResp
3160 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003161 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003162 bmcLogEntry = {
3163 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3164 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
3165 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3166 "PostCodes/Entries/" +
3167 postcodeEntryID},
3168 {"Name", "POST Code Log Entry"},
3169 {"Id", postcodeEntryID},
3170 {"Message", std::move(msg)},
3171 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3172 {"MessageArgs", std::move(messageArgs)},
3173 {"EntryType", "Event"},
3174 {"Severity", std::move(severity)},
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003175 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003176 }
3177}
3178
3179static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
3180 const uint16_t bootIndex,
3181 const uint64_t codeIndex)
3182{
3183 crow::connections::systemBus->async_method_call(
3184 [aResp, bootIndex, codeIndex](
3185 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003186 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003187 if (ec)
3188 {
3189 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3190 messages::internalError(aResp->res);
3191 return;
3192 }
3193
3194 // skip the empty postcode boots
3195 if (postcode.empty())
3196 {
3197 return;
3198 }
3199
3200 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3201
3202 aResp->res.jsonValue["Members@odata.count"] =
3203 aResp->res.jsonValue["Members"].size();
3204 },
3205 "xyz.openbmc_project.State.Boot.PostCode",
3206 "/xyz/openbmc_project/State/Boot/PostCode",
3207 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3208 bootIndex);
3209}
3210
3211static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
3212 const uint16_t bootIndex,
3213 const uint16_t bootCount,
3214 const uint64_t entryCount, const uint64_t skip,
3215 const uint64_t top)
3216{
3217 crow::connections::systemBus->async_method_call(
3218 [aResp, bootIndex, bootCount, entryCount, skip,
3219 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003220 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003221 if (ec)
3222 {
3223 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3224 messages::internalError(aResp->res);
3225 return;
3226 }
3227
3228 uint64_t endCount = entryCount;
3229 if (!postcode.empty())
3230 {
3231 endCount = entryCount + postcode.size();
3232
3233 if ((skip < endCount) && ((top + skip) > entryCount))
3234 {
3235 uint64_t thisBootSkip =
3236 std::max(skip, entryCount) - entryCount;
3237 uint64_t thisBootTop =
3238 std::min(top + skip, endCount) - entryCount;
3239
3240 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3241 thisBootSkip, thisBootTop);
3242 }
3243 aResp->res.jsonValue["Members@odata.count"] = endCount;
3244 }
3245
3246 // continue to previous bootIndex
3247 if (bootIndex < bootCount)
3248 {
3249 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3250 bootCount, endCount, skip, top);
3251 }
3252 else
3253 {
3254 aResp->res.jsonValue["Members@odata.nextLink"] =
3255 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3256 "Entries?$skip=" +
3257 std::to_string(skip + top);
3258 }
3259 },
3260 "xyz.openbmc_project.State.Boot.PostCode",
3261 "/xyz/openbmc_project/State/Boot/PostCode",
3262 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3263 bootIndex);
3264}
3265
3266static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
3267 const uint64_t skip, const uint64_t top)
3268{
3269 uint64_t entryCount = 0;
3270 crow::connections::systemBus->async_method_call(
3271 [aResp, entryCount, skip,
3272 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003273 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003274 if (ec)
3275 {
3276 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3277 messages::internalError(aResp->res);
3278 return;
3279 }
3280 auto pVal = std::get_if<uint16_t>(&bootCount);
3281 if (pVal)
3282 {
3283 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3284 }
3285 else
3286 {
3287 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3288 }
3289 },
3290 "xyz.openbmc_project.State.Boot.PostCode",
3291 "/xyz/openbmc_project/State/Boot/PostCode",
3292 "org.freedesktop.DBus.Properties", "Get",
3293 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3294}
3295
3296class PostCodesEntryCollection : public Node
3297{
3298 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003299 PostCodesEntryCollection(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003300 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3301 {
3302 entityPrivileges = {
3303 {boost::beast::http::verb::get, {{"Login"}}},
3304 {boost::beast::http::verb::head, {{"Login"}}},
3305 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3306 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3307 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3308 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3309 }
3310
3311 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003312 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00003313 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003314 {
3315 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3316
3317 asyncResp->res.jsonValue["@odata.type"] =
3318 "#LogEntryCollection.LogEntryCollection";
3319 asyncResp->res.jsonValue["@odata.context"] =
3320 "/redfish/v1/"
3321 "$metadata#LogEntryCollection.LogEntryCollection";
3322 asyncResp->res.jsonValue["@odata.id"] =
3323 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3324 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3325 asyncResp->res.jsonValue["Description"] =
3326 "Collection of POST Code Log Entries";
3327 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3328 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3329
3330 uint64_t skip = 0;
3331 uint64_t top = maxEntriesPerPage; // Show max entries by default
3332 if (!getSkipParam(asyncResp->res, req, skip))
3333 {
3334 return;
3335 }
3336 if (!getTopParam(asyncResp->res, req, top))
3337 {
3338 return;
3339 }
3340 getCurrentBootNumber(asyncResp, skip, top);
3341 }
3342};
3343
3344class PostCodesEntry : public Node
3345{
3346 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003347 PostCodesEntry(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003348 Node(app,
3349 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3350 std::string())
3351 {
3352 entityPrivileges = {
3353 {boost::beast::http::verb::get, {{"Login"}}},
3354 {boost::beast::http::verb::head, {{"Login"}}},
3355 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3356 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3357 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3358 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3359 }
3360
3361 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003362 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003363 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003364 {
3365 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3366 if (params.size() != 1)
3367 {
3368 messages::internalError(asyncResp->res);
3369 return;
3370 }
3371
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003372 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003373
3374 size_t bootPos = targetID.find('B');
3375 if (bootPos == std::string::npos)
3376 {
3377 // Requested ID was not found
3378 messages::resourceMissingAtURI(asyncResp->res, targetID);
3379 return;
3380 }
3381 std::string_view bootIndexStr(targetID);
3382 bootIndexStr.remove_prefix(bootPos + 1);
3383 uint16_t bootIndex = 0;
3384 uint64_t codeIndex = 0;
3385 size_t dashPos = bootIndexStr.find('-');
3386
3387 if (dashPos == std::string::npos)
3388 {
3389 return;
3390 }
3391 std::string_view codeIndexStr(bootIndexStr);
3392 bootIndexStr.remove_suffix(dashPos);
3393 codeIndexStr.remove_prefix(dashPos + 1);
3394
3395 bootIndex = static_cast<uint16_t>(
Ed Tanous23a21a12020-07-25 04:45:05 +00003396 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3397 codeIndex = strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003398 if (bootIndex == 0 || codeIndex == 0)
3399 {
3400 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3401 << params[0];
3402 }
3403
3404 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3405 asyncResp->res.jsonValue["@odata.context"] =
3406 "/redfish/v1/$metadata#LogEntry.LogEntry";
3407 asyncResp->res.jsonValue["@odata.id"] =
3408 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3409 "Entries";
3410 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3411 asyncResp->res.jsonValue["Description"] =
3412 "Collection of POST Code Log Entries";
3413 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3414 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3415
3416 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3417 }
3418};
3419
Ed Tanous1da66f72018-07-27 16:13:37 -07003420} // namespace redfish