blob: ce5b2c8c5a0e6a6cf7aeba7d4695115c4885f4ae [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* crashdumpObject = "com.intel.crashdump";
40constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* crashdumpInterface = "com.intel.crashdump";
42constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070043 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070048constexpr char const* crashdumpTelemetryInterface =
49 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070050
Jason M. Bills4851d452019-03-28 11:27:48 -070051namespace message_registries
52{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053static const Message* getMessageFromRegistry(
54 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070055 const boost::beast::span<const MessageEntry> registry)
56{
57 boost::beast::span<const MessageEntry>::const_iterator messageIt =
58 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050059 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070060 return !std::strcmp(messageEntry.first,
61 messageKey.c_str());
62 });
63 if (messageIt != registry.cend())
64 {
65 return &messageIt->second;
66 }
67
68 return nullptr;
69}
70
Gunnar Mills1214b7e2020-06-04 10:11:30 -050071static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070072{
73 // Redfish MessageIds are in the form
74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
75 // the right Message
76 std::vector<std::string> fields;
77 fields.reserve(4);
78 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 std::string& registryName = fields[0];
80 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070081
82 // Find the right registry and check it for the MessageKey
83 if (std::string(base::header.registryPrefix) == registryName)
84 {
85 return getMessageFromRegistry(
86 messageKey, boost::beast::span<const MessageEntry>(base::registry));
87 }
88 if (std::string(openbmc::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey,
92 boost::beast::span<const MessageEntry>(openbmc::registry));
93 }
94 return nullptr;
95}
96} // namespace message_registries
97
James Feistf6150402019-01-08 10:36:20 -080098namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070099
Andrew Geisslercb92c032018-08-17 07:56:14 -0700100using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500101 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
102 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700103
104using GetManagedObjectsType = boost::container::flat_map<
105 sdbusplus::message::object_path,
106 boost::container::flat_map<std::string, GetManagedPropertyType>>;
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
Ed Tanousd4d25792020-09-29 15:15:03 -0700110 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114 {
115 return "Critical";
116 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700117 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700120 {
121 return "OK";
122 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700123 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700124 {
125 return "Warning";
126 }
127 return "";
128}
129
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500130static int getJournalMetadata(sd_journal* journal,
131 const std::string_view& field,
132 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700133{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700135 size_t length = 0;
136 int ret = 0;
137 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700138 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 if (ret < 0)
141 {
142 return ret;
143 }
Ed Tanous39e77502019-03-04 17:35:53 -0800144 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 // Only use the content after the "=" character.
146 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
147 return ret;
148}
149
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150static int getJournalMetadata(sd_journal* journal,
151 const std::string_view& field, const int& base,
152 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700153{
154 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800155 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 // Get the metadata from the requested field of the journal entry
157 ret = getJournalMetadata(journal, field, metadata);
158 if (ret < 0)
159 {
160 return ret;
161 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000162 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700163 return ret;
164}
165
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800167{
168 int ret = 0;
169 uint64_t timestamp = 0;
170 ret = sd_journal_get_realtime_usec(journal, &timestamp);
171 if (ret < 0)
172 {
173 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
174 << strerror(-ret);
175 return false;
176 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500177 entryTimestamp = crow::utility::getDateTime(
178 static_cast<std::time_t>(timestamp / 1000 / 1000));
179 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800180}
181
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182static bool getSkipParam(crow::Response& res, const crow::Request& req,
183 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700184{
James Feist5a7e8772020-07-22 09:08:38 -0700185 boost::urls::url_view::params_type::iterator it =
186 req.urlParams.find("$skip");
187 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700188 {
James Feist5a7e8772020-07-22 09:08:38 -0700189 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500190 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700191 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
192 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
194
195 messages::queryParameterValueTypeError(res, std::string(skipParam),
196 "$skip");
197 return false;
198 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 }
200 return true;
201}
202
Ed Tanous271584a2019-07-09 16:24:22 -0700203static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204static bool getTopParam(crow::Response& res, const crow::Request& req,
205 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
James Feist5a7e8772020-07-22 09:08:38 -0700207 boost::urls::url_view::params_type::iterator it =
208 req.urlParams.find("$top");
209 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700210 {
James Feist5a7e8772020-07-22 09:08:38 -0700211 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700213 top = std::strtoul(topParam.c_str(), &ptr, 10);
214 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 {
216 messages::queryParameterValueTypeError(res, std::string(topParam),
217 "$top");
218 return false;
219 }
Ed Tanous271584a2019-07-09 16:24:22 -0700220 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 {
222
223 messages::queryParameterOutOfRange(
224 res, std::to_string(top), "$top",
225 "1-" + std::to_string(maxEntriesPerPage));
226 return false;
227 }
228 }
229 return true;
230}
231
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700233 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700234{
235 int ret = 0;
236 static uint64_t prevTs = 0;
237 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700238 if (firstEntry)
239 {
240 prevTs = 0;
241 }
242
Jason M. Bills16428a12018-11-02 12:42:29 -0700243 // Get the entry timestamp
244 uint64_t curTs = 0;
245 ret = sd_journal_get_realtime_usec(journal, &curTs);
246 if (ret < 0)
247 {
248 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
249 << strerror(-ret);
250 return false;
251 }
252 // If the timestamp isn't unique, increment the index
253 if (curTs == prevTs)
254 {
255 index++;
256 }
257 else
258 {
259 // Otherwise, reset it
260 index = 0;
261 }
262 // Save the timestamp
263 prevTs = curTs;
264
265 entryID = std::to_string(curTs);
266 if (index > 0)
267 {
268 entryID += "_" + std::to_string(index);
269 }
270 return true;
271}
272
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500273static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700274 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700275{
Ed Tanous271584a2019-07-09 16:24:22 -0700276 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700277 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700278 if (firstEntry)
279 {
280 prevTs = 0;
281 }
282
Jason M. Bills95820182019-04-22 16:25:34 -0700283 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700284 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700285 std::tm timeStruct = {};
286 std::istringstream entryStream(logEntry);
287 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
288 {
289 curTs = std::mktime(&timeStruct);
290 }
291 // If the timestamp isn't unique, increment the index
292 if (curTs == prevTs)
293 {
294 index++;
295 }
296 else
297 {
298 // Otherwise, reset it
299 index = 0;
300 }
301 // Save the timestamp
302 prevTs = curTs;
303
304 entryID = std::to_string(curTs);
305 if (index > 0)
306 {
307 entryID += "_" + std::to_string(index);
308 }
309 return true;
310}
311
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500312static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
313 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700314{
315 if (entryID.empty())
316 {
317 return false;
318 }
319 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800320 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700321
322 auto underscorePos = tsStr.find("_");
323 if (underscorePos != tsStr.npos)
324 {
325 // Timestamp has an index
326 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800327 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700328 indexStr.remove_prefix(underscorePos + 1);
329 std::size_t pos;
330 try
331 {
Ed Tanous39e77502019-03-04 17:35:53 -0800332 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700333 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500334 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700335 {
336 messages::resourceMissingAtURI(res, entryID);
337 return false;
338 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 {
341 messages::resourceMissingAtURI(res, entryID);
342 return false;
343 }
344 if (pos != indexStr.size())
345 {
346 messages::resourceMissingAtURI(res, entryID);
347 return false;
348 }
349 }
350 // Timestamp has no index
351 std::size_t pos;
352 try
353 {
Ed Tanous39e77502019-03-04 17:35:53 -0800354 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700355 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700357 {
358 messages::resourceMissingAtURI(res, entryID);
359 return false;
360 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500361 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700362 {
363 messages::resourceMissingAtURI(res, entryID);
364 return false;
365 }
366 if (pos != tsStr.size())
367 {
368 messages::resourceMissingAtURI(res, entryID);
369 return false;
370 }
371 return true;
372}
373
Jason M. Bills95820182019-04-22 16:25:34 -0700374static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500375 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700376{
377 static const std::filesystem::path redfishLogDir = "/var/log";
378 static const std::string redfishLogFilename = "redfish";
379
380 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500381 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700382 std::filesystem::directory_iterator(redfishLogDir))
383 {
384 // If we find a redfish log file, save the path
385 std::string filename = dirEnt.path().filename();
386 if (boost::starts_with(filename, redfishLogFilename))
387 {
388 redfishLogFiles.emplace_back(redfishLogDir / filename);
389 }
390 }
391 // As the log files rotate, they are appended with a ".#" that is higher for
392 // the older logs. Since we don't expect more than 10 log files, we
393 // can just sort the list to get them in order from newest to oldest
394 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
395
396 return !redfishLogFiles.empty();
397}
398
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500399inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
400 const std::string& dumpType)
401{
402 std::string dumpPath;
403 if (dumpType == "BMC")
404 {
405 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
406 }
407 else if (dumpType == "System")
408 {
409 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
410 }
411 else
412 {
413 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
414 messages::internalError(asyncResp->res);
415 return;
416 }
417
418 crow::connections::systemBus->async_method_call(
419 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
420 GetManagedObjectsType& resp) {
421 if (ec)
422 {
423 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
424 messages::internalError(asyncResp->res);
425 return;
426 }
427
428 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
429 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500430 std::string dumpEntryPath =
431 "/xyz/openbmc_project/dump/" +
432 std::string(boost::algorithm::to_lower_copy(dumpType)) +
433 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434
435 for (auto& object : resp)
436 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500437 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500438 {
439 continue;
440 }
441 std::time_t timestamp;
442 uint64_t size = 0;
443 entriesArray.push_back({});
444 nlohmann::json& thisEntry = entriesArray.back();
445 const std::string& path =
446 static_cast<const std::string&>(object.first);
Ed Tanousf23b7292020-10-15 09:41:17 -0700447 std::size_t lastPos = path.rfind('/');
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500448 if (lastPos == std::string::npos)
449 {
450 continue;
451 }
452 std::string entryID = path.substr(lastPos + 1);
453
454 for (auto& interfaceMap : object.second)
455 {
456 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
457 {
458
459 for (auto& propertyMap : interfaceMap.second)
460 {
461 if (propertyMap.first == "Size")
462 {
463 auto sizePtr =
464 std::get_if<uint64_t>(&propertyMap.second);
465 if (sizePtr == nullptr)
466 {
467 messages::internalError(asyncResp->res);
468 break;
469 }
470 size = *sizePtr;
471 break;
472 }
473 }
474 }
475 else if (interfaceMap.first ==
476 "xyz.openbmc_project.Time.EpochTime")
477 {
478
479 for (auto& propertyMap : interfaceMap.second)
480 {
481 if (propertyMap.first == "Elapsed")
482 {
483 const uint64_t* usecsTimeStamp =
484 std::get_if<uint64_t>(&propertyMap.second);
485 if (usecsTimeStamp == nullptr)
486 {
487 messages::internalError(asyncResp->res);
488 break;
489 }
490 timestamp =
491 static_cast<std::time_t>(*usecsTimeStamp);
492 break;
493 }
494 }
495 }
496 }
497
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500498 thisEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500499 thisEntry["@odata.id"] = dumpPath + entryID;
500 thisEntry["Id"] = entryID;
501 thisEntry["EntryType"] = "Event";
502 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
503 thisEntry["Name"] = dumpType + " Dump Entry";
504
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500505 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500506
507 if (dumpType == "BMC")
508 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500509 thisEntry["DiagnosticDataType"] = "Manager";
510 thisEntry["AdditionalDataURI"] =
511 "/redfish/v1/Managers/bmc/LogServices/Dump/"
512 "attachment/" +
513 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500514 }
515 else if (dumpType == "System")
516 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500517 thisEntry["DiagnosticDataType"] = "OEM";
518 thisEntry["OEMDiagnosticDataType"] = "System";
519 thisEntry["AdditionalDataURI"] =
520 "/redfish/v1/Systems/system/LogServices/Dump/"
521 "attachment/" +
522 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 }
524 }
525 asyncResp->res.jsonValue["Members@odata.count"] =
526 entriesArray.size();
527 },
528 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
529 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
530}
531
532inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
533 const std::string& entryID,
534 const std::string& dumpType)
535{
536 std::string dumpPath;
537 if (dumpType == "BMC")
538 {
539 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
540 }
541 else if (dumpType == "System")
542 {
543 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
544 }
545 else
546 {
547 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
548 messages::internalError(asyncResp->res);
549 return;
550 }
551
552 crow::connections::systemBus->async_method_call(
553 [asyncResp, entryID, dumpPath, dumpType](
554 const boost::system::error_code ec, GetManagedObjectsType& resp) {
555 if (ec)
556 {
557 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
558 messages::internalError(asyncResp->res);
559 return;
560 }
561
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500562 bool foundDumpEntry = false;
563 std::string dumpEntryPath =
564 "/xyz/openbmc_project/dump/" +
565 std::string(boost::algorithm::to_lower_copy(dumpType)) +
566 "/entry/";
567
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500568 for (auto& objectPath : resp)
569 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500570 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500571 {
572 continue;
573 }
574
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500575 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 std::time_t timestamp;
577 uint64_t size = 0;
578
579 for (auto& interfaceMap : objectPath.second)
580 {
581 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
582 {
583 for (auto& propertyMap : interfaceMap.second)
584 {
585 if (propertyMap.first == "Size")
586 {
587 auto sizePtr =
588 std::get_if<uint64_t>(&propertyMap.second);
589 if (sizePtr == nullptr)
590 {
591 messages::internalError(asyncResp->res);
592 break;
593 }
594 size = *sizePtr;
595 break;
596 }
597 }
598 }
599 else if (interfaceMap.first ==
600 "xyz.openbmc_project.Time.EpochTime")
601 {
602 for (auto& propertyMap : interfaceMap.second)
603 {
604 if (propertyMap.first == "Elapsed")
605 {
606 const uint64_t* usecsTimeStamp =
607 std::get_if<uint64_t>(&propertyMap.second);
608 if (usecsTimeStamp == nullptr)
609 {
610 messages::internalError(asyncResp->res);
611 break;
612 }
613 timestamp =
614 static_cast<std::time_t>(*usecsTimeStamp);
615 break;
616 }
617 }
618 }
619 }
620
621 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500622 "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500623 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
624 asyncResp->res.jsonValue["Id"] = entryID;
625 asyncResp->res.jsonValue["EntryType"] = "Event";
626 asyncResp->res.jsonValue["Created"] =
627 crow::utility::getDateTime(timestamp);
628 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
629
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500630 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500631
632 if (dumpType == "BMC")
633 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500634 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
635 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500636 "/redfish/v1/Managers/bmc/LogServices/Dump/"
637 "attachment/" +
638 entryID;
639 }
640 else if (dumpType == "System")
641 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500642 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
643 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500644 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500645 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500646 "/redfish/v1/Systems/system/LogServices/Dump/"
647 "attachment/" +
648 entryID;
649 }
650 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500651 if (foundDumpEntry == false)
652 {
653 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
654 messages::internalError(asyncResp->res);
655 return;
656 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500657 },
658 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
659 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
660}
661
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500662inline void deleteDumpEntry(crow::Response& res, const std::string& entryID,
663 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500664{
665 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
666
667 auto respHandler = [asyncResp](const boost::system::error_code ec) {
668 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
669 if (ec)
670 {
671 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
672 << ec;
673 messages::internalError(asyncResp->res);
674 return;
675 }
676 };
677 crow::connections::systemBus->async_method_call(
678 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500679 "/xyz/openbmc_project/dump/" +
680 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
681 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500682 "xyz.openbmc_project.Object.Delete", "Delete");
683}
684
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500685inline void createDumpTaskCallback(const crow::Request& req,
Ed Tanousb5a76932020-09-29 16:16:58 -0700686 const std::shared_ptr<AsyncResp>& asyncResp,
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500687 const uint32_t& dumpId,
688 const std::string& dumpPath,
689 const std::string& dumpType)
690{
691 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500692 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500693 boost::system::error_code err, sdbusplus::message::message& m,
694 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000695 if (err)
696 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500697 BMCWEB_LOG_ERROR << "Error in creating a dump";
698 taskData->state = "Cancelled";
699 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000700 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500701 std::vector<std::pair<
702 std::string,
703 std::vector<std::pair<std::string, std::variant<std::string>>>>>
704 interfacesList;
705
706 sdbusplus::message::object_path objPath;
707
708 m.read(objPath, interfacesList);
709
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500710 if (objPath.str ==
711 "/xyz/openbmc_project/dump/" +
712 std::string(boost::algorithm::to_lower_copy(dumpType)) +
713 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500714 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500715 nlohmann::json retMessage = messages::success();
716 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500717
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500718 std::string headerLoc =
719 "Location: " + dumpPath + std::to_string(dumpId);
720 taskData->payload->httpHeaders.emplace_back(
721 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500723 taskData->state = "Completed";
724 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500726 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500727 },
728 "type='signal',interface='org.freedesktop.DBus."
729 "ObjectManager',"
730 "member='InterfacesAdded', "
731 "path='/xyz/openbmc_project/dump'");
732
733 task->startTimer(std::chrono::minutes(3));
734 task->populateResp(asyncResp->res);
735 task->payload.emplace(req);
736}
737
738inline void createDump(crow::Response& res, const crow::Request& req,
739 const std::string& dumpType)
740{
741 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
742
743 std::string dumpPath;
744 if (dumpType == "BMC")
745 {
746 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
747 }
748 else if (dumpType == "System")
749 {
750 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
751 }
752 else
753 {
754 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
755 messages::internalError(asyncResp->res);
756 return;
757 }
758
759 std::optional<std::string> diagnosticDataType;
760 std::optional<std::string> oemDiagnosticDataType;
761
762 if (!redfish::json_util::readJson(
763 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
764 "OEMDiagnosticDataType", oemDiagnosticDataType))
765 {
766 return;
767 }
768
769 if (dumpType == "System")
770 {
771 if (!oemDiagnosticDataType || !diagnosticDataType)
772 {
773 BMCWEB_LOG_ERROR << "CreateDump action parameter "
774 "'DiagnosticDataType'/"
775 "'OEMDiagnosticDataType' value not found!";
776 messages::actionParameterMissing(
777 asyncResp->res, "CollectDiagnosticData",
778 "DiagnosticDataType & OEMDiagnosticDataType");
779 return;
780 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700781 if ((*oemDiagnosticDataType != "System") ||
782 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500783 {
784 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
785 messages::invalidObject(asyncResp->res,
786 "System Dump creation parameters");
787 return;
788 }
789 }
790 else if (dumpType == "BMC")
791 {
792 if (!diagnosticDataType)
793 {
794 BMCWEB_LOG_ERROR << "CreateDump action parameter "
795 "'DiagnosticDataType' not found!";
796 messages::actionParameterMissing(
797 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
798 return;
799 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700800 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500801 {
802 BMCWEB_LOG_ERROR
803 << "Wrong parameter value passed for 'DiagnosticDataType'";
804 messages::invalidObject(asyncResp->res,
805 "BMC Dump creation parameters");
806 return;
807 }
808 }
809
810 crow::connections::systemBus->async_method_call(
811 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
812 const uint32_t& dumpId) {
813 if (ec)
814 {
815 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
816 messages::internalError(asyncResp->res);
817 return;
818 }
819 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
820
821 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
822 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500823 "xyz.openbmc_project.Dump.Manager",
824 "/xyz/openbmc_project/dump/" +
825 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500826 "xyz.openbmc_project.Dump.Create", "CreateDump");
827}
828
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500829inline void clearDump(crow::Response& res, const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500830{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500831 std::string dumpTypeLowerCopy =
832 std::string(boost::algorithm::to_lower_copy(dumpType));
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500833 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
834 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500835 [asyncResp, dumpType](const boost::system::error_code ec,
836 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500837 if (ec)
838 {
839 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
840 messages::internalError(asyncResp->res);
841 return;
842 }
843
844 for (const std::string& path : subTreePaths)
845 {
Ed Tanousf23b7292020-10-15 09:41:17 -0700846 std::size_t pos = path.rfind('/');
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500847 if (pos != std::string::npos)
848 {
849 std::string logID = path.substr(pos + 1);
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500850 deleteDumpEntry(asyncResp->res, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500851 }
852 }
853 },
854 "xyz.openbmc_project.ObjectMapper",
855 "/xyz/openbmc_project/object_mapper",
856 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500857 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
858 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
859 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500860}
861
Ed Tanous2c70f802020-09-28 14:29:23 -0700862static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500863 const std::vector<std::pair<std::string, VariantType>>& params,
864 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700865{
866 for (auto property : params)
867 {
868 if (property.first == "Timestamp")
869 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500870 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500871 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700872 if (value != nullptr)
873 {
874 timestamp = *value;
875 }
876 }
877 else if (property.first == "Filename")
878 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500879 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500880 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700881 if (value != nullptr)
882 {
883 filename = *value;
884 }
885 }
886 else if (property.first == "Log")
887 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500888 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500889 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700890 if (value != nullptr)
891 {
892 logfile = *value;
893 }
894 }
895 }
896}
897
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500898constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800899class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700900{
901 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700902 SystemLogServiceCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800903 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800904 {
905 entityPrivileges = {
906 {boost::beast::http::verb::get, {{"Login"}}},
907 {boost::beast::http::verb::head, {{"Login"}}},
908 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
909 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
910 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
911 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
912 }
913
914 private:
915 /**
916 * Functions triggers appropriate requests on DBus
917 */
Ed Tanouscb13a392020-07-25 19:02:03 +0000918 void doGet(crow::Response& res, const crow::Request&,
919 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800920 {
921 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800922 // Collections don't include the static data added by SubRoute because
923 // it has a duplicate entry for members
924 asyncResp->res.jsonValue["@odata.type"] =
925 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800926 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800927 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800928 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
929 asyncResp->res.jsonValue["Description"] =
930 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500931 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800932 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800933 logServiceArray.push_back(
934 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500935#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600936 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500937 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600938#endif
939
Jason M. Billsd53dd412019-02-12 17:16:22 -0800940#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
941 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500942 {{"@odata.id",
943 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800944#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800945 asyncResp->res.jsonValue["Members@odata.count"] =
946 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800947
948 crow::connections::systemBus->async_method_call(
949 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500950 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800951 if (ec)
952 {
953 BMCWEB_LOG_ERROR << ec;
954 return;
955 }
956
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500957 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800958 {
959 if (pathStr.find("PostCode") != std::string::npos)
960 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000961 nlohmann::json& logServiceArrayLocal =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800962 asyncResp->res.jsonValue["Members"];
Ed Tanous23a21a12020-07-25 04:45:05 +0000963 logServiceArrayLocal.push_back(
ZhikuiRena3316fc2020-01-29 14:58:08 -0800964 {{"@odata.id", "/redfish/v1/Systems/system/"
965 "LogServices/PostCodes"}});
966 asyncResp->res.jsonValue["Members@odata.count"] =
Ed Tanous23a21a12020-07-25 04:45:05 +0000967 logServiceArrayLocal.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800968 return;
969 }
970 }
971 },
972 "xyz.openbmc_project.ObjectMapper",
973 "/xyz/openbmc_project/object_mapper",
974 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500975 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800976 }
977};
978
979class EventLogService : public Node
980{
981 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700982 EventLogService(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800983 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800984 {
985 entityPrivileges = {
986 {boost::beast::http::verb::get, {{"Login"}}},
987 {boost::beast::http::verb::head, {{"Login"}}},
988 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
989 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
990 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
991 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
992 }
993
994 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000995 void doGet(crow::Response& res, const crow::Request&,
996 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800997 {
998 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
999
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001000 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001001 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001002 asyncResp->res.jsonValue["@odata.type"] =
1003 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001004 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1005 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001006 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001007 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1008 asyncResp->res.jsonValue["Entries"] = {
1009 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001010 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001011 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1012
1013 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1014 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001015 }
1016};
1017
Tim Lee1f56a3a2019-10-09 10:17:57 +08001018class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001019{
1020 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001021 JournalEventLogClear(App& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001022 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1023 "LogService.ClearLog/")
1024 {
1025 entityPrivileges = {
1026 {boost::beast::http::verb::get, {{"Login"}}},
1027 {boost::beast::http::verb::head, {{"Login"}}},
1028 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1029 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1030 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1031 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1032 }
1033
1034 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001035 void doPost(crow::Response& res, const crow::Request&,
1036 const std::vector<std::string>&) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001037 {
1038 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1039
1040 // Clear the EventLog by deleting the log files
1041 std::vector<std::filesystem::path> redfishLogFiles;
1042 if (getRedfishLogFiles(redfishLogFiles))
1043 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001044 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001045 {
1046 std::error_code ec;
1047 std::filesystem::remove(file, ec);
1048 }
1049 }
1050
1051 // Reload rsyslog so it knows to start new log files
1052 crow::connections::systemBus->async_method_call(
1053 [asyncResp](const boost::system::error_code ec) {
1054 if (ec)
1055 {
1056 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1057 messages::internalError(asyncResp->res);
1058 return;
1059 }
1060
1061 messages::success(asyncResp->res);
1062 },
1063 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1064 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1065 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001066 }
1067};
1068
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001069static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001070 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001071 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001072{
Jason M. Bills95820182019-04-22 16:25:34 -07001073 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001074 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001075 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001076 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001077 {
1078 return 1;
1079 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001080 std::string timestamp = logEntry.substr(0, space);
1081 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001082 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001083 if (entryStart == std::string::npos)
1084 {
1085 return 1;
1086 }
1087 std::string_view entry(logEntry);
1088 entry.remove_prefix(entryStart);
1089 // Use split to separate the entry into its fields
1090 std::vector<std::string> logEntryFields;
1091 boost::split(logEntryFields, entry, boost::is_any_of(","),
1092 boost::token_compress_on);
1093 // We need at least a MessageId to be valid
1094 if (logEntryFields.size() < 1)
1095 {
1096 return 1;
1097 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001098 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001099
Jason M. Bills4851d452019-03-28 11:27:48 -07001100 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001101 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001102 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103
Jason M. Bills4851d452019-03-28 11:27:48 -07001104 std::string msg;
1105 std::string severity;
1106 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001107 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001108 msg = message->message;
1109 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001110 }
1111
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001112 // Get the MessageArgs from the log if there are any
1113 boost::beast::span<std::string> messageArgs;
1114 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001115 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001116 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001117 // If the first string is empty, assume there are no MessageArgs
1118 std::size_t messageArgsSize = 0;
1119 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001120 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001121 messageArgsSize = logEntryFields.size() - 1;
1122 }
1123
Ed Tanous23a21a12020-07-25 04:45:05 +00001124 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001125
1126 // Fill the MessageArgs into the Message
1127 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001128 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001129 {
1130 std::string argStr = "%" + std::to_string(++i);
1131 size_t argPos = msg.find(argStr);
1132 if (argPos != std::string::npos)
1133 {
1134 msg.replace(argPos, argStr.length(), messageArg);
1135 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001136 }
1137 }
1138
Jason M. Bills95820182019-04-22 16:25:34 -07001139 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1140 // format which matches the Redfish format except for the fractional seconds
1141 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001142 std::size_t dot = timestamp.find_first_of('.');
1143 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001144 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001145 {
Jason M. Bills95820182019-04-22 16:25:34 -07001146 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001147 }
1148
1149 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001150 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001151 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001152 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001153 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001154 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001155 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001156 {"Id", logEntryID},
1157 {"Message", std::move(msg)},
1158 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001159 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001160 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001161 {"Severity", std::move(severity)},
1162 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001163 return 0;
1164}
1165
Anthony Wilson27062602019-04-22 02:10:09 -05001166class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001167{
1168 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001169 JournalEventLogEntryCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001170 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001171 {
1172 entityPrivileges = {
1173 {boost::beast::http::verb::get, {{"Login"}}},
1174 {boost::beast::http::verb::head, {{"Login"}}},
1175 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1176 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1177 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1178 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1179 }
1180
1181 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001182 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001183 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001184 {
1185 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001186 uint64_t skip = 0;
1187 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001188 if (!getSkipParam(asyncResp->res, req, skip))
1189 {
1190 return;
1191 }
1192 if (!getTopParam(asyncResp->res, req, top))
1193 {
1194 return;
1195 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196 // Collections don't include the static data added by SubRoute because
1197 // it has a duplicate entry for members
1198 asyncResp->res.jsonValue["@odata.type"] =
1199 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001200 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001201 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001202 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1203 asyncResp->res.jsonValue["Description"] =
1204 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001205
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001206 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001207 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001208 // Go through the log files and create a unique ID for each entry
1209 std::vector<std::filesystem::path> redfishLogFiles;
1210 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001211 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001212 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001213
1214 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001215 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1216 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001217 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001218 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001219 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001220 {
1221 continue;
1222 }
1223
Jason M. Billse85d6b12019-07-29 17:01:15 -07001224 // Reset the unique ID on the first entry
1225 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001226 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001227 {
Jason M. Bills95820182019-04-22 16:25:34 -07001228 entryCount++;
1229 // Handle paging using skip (number of entries to skip from the
1230 // start) and top (number of entries to display)
1231 if (entryCount <= skip || entryCount > skip + top)
1232 {
1233 continue;
1234 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001235
Jason M. Bills95820182019-04-22 16:25:34 -07001236 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001237 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001238 {
1239 continue;
1240 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001241
Jason M. Billse85d6b12019-07-29 17:01:15 -07001242 if (firstEntry)
1243 {
1244 firstEntry = false;
1245 }
1246
Jason M. Bills95820182019-04-22 16:25:34 -07001247 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001248 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001249 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1250 {
1251 messages::internalError(asyncResp->res);
1252 return;
1253 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001254 }
1255 }
1256 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1257 if (skip + top < entryCount)
1258 {
1259 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001260 "/redfish/v1/Systems/system/LogServices/EventLog/"
1261 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001262 std::to_string(skip + top);
1263 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001264 }
1265};
1266
Jason M. Bills897967d2019-07-29 17:05:30 -07001267class JournalEventLogEntry : public Node
1268{
1269 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001270 JournalEventLogEntry(App& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001271 Node(app,
1272 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1273 std::string())
1274 {
1275 entityPrivileges = {
1276 {boost::beast::http::verb::get, {{"Login"}}},
1277 {boost::beast::http::verb::head, {{"Login"}}},
1278 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1279 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1280 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1281 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1282 }
1283
1284 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001285 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001286 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001287 {
1288 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1289 if (params.size() != 1)
1290 {
1291 messages::internalError(asyncResp->res);
1292 return;
1293 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001294 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001295
1296 // Go through the log files and check the unique ID for each entry to
1297 // find the target entry
1298 std::vector<std::filesystem::path> redfishLogFiles;
1299 getRedfishLogFiles(redfishLogFiles);
1300 std::string logEntry;
1301
1302 // Oldest logs are in the last file, so start there and loop backwards
1303 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1304 it++)
1305 {
1306 std::ifstream logStream(*it);
1307 if (!logStream.is_open())
1308 {
1309 continue;
1310 }
1311
1312 // Reset the unique ID on the first entry
1313 bool firstEntry = true;
1314 while (std::getline(logStream, logEntry))
1315 {
1316 std::string idStr;
1317 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1318 {
1319 continue;
1320 }
1321
1322 if (firstEntry)
1323 {
1324 firstEntry = false;
1325 }
1326
1327 if (idStr == targetID)
1328 {
1329 if (fillEventLogEntryJson(idStr, logEntry,
1330 asyncResp->res.jsonValue) != 0)
1331 {
1332 messages::internalError(asyncResp->res);
1333 return;
1334 }
1335 return;
1336 }
1337 }
1338 }
1339 // Requested ID was not found
1340 messages::resourceMissingAtURI(asyncResp->res, targetID);
1341 }
1342};
1343
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001344class DBusEventLogEntryCollection : public Node
1345{
1346 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001347 DBusEventLogEntryCollection(App& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001348 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1349 {
1350 entityPrivileges = {
1351 {boost::beast::http::verb::get, {{"Login"}}},
1352 {boost::beast::http::verb::head, {{"Login"}}},
1353 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1354 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1355 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1356 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1357 }
1358
1359 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001360 void doGet(crow::Response& res, const crow::Request&,
1361 const std::vector<std::string>&) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001362 {
1363 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1364
1365 // Collections don't include the static data added by SubRoute because
1366 // it has a duplicate entry for members
1367 asyncResp->res.jsonValue["@odata.type"] =
1368 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001369 asyncResp->res.jsonValue["@odata.id"] =
1370 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1371 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1372 asyncResp->res.jsonValue["Description"] =
1373 "Collection of System Event Log Entries";
1374
Andrew Geisslercb92c032018-08-17 07:56:14 -07001375 // DBus implementation of EventLog/Entries
1376 // Make call to Logging Service to find all log entry objects
1377 crow::connections::systemBus->async_method_call(
1378 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001379 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001380 if (ec)
1381 {
1382 // TODO Handle for specific error code
1383 BMCWEB_LOG_ERROR
1384 << "getLogEntriesIfaceData resp_handler got error "
1385 << ec;
1386 messages::internalError(asyncResp->res);
1387 return;
1388 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001389 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001390 asyncResp->res.jsonValue["Members"];
1391 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001392 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001393 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001394 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001395 {
1396 if (interfaceMap.first !=
1397 "xyz.openbmc_project.Logging.Entry")
1398 {
1399 BMCWEB_LOG_DEBUG << "Bailing early on "
1400 << interfaceMap.first;
1401 continue;
1402 }
1403 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001404 nlohmann::json& thisEntry = entriesArray.back();
1405 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001406 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001407 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001408 std::string* severity = nullptr;
1409 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001410
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001411 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001412 {
1413 if (propertyMap.first == "Id")
1414 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001415 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001416 if (id == nullptr)
1417 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001418 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001419 }
1420 }
1421 else if (propertyMap.first == "Timestamp")
1422 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001423 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001424 std::get_if<uint64_t>(&propertyMap.second);
1425 if (millisTimeStamp == nullptr)
1426 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001427 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001428 }
George Liuebd45902020-08-26 14:21:10 +08001429 else
1430 {
1431 timestamp = crow::utility::getTimestamp(
1432 *millisTimeStamp);
1433 }
George Liud139c232020-08-18 18:48:57 +08001434 }
1435 else if (propertyMap.first == "UpdateTimestamp")
1436 {
1437 const uint64_t* millisTimeStamp =
1438 std::get_if<uint64_t>(&propertyMap.second);
1439 if (millisTimeStamp == nullptr)
1440 {
1441 messages::internalError(asyncResp->res);
1442 }
George Liuebd45902020-08-26 14:21:10 +08001443 else
1444 {
1445 updateTimestamp =
1446 crow::utility::getTimestamp(
1447 *millisTimeStamp);
1448 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001449 }
1450 else if (propertyMap.first == "Severity")
1451 {
1452 severity = std::get_if<std::string>(
1453 &propertyMap.second);
1454 if (severity == nullptr)
1455 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001456 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001457 }
1458 }
1459 else if (propertyMap.first == "Message")
1460 {
1461 message = std::get_if<std::string>(
1462 &propertyMap.second);
1463 if (message == nullptr)
1464 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001465 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001466 }
1467 }
1468 }
1469 thisEntry = {
George Liud139c232020-08-18 18:48:57 +08001470 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001471 {"@odata.id",
1472 "/redfish/v1/Systems/system/LogServices/EventLog/"
1473 "Entries/" +
1474 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001475 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001476 {"Id", std::to_string(*id)},
1477 {"Message", *message},
1478 {"EntryType", "Event"},
1479 {"Severity",
1480 translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001481 {"Created", crow::utility::getDateTime(timestamp)},
1482 {"Modified",
1483 crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001484 }
1485 }
1486 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001487 [](const nlohmann::json& left,
1488 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001489 return (left["Id"] <= right["Id"]);
1490 });
1491 asyncResp->res.jsonValue["Members@odata.count"] =
1492 entriesArray.size();
1493 },
1494 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1495 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001496 }
1497};
1498
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001499class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001500{
1501 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001502 DBusEventLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001503 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001504 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1505 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001506 {
1507 entityPrivileges = {
1508 {boost::beast::http::verb::get, {{"Login"}}},
1509 {boost::beast::http::verb::head, {{"Login"}}},
1510 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1511 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1512 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1513 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1514 }
1515
1516 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001517 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001518 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001519 {
1520 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001521 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001522 {
1523 messages::internalError(asyncResp->res);
1524 return;
1525 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001526 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001527
Andrew Geisslercb92c032018-08-17 07:56:14 -07001528 // DBus implementation of EventLog/Entries
1529 // Make call to Logging Service to find all log entry objects
1530 crow::connections::systemBus->async_method_call(
1531 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001532 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001533 if (ec)
1534 {
1535 BMCWEB_LOG_ERROR
1536 << "EventLogEntry (DBus) resp_handler got error " << ec;
1537 messages::internalError(asyncResp->res);
1538 return;
1539 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001540 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001541 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001542 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001543 std::string* severity = nullptr;
1544 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001545
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001546 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001547 {
1548 if (propertyMap.first == "Id")
1549 {
1550 id = std::get_if<uint32_t>(&propertyMap.second);
1551 if (id == nullptr)
1552 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001553 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001554 }
1555 }
1556 else if (propertyMap.first == "Timestamp")
1557 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001558 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001559 std::get_if<uint64_t>(&propertyMap.second);
1560 if (millisTimeStamp == nullptr)
1561 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001562 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001563 }
George Liuebd45902020-08-26 14:21:10 +08001564 else
1565 {
1566 timestamp =
1567 crow::utility::getTimestamp(*millisTimeStamp);
1568 }
George Liud139c232020-08-18 18:48:57 +08001569 }
1570 else if (propertyMap.first == "UpdateTimestamp")
1571 {
1572 const uint64_t* millisTimeStamp =
1573 std::get_if<uint64_t>(&propertyMap.second);
1574 if (millisTimeStamp == nullptr)
1575 {
1576 messages::internalError(asyncResp->res);
1577 }
George Liuebd45902020-08-26 14:21:10 +08001578 else
1579 {
1580 updateTimestamp =
1581 crow::utility::getTimestamp(*millisTimeStamp);
1582 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001583 }
1584 else if (propertyMap.first == "Severity")
1585 {
1586 severity =
1587 std::get_if<std::string>(&propertyMap.second);
1588 if (severity == nullptr)
1589 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001590 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001591 }
1592 }
1593 else if (propertyMap.first == "Message")
1594 {
1595 message = std::get_if<std::string>(&propertyMap.second);
1596 if (message == nullptr)
1597 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001598 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001599 }
1600 }
1601 }
Ed Tanous271584a2019-07-09 16:24:22 -07001602 if (id == nullptr || message == nullptr || severity == nullptr)
1603 {
1604 return;
1605 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001606 asyncResp->res.jsonValue = {
George Liud139c232020-08-18 18:48:57 +08001607 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001608 {"@odata.id",
1609 "/redfish/v1/Systems/system/LogServices/EventLog/"
1610 "Entries/" +
1611 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001612 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001613 {"Id", std::to_string(*id)},
1614 {"Message", *message},
1615 {"EntryType", "Event"},
1616 {"Severity", translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001617 {"Created", crow::utility::getDateTime(timestamp)},
1618 {"Modified", crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001619 },
1620 "xyz.openbmc_project.Logging",
1621 "/xyz/openbmc_project/logging/entry/" + entryID,
1622 "org.freedesktop.DBus.Properties", "GetAll",
1623 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001624 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001625
Ed Tanouscb13a392020-07-25 19:02:03 +00001626 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001627 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001628 {
1629
1630 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1631
1632 auto asyncResp = std::make_shared<AsyncResp>(res);
1633
1634 if (params.size() != 1)
1635 {
1636 messages::internalError(asyncResp->res);
1637 return;
1638 }
1639 std::string entryID = params[0];
1640
1641 dbus::utility::escapePathForDbus(entryID);
1642
1643 // Process response from Logging service.
1644 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1645 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1646 if (ec)
1647 {
1648 // TODO Handle for specific error code
1649 BMCWEB_LOG_ERROR
1650 << "EventLogEntry (DBus) doDelete respHandler got error "
1651 << ec;
1652 asyncResp->res.result(
1653 boost::beast::http::status::internal_server_error);
1654 return;
1655 }
1656
1657 asyncResp->res.result(boost::beast::http::status::ok);
1658 };
1659
1660 // Make call to Logging service to request Delete Log
1661 crow::connections::systemBus->async_method_call(
1662 respHandler, "xyz.openbmc_project.Logging",
1663 "/xyz/openbmc_project/logging/entry/" + entryID,
1664 "xyz.openbmc_project.Object.Delete", "Delete");
1665 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001666};
1667
1668class BMCLogServiceCollection : public Node
1669{
1670 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001671 BMCLogServiceCollection(App& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001672 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001673 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001674 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001675 {boost::beast::http::verb::get, {{"Login"}}},
1676 {boost::beast::http::verb::head, {{"Login"}}},
1677 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1678 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1679 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1680 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001681 }
1682
1683 private:
1684 /**
1685 * Functions triggers appropriate requests on DBus
1686 */
Ed Tanouscb13a392020-07-25 19:02:03 +00001687 void doGet(crow::Response& res, const crow::Request&,
1688 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001689 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001690 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001691 // Collections don't include the static data added by SubRoute because
1692 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001693 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001694 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001695 asyncResp->res.jsonValue["@odata.id"] =
1696 "/redfish/v1/Managers/bmc/LogServices";
1697 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1698 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001699 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001700 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001701 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001702#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1703 logServiceArray.push_back(
1704 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1705#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001706#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1707 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001708 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001709#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001710 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001711 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001712 }
1713};
1714
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001715class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001716{
1717 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001718 BMCJournalLogService(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001719 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001720 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001721 entityPrivileges = {
1722 {boost::beast::http::verb::get, {{"Login"}}},
1723 {boost::beast::http::verb::head, {{"Login"}}},
1724 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1725 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1726 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1727 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1728 }
1729
1730 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001731 void doGet(crow::Response& res, const crow::Request&,
1732 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001733 {
1734 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001735 asyncResp->res.jsonValue["@odata.type"] =
1736 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001737 asyncResp->res.jsonValue["@odata.id"] =
1738 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001739 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1740 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1741 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001742 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001743 asyncResp->res.jsonValue["Entries"] = {
1744 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001745 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001746 }
1747};
1748
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001749static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1750 sd_journal* journal,
1751 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001752{
1753 // Get the Log Entry contents
1754 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001755
Ed Tanous39e77502019-03-04 17:35:53 -08001756 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001757 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001758 if (ret < 0)
1759 {
1760 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1761 return 1;
1762 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001763
1764 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001765 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001766 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001767 if (ret < 0)
1768 {
1769 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001770 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001771
1772 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001773 std::string entryTimeStr;
1774 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001775 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001776 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001777 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001778
1779 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001780 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001781 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001782 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1783 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001784 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001785 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001786 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001787 {"EntryType", "Oem"},
1788 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001789 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001790 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001791 {"Created", std::move(entryTimeStr)}};
1792 return 0;
1793}
1794
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001795class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001796{
1797 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001798 BMCJournalLogEntryCollection(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001799 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001800 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001801 entityPrivileges = {
1802 {boost::beast::http::verb::get, {{"Login"}}},
1803 {boost::beast::http::verb::head, {{"Login"}}},
1804 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1805 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1806 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1807 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1808 }
1809
1810 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001811 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001812 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001813 {
1814 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001815 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001816 uint64_t skip = 0;
1817 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001818 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001819 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001820 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001821 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001822 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001823 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001824 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001825 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001826 // Collections don't include the static data added by SubRoute because
1827 // it has a duplicate entry for members
1828 asyncResp->res.jsonValue["@odata.type"] =
1829 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001830 asyncResp->res.jsonValue["@odata.id"] =
1831 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001832 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001833 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001834 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1835 asyncResp->res.jsonValue["Description"] =
1836 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001837 asyncResp->res.jsonValue["@odata.id"] =
1838 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001840 logEntryArray = nlohmann::json::array();
1841
1842 // Go through the journal and use the timestamp to create a unique ID
1843 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001844 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001845 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1846 if (ret < 0)
1847 {
1848 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001849 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001850 return;
1851 }
1852 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1853 journalTmp, sd_journal_close);
1854 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001855 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001856 // Reset the unique ID on the first entry
1857 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001858 SD_JOURNAL_FOREACH(journal.get())
1859 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001860 entryCount++;
1861 // Handle paging using skip (number of entries to skip from the
1862 // start) and top (number of entries to display)
1863 if (entryCount <= skip || entryCount > skip + top)
1864 {
1865 continue;
1866 }
1867
Jason M. Bills16428a12018-11-02 12:42:29 -07001868 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001869 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001870 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001871 continue;
1872 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001873
Jason M. Billse85d6b12019-07-29 17:01:15 -07001874 if (firstEntry)
1875 {
1876 firstEntry = false;
1877 }
1878
Jason M. Billse1f26342018-07-18 12:12:00 -07001879 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001880 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001881 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1882 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001883 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001884 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001885 return;
1886 }
1887 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001888 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1889 if (skip + top < entryCount)
1890 {
1891 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001892 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001893 std::to_string(skip + top);
1894 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001895 }
1896};
1897
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001898class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001899{
1900 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001901 BMCJournalLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001902 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001903 std::string())
1904 {
1905 entityPrivileges = {
1906 {boost::beast::http::verb::get, {{"Login"}}},
1907 {boost::beast::http::verb::head, {{"Login"}}},
1908 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1909 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1910 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1911 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1912 }
1913
1914 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001915 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001916 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001917 {
1918 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1919 if (params.size() != 1)
1920 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001921 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001922 return;
1923 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001924 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001925 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001926 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001927 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001928 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001929 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001930 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001931 }
1932
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001933 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001934 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1935 if (ret < 0)
1936 {
1937 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001938 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001939 return;
1940 }
1941 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1942 journalTmp, sd_journal_close);
1943 journalTmp = nullptr;
1944 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001945 // tracking the unique ID
1946 std::string idStr;
1947 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001948 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301949 if (ret < 0)
1950 {
1951 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1952 << strerror(-ret);
1953 messages::internalError(asyncResp->res);
1954 return;
1955 }
Ed Tanous271584a2019-07-09 16:24:22 -07001956 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001957 {
1958 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001959 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1960 {
1961 messages::internalError(asyncResp->res);
1962 return;
1963 }
1964 if (firstEntry)
1965 {
1966 firstEntry = false;
1967 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001968 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001969 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001970 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001971 {
1972 messages::resourceMissingAtURI(asyncResp->res, entryID);
1973 return;
1974 }
1975
1976 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1977 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001978 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001979 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001980 return;
1981 }
1982 }
1983};
1984
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001985class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001986{
1987 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001988 BMCDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001989 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001990 {
1991 entityPrivileges = {
1992 {boost::beast::http::verb::get, {{"Login"}}},
1993 {boost::beast::http::verb::head, {{"Login"}}},
1994 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1995 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1996 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1997 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1998 }
1999
2000 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002001 void doGet(crow::Response& res, const crow::Request&,
2002 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002003 {
2004 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2005
2006 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002007 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002008 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002009 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002010 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2011 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2012 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002013 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002014 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002015 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2016 asyncResp->res.jsonValue["Actions"] = {
2017 {"#LogService.ClearLog",
2018 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2019 "Actions/LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002020 {"#LogService.CollectDiagnosticData",
2021 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2022 "Actions/LogService.CollectDiagnosticData"}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002023 }
2024};
2025
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002026class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002027{
2028 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002029 BMCDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002030 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002031 {
2032 entityPrivileges = {
2033 {boost::beast::http::verb::get, {{"Login"}}},
2034 {boost::beast::http::verb::head, {{"Login"}}},
2035 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2036 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2037 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2038 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2039 }
2040
2041 private:
2042 /**
2043 * Functions triggers appropriate requests on DBus
2044 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002045 void doGet(crow::Response& res, const crow::Request&,
2046 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002047 {
2048 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2049
2050 asyncResp->res.jsonValue["@odata.type"] =
2051 "#LogEntryCollection.LogEntryCollection";
2052 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002053 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2054 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002055 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002056 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002057
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002058 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002059 }
2060};
2061
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002062class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002063{
2064 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002065 BMCDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002066 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002067 std::string())
2068 {
2069 entityPrivileges = {
2070 {boost::beast::http::verb::get, {{"Login"}}},
2071 {boost::beast::http::verb::head, {{"Login"}}},
2072 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2073 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2074 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2075 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2076 }
2077
2078 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002079 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002080 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002081 {
2082 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2083 if (params.size() != 1)
2084 {
2085 messages::internalError(asyncResp->res);
2086 return;
2087 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002088 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002089 }
2090
Ed Tanouscb13a392020-07-25 19:02:03 +00002091 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002092 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002093 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002094 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002095 if (params.size() != 1)
2096 {
2097 messages::internalError(asyncResp->res);
2098 return;
2099 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002100 deleteDumpEntry(asyncResp->res, params[0], "bmc");
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002101 }
2102};
raviteja-bc9bb6862020-02-03 11:53:32 -06002103
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002104class BMCDumpCreate : public Node
2105{
2106 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002107 BMCDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002108 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002109 "Actions/"
2110 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002111 {
2112 entityPrivileges = {
2113 {boost::beast::http::verb::get, {{"Login"}}},
2114 {boost::beast::http::verb::head, {{"Login"}}},
2115 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2116 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2117 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2118 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2119 }
2120
2121 private:
2122 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002123 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002124 {
2125 createDump(res, req, "BMC");
2126 }
2127};
2128
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002129class BMCDumpClear : public Node
2130{
2131 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002132 BMCDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002133 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2134 "Actions/"
2135 "LogService.ClearLog/")
2136 {
2137 entityPrivileges = {
2138 {boost::beast::http::verb::get, {{"Login"}}},
2139 {boost::beast::http::verb::head, {{"Login"}}},
2140 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2141 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2142 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2143 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2144 }
2145
2146 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002147 void doPost(crow::Response& res, const crow::Request&,
2148 const std::vector<std::string>&) override
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002149 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002150 clearDump(res, "BMC");
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002151 }
2152};
2153
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002154class SystemDumpService : public Node
2155{
2156 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002157 SystemDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002158 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2159 {
2160 entityPrivileges = {
2161 {boost::beast::http::verb::get, {{"Login"}}},
2162 {boost::beast::http::verb::head, {{"Login"}}},
2163 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2164 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2165 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2166 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2167 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002168
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002169 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002170 void doGet(crow::Response& res, const crow::Request&,
2171 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002172 {
2173 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002174
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002175 asyncResp->res.jsonValue["@odata.id"] =
2176 "/redfish/v1/Systems/system/LogServices/Dump";
2177 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002178 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002179 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2180 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2181 asyncResp->res.jsonValue["Id"] = "Dump";
2182 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2183 asyncResp->res.jsonValue["Entries"] = {
2184 {"@odata.id",
2185 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2186 asyncResp->res.jsonValue["Actions"] = {
2187 {"#LogService.ClearLog",
2188 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2189 "LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002190 {"#LogService.CollectDiagnosticData",
2191 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2192 "LogService.CollectDiagnosticData"}}}};
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002193 }
2194};
2195
2196class SystemDumpEntryCollection : public Node
2197{
2198 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002199 SystemDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002200 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2201 {
2202 entityPrivileges = {
2203 {boost::beast::http::verb::get, {{"Login"}}},
2204 {boost::beast::http::verb::head, {{"Login"}}},
2205 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2206 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2207 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2208 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2209 }
2210
2211 private:
2212 /**
2213 * Functions triggers appropriate requests on DBus
2214 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002215 void doGet(crow::Response& res, const crow::Request&,
2216 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002217 {
2218 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2219
2220 asyncResp->res.jsonValue["@odata.type"] =
2221 "#LogEntryCollection.LogEntryCollection";
2222 asyncResp->res.jsonValue["@odata.id"] =
2223 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2224 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2225 asyncResp->res.jsonValue["Description"] =
2226 "Collection of System Dump Entries";
2227
2228 getDumpEntryCollection(asyncResp, "System");
2229 }
2230};
2231
2232class SystemDumpEntry : public Node
2233{
2234 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002235 SystemDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002236 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2237 std::string())
2238 {
2239 entityPrivileges = {
2240 {boost::beast::http::verb::get, {{"Login"}}},
2241 {boost::beast::http::verb::head, {{"Login"}}},
2242 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2243 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2244 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2245 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2246 }
2247
2248 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002249 void doGet(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002250 const std::vector<std::string>& params) override
2251 {
2252 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2253 if (params.size() != 1)
2254 {
2255 messages::internalError(asyncResp->res);
2256 return;
2257 }
2258 getDumpEntryById(asyncResp, params[0], "System");
2259 }
2260
Ed Tanouscb13a392020-07-25 19:02:03 +00002261 void doDelete(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002262 const std::vector<std::string>& params) override
2263 {
2264 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2265 if (params.size() != 1)
2266 {
2267 messages::internalError(asyncResp->res);
2268 return;
2269 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002270 deleteDumpEntry(asyncResp->res, params[0], "system");
raviteja-bc9bb6862020-02-03 11:53:32 -06002271 }
2272};
2273
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002274class SystemDumpCreate : public Node
2275{
2276 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002277 SystemDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002278 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002279 "Actions/"
2280 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002281 {
2282 entityPrivileges = {
2283 {boost::beast::http::verb::get, {{"Login"}}},
2284 {boost::beast::http::verb::head, {{"Login"}}},
2285 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2286 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2287 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2288 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2289 }
2290
2291 private:
2292 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002293 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002294 {
2295 createDump(res, req, "System");
2296 }
2297};
2298
raviteja-b013487e2020-03-03 03:20:48 -06002299class SystemDumpClear : public Node
2300{
2301 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002302 SystemDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002303 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002304 "Actions/"
2305 "LogService.ClearLog/")
2306 {
2307 entityPrivileges = {
2308 {boost::beast::http::verb::get, {{"Login"}}},
2309 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002310 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2311 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2312 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002313 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2314 }
2315
2316 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002317 void doPost(crow::Response& res, const crow::Request&,
2318 const std::vector<std::string>&) override
raviteja-b013487e2020-03-03 03:20:48 -06002319 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002320 clearDump(res, "System");
raviteja-b013487e2020-03-03 03:20:48 -06002321 }
2322};
2323
Jason M. Bills424c4172019-03-21 13:50:33 -07002324class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002325{
2326 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002327 CrashdumpService(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002328 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002329 {
AppaRao Puli39460282020-04-07 17:03:04 +05302330 // Note: Deviated from redfish privilege registry for GET & HEAD
2331 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002332 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302333 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2334 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002335 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2336 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2337 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2338 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002339 }
2340
2341 private:
2342 /**
2343 * Functions triggers appropriate requests on DBus
2344 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002345 void doGet(crow::Response& res, const crow::Request&,
2346 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002347 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002348 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002349 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002350 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002351 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002352 asyncResp->res.jsonValue["@odata.type"] =
2353 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002354 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2355 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2356 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002357 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2358 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002359 asyncResp->res.jsonValue["Entries"] = {
2360 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002361 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002362 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002363 {"#LogService.ClearLog",
2364 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2365 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002366 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002367 {{"#Crashdump.OnDemand",
2368 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002369 "Actions/Oem/Crashdump.OnDemand"}}},
2370 {"#Crashdump.Telemetry",
2371 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2372 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002373
2374#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002375 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002376 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002377 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2378 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002379#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002380 }
2381};
2382
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002383class CrashdumpClear : public Node
2384{
2385 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002386 CrashdumpClear(App& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002387 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2388 "LogService.ClearLog/")
2389 {
AppaRao Puli39460282020-04-07 17:03:04 +05302390 // Note: Deviated from redfish privilege registry for GET & HEAD
2391 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002392 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302393 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2394 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002395 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2396 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2397 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2398 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2399 }
2400
2401 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002402 void doPost(crow::Response& res, const crow::Request&,
2403 const std::vector<std::string>&) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002404 {
2405 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2406
2407 crow::connections::systemBus->async_method_call(
2408 [asyncResp](const boost::system::error_code ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002409 const std::string&) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002410 if (ec)
2411 {
2412 messages::internalError(asyncResp->res);
2413 return;
2414 }
2415 messages::success(asyncResp->res);
2416 },
2417 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2418 }
2419};
2420
Ed Tanousb5a76932020-09-29 16:16:58 -07002421static void logCrashdumpEntry(const std::shared_ptr<AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002422 const std::string& logID,
2423 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002424{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002425 auto getStoredLogCallback =
2426 [asyncResp, logID, &logEntryJson](
2427 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002428 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002429 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002430 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002431 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2432 if (ec.value() ==
2433 boost::system::linux_error::bad_request_descriptor)
2434 {
2435 messages::resourceNotFound(asyncResp->res, "LogEntry",
2436 logID);
2437 }
2438 else
2439 {
2440 messages::internalError(asyncResp->res);
2441 }
2442 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002443 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002444
Johnathan Mantey043a0532020-03-10 17:15:28 -07002445 std::string timestamp{};
2446 std::string filename{};
2447 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002448 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002449
2450 if (filename.empty() || timestamp.empty())
2451 {
2452 messages::resourceMissingAtURI(asyncResp->res, logID);
2453 return;
2454 }
2455
2456 std::string crashdumpURI =
2457 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2458 logID + "/" + filename;
2459 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2460 {"@odata.id", "/redfish/v1/Systems/system/"
2461 "LogServices/Crashdump/Entries/" +
2462 logID},
2463 {"Name", "CPU Crashdump"},
2464 {"Id", logID},
2465 {"EntryType", "Oem"},
2466 {"OemRecordFormat", "Crashdump URI"},
2467 {"Message", std::move(crashdumpURI)},
2468 {"Created", std::move(timestamp)}};
2469 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002470 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002471 std::move(getStoredLogCallback), crashdumpObject,
2472 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002473 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002474}
2475
Jason M. Bills424c4172019-03-21 13:50:33 -07002476class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002477{
2478 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002479 CrashdumpEntryCollection(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002480 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002481 {
AppaRao Puli39460282020-04-07 17:03:04 +05302482 // Note: Deviated from redfish privilege registry for GET & HEAD
2483 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002484 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302485 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2486 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002487 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2488 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2489 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2490 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002491 }
2492
2493 private:
2494 /**
2495 * Functions triggers appropriate requests on DBus
2496 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002497 void doGet(crow::Response& res, const crow::Request&,
2498 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002499 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002500 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002501 // Collections don't include the static data added by SubRoute because
2502 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002503 auto getLogEntriesCallback = [asyncResp](
2504 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002505 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002506 if (ec)
2507 {
2508 if (ec.value() !=
2509 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002510 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002511 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2512 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002513 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002514 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002515 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002516 }
2517 asyncResp->res.jsonValue["@odata.type"] =
2518 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002519 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002520 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002521 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002522 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002523 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002524 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002525 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002526 std::vector<std::string> logIDs;
2527 // Get the list of log entries and build up an empty array big
2528 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002529 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002530 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002531 // Get the log ID
Ed Tanousf23b7292020-10-15 09:41:17 -07002532 std::size_t lastPos = objpath.rfind('/');
Jason M. Billse855dd22019-10-08 11:37:48 -07002533 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002534 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002535 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002536 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002537 logIDs.emplace_back(objpath.substr(lastPos + 1));
2538
2539 // Add a space for the log entry to the array
2540 logEntryArray.push_back({});
2541 }
2542 // Now go through and set up async calls to fill in the entries
2543 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002544 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002545 {
2546 // Add the log entry to the array
2547 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002548 }
2549 asyncResp->res.jsonValue["Members@odata.count"] =
2550 logEntryArray.size();
2551 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002552 crow::connections::systemBus->async_method_call(
2553 std::move(getLogEntriesCallback),
2554 "xyz.openbmc_project.ObjectMapper",
2555 "/xyz/openbmc_project/object_mapper",
2556 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002557 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002558 }
2559};
2560
Jason M. Bills424c4172019-03-21 13:50:33 -07002561class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002562{
2563 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002564 CrashdumpEntry(App& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002565 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002566 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002567 std::string())
2568 {
AppaRao Puli39460282020-04-07 17:03:04 +05302569 // Note: Deviated from redfish privilege registry for GET & HEAD
2570 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002571 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302572 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2573 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002574 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2575 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2576 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2577 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002578 }
2579
2580 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002581 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002582 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002583 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002584 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002585 if (params.size() != 1)
2586 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002587 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002588 return;
2589 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002590 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002591 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2592 }
2593};
2594
2595class CrashdumpFile : public Node
2596{
2597 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002598 CrashdumpFile(App& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002599 Node(app,
2600 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2601 "<str>/",
2602 std::string(), std::string())
2603 {
AppaRao Puli39460282020-04-07 17:03:04 +05302604 // Note: Deviated from redfish privilege registry for GET & HEAD
2605 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002606 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302607 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2608 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002609 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2610 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2611 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2612 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2613 }
2614
2615 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002616 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002617 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002618 {
2619 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2620 if (params.size() != 2)
2621 {
2622 messages::internalError(asyncResp->res);
2623 return;
2624 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002625 const std::string& logID = params[0];
2626 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002627
Johnathan Mantey043a0532020-03-10 17:15:28 -07002628 auto getStoredLogCallback =
2629 [asyncResp, logID, fileName](
2630 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002631 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002632 if (ec)
2633 {
2634 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2635 << ec.message();
2636 messages::internalError(asyncResp->res);
2637 return;
2638 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002639
Johnathan Mantey043a0532020-03-10 17:15:28 -07002640 std::string dbusFilename{};
2641 std::string dbusTimestamp{};
2642 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002643
Ed Tanous2c70f802020-09-28 14:29:23 -07002644 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002645 dbusFilepath);
2646
2647 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2648 dbusFilepath.empty())
2649 {
2650 messages::resourceMissingAtURI(asyncResp->res, fileName);
2651 return;
2652 }
2653
2654 // Verify the file name parameter is correct
2655 if (fileName != dbusFilename)
2656 {
2657 messages::resourceMissingAtURI(asyncResp->res, fileName);
2658 return;
2659 }
2660
2661 if (!std::filesystem::exists(dbusFilepath))
2662 {
2663 messages::resourceMissingAtURI(asyncResp->res, fileName);
2664 return;
2665 }
2666 std::ifstream ifs(dbusFilepath, std::ios::in |
2667 std::ios::binary |
2668 std::ios::ate);
2669 std::ifstream::pos_type fileSize = ifs.tellg();
2670 if (fileSize < 0)
2671 {
2672 messages::generalError(asyncResp->res);
2673 return;
2674 }
2675 ifs.seekg(0, std::ios::beg);
2676
2677 auto crashData = std::make_unique<char[]>(
2678 static_cast<unsigned int>(fileSize));
2679
2680 ifs.read(crashData.get(), static_cast<int>(fileSize));
2681
2682 // The cast to std::string is intentional in order to use the
2683 // assign() that applies move mechanics
2684 asyncResp->res.body().assign(
2685 static_cast<std::string>(crashData.get()));
2686
2687 // Configure this to be a file download when accessed from
2688 // a browser
2689 asyncResp->res.addHeader("Content-Disposition", "attachment");
2690 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002691 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002692 std::move(getStoredLogCallback), crashdumpObject,
2693 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002694 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002695 }
2696};
2697
Jason M. Bills424c4172019-03-21 13:50:33 -07002698class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002699{
2700 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002701 OnDemandCrashdump(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002702 Node(app,
2703 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2704 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002705 {
AppaRao Puli39460282020-04-07 17:03:04 +05302706 // Note: Deviated from redfish privilege registry for GET & HEAD
2707 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002708 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302709 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2710 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2711 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2712 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2713 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2714 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002715 }
2716
2717 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002718 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002719 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002720 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002721 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002722
James Feistfe306722020-03-12 16:32:08 -07002723 auto generateonDemandLogCallback = [asyncResp,
2724 req](const boost::system::error_code
2725 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002726 const std::string&) {
James Feist46229572020-02-19 15:11:58 -08002727 if (ec)
2728 {
2729 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002730 {
James Feist46229572020-02-19 15:11:58 -08002731 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002732 }
James Feist46229572020-02-19 15:11:58 -08002733 else if (ec.value() ==
2734 boost::system::errc::device_or_resource_busy)
2735 {
2736 messages::serviceTemporarilyUnavailable(asyncResp->res,
2737 "60");
2738 }
2739 else
2740 {
2741 messages::internalError(asyncResp->res);
2742 }
2743 return;
2744 }
2745 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002746 [](boost::system::error_code err, sdbusplus::message::message&,
2747 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002748 if (!err)
2749 {
James Feiste5d50062020-05-11 17:29:00 -07002750 taskData->messages.emplace_back(
2751 messages::taskCompletedOK(
2752 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002753 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002754 }
James Feist32898ce2020-03-10 16:16:52 -07002755 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002756 },
James Feist46229572020-02-19 15:11:58 -08002757 "type='signal',interface='org.freedesktop.DBus.Properties',"
2758 "member='PropertiesChanged',arg0namespace='com.intel."
2759 "crashdump'");
2760 task->startTimer(std::chrono::minutes(5));
2761 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002762 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002763 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002764 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002765 std::move(generateonDemandLogCallback), crashdumpObject,
2766 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002767 }
2768};
2769
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002770class TelemetryCrashdump : public Node
2771{
2772 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002773 TelemetryCrashdump(App& app) :
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002774 Node(app,
2775 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2776 "Crashdump.Telemetry/")
2777 {
2778 // Note: Deviated from redfish privilege registry for GET & HEAD
2779 // method for security reasons.
2780 entityPrivileges = {
2781 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2782 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2783 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2784 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2785 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2786 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2787 }
2788
2789 private:
2790 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002791 const std::vector<std::string>&) override
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002792 {
2793 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2794
2795 auto generateTelemetryLogCallback = [asyncResp, req](
2796 const boost::system::error_code
2797 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002798 const std::string&) {
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002799 if (ec)
2800 {
2801 if (ec.value() == boost::system::errc::operation_not_supported)
2802 {
2803 messages::resourceInStandby(asyncResp->res);
2804 }
2805 else if (ec.value() ==
2806 boost::system::errc::device_or_resource_busy)
2807 {
2808 messages::serviceTemporarilyUnavailable(asyncResp->res,
2809 "60");
2810 }
2811 else
2812 {
2813 messages::internalError(asyncResp->res);
2814 }
2815 return;
2816 }
2817 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2818 [](boost::system::error_code err, sdbusplus::message::message&,
2819 const std::shared_ptr<task::TaskData>& taskData) {
2820 if (!err)
2821 {
2822 taskData->messages.emplace_back(
2823 messages::taskCompletedOK(
2824 std::to_string(taskData->index)));
2825 taskData->state = "Completed";
2826 }
2827 return task::completed;
2828 },
2829 "type='signal',interface='org.freedesktop.DBus.Properties',"
2830 "member='PropertiesChanged',arg0namespace='com.intel."
2831 "crashdump'");
2832 task->startTimer(std::chrono::minutes(5));
2833 task->populateResp(asyncResp->res);
2834 task->payload.emplace(req);
2835 };
2836 crow::connections::systemBus->async_method_call(
2837 std::move(generateTelemetryLogCallback), crashdumpObject,
2838 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2839 }
2840};
2841
Jason M. Billse1f26342018-07-18 12:12:00 -07002842class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002843{
2844 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002845 SendRawPECI(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002846 Node(app,
2847 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2848 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002849 {
AppaRao Puli39460282020-04-07 17:03:04 +05302850 // Note: Deviated from redfish privilege registry for GET & HEAD
2851 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002852 entityPrivileges = {
2853 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2854 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2855 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2856 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2857 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2858 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2859 }
2860
2861 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002862 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002863 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002864 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002865 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002866 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002867
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002868 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002869 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002870 return;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002871 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002872 uint32_t idx = 0;
2873 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002874 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002875 if (cmd.size() < 3)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002876 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002877 std::string s("[");
2878 for (auto const& val : cmd)
2879 {
2880 if (val != *cmd.begin())
2881 {
2882 s += ",";
2883 }
2884 s += std::to_string(val);
2885 }
2886 s += "]";
2887 messages::actionParameterValueFormatError(
2888 res, s, "PECICommands[" + std::to_string(idx) + "]",
2889 "SendRawPeci");
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002890 return;
2891 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002892 idx++;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002893 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002894 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002895 auto sendRawPECICallback =
2896 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002897 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002898 if (ec)
2899 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002900 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002901 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002902 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002903 return;
2904 }
2905 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2906 {"PECIResponse", resp}};
2907 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002908 // Call the SendRawPECI command with the provided data
2909 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002910 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002911 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002912 }
2913};
2914
Andrew Geisslercb92c032018-08-17 07:56:14 -07002915/**
2916 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2917 */
2918class DBusLogServiceActionsClear : public Node
2919{
2920 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002921 DBusLogServiceActionsClear(App& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002922 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002923 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002924 {
2925 entityPrivileges = {
2926 {boost::beast::http::verb::get, {{"Login"}}},
2927 {boost::beast::http::verb::head, {{"Login"}}},
2928 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2929 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2930 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2931 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2932 }
2933
2934 private:
2935 /**
2936 * Function handles POST method request.
2937 * The Clear Log actions does not require any parameter.The action deletes
2938 * all entries found in the Entries collection for this Log Service.
2939 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002940 void doPost(crow::Response& res, const crow::Request&,
2941 const std::vector<std::string>&) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002942 {
2943 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2944
2945 auto asyncResp = std::make_shared<AsyncResp>(res);
2946 // Process response from Logging service.
Ed Tanous2c70f802020-09-28 14:29:23 -07002947 auto respHandler = [asyncResp](const boost::system::error_code ec) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07002948 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2949 if (ec)
2950 {
2951 // TODO Handle for specific error code
2952 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2953 asyncResp->res.result(
2954 boost::beast::http::status::internal_server_error);
2955 return;
2956 }
2957
2958 asyncResp->res.result(boost::beast::http::status::no_content);
2959 };
2960
2961 // Make call to Logging service to request Clear Log
2962 crow::connections::systemBus->async_method_call(
Ed Tanous2c70f802020-09-28 14:29:23 -07002963 respHandler, "xyz.openbmc_project.Logging",
Andrew Geisslercb92c032018-08-17 07:56:14 -07002964 "/xyz/openbmc_project/logging",
2965 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2966 }
2967};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002968
2969/****************************************************
2970 * Redfish PostCode interfaces
2971 * using DBUS interface: getPostCodesTS
2972 ******************************************************/
2973class PostCodesLogService : public Node
2974{
2975 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002976 PostCodesLogService(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002977 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2978 {
2979 entityPrivileges = {
2980 {boost::beast::http::verb::get, {{"Login"}}},
2981 {boost::beast::http::verb::head, {{"Login"}}},
2982 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2983 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2984 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2985 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2986 }
2987
2988 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002989 void doGet(crow::Response& res, const crow::Request&,
2990 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002991 {
2992 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2993
2994 asyncResp->res.jsonValue = {
2995 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2996 {"@odata.type", "#LogService.v1_1_0.LogService"},
ZhikuiRena3316fc2020-01-29 14:58:08 -08002997 {"Name", "POST Code Log Service"},
2998 {"Description", "POST Code Log Service"},
2999 {"Id", "BIOS POST Code Log"},
3000 {"OverWritePolicy", "WrapsWhenFull"},
3001 {"Entries",
3002 {{"@odata.id",
3003 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3004 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3005 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3006 "Actions/LogService.ClearLog"}};
3007 }
3008};
3009
3010class PostCodesClear : public Node
3011{
3012 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003013 PostCodesClear(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003014 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3015 "LogService.ClearLog/")
3016 {
3017 entityPrivileges = {
3018 {boost::beast::http::verb::get, {{"Login"}}},
3019 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303020 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3021 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3022 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3023 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003024 }
3025
3026 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003027 void doPost(crow::Response& res, const crow::Request&,
3028 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003029 {
3030 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3031
3032 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3033 // Make call to post-code service to request clear all
3034 crow::connections::systemBus->async_method_call(
3035 [asyncResp](const boost::system::error_code ec) {
3036 if (ec)
3037 {
3038 // TODO Handle for specific error code
3039 BMCWEB_LOG_ERROR
3040 << "doClearPostCodes resp_handler got error " << ec;
3041 asyncResp->res.result(
3042 boost::beast::http::status::internal_server_error);
3043 messages::internalError(asyncResp->res);
3044 return;
3045 }
3046 },
3047 "xyz.openbmc_project.State.Boot.PostCode",
3048 "/xyz/openbmc_project/State/Boot/PostCode",
3049 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3050 }
3051};
3052
3053static void fillPostCodeEntry(
Ed Tanousb5a76932020-09-29 16:16:58 -07003054 const std::shared_ptr<AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003055 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003056 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3057 const uint64_t skip = 0, const uint64_t top = 0)
3058{
3059 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003060 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003061 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003062
3063 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003064 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003065
3066 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003067 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003068 {
3069 currentCodeIndex++;
3070 std::string postcodeEntryID =
3071 "B" + std::to_string(bootIndex) + "-" +
3072 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3073
3074 uint64_t usecSinceEpoch = code.first;
3075 uint64_t usTimeOffset = 0;
3076
3077 if (1 == currentCodeIndex)
3078 { // already incremented
3079 firstCodeTimeUs = code.first;
3080 }
3081 else
3082 {
3083 usTimeOffset = code.first - firstCodeTimeUs;
3084 }
3085
3086 // skip if no specific codeIndex is specified and currentCodeIndex does
3087 // not fall between top and skip
3088 if ((codeIndex == 0) &&
3089 (currentCodeIndex <= skip || currentCodeIndex > top))
3090 {
3091 continue;
3092 }
3093
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003094 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003095 // currentIndex
3096 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3097 {
3098 // This is done for simplicity. 1st entry is needed to calculate
3099 // time offset. To improve efficiency, one can get to the entry
3100 // directly (possibly with flatmap's nth method)
3101 continue;
3102 }
3103
3104 // currentCodeIndex is within top and skip or equal to specified code
3105 // index
3106
3107 // Get the Created time from the timestamp
3108 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003109 entryTimeStr = crow::utility::getDateTime(
3110 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003111
3112 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3113 std::ostringstream hexCode;
3114 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3115 << code.second;
3116 std::ostringstream timeOffsetStr;
3117 // Set Fixed -Point Notation
3118 timeOffsetStr << std::fixed;
3119 // Set precision to 4 digits
3120 timeOffsetStr << std::setprecision(4);
3121 // Add double to stream
3122 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3123 std::vector<std::string> messageArgs = {
3124 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3125
3126 // Get MessageArgs template from message registry
3127 std::string msg;
3128 if (message != nullptr)
3129 {
3130 msg = message->message;
3131
3132 // fill in this post code value
3133 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003134 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003135 {
3136 std::string argStr = "%" + std::to_string(++i);
3137 size_t argPos = msg.find(argStr);
3138 if (argPos != std::string::npos)
3139 {
3140 msg.replace(argPos, argStr.length(), messageArg);
3141 }
3142 }
3143 }
3144
Tim Leed4342a92020-04-27 11:47:58 +08003145 // Get Severity template from message registry
3146 std::string severity;
3147 if (message != nullptr)
3148 {
3149 severity = message->severity;
3150 }
3151
ZhikuiRena3316fc2020-01-29 14:58:08 -08003152 // add to AsyncResp
3153 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003154 nlohmann::json& bmcLogEntry = logEntryArray.back();
Gunnar Mills743e9a12020-10-26 12:44:53 -05003155 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3156 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3157 "PostCodes/Entries/" +
3158 postcodeEntryID},
3159 {"Name", "POST Code Log Entry"},
3160 {"Id", postcodeEntryID},
3161 {"Message", std::move(msg)},
3162 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3163 {"MessageArgs", std::move(messageArgs)},
3164 {"EntryType", "Event"},
3165 {"Severity", std::move(severity)},
3166 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003167 }
3168}
3169
Ed Tanousb5a76932020-09-29 16:16:58 -07003170static void getPostCodeForEntry(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003171 const uint16_t bootIndex,
3172 const uint64_t codeIndex)
3173{
3174 crow::connections::systemBus->async_method_call(
3175 [aResp, bootIndex, codeIndex](
3176 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003177 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003178 if (ec)
3179 {
3180 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3181 messages::internalError(aResp->res);
3182 return;
3183 }
3184
3185 // skip the empty postcode boots
3186 if (postcode.empty())
3187 {
3188 return;
3189 }
3190
3191 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3192
3193 aResp->res.jsonValue["Members@odata.count"] =
3194 aResp->res.jsonValue["Members"].size();
3195 },
3196 "xyz.openbmc_project.State.Boot.PostCode",
3197 "/xyz/openbmc_project/State/Boot/PostCode",
3198 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3199 bootIndex);
3200}
3201
Ed Tanousb5a76932020-09-29 16:16:58 -07003202static void getPostCodeForBoot(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003203 const uint16_t bootIndex,
3204 const uint16_t bootCount,
3205 const uint64_t entryCount, const uint64_t skip,
3206 const uint64_t top)
3207{
3208 crow::connections::systemBus->async_method_call(
3209 [aResp, bootIndex, bootCount, entryCount, skip,
3210 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003211 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003212 if (ec)
3213 {
3214 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3215 messages::internalError(aResp->res);
3216 return;
3217 }
3218
3219 uint64_t endCount = entryCount;
3220 if (!postcode.empty())
3221 {
3222 endCount = entryCount + postcode.size();
3223
3224 if ((skip < endCount) && ((top + skip) > entryCount))
3225 {
3226 uint64_t thisBootSkip =
3227 std::max(skip, entryCount) - entryCount;
3228 uint64_t thisBootTop =
3229 std::min(top + skip, endCount) - entryCount;
3230
3231 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3232 thisBootSkip, thisBootTop);
3233 }
3234 aResp->res.jsonValue["Members@odata.count"] = endCount;
3235 }
3236
3237 // continue to previous bootIndex
3238 if (bootIndex < bootCount)
3239 {
3240 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3241 bootCount, endCount, skip, top);
3242 }
3243 else
3244 {
3245 aResp->res.jsonValue["Members@odata.nextLink"] =
3246 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3247 "Entries?$skip=" +
3248 std::to_string(skip + top);
3249 }
3250 },
3251 "xyz.openbmc_project.State.Boot.PostCode",
3252 "/xyz/openbmc_project/State/Boot/PostCode",
3253 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3254 bootIndex);
3255}
3256
Ed Tanousb5a76932020-09-29 16:16:58 -07003257static void getCurrentBootNumber(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003258 const uint64_t skip, const uint64_t top)
3259{
3260 uint64_t entryCount = 0;
3261 crow::connections::systemBus->async_method_call(
3262 [aResp, entryCount, skip,
3263 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003264 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003265 if (ec)
3266 {
3267 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3268 messages::internalError(aResp->res);
3269 return;
3270 }
3271 auto pVal = std::get_if<uint16_t>(&bootCount);
3272 if (pVal)
3273 {
3274 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3275 }
3276 else
3277 {
3278 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3279 }
3280 },
3281 "xyz.openbmc_project.State.Boot.PostCode",
3282 "/xyz/openbmc_project/State/Boot/PostCode",
3283 "org.freedesktop.DBus.Properties", "Get",
3284 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3285}
3286
3287class PostCodesEntryCollection : public Node
3288{
3289 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003290 PostCodesEntryCollection(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003291 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3292 {
3293 entityPrivileges = {
3294 {boost::beast::http::verb::get, {{"Login"}}},
3295 {boost::beast::http::verb::head, {{"Login"}}},
3296 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3297 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3298 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3299 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3300 }
3301
3302 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003303 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00003304 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003305 {
3306 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3307
3308 asyncResp->res.jsonValue["@odata.type"] =
3309 "#LogEntryCollection.LogEntryCollection";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003310 asyncResp->res.jsonValue["@odata.id"] =
3311 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3312 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3313 asyncResp->res.jsonValue["Description"] =
3314 "Collection of POST Code Log Entries";
3315 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3316 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3317
3318 uint64_t skip = 0;
3319 uint64_t top = maxEntriesPerPage; // Show max entries by default
3320 if (!getSkipParam(asyncResp->res, req, skip))
3321 {
3322 return;
3323 }
3324 if (!getTopParam(asyncResp->res, req, top))
3325 {
3326 return;
3327 }
3328 getCurrentBootNumber(asyncResp, skip, top);
3329 }
3330};
3331
3332class PostCodesEntry : public Node
3333{
3334 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003335 PostCodesEntry(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003336 Node(app,
3337 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3338 std::string())
3339 {
3340 entityPrivileges = {
3341 {boost::beast::http::verb::get, {{"Login"}}},
3342 {boost::beast::http::verb::head, {{"Login"}}},
3343 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3344 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3345 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3346 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3347 }
3348
3349 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003350 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003351 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003352 {
3353 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3354 if (params.size() != 1)
3355 {
3356 messages::internalError(asyncResp->res);
3357 return;
3358 }
3359
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003360 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003361
3362 size_t bootPos = targetID.find('B');
3363 if (bootPos == std::string::npos)
3364 {
3365 // Requested ID was not found
3366 messages::resourceMissingAtURI(asyncResp->res, targetID);
3367 return;
3368 }
3369 std::string_view bootIndexStr(targetID);
3370 bootIndexStr.remove_prefix(bootPos + 1);
3371 uint16_t bootIndex = 0;
3372 uint64_t codeIndex = 0;
3373 size_t dashPos = bootIndexStr.find('-');
3374
3375 if (dashPos == std::string::npos)
3376 {
3377 return;
3378 }
3379 std::string_view codeIndexStr(bootIndexStr);
3380 bootIndexStr.remove_suffix(dashPos);
3381 codeIndexStr.remove_prefix(dashPos + 1);
3382
3383 bootIndex = static_cast<uint16_t>(
Ed Tanous23a21a12020-07-25 04:45:05 +00003384 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3385 codeIndex = strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003386 if (bootIndex == 0 || codeIndex == 0)
3387 {
3388 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3389 << params[0];
3390 }
3391
3392 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003393 asyncResp->res.jsonValue["@odata.id"] =
3394 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3395 "Entries";
3396 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3397 asyncResp->res.jsonValue["Description"] =
3398 "Collection of POST Code Log Entries";
3399 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3400 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3401
3402 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3403 }
3404};
3405
Ed Tanous1da66f72018-07-27 16:13:37 -07003406} // namespace redfish