blob: 22c3ad78bbe4f0beb1af118ba546644531bc4c41 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032
James Feist4418c7f2019-04-15 11:09:15 -070033#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070034#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080035#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070036
37namespace redfish
38{
39
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040constexpr char const* crashdumpObject = "com.intel.crashdump";
41constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042constexpr char const* crashdumpInterface = "com.intel.crashdump";
43constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070044 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070049constexpr char const* crashdumpTelemetryInterface =
50 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070051
Jason M. Bills4851d452019-03-28 11:27:48 -070052namespace message_registries
53{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054static const Message* getMessageFromRegistry(
55 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070056 const boost::beast::span<const MessageEntry> registry)
57{
58 boost::beast::span<const MessageEntry>::const_iterator messageIt =
59 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050060 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070061 return !std::strcmp(messageEntry.first,
62 messageKey.c_str());
63 });
64 if (messageIt != registry.cend())
65 {
66 return &messageIt->second;
67 }
68
69 return nullptr;
70}
71
Gunnar Mills1214b7e2020-06-04 10:11:30 -050072static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070073{
74 // Redfish MessageIds are in the form
75 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
76 // the right Message
77 std::vector<std::string> fields;
78 fields.reserve(4);
79 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050080 std::string& registryName = fields[0];
81 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070082
83 // Find the right registry and check it for the MessageKey
84 if (std::string(base::header.registryPrefix) == registryName)
85 {
86 return getMessageFromRegistry(
87 messageKey, boost::beast::span<const MessageEntry>(base::registry));
88 }
89 if (std::string(openbmc::header.registryPrefix) == registryName)
90 {
91 return getMessageFromRegistry(
92 messageKey,
93 boost::beast::span<const MessageEntry>(openbmc::registry));
94 }
95 return nullptr;
96}
97} // namespace message_registries
98
James Feistf6150402019-01-08 10:36:20 -080099namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700100
Andrew Geisslercb92c032018-08-17 07:56:14 -0700101using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500102 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
103 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700104
105using GetManagedObjectsType = boost::container::flat_map<
106 sdbusplus::message::object_path,
107 boost::container::flat_map<std::string, GetManagedPropertyType>>;
108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700110{
111 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
112 {
113 return "Critical";
114 }
115 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
116 {
117 return "Critical";
118 }
119 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
120 {
121 return "OK";
122 }
123 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
124 {
125 return "Critical";
126 }
127 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
128 {
129 return "Critical";
130 }
131 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
132 {
133 return "OK";
134 }
135 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
136 {
137 return "OK";
138 }
139 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
140 {
141 return "Warning";
142 }
143 return "";
144}
145
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146static int getJournalMetadata(sd_journal* journal,
147 const std::string_view& field,
148 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700149{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700151 size_t length = 0;
152 int ret = 0;
153 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700154 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 if (ret < 0)
157 {
158 return ret;
159 }
Ed Tanous39e77502019-03-04 17:35:53 -0800160 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700161 // Only use the content after the "=" character.
162 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
163 return ret;
164}
165
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166static int getJournalMetadata(sd_journal* journal,
167 const std::string_view& field, const int& base,
168 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700169{
170 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800171 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700172 // Get the metadata from the requested field of the journal entry
173 ret = getJournalMetadata(journal, field, metadata);
174 if (ret < 0)
175 {
176 return ret;
177 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000178 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700179 return ret;
180}
181
ZhikuiRena3316fc2020-01-29 14:58:08 -0800182static bool getTimestampStr(const uint64_t usecSinceEpoch,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500183 std::string& entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700184{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800185 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500186 struct tm* loctime = localtime(&t);
Jason M. Bills16428a12018-11-02 12:42:29 -0700187 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700188 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189 {
190 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
191 }
192 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800193 std::string_view t1(entryTime);
194 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700195 if (t1.size() > 2 && t2.size() > 2)
196 {
197 t1.remove_suffix(2);
198 t2.remove_prefix(t2.size() - 2);
199 }
Ed Tanous39e77502019-03-04 17:35:53 -0800200 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 return true;
202}
203
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800205{
206 int ret = 0;
207 uint64_t timestamp = 0;
208 ret = sd_journal_get_realtime_usec(journal, &timestamp);
209 if (ret < 0)
210 {
211 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
212 << strerror(-ret);
213 return false;
214 }
215 return getTimestampStr(timestamp, entryTimestamp);
216}
217
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500218static bool getSkipParam(crow::Response& res, const crow::Request& req,
219 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700220{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221 char* skipParam = req.urlParams.get("$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 if (skipParam != nullptr)
223 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700225 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700226 if (*skipParam == '\0' || *ptr != '\0')
227 {
228
229 messages::queryParameterValueTypeError(res, std::string(skipParam),
230 "$skip");
231 return false;
232 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700233 }
234 return true;
235}
236
Ed Tanous271584a2019-07-09 16:24:22 -0700237static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500238static bool getTopParam(crow::Response& res, const crow::Request& req,
239 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700240{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500241 char* topParam = req.urlParams.get("$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700242 if (topParam != nullptr)
243 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700245 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 if (*topParam == '\0' || *ptr != '\0')
247 {
248 messages::queryParameterValueTypeError(res, std::string(topParam),
249 "$top");
250 return false;
251 }
Ed Tanous271584a2019-07-09 16:24:22 -0700252 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700253 {
254
255 messages::queryParameterOutOfRange(
256 res, std::to_string(top), "$top",
257 "1-" + std::to_string(maxEntriesPerPage));
258 return false;
259 }
260 }
261 return true;
262}
263
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500264static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700265 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700266{
267 int ret = 0;
268 static uint64_t prevTs = 0;
269 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700270 if (firstEntry)
271 {
272 prevTs = 0;
273 }
274
Jason M. Bills16428a12018-11-02 12:42:29 -0700275 // Get the entry timestamp
276 uint64_t curTs = 0;
277 ret = sd_journal_get_realtime_usec(journal, &curTs);
278 if (ret < 0)
279 {
280 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
281 << strerror(-ret);
282 return false;
283 }
284 // If the timestamp isn't unique, increment the index
285 if (curTs == prevTs)
286 {
287 index++;
288 }
289 else
290 {
291 // Otherwise, reset it
292 index = 0;
293 }
294 // Save the timestamp
295 prevTs = curTs;
296
297 entryID = std::to_string(curTs);
298 if (index > 0)
299 {
300 entryID += "_" + std::to_string(index);
301 }
302 return true;
303}
304
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500305static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700306 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700307{
Ed Tanous271584a2019-07-09 16:24:22 -0700308 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700309 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700310 if (firstEntry)
311 {
312 prevTs = 0;
313 }
314
Jason M. Bills95820182019-04-22 16:25:34 -0700315 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700316 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700317 std::tm timeStruct = {};
318 std::istringstream entryStream(logEntry);
319 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
320 {
321 curTs = std::mktime(&timeStruct);
322 }
323 // If the timestamp isn't unique, increment the index
324 if (curTs == prevTs)
325 {
326 index++;
327 }
328 else
329 {
330 // Otherwise, reset it
331 index = 0;
332 }
333 // Save the timestamp
334 prevTs = curTs;
335
336 entryID = std::to_string(curTs);
337 if (index > 0)
338 {
339 entryID += "_" + std::to_string(index);
340 }
341 return true;
342}
343
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500344static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
345 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700346{
347 if (entryID.empty())
348 {
349 return false;
350 }
351 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800352 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700353
354 auto underscorePos = tsStr.find("_");
355 if (underscorePos != tsStr.npos)
356 {
357 // Timestamp has an index
358 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800359 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700360 indexStr.remove_prefix(underscorePos + 1);
361 std::size_t pos;
362 try
363 {
Ed Tanous39e77502019-03-04 17:35:53 -0800364 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700365 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500366 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700367 {
368 messages::resourceMissingAtURI(res, entryID);
369 return false;
370 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500371 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700372 {
373 messages::resourceMissingAtURI(res, entryID);
374 return false;
375 }
376 if (pos != indexStr.size())
377 {
378 messages::resourceMissingAtURI(res, entryID);
379 return false;
380 }
381 }
382 // Timestamp has no index
383 std::size_t pos;
384 try
385 {
Ed Tanous39e77502019-03-04 17:35:53 -0800386 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700389 {
390 messages::resourceMissingAtURI(res, entryID);
391 return false;
392 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500393 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700394 {
395 messages::resourceMissingAtURI(res, entryID);
396 return false;
397 }
398 if (pos != tsStr.size())
399 {
400 messages::resourceMissingAtURI(res, entryID);
401 return false;
402 }
403 return true;
404}
405
Jason M. Bills95820182019-04-22 16:25:34 -0700406static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500407 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700408{
409 static const std::filesystem::path redfishLogDir = "/var/log";
410 static const std::string redfishLogFilename = "redfish";
411
412 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500413 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700414 std::filesystem::directory_iterator(redfishLogDir))
415 {
416 // If we find a redfish log file, save the path
417 std::string filename = dirEnt.path().filename();
418 if (boost::starts_with(filename, redfishLogFilename))
419 {
420 redfishLogFiles.emplace_back(redfishLogDir / filename);
421 }
422 }
423 // As the log files rotate, they are appended with a ".#" that is higher for
424 // the older logs. Since we don't expect more than 10 log files, we
425 // can just sort the list to get them in order from newest to oldest
426 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
427
428 return !redfishLogFiles.empty();
429}
430
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
432 const std::string& dumpType)
433{
434 std::string dumpPath;
435 if (dumpType == "BMC")
436 {
437 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
438 }
439 else if (dumpType == "System")
440 {
441 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
442 }
443 else
444 {
445 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
446 messages::internalError(asyncResp->res);
447 return;
448 }
449
450 crow::connections::systemBus->async_method_call(
451 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
452 GetManagedObjectsType& resp) {
453 if (ec)
454 {
455 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
456 messages::internalError(asyncResp->res);
457 return;
458 }
459
460 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
461 entriesArray = nlohmann::json::array();
462
463 for (auto& object : resp)
464 {
465 bool foundDumpEntry = false;
466 for (auto& interfaceMap : object.second)
467 {
468 if (interfaceMap.first ==
469 ("xyz.openbmc_project.Dump.Entry." + dumpType))
470 {
471 foundDumpEntry = true;
472 break;
473 }
474 }
475
476 if (foundDumpEntry == false)
477 {
478 continue;
479 }
480 std::time_t timestamp;
481 uint64_t size = 0;
482 entriesArray.push_back({});
483 nlohmann::json& thisEntry = entriesArray.back();
484 const std::string& path =
485 static_cast<const std::string&>(object.first);
486 std::size_t lastPos = path.rfind("/");
487 if (lastPos == std::string::npos)
488 {
489 continue;
490 }
491 std::string entryID = path.substr(lastPos + 1);
492
493 for (auto& interfaceMap : object.second)
494 {
495 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
496 {
497
498 for (auto& propertyMap : interfaceMap.second)
499 {
500 if (propertyMap.first == "Size")
501 {
502 auto sizePtr =
503 std::get_if<uint64_t>(&propertyMap.second);
504 if (sizePtr == nullptr)
505 {
506 messages::internalError(asyncResp->res);
507 break;
508 }
509 size = *sizePtr;
510 break;
511 }
512 }
513 }
514 else if (interfaceMap.first ==
515 "xyz.openbmc_project.Time.EpochTime")
516 {
517
518 for (auto& propertyMap : interfaceMap.second)
519 {
520 if (propertyMap.first == "Elapsed")
521 {
522 const uint64_t* usecsTimeStamp =
523 std::get_if<uint64_t>(&propertyMap.second);
524 if (usecsTimeStamp == nullptr)
525 {
526 messages::internalError(asyncResp->res);
527 break;
528 }
529 timestamp =
530 static_cast<std::time_t>(*usecsTimeStamp);
531 break;
532 }
533 }
534 }
535 }
536
537 thisEntry["@odata.type"] = "#LogEntry.v1_5_1.LogEntry";
538 thisEntry["@odata.id"] = dumpPath + entryID;
539 thisEntry["Id"] = entryID;
540 thisEntry["EntryType"] = "Event";
541 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
542 thisEntry["Name"] = dumpType + " Dump Entry";
543
544 thisEntry["Oem"]["OpenBmc"]["@odata.type"] =
545 "#OemLogEntry.v1_0_0.OpenBmc";
546 thisEntry["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] = size;
547
548 if (dumpType == "BMC")
549 {
550 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] =
551 "Manager";
552 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
553 "/redfish/v1/Managers/bmc/LogServices/Dump/"
554 "attachment/" +
555 entryID;
556 }
557 else if (dumpType == "System")
558 {
559 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] = "OEM";
560 thisEntry["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
561 "System";
562 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
563 "/redfish/v1/Systems/system/LogServices/Dump/"
564 "attachment/" +
565 entryID;
566 }
567 }
568 asyncResp->res.jsonValue["Members@odata.count"] =
569 entriesArray.size();
570 },
571 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
572 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
573}
574
575inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
576 const std::string& entryID,
577 const std::string& dumpType)
578{
579 std::string dumpPath;
580 if (dumpType == "BMC")
581 {
582 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
583 }
584 else if (dumpType == "System")
585 {
586 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
587 }
588 else
589 {
590 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
591 messages::internalError(asyncResp->res);
592 return;
593 }
594
595 crow::connections::systemBus->async_method_call(
596 [asyncResp, entryID, dumpPath, dumpType](
597 const boost::system::error_code ec, GetManagedObjectsType& resp) {
598 if (ec)
599 {
600 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
601 messages::internalError(asyncResp->res);
602 return;
603 }
604
605 for (auto& objectPath : resp)
606 {
607 if (objectPath.first.str.find(
608 "/xyz/openbmc_project/dump/entry/" + entryID) ==
609 std::string::npos)
610 {
611 continue;
612 }
613
614 bool foundDumpEntry = false;
615 for (auto& interfaceMap : objectPath.second)
616 {
617 if (interfaceMap.first ==
618 ("xyz.openbmc_project.Dump.Entry." + dumpType))
619 {
620 foundDumpEntry = true;
621 break;
622 }
623 }
624 if (foundDumpEntry == false)
625 {
626 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
627 messages::internalError(asyncResp->res);
628 return;
629 }
630
631 std::time_t timestamp;
632 uint64_t size = 0;
633
634 for (auto& interfaceMap : objectPath.second)
635 {
636 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
637 {
638 for (auto& propertyMap : interfaceMap.second)
639 {
640 if (propertyMap.first == "Size")
641 {
642 auto sizePtr =
643 std::get_if<uint64_t>(&propertyMap.second);
644 if (sizePtr == nullptr)
645 {
646 messages::internalError(asyncResp->res);
647 break;
648 }
649 size = *sizePtr;
650 break;
651 }
652 }
653 }
654 else if (interfaceMap.first ==
655 "xyz.openbmc_project.Time.EpochTime")
656 {
657 for (auto& propertyMap : interfaceMap.second)
658 {
659 if (propertyMap.first == "Elapsed")
660 {
661 const uint64_t* usecsTimeStamp =
662 std::get_if<uint64_t>(&propertyMap.second);
663 if (usecsTimeStamp == nullptr)
664 {
665 messages::internalError(asyncResp->res);
666 break;
667 }
668 timestamp =
669 static_cast<std::time_t>(*usecsTimeStamp);
670 break;
671 }
672 }
673 }
674 }
675
676 asyncResp->res.jsonValue["@odata.type"] =
677 "#LogEntry.v1_5_1.LogEntry";
678 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
679 asyncResp->res.jsonValue["Id"] = entryID;
680 asyncResp->res.jsonValue["EntryType"] = "Event";
681 asyncResp->res.jsonValue["Created"] =
682 crow::utility::getDateTime(timestamp);
683 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
684
685 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] =
686 "#OemLogEntry.v1_0_0.OpenBmc";
687 asyncResp->res
688 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] =
689 size;
690
691 if (dumpType == "BMC")
692 {
693 asyncResp->res
694 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
695 "Manager";
696 asyncResp->res
697 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
698 "/redfish/v1/Managers/bmc/LogServices/Dump/"
699 "attachment/" +
700 entryID;
701 }
702 else if (dumpType == "System")
703 {
704 asyncResp->res
705 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
706 "OEM";
707 asyncResp->res
708 .jsonValue["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
709 "System";
710 asyncResp->res
711 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
712 "/redfish/v1/Systems/system/LogServices/Dump/"
713 "attachment/" +
714 entryID;
715 }
716 }
717 },
718 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
719 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
720}
721
722inline void deleteDumpEntry(crow::Response& res, const std::string& entryID)
723{
724 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
725
726 auto respHandler = [asyncResp](const boost::system::error_code ec) {
727 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
728 if (ec)
729 {
730 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
731 << ec;
732 messages::internalError(asyncResp->res);
733 return;
734 }
735 };
736 crow::connections::systemBus->async_method_call(
737 respHandler, "xyz.openbmc_project.Dump.Manager",
738 "/xyz/openbmc_project/dump/entry/" + entryID,
739 "xyz.openbmc_project.Object.Delete", "Delete");
740}
741
Johnathan Mantey043a0532020-03-10 17:15:28 -0700742static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500743 const std::vector<std::pair<std::string, VariantType>>& params,
744 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700745{
746 for (auto property : params)
747 {
748 if (property.first == "Timestamp")
749 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500750 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500751 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700752 if (value != nullptr)
753 {
754 timestamp = *value;
755 }
756 }
757 else if (property.first == "Filename")
758 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500759 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500760 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700761 if (value != nullptr)
762 {
763 filename = *value;
764 }
765 }
766 else if (property.first == "Log")
767 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500768 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500769 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700770 if (value != nullptr)
771 {
772 logfile = *value;
773 }
774 }
775 }
776}
777
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500778constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800779class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700780{
781 public:
782 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500783 SystemLogServiceCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800784 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800785 {
786 entityPrivileges = {
787 {boost::beast::http::verb::get, {{"Login"}}},
788 {boost::beast::http::verb::head, {{"Login"}}},
789 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
790 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
791 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
792 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
793 }
794
795 private:
796 /**
797 * Functions triggers appropriate requests on DBus
798 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500799 void doGet(crow::Response& res, const crow::Request& req,
800 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800801 {
802 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800803 // Collections don't include the static data added by SubRoute because
804 // it has a duplicate entry for members
805 asyncResp->res.jsonValue["@odata.type"] =
806 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800807 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800808 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800809 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
810 asyncResp->res.jsonValue["Description"] =
811 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500812 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800813 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800814 logServiceArray.push_back(
815 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500816#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600817 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500818 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600819#endif
820
Jason M. Billsd53dd412019-02-12 17:16:22 -0800821#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
822 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500823 {{"@odata.id",
824 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800825#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800826 asyncResp->res.jsonValue["Members@odata.count"] =
827 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800828
829 crow::connections::systemBus->async_method_call(
830 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500831 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800832 if (ec)
833 {
834 BMCWEB_LOG_ERROR << ec;
835 return;
836 }
837
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500838 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800839 {
840 if (pathStr.find("PostCode") != std::string::npos)
841 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500842 nlohmann::json& logServiceArray =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800843 asyncResp->res.jsonValue["Members"];
844 logServiceArray.push_back(
845 {{"@odata.id", "/redfish/v1/Systems/system/"
846 "LogServices/PostCodes"}});
847 asyncResp->res.jsonValue["Members@odata.count"] =
848 logServiceArray.size();
849 return;
850 }
851 }
852 },
853 "xyz.openbmc_project.ObjectMapper",
854 "/xyz/openbmc_project/object_mapper",
855 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500856 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800857 }
858};
859
860class EventLogService : public Node
861{
862 public:
863 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500864 EventLogService(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800865 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800866 {
867 entityPrivileges = {
868 {boost::beast::http::verb::get, {{"Login"}}},
869 {boost::beast::http::verb::head, {{"Login"}}},
870 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
871 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
872 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
874 }
875
876 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500877 void doGet(crow::Response& res, const crow::Request& req,
878 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800879 {
880 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
881
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800882 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800883 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800884 asyncResp->res.jsonValue["@odata.type"] =
885 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800886 asyncResp->res.jsonValue["Name"] = "Event Log Service";
887 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500888 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800889 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
890 asyncResp->res.jsonValue["Entries"] = {
891 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800892 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500893 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
894
895 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
896 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700897 }
898};
899
Tim Lee1f56a3a2019-10-09 10:17:57 +0800900class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700901{
902 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500903 JournalEventLogClear(CrowApp& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700904 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
905 "LogService.ClearLog/")
906 {
907 entityPrivileges = {
908 {boost::beast::http::verb::get, {{"Login"}}},
909 {boost::beast::http::verb::head, {{"Login"}}},
910 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
911 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
912 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
913 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
914 }
915
916 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500917 void doPost(crow::Response& res, const crow::Request& req,
918 const std::vector<std::string>& params) override
Jason M. Bills489640c2019-05-17 09:56:36 -0700919 {
920 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
921
922 // Clear the EventLog by deleting the log files
923 std::vector<std::filesystem::path> redfishLogFiles;
924 if (getRedfishLogFiles(redfishLogFiles))
925 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500926 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -0700927 {
928 std::error_code ec;
929 std::filesystem::remove(file, ec);
930 }
931 }
932
933 // Reload rsyslog so it knows to start new log files
934 crow::connections::systemBus->async_method_call(
935 [asyncResp](const boost::system::error_code ec) {
936 if (ec)
937 {
938 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
939 messages::internalError(asyncResp->res);
940 return;
941 }
942
943 messages::success(asyncResp->res);
944 },
945 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
946 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
947 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800948 }
949};
950
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500951static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -0700952 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500953 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800954{
Jason M. Bills95820182019-04-22 16:25:34 -0700955 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700956 // First get the Timestamp
957 size_t space = logEntry.find_first_of(" ");
958 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700959 {
960 return 1;
961 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700962 std::string timestamp = logEntry.substr(0, space);
963 // Then get the log contents
964 size_t entryStart = logEntry.find_first_not_of(" ", space);
965 if (entryStart == std::string::npos)
966 {
967 return 1;
968 }
969 std::string_view entry(logEntry);
970 entry.remove_prefix(entryStart);
971 // Use split to separate the entry into its fields
972 std::vector<std::string> logEntryFields;
973 boost::split(logEntryFields, entry, boost::is_any_of(","),
974 boost::token_compress_on);
975 // We need at least a MessageId to be valid
976 if (logEntryFields.size() < 1)
977 {
978 return 1;
979 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500980 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700981
Jason M. Bills4851d452019-03-28 11:27:48 -0700982 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500983 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -0700984 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800985
Jason M. Bills4851d452019-03-28 11:27:48 -0700986 std::string msg;
987 std::string severity;
988 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800989 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700990 msg = message->message;
991 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800992 }
993
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700994 // Get the MessageArgs from the log if there are any
995 boost::beast::span<std::string> messageArgs;
996 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700997 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500998 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700999 // If the first string is empty, assume there are no MessageArgs
1000 std::size_t messageArgsSize = 0;
1001 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001002 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001003 messageArgsSize = logEntryFields.size() - 1;
1004 }
1005
1006 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
1007
1008 // Fill the MessageArgs into the Message
1009 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001010 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001011 {
1012 std::string argStr = "%" + std::to_string(++i);
1013 size_t argPos = msg.find(argStr);
1014 if (argPos != std::string::npos)
1015 {
1016 msg.replace(argPos, argStr.length(), messageArg);
1017 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001018 }
1019 }
1020
Jason M. Bills95820182019-04-22 16:25:34 -07001021 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1022 // format which matches the Redfish format except for the fractional seconds
1023 // between the '.' and the '+', so just remove them.
1024 std::size_t dot = timestamp.find_first_of(".");
1025 std::size_t plus = timestamp.find_first_of("+");
1026 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001027 {
Jason M. Bills95820182019-04-22 16:25:34 -07001028 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001029 }
1030
1031 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001032 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001033 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001034 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001035 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001036 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001037 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001038 {"Id", logEntryID},
1039 {"Message", std::move(msg)},
1040 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001041 {"MessageArgs", std::move(messageArgs)},
1042 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001043 {"Severity", std::move(severity)},
1044 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001045 return 0;
1046}
1047
Anthony Wilson27062602019-04-22 02:10:09 -05001048class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001049{
1050 public:
1051 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001052 JournalEventLogEntryCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001053 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001054 {
1055 entityPrivileges = {
1056 {boost::beast::http::verb::get, {{"Login"}}},
1057 {boost::beast::http::verb::head, {{"Login"}}},
1058 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1059 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1060 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1061 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1062 }
1063
1064 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001065 void doGet(crow::Response& res, const crow::Request& req,
1066 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001067 {
1068 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001069 uint64_t skip = 0;
1070 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001071 if (!getSkipParam(asyncResp->res, req, skip))
1072 {
1073 return;
1074 }
1075 if (!getTopParam(asyncResp->res, req, top))
1076 {
1077 return;
1078 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001079 // Collections don't include the static data added by SubRoute because
1080 // it has a duplicate entry for members
1081 asyncResp->res.jsonValue["@odata.type"] =
1082 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001083 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001084 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1086 asyncResp->res.jsonValue["Description"] =
1087 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001088
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001089 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001090 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001091 // Go through the log files and create a unique ID for each entry
1092 std::vector<std::filesystem::path> redfishLogFiles;
1093 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001094 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001095 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001096
1097 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001098 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1099 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001100 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001101 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001102 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103 {
1104 continue;
1105 }
1106
Jason M. Billse85d6b12019-07-29 17:01:15 -07001107 // Reset the unique ID on the first entry
1108 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001109 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001110 {
Jason M. Bills95820182019-04-22 16:25:34 -07001111 entryCount++;
1112 // Handle paging using skip (number of entries to skip from the
1113 // start) and top (number of entries to display)
1114 if (entryCount <= skip || entryCount > skip + top)
1115 {
1116 continue;
1117 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001118
Jason M. Bills95820182019-04-22 16:25:34 -07001119 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001120 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001121 {
1122 continue;
1123 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001124
Jason M. Billse85d6b12019-07-29 17:01:15 -07001125 if (firstEntry)
1126 {
1127 firstEntry = false;
1128 }
1129
Jason M. Bills95820182019-04-22 16:25:34 -07001130 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001131 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001132 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1133 {
1134 messages::internalError(asyncResp->res);
1135 return;
1136 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001137 }
1138 }
1139 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1140 if (skip + top < entryCount)
1141 {
1142 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001143 "/redfish/v1/Systems/system/LogServices/EventLog/"
1144 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001145 std::to_string(skip + top);
1146 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001147 }
1148};
1149
Jason M. Bills897967d2019-07-29 17:05:30 -07001150class JournalEventLogEntry : public Node
1151{
1152 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001153 JournalEventLogEntry(CrowApp& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001154 Node(app,
1155 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1156 std::string())
1157 {
1158 entityPrivileges = {
1159 {boost::beast::http::verb::get, {{"Login"}}},
1160 {boost::beast::http::verb::head, {{"Login"}}},
1161 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1162 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1163 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1164 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1165 }
1166
1167 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001168 void doGet(crow::Response& res, const crow::Request& req,
1169 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001170 {
1171 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1172 if (params.size() != 1)
1173 {
1174 messages::internalError(asyncResp->res);
1175 return;
1176 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001177 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001178
1179 // Go through the log files and check the unique ID for each entry to
1180 // find the target entry
1181 std::vector<std::filesystem::path> redfishLogFiles;
1182 getRedfishLogFiles(redfishLogFiles);
1183 std::string logEntry;
1184
1185 // Oldest logs are in the last file, so start there and loop backwards
1186 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1187 it++)
1188 {
1189 std::ifstream logStream(*it);
1190 if (!logStream.is_open())
1191 {
1192 continue;
1193 }
1194
1195 // Reset the unique ID on the first entry
1196 bool firstEntry = true;
1197 while (std::getline(logStream, logEntry))
1198 {
1199 std::string idStr;
1200 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1201 {
1202 continue;
1203 }
1204
1205 if (firstEntry)
1206 {
1207 firstEntry = false;
1208 }
1209
1210 if (idStr == targetID)
1211 {
1212 if (fillEventLogEntryJson(idStr, logEntry,
1213 asyncResp->res.jsonValue) != 0)
1214 {
1215 messages::internalError(asyncResp->res);
1216 return;
1217 }
1218 return;
1219 }
1220 }
1221 }
1222 // Requested ID was not found
1223 messages::resourceMissingAtURI(asyncResp->res, targetID);
1224 }
1225};
1226
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001227class DBusEventLogEntryCollection : public Node
1228{
1229 public:
1230 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001231 DBusEventLogEntryCollection(CrowApp& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001232 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1233 {
1234 entityPrivileges = {
1235 {boost::beast::http::verb::get, {{"Login"}}},
1236 {boost::beast::http::verb::head, {{"Login"}}},
1237 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1238 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1239 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1240 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1241 }
1242
1243 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001244 void doGet(crow::Response& res, const crow::Request& req,
1245 const std::vector<std::string>& params) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001246 {
1247 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1248
1249 // Collections don't include the static data added by SubRoute because
1250 // it has a duplicate entry for members
1251 asyncResp->res.jsonValue["@odata.type"] =
1252 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001253 asyncResp->res.jsonValue["@odata.id"] =
1254 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1255 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1256 asyncResp->res.jsonValue["Description"] =
1257 "Collection of System Event Log Entries";
1258
Andrew Geisslercb92c032018-08-17 07:56:14 -07001259 // DBus implementation of EventLog/Entries
1260 // Make call to Logging Service to find all log entry objects
1261 crow::connections::systemBus->async_method_call(
1262 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001263 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001264 if (ec)
1265 {
1266 // TODO Handle for specific error code
1267 BMCWEB_LOG_ERROR
1268 << "getLogEntriesIfaceData resp_handler got error "
1269 << ec;
1270 messages::internalError(asyncResp->res);
1271 return;
1272 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001273 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001274 asyncResp->res.jsonValue["Members"];
1275 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001276 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001277 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001278 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001279 {
1280 if (interfaceMap.first !=
1281 "xyz.openbmc_project.Logging.Entry")
1282 {
1283 BMCWEB_LOG_DEBUG << "Bailing early on "
1284 << interfaceMap.first;
1285 continue;
1286 }
1287 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001288 nlohmann::json& thisEntry = entriesArray.back();
1289 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001290 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001291 std::string* severity = nullptr;
1292 std::string* message = nullptr;
1293 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001294 {
1295 if (propertyMap.first == "Id")
1296 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001297 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001298 if (id == nullptr)
1299 {
1300 messages::propertyMissing(asyncResp->res,
1301 "Id");
1302 }
1303 }
1304 else if (propertyMap.first == "Timestamp")
1305 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001306 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001307 std::get_if<uint64_t>(&propertyMap.second);
1308 if (millisTimeStamp == nullptr)
1309 {
1310 messages::propertyMissing(asyncResp->res,
1311 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001312 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001313 }
1314 // Retrieve Created property with format:
1315 // yyyy-mm-ddThh:mm:ss
1316 std::chrono::milliseconds chronoTimeStamp(
1317 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001318 timestamp = std::chrono::duration_cast<
1319 std::chrono::duration<int>>(
1320 chronoTimeStamp)
1321 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001322 }
1323 else if (propertyMap.first == "Severity")
1324 {
1325 severity = std::get_if<std::string>(
1326 &propertyMap.second);
1327 if (severity == nullptr)
1328 {
1329 messages::propertyMissing(asyncResp->res,
1330 "Severity");
1331 }
1332 }
1333 else if (propertyMap.first == "Message")
1334 {
1335 message = std::get_if<std::string>(
1336 &propertyMap.second);
1337 if (message == nullptr)
1338 {
1339 messages::propertyMissing(asyncResp->res,
1340 "Message");
1341 }
1342 }
1343 }
1344 thisEntry = {
1345 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001346 {"@odata.id",
1347 "/redfish/v1/Systems/system/LogServices/EventLog/"
1348 "Entries/" +
1349 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001350 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001351 {"Id", std::to_string(*id)},
1352 {"Message", *message},
1353 {"EntryType", "Event"},
1354 {"Severity",
1355 translateSeverityDbusToRedfish(*severity)},
1356 {"Created", crow::utility::getDateTime(timestamp)}};
1357 }
1358 }
1359 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001360 [](const nlohmann::json& left,
1361 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001362 return (left["Id"] <= right["Id"]);
1363 });
1364 asyncResp->res.jsonValue["Members@odata.count"] =
1365 entriesArray.size();
1366 },
1367 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1368 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001369 }
1370};
1371
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001372class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001373{
1374 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001375 DBusEventLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001376 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001377 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1378 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001379 {
1380 entityPrivileges = {
1381 {boost::beast::http::verb::get, {{"Login"}}},
1382 {boost::beast::http::verb::head, {{"Login"}}},
1383 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1384 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1385 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1386 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1387 }
1388
1389 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001390 void doGet(crow::Response& res, const crow::Request& req,
1391 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001392 {
1393 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001394 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001395 {
1396 messages::internalError(asyncResp->res);
1397 return;
1398 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001399 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001400
Andrew Geisslercb92c032018-08-17 07:56:14 -07001401 // DBus implementation of EventLog/Entries
1402 // Make call to Logging Service to find all log entry objects
1403 crow::connections::systemBus->async_method_call(
1404 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001405 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001406 if (ec)
1407 {
1408 BMCWEB_LOG_ERROR
1409 << "EventLogEntry (DBus) resp_handler got error " << ec;
1410 messages::internalError(asyncResp->res);
1411 return;
1412 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001413 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001414 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001415 std::string* severity = nullptr;
1416 std::string* message = nullptr;
1417 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001418 {
1419 if (propertyMap.first == "Id")
1420 {
1421 id = std::get_if<uint32_t>(&propertyMap.second);
1422 if (id == nullptr)
1423 {
1424 messages::propertyMissing(asyncResp->res, "Id");
1425 }
1426 }
1427 else if (propertyMap.first == "Timestamp")
1428 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001429 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001430 std::get_if<uint64_t>(&propertyMap.second);
1431 if (millisTimeStamp == nullptr)
1432 {
1433 messages::propertyMissing(asyncResp->res,
1434 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001435 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001436 }
1437 // Retrieve Created property with format:
1438 // yyyy-mm-ddThh:mm:ss
1439 std::chrono::milliseconds chronoTimeStamp(
1440 *millisTimeStamp);
1441 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001442 std::chrono::duration_cast<
1443 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001444 .count();
1445 }
1446 else if (propertyMap.first == "Severity")
1447 {
1448 severity =
1449 std::get_if<std::string>(&propertyMap.second);
1450 if (severity == nullptr)
1451 {
1452 messages::propertyMissing(asyncResp->res,
1453 "Severity");
1454 }
1455 }
1456 else if (propertyMap.first == "Message")
1457 {
1458 message = std::get_if<std::string>(&propertyMap.second);
1459 if (message == nullptr)
1460 {
1461 messages::propertyMissing(asyncResp->res,
1462 "Message");
1463 }
1464 }
1465 }
Ed Tanous271584a2019-07-09 16:24:22 -07001466 if (id == nullptr || message == nullptr || severity == nullptr)
1467 {
1468 return;
1469 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001470 asyncResp->res.jsonValue = {
1471 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001472 {"@odata.id",
1473 "/redfish/v1/Systems/system/LogServices/EventLog/"
1474 "Entries/" +
1475 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001476 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001477 {"Id", std::to_string(*id)},
1478 {"Message", *message},
1479 {"EntryType", "Event"},
1480 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001481 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001482 },
1483 "xyz.openbmc_project.Logging",
1484 "/xyz/openbmc_project/logging/entry/" + entryID,
1485 "org.freedesktop.DBus.Properties", "GetAll",
1486 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001487 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001488
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001489 void doDelete(crow::Response& res, const crow::Request& req,
1490 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001491 {
1492
1493 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1494
1495 auto asyncResp = std::make_shared<AsyncResp>(res);
1496
1497 if (params.size() != 1)
1498 {
1499 messages::internalError(asyncResp->res);
1500 return;
1501 }
1502 std::string entryID = params[0];
1503
1504 dbus::utility::escapePathForDbus(entryID);
1505
1506 // Process response from Logging service.
1507 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1508 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1509 if (ec)
1510 {
1511 // TODO Handle for specific error code
1512 BMCWEB_LOG_ERROR
1513 << "EventLogEntry (DBus) doDelete respHandler got error "
1514 << ec;
1515 asyncResp->res.result(
1516 boost::beast::http::status::internal_server_error);
1517 return;
1518 }
1519
1520 asyncResp->res.result(boost::beast::http::status::ok);
1521 };
1522
1523 // Make call to Logging service to request Delete Log
1524 crow::connections::systemBus->async_method_call(
1525 respHandler, "xyz.openbmc_project.Logging",
1526 "/xyz/openbmc_project/logging/entry/" + entryID,
1527 "xyz.openbmc_project.Object.Delete", "Delete");
1528 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001529};
1530
1531class BMCLogServiceCollection : public Node
1532{
1533 public:
1534 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001535 BMCLogServiceCollection(CrowApp& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001536 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001537 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001538 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001539 {boost::beast::http::verb::get, {{"Login"}}},
1540 {boost::beast::http::verb::head, {{"Login"}}},
1541 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1542 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1543 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1544 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001545 }
1546
1547 private:
1548 /**
1549 * Functions triggers appropriate requests on DBus
1550 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001551 void doGet(crow::Response& res, const crow::Request& req,
1552 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001553 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001554 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001555 // Collections don't include the static data added by SubRoute because
1556 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001557 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001558 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001559 asyncResp->res.jsonValue["@odata.id"] =
1560 "/redfish/v1/Managers/bmc/LogServices";
1561 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1562 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001563 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001564 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001565 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001566#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1567 logServiceArray.push_back(
1568 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1569#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001570#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1571 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001572 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001573#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001574 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001575 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001576 }
1577};
1578
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001579class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001580{
1581 public:
1582 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001583 BMCJournalLogService(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001584 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001585 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001586 entityPrivileges = {
1587 {boost::beast::http::verb::get, {{"Login"}}},
1588 {boost::beast::http::verb::head, {{"Login"}}},
1589 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1590 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1591 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1592 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1593 }
1594
1595 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001596 void doGet(crow::Response& res, const crow::Request& req,
1597 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001598 {
1599 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001600 asyncResp->res.jsonValue["@odata.type"] =
1601 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001602 asyncResp->res.jsonValue["@odata.id"] =
1603 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001604 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1605 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1606 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001607 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001608 asyncResp->res.jsonValue["Entries"] = {
1609 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001610 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001611 }
1612};
1613
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001614static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1615 sd_journal* journal,
1616 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001617{
1618 // Get the Log Entry contents
1619 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001620
Ed Tanous39e77502019-03-04 17:35:53 -08001621 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001622 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001623 if (ret < 0)
1624 {
1625 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1626 return 1;
1627 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001628
1629 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001630 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001631 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001632 if (ret < 0)
1633 {
1634 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001635 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001636
1637 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001638 std::string entryTimeStr;
1639 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001640 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001641 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001642 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001643
1644 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001645 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001646 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001647 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1648 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001649 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001650 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001651 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001652 {"EntryType", "Oem"},
1653 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001654 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001655 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001656 {"Created", std::move(entryTimeStr)}};
1657 return 0;
1658}
1659
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001660class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001661{
1662 public:
1663 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001664 BMCJournalLogEntryCollection(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001665 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001666 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001667 entityPrivileges = {
1668 {boost::beast::http::verb::get, {{"Login"}}},
1669 {boost::beast::http::verb::head, {{"Login"}}},
1670 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1671 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1672 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1673 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1674 }
1675
1676 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001677 void doGet(crow::Response& res, const crow::Request& req,
1678 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001679 {
1680 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001681 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001682 uint64_t skip = 0;
1683 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001684 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001685 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001686 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001687 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001688 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001689 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001690 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001691 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001692 // Collections don't include the static data added by SubRoute because
1693 // it has a duplicate entry for members
1694 asyncResp->res.jsonValue["@odata.type"] =
1695 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001696 asyncResp->res.jsonValue["@odata.id"] =
1697 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001698 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001699 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001700 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1701 asyncResp->res.jsonValue["Description"] =
1702 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001703 asyncResp->res.jsonValue["@odata.id"] =
1704 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001705 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001706 logEntryArray = nlohmann::json::array();
1707
1708 // Go through the journal and use the timestamp to create a unique ID
1709 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001710 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001711 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1712 if (ret < 0)
1713 {
1714 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001715 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001716 return;
1717 }
1718 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1719 journalTmp, sd_journal_close);
1720 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001721 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001722 // Reset the unique ID on the first entry
1723 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001724 SD_JOURNAL_FOREACH(journal.get())
1725 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001726 entryCount++;
1727 // Handle paging using skip (number of entries to skip from the
1728 // start) and top (number of entries to display)
1729 if (entryCount <= skip || entryCount > skip + top)
1730 {
1731 continue;
1732 }
1733
Jason M. Bills16428a12018-11-02 12:42:29 -07001734 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001735 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001736 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001737 continue;
1738 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001739
Jason M. Billse85d6b12019-07-29 17:01:15 -07001740 if (firstEntry)
1741 {
1742 firstEntry = false;
1743 }
1744
Jason M. Billse1f26342018-07-18 12:12:00 -07001745 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001746 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001747 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1748 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001749 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001750 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001751 return;
1752 }
1753 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001754 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1755 if (skip + top < entryCount)
1756 {
1757 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001758 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001759 std::to_string(skip + top);
1760 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001761 }
1762};
1763
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001764class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001765{
1766 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001767 BMCJournalLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001768 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001769 std::string())
1770 {
1771 entityPrivileges = {
1772 {boost::beast::http::verb::get, {{"Login"}}},
1773 {boost::beast::http::verb::head, {{"Login"}}},
1774 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1775 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1776 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1777 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1778 }
1779
1780 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001781 void doGet(crow::Response& res, const crow::Request& req,
1782 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001783 {
1784 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1785 if (params.size() != 1)
1786 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001787 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001788 return;
1789 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001790 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001791 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001792 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001793 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001794 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001795 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001796 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001797 }
1798
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001799 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001800 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1801 if (ret < 0)
1802 {
1803 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001804 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001805 return;
1806 }
1807 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1808 journalTmp, sd_journal_close);
1809 journalTmp = nullptr;
1810 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001811 // tracking the unique ID
1812 std::string idStr;
1813 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001814 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301815 if (ret < 0)
1816 {
1817 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1818 << strerror(-ret);
1819 messages::internalError(asyncResp->res);
1820 return;
1821 }
Ed Tanous271584a2019-07-09 16:24:22 -07001822 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001823 {
1824 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001825 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1826 {
1827 messages::internalError(asyncResp->res);
1828 return;
1829 }
1830 if (firstEntry)
1831 {
1832 firstEntry = false;
1833 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001834 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001835 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001836 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001837 {
1838 messages::resourceMissingAtURI(asyncResp->res, entryID);
1839 return;
1840 }
1841
1842 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1843 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001844 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001845 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001846 return;
1847 }
1848 }
1849};
1850
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001851class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001852{
1853 public:
1854 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001855 BMCDumpService(CrowApp& app) :
1856 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001857 {
1858 entityPrivileges = {
1859 {boost::beast::http::verb::get, {{"Login"}}},
1860 {boost::beast::http::verb::head, {{"Login"}}},
1861 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1862 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1863 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1864 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1865 }
1866
1867 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001868 void doGet(crow::Response& res, const crow::Request& req,
1869 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001870 {
1871 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1872
1873 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001874 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06001875 asyncResp->res.jsonValue["@odata.type"] =
1876 "#LogService.v1_1_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001877 asyncResp->res.jsonValue["Name"] = "Dump LogService";
1878 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
1879 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06001880 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06001881 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001882 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
1883 asyncResp->res.jsonValue["Actions"] = {
1884 {"#LogService.ClearLog",
1885 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
1886 "Actions/LogService.ClearLog"}}},
1887 {"Oem",
1888 {{"#OemLogService.CollectDiagnosticData",
1889 {{"target",
1890 "/redfish/v1/Managers/bmc/LogServices/Dump/"
1891 "Actions/Oem/OemLogService.CollectDiagnosticData"}}}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06001892 }
1893};
1894
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001895class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001896{
1897 public:
1898 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001899 BMCDumpEntryCollection(CrowApp& app) :
1900 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001901 {
1902 entityPrivileges = {
1903 {boost::beast::http::verb::get, {{"Login"}}},
1904 {boost::beast::http::verb::head, {{"Login"}}},
1905 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1906 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1907 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1908 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1909 }
1910
1911 private:
1912 /**
1913 * Functions triggers appropriate requests on DBus
1914 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001915 void doGet(crow::Response& res, const crow::Request& req,
1916 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001917 {
1918 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1919
1920 asyncResp->res.jsonValue["@odata.type"] =
1921 "#LogEntryCollection.LogEntryCollection";
1922 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001923 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
1924 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06001925 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001926 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06001927
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001928 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06001929 }
1930};
1931
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001932class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001933{
1934 public:
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001935 BMCDumpEntry(CrowApp& app) :
1936 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06001937 std::string())
1938 {
1939 entityPrivileges = {
1940 {boost::beast::http::verb::get, {{"Login"}}},
1941 {boost::beast::http::verb::head, {{"Login"}}},
1942 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1943 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1944 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1945 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1946 }
1947
1948 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001949 void doGet(crow::Response& res, const crow::Request& req,
1950 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001951 {
1952 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1953 if (params.size() != 1)
1954 {
1955 messages::internalError(asyncResp->res);
1956 return;
1957 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001958 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06001959 }
1960
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001961 void doDelete(crow::Response& res, const crow::Request& req,
1962 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001963 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001964 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06001965 if (params.size() != 1)
1966 {
1967 messages::internalError(asyncResp->res);
1968 return;
1969 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001970 deleteDumpEntry(asyncResp->res, params[0]);
1971 }
1972};
raviteja-bc9bb6862020-02-03 11:53:32 -06001973
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001974class SystemDumpService : public Node
1975{
1976 public:
1977 template <typename CrowApp>
1978 SystemDumpService(CrowApp& app) :
1979 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
1980 {
1981 entityPrivileges = {
1982 {boost::beast::http::verb::get, {{"Login"}}},
1983 {boost::beast::http::verb::head, {{"Login"}}},
1984 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1985 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1986 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1987 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1988 }
raviteja-bc9bb6862020-02-03 11:53:32 -06001989
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001990 private:
1991 void doGet(crow::Response& res, const crow::Request& req,
1992 const std::vector<std::string>& params) override
1993 {
1994 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06001995
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001996 asyncResp->res.jsonValue["@odata.id"] =
1997 "/redfish/v1/Systems/system/LogServices/Dump";
1998 asyncResp->res.jsonValue["@odata.type"] =
1999 "#LogService.v1_1_0.LogService";
2000 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2001 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2002 asyncResp->res.jsonValue["Id"] = "Dump";
2003 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2004 asyncResp->res.jsonValue["Entries"] = {
2005 {"@odata.id",
2006 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2007 asyncResp->res.jsonValue["Actions"] = {
2008 {"#LogService.ClearLog",
2009 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2010 "LogService.ClearLog"}}},
2011 {"Oem",
2012 {{"#OemLogService.CollectDiagnosticData",
2013 {{"target",
2014 "/redfish/v1/Systems/system/LogServices/Dump/Actions/Oem/"
2015 "OemLogService.CollectDiagnosticData"}}}}}};
2016 }
2017};
2018
2019class SystemDumpEntryCollection : public Node
2020{
2021 public:
2022 template <typename CrowApp>
2023 SystemDumpEntryCollection(CrowApp& app) :
2024 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2025 {
2026 entityPrivileges = {
2027 {boost::beast::http::verb::get, {{"Login"}}},
2028 {boost::beast::http::verb::head, {{"Login"}}},
2029 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2030 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2031 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2032 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2033 }
2034
2035 private:
2036 /**
2037 * Functions triggers appropriate requests on DBus
2038 */
2039 void doGet(crow::Response& res, const crow::Request& req,
2040 const std::vector<std::string>& params) override
2041 {
2042 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2043
2044 asyncResp->res.jsonValue["@odata.type"] =
2045 "#LogEntryCollection.LogEntryCollection";
2046 asyncResp->res.jsonValue["@odata.id"] =
2047 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2048 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2049 asyncResp->res.jsonValue["Description"] =
2050 "Collection of System Dump Entries";
2051
2052 getDumpEntryCollection(asyncResp, "System");
2053 }
2054};
2055
2056class SystemDumpEntry : public Node
2057{
2058 public:
2059 SystemDumpEntry(CrowApp& app) :
2060 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2061 std::string())
2062 {
2063 entityPrivileges = {
2064 {boost::beast::http::verb::get, {{"Login"}}},
2065 {boost::beast::http::verb::head, {{"Login"}}},
2066 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2067 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2068 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2069 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2070 }
2071
2072 private:
2073 void doGet(crow::Response& res, const crow::Request& req,
2074 const std::vector<std::string>& params) override
2075 {
2076 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2077 if (params.size() != 1)
2078 {
2079 messages::internalError(asyncResp->res);
2080 return;
2081 }
2082 getDumpEntryById(asyncResp, params[0], "System");
2083 }
2084
2085 void doDelete(crow::Response& res, const crow::Request& req,
2086 const std::vector<std::string>& params) override
2087 {
2088 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2089 if (params.size() != 1)
2090 {
2091 messages::internalError(asyncResp->res);
2092 return;
2093 }
2094 deleteDumpEntry(asyncResp->res, params[0]);
raviteja-bc9bb6862020-02-03 11:53:32 -06002095 }
2096};
2097
raviteja-b06578432020-02-03 12:50:42 -06002098class SystemDumpEntryDownload : public Node
2099{
2100 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002101 SystemDumpEntryDownload(CrowApp& app) :
raviteja-b06578432020-02-03 12:50:42 -06002102 Node(app,
2103 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
2104 "Actions/"
2105 "LogEntry.DownloadLog/",
2106 std::string())
2107 {
2108 entityPrivileges = {
2109 {boost::beast::http::verb::get, {{"Login"}}},
2110 {boost::beast::http::verb::head, {{"Login"}}},
2111 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2112 }
2113
2114 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002115 void doPost(crow::Response& res, const crow::Request& req,
2116 const std::vector<std::string>& params) override
raviteja-b06578432020-02-03 12:50:42 -06002117 {
2118 if (params.size() != 1)
2119 {
2120 messages::internalError(res);
2121 return;
2122 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002123 const std::string& entryID = params[0];
raviteja-b06578432020-02-03 12:50:42 -06002124 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
2125 }
2126};
2127
raviteja-b013487e2020-03-03 03:20:48 -06002128class SystemDumpClear : public Node
2129{
2130 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002131 SystemDumpClear(CrowApp& app) :
raviteja-b013487e2020-03-03 03:20:48 -06002132 Node(app, "/redfish/v1/Systems/system/LogServices/System/"
2133 "Actions/"
2134 "LogService.ClearLog/")
2135 {
2136 entityPrivileges = {
2137 {boost::beast::http::verb::get, {{"Login"}}},
2138 {boost::beast::http::verb::head, {{"Login"}}},
2139 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2140 }
2141
2142 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002143 void doPost(crow::Response& res, const crow::Request& req,
2144 const std::vector<std::string>& params) override
raviteja-b013487e2020-03-03 03:20:48 -06002145 {
2146
2147 auto asyncResp = std::make_shared<AsyncResp>(res);
2148 crow::connections::systemBus->async_method_call(
2149 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002150 const std::vector<std::string>& dumpList) {
raviteja-b013487e2020-03-03 03:20:48 -06002151 if (ec)
2152 {
2153 messages::internalError(asyncResp->res);
2154 return;
2155 }
2156
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002157 for (const std::string& objectPath : dumpList)
raviteja-b013487e2020-03-03 03:20:48 -06002158 {
2159 std::size_t pos = objectPath.rfind("/");
2160 if (pos != std::string::npos)
2161 {
2162 std::string logID = objectPath.substr(pos + 1);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002163 deleteDumpEntry(asyncResp->res, logID);
raviteja-b013487e2020-03-03 03:20:48 -06002164 }
2165 }
2166 },
2167 "xyz.openbmc_project.ObjectMapper",
2168 "/xyz/openbmc_project/object_mapper",
2169 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2170 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002171 std::array<const char*, 1>{
raviteja-b013487e2020-03-03 03:20:48 -06002172 "xyz.openbmc_project.Dump.Entry.System"});
2173 }
2174};
2175
Jason M. Bills424c4172019-03-21 13:50:33 -07002176class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002177{
2178 public:
2179 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002180 CrashdumpService(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002181 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002182 {
AppaRao Puli39460282020-04-07 17:03:04 +05302183 // Note: Deviated from redfish privilege registry for GET & HEAD
2184 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002185 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302186 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2187 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002188 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2189 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2190 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2191 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002192 }
2193
2194 private:
2195 /**
2196 * Functions triggers appropriate requests on DBus
2197 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002198 void doGet(crow::Response& res, const crow::Request& req,
2199 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002200 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002201 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002202 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002203 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002204 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002205 asyncResp->res.jsonValue["@odata.type"] =
2206 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002207 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2208 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2209 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002210 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2211 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002212 asyncResp->res.jsonValue["Entries"] = {
2213 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002214 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002215 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002216 {"#LogService.ClearLog",
2217 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2218 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002219 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002220 {{"#Crashdump.OnDemand",
2221 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002222 "Actions/Oem/Crashdump.OnDemand"}}},
2223 {"#Crashdump.Telemetry",
2224 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2225 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002226
2227#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002228 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002229 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002230 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2231 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002232#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002233 }
2234};
2235
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002236class CrashdumpClear : public Node
2237{
2238 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002239 CrashdumpClear(CrowApp& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002240 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2241 "LogService.ClearLog/")
2242 {
AppaRao Puli39460282020-04-07 17:03:04 +05302243 // Note: Deviated from redfish privilege registry for GET & HEAD
2244 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002245 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302246 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2247 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002248 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2249 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2250 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2251 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2252 }
2253
2254 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002255 void doPost(crow::Response& res, const crow::Request& req,
2256 const std::vector<std::string>& params) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002257 {
2258 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2259
2260 crow::connections::systemBus->async_method_call(
2261 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002262 const std::string& resp) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002263 if (ec)
2264 {
2265 messages::internalError(asyncResp->res);
2266 return;
2267 }
2268 messages::success(asyncResp->res);
2269 },
2270 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2271 }
2272};
2273
Jason M. Billse855dd22019-10-08 11:37:48 -07002274static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002275 const std::string& logID,
2276 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002277{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002278 auto getStoredLogCallback =
2279 [asyncResp, logID, &logEntryJson](
2280 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002281 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002282 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002283 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002284 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2285 if (ec.value() ==
2286 boost::system::linux_error::bad_request_descriptor)
2287 {
2288 messages::resourceNotFound(asyncResp->res, "LogEntry",
2289 logID);
2290 }
2291 else
2292 {
2293 messages::internalError(asyncResp->res);
2294 }
2295 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002296 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002297
Johnathan Mantey043a0532020-03-10 17:15:28 -07002298 std::string timestamp{};
2299 std::string filename{};
2300 std::string logfile{};
2301 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2302
2303 if (filename.empty() || timestamp.empty())
2304 {
2305 messages::resourceMissingAtURI(asyncResp->res, logID);
2306 return;
2307 }
2308
2309 std::string crashdumpURI =
2310 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2311 logID + "/" + filename;
2312 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2313 {"@odata.id", "/redfish/v1/Systems/system/"
2314 "LogServices/Crashdump/Entries/" +
2315 logID},
2316 {"Name", "CPU Crashdump"},
2317 {"Id", logID},
2318 {"EntryType", "Oem"},
2319 {"OemRecordFormat", "Crashdump URI"},
2320 {"Message", std::move(crashdumpURI)},
2321 {"Created", std::move(timestamp)}};
2322 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002323 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002324 std::move(getStoredLogCallback), crashdumpObject,
2325 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002326 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002327}
2328
Jason M. Bills424c4172019-03-21 13:50:33 -07002329class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002330{
2331 public:
2332 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002333 CrashdumpEntryCollection(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002334 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002335 {
AppaRao Puli39460282020-04-07 17:03:04 +05302336 // Note: Deviated from redfish privilege registry for GET & HEAD
2337 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002338 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302339 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2340 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002341 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2342 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2343 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2344 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002345 }
2346
2347 private:
2348 /**
2349 * Functions triggers appropriate requests on DBus
2350 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002351 void doGet(crow::Response& res, const crow::Request& req,
2352 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002353 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002354 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002355 // Collections don't include the static data added by SubRoute because
2356 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002357 auto getLogEntriesCallback = [asyncResp](
2358 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002359 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002360 if (ec)
2361 {
2362 if (ec.value() !=
2363 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002364 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002365 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2366 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002367 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002368 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002369 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002370 }
2371 asyncResp->res.jsonValue["@odata.type"] =
2372 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002373 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002374 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002375 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002376 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002377 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002378 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002379 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002380 std::vector<std::string> logIDs;
2381 // Get the list of log entries and build up an empty array big
2382 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002383 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002384 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002385 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002386 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002387 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002388 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002389 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002390 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002391 logIDs.emplace_back(objpath.substr(lastPos + 1));
2392
2393 // Add a space for the log entry to the array
2394 logEntryArray.push_back({});
2395 }
2396 // Now go through and set up async calls to fill in the entries
2397 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002398 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002399 {
2400 // Add the log entry to the array
2401 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002402 }
2403 asyncResp->res.jsonValue["Members@odata.count"] =
2404 logEntryArray.size();
2405 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002406 crow::connections::systemBus->async_method_call(
2407 std::move(getLogEntriesCallback),
2408 "xyz.openbmc_project.ObjectMapper",
2409 "/xyz/openbmc_project/object_mapper",
2410 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002411 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002412 }
2413};
2414
Jason M. Bills424c4172019-03-21 13:50:33 -07002415class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002416{
2417 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002418 CrashdumpEntry(CrowApp& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002419 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002420 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002421 std::string())
2422 {
AppaRao Puli39460282020-04-07 17:03:04 +05302423 // Note: Deviated from redfish privilege registry for GET & HEAD
2424 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002425 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302426 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2427 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002428 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2429 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2430 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2431 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002432 }
2433
2434 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002435 void doGet(crow::Response& res, const crow::Request& req,
2436 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002437 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002438 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002439 if (params.size() != 1)
2440 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002441 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002442 return;
2443 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002444 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002445 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2446 }
2447};
2448
2449class CrashdumpFile : public Node
2450{
2451 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002452 CrashdumpFile(CrowApp& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002453 Node(app,
2454 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2455 "<str>/",
2456 std::string(), std::string())
2457 {
AppaRao Puli39460282020-04-07 17:03:04 +05302458 // Note: Deviated from redfish privilege registry for GET & HEAD
2459 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002460 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302461 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2462 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002463 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2464 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2465 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2466 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2467 }
2468
2469 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002470 void doGet(crow::Response& res, const crow::Request& req,
2471 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002472 {
2473 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2474 if (params.size() != 2)
2475 {
2476 messages::internalError(asyncResp->res);
2477 return;
2478 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002479 const std::string& logID = params[0];
2480 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002481
Johnathan Mantey043a0532020-03-10 17:15:28 -07002482 auto getStoredLogCallback =
2483 [asyncResp, logID, fileName](
2484 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002485 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002486 if (ec)
2487 {
2488 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2489 << ec.message();
2490 messages::internalError(asyncResp->res);
2491 return;
2492 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002493
Johnathan Mantey043a0532020-03-10 17:15:28 -07002494 std::string dbusFilename{};
2495 std::string dbusTimestamp{};
2496 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002497
Johnathan Mantey043a0532020-03-10 17:15:28 -07002498 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2499 dbusFilepath);
2500
2501 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2502 dbusFilepath.empty())
2503 {
2504 messages::resourceMissingAtURI(asyncResp->res, fileName);
2505 return;
2506 }
2507
2508 // Verify the file name parameter is correct
2509 if (fileName != dbusFilename)
2510 {
2511 messages::resourceMissingAtURI(asyncResp->res, fileName);
2512 return;
2513 }
2514
2515 if (!std::filesystem::exists(dbusFilepath))
2516 {
2517 messages::resourceMissingAtURI(asyncResp->res, fileName);
2518 return;
2519 }
2520 std::ifstream ifs(dbusFilepath, std::ios::in |
2521 std::ios::binary |
2522 std::ios::ate);
2523 std::ifstream::pos_type fileSize = ifs.tellg();
2524 if (fileSize < 0)
2525 {
2526 messages::generalError(asyncResp->res);
2527 return;
2528 }
2529 ifs.seekg(0, std::ios::beg);
2530
2531 auto crashData = std::make_unique<char[]>(
2532 static_cast<unsigned int>(fileSize));
2533
2534 ifs.read(crashData.get(), static_cast<int>(fileSize));
2535
2536 // The cast to std::string is intentional in order to use the
2537 // assign() that applies move mechanics
2538 asyncResp->res.body().assign(
2539 static_cast<std::string>(crashData.get()));
2540
2541 // Configure this to be a file download when accessed from
2542 // a browser
2543 asyncResp->res.addHeader("Content-Disposition", "attachment");
2544 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002545 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002546 std::move(getStoredLogCallback), crashdumpObject,
2547 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002548 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002549 }
2550};
2551
Jason M. Bills424c4172019-03-21 13:50:33 -07002552class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002553{
2554 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002555 OnDemandCrashdump(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002556 Node(app,
2557 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2558 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002559 {
AppaRao Puli39460282020-04-07 17:03:04 +05302560 // Note: Deviated from redfish privilege registry for GET & HEAD
2561 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002562 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302563 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2564 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2565 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2566 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2567 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2568 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002569 }
2570
2571 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002572 void doPost(crow::Response& res, const crow::Request& req,
2573 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002574 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002575 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002576
James Feistfe306722020-03-12 16:32:08 -07002577 auto generateonDemandLogCallback = [asyncResp,
2578 req](const boost::system::error_code
2579 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002580 const std::string& resp) {
James Feist46229572020-02-19 15:11:58 -08002581 if (ec)
2582 {
2583 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002584 {
James Feist46229572020-02-19 15:11:58 -08002585 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002586 }
James Feist46229572020-02-19 15:11:58 -08002587 else if (ec.value() ==
2588 boost::system::errc::device_or_resource_busy)
2589 {
2590 messages::serviceTemporarilyUnavailable(asyncResp->res,
2591 "60");
2592 }
2593 else
2594 {
2595 messages::internalError(asyncResp->res);
2596 }
2597 return;
2598 }
2599 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002600 [](boost::system::error_code err, sdbusplus::message::message&,
2601 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002602 if (!err)
2603 {
James Feiste5d50062020-05-11 17:29:00 -07002604 taskData->messages.emplace_back(
2605 messages::taskCompletedOK(
2606 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002607 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002608 }
James Feist32898ce2020-03-10 16:16:52 -07002609 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002610 },
James Feist46229572020-02-19 15:11:58 -08002611 "type='signal',interface='org.freedesktop.DBus.Properties',"
2612 "member='PropertiesChanged',arg0namespace='com.intel."
2613 "crashdump'");
2614 task->startTimer(std::chrono::minutes(5));
2615 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002616 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002617 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002618 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002619 std::move(generateonDemandLogCallback), crashdumpObject,
2620 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002621 }
2622};
2623
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002624class TelemetryCrashdump : public Node
2625{
2626 public:
2627 TelemetryCrashdump(CrowApp& app) :
2628 Node(app,
2629 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2630 "Crashdump.Telemetry/")
2631 {
2632 // Note: Deviated from redfish privilege registry for GET & HEAD
2633 // method for security reasons.
2634 entityPrivileges = {
2635 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2636 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2637 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2638 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2639 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2640 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2641 }
2642
2643 private:
2644 void doPost(crow::Response& res, const crow::Request& req,
2645 const std::vector<std::string>& params) override
2646 {
2647 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2648
2649 auto generateTelemetryLogCallback = [asyncResp, req](
2650 const boost::system::error_code
2651 ec,
2652 const std::string& resp) {
2653 if (ec)
2654 {
2655 if (ec.value() == boost::system::errc::operation_not_supported)
2656 {
2657 messages::resourceInStandby(asyncResp->res);
2658 }
2659 else if (ec.value() ==
2660 boost::system::errc::device_or_resource_busy)
2661 {
2662 messages::serviceTemporarilyUnavailable(asyncResp->res,
2663 "60");
2664 }
2665 else
2666 {
2667 messages::internalError(asyncResp->res);
2668 }
2669 return;
2670 }
2671 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2672 [](boost::system::error_code err, sdbusplus::message::message&,
2673 const std::shared_ptr<task::TaskData>& taskData) {
2674 if (!err)
2675 {
2676 taskData->messages.emplace_back(
2677 messages::taskCompletedOK(
2678 std::to_string(taskData->index)));
2679 taskData->state = "Completed";
2680 }
2681 return task::completed;
2682 },
2683 "type='signal',interface='org.freedesktop.DBus.Properties',"
2684 "member='PropertiesChanged',arg0namespace='com.intel."
2685 "crashdump'");
2686 task->startTimer(std::chrono::minutes(5));
2687 task->populateResp(asyncResp->res);
2688 task->payload.emplace(req);
2689 };
2690 crow::connections::systemBus->async_method_call(
2691 std::move(generateTelemetryLogCallback), crashdumpObject,
2692 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2693 }
2694};
2695
Jason M. Billse1f26342018-07-18 12:12:00 -07002696class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002697{
2698 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002699 SendRawPECI(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002700 Node(app,
2701 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2702 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002703 {
AppaRao Puli39460282020-04-07 17:03:04 +05302704 // Note: Deviated from redfish privilege registry for GET & HEAD
2705 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002706 entityPrivileges = {
2707 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2708 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2709 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2710 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2711 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2712 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2713 }
2714
2715 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002716 void doPost(crow::Response& res, const crow::Request& req,
2717 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002718 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002719 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002720 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002721
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002722 nlohmann::json reqJson =
2723 nlohmann::json::parse(req.body, nullptr, false);
2724 if (reqJson.find("PECICommands") != reqJson.end())
2725 {
2726 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2727 {
2728 return;
2729 }
2730 uint32_t idx = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002731 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002732 {
2733 if (cmd.size() < 3)
2734 {
2735 std::string s("[");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002736 for (auto const& val : cmd)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002737 {
2738 if (val != *cmd.begin())
2739 {
2740 s += ",";
2741 }
2742 s += std::to_string(val);
2743 }
2744 s += "]";
2745 messages::actionParameterValueFormatError(
2746 res, s, "PECICommands[" + std::to_string(idx) + "]",
2747 "SendRawPeci");
2748 return;
2749 }
2750 idx++;
2751 }
2752 }
2753 else
2754 {
2755 /* This interface is deprecated */
2756 uint8_t clientAddress = 0;
2757 uint8_t readLength = 0;
2758 std::vector<uint8_t> peciCommand;
2759 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2760 "ReadLength", readLength, "PECICommand",
2761 peciCommand))
2762 {
2763 return;
2764 }
2765 peciCommands.push_back({clientAddress, 0, readLength});
2766 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2767 peciCommand.end());
2768 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002769 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002770 auto sendRawPECICallback =
2771 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002772 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002773 if (ec)
2774 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002775 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002776 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002777 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002778 return;
2779 }
2780 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2781 {"PECIResponse", resp}};
2782 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002783 // Call the SendRawPECI command with the provided data
2784 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002785 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002786 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002787 }
2788};
2789
Andrew Geisslercb92c032018-08-17 07:56:14 -07002790/**
2791 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2792 */
2793class DBusLogServiceActionsClear : public Node
2794{
2795 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002796 DBusLogServiceActionsClear(CrowApp& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002797 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002798 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002799 {
2800 entityPrivileges = {
2801 {boost::beast::http::verb::get, {{"Login"}}},
2802 {boost::beast::http::verb::head, {{"Login"}}},
2803 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2804 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2805 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2806 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2807 }
2808
2809 private:
2810 /**
2811 * Function handles POST method request.
2812 * The Clear Log actions does not require any parameter.The action deletes
2813 * all entries found in the Entries collection for this Log Service.
2814 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002815 void doPost(crow::Response& res, const crow::Request& req,
2816 const std::vector<std::string>& params) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002817 {
2818 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2819
2820 auto asyncResp = std::make_shared<AsyncResp>(res);
2821 // Process response from Logging service.
2822 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2823 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2824 if (ec)
2825 {
2826 // TODO Handle for specific error code
2827 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2828 asyncResp->res.result(
2829 boost::beast::http::status::internal_server_error);
2830 return;
2831 }
2832
2833 asyncResp->res.result(boost::beast::http::status::no_content);
2834 };
2835
2836 // Make call to Logging service to request Clear Log
2837 crow::connections::systemBus->async_method_call(
2838 resp_handler, "xyz.openbmc_project.Logging",
2839 "/xyz/openbmc_project/logging",
2840 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2841 }
2842};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002843
2844/****************************************************
2845 * Redfish PostCode interfaces
2846 * using DBUS interface: getPostCodesTS
2847 ******************************************************/
2848class PostCodesLogService : public Node
2849{
2850 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002851 PostCodesLogService(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002852 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2853 {
2854 entityPrivileges = {
2855 {boost::beast::http::verb::get, {{"Login"}}},
2856 {boost::beast::http::verb::head, {{"Login"}}},
2857 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2858 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2859 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2860 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2861 }
2862
2863 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002864 void doGet(crow::Response& res, const crow::Request& req,
2865 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002866 {
2867 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2868
2869 asyncResp->res.jsonValue = {
2870 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2871 {"@odata.type", "#LogService.v1_1_0.LogService"},
2872 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2873 {"Name", "POST Code Log Service"},
2874 {"Description", "POST Code Log Service"},
2875 {"Id", "BIOS POST Code Log"},
2876 {"OverWritePolicy", "WrapsWhenFull"},
2877 {"Entries",
2878 {{"@odata.id",
2879 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2880 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2881 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2882 "Actions/LogService.ClearLog"}};
2883 }
2884};
2885
2886class PostCodesClear : public Node
2887{
2888 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002889 PostCodesClear(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002890 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2891 "LogService.ClearLog/")
2892 {
2893 entityPrivileges = {
2894 {boost::beast::http::verb::get, {{"Login"}}},
2895 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302896 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2897 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2898 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2899 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002900 }
2901
2902 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002903 void doPost(crow::Response& res, const crow::Request& req,
2904 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002905 {
2906 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2907
2908 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2909 // Make call to post-code service to request clear all
2910 crow::connections::systemBus->async_method_call(
2911 [asyncResp](const boost::system::error_code ec) {
2912 if (ec)
2913 {
2914 // TODO Handle for specific error code
2915 BMCWEB_LOG_ERROR
2916 << "doClearPostCodes resp_handler got error " << ec;
2917 asyncResp->res.result(
2918 boost::beast::http::status::internal_server_error);
2919 messages::internalError(asyncResp->res);
2920 return;
2921 }
2922 },
2923 "xyz.openbmc_project.State.Boot.PostCode",
2924 "/xyz/openbmc_project/State/Boot/PostCode",
2925 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2926 }
2927};
2928
2929static void fillPostCodeEntry(
2930 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002931 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002932 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2933 const uint64_t skip = 0, const uint64_t top = 0)
2934{
2935 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002936 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08002937 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002938
2939 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002940 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002941
2942 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002943 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002944 {
2945 currentCodeIndex++;
2946 std::string postcodeEntryID =
2947 "B" + std::to_string(bootIndex) + "-" +
2948 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2949
2950 uint64_t usecSinceEpoch = code.first;
2951 uint64_t usTimeOffset = 0;
2952
2953 if (1 == currentCodeIndex)
2954 { // already incremented
2955 firstCodeTimeUs = code.first;
2956 }
2957 else
2958 {
2959 usTimeOffset = code.first - firstCodeTimeUs;
2960 }
2961
2962 // skip if no specific codeIndex is specified and currentCodeIndex does
2963 // not fall between top and skip
2964 if ((codeIndex == 0) &&
2965 (currentCodeIndex <= skip || currentCodeIndex > top))
2966 {
2967 continue;
2968 }
2969
Gunnar Mills4e0453b2020-07-08 14:00:30 -05002970 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08002971 // currentIndex
2972 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2973 {
2974 // This is done for simplicity. 1st entry is needed to calculate
2975 // time offset. To improve efficiency, one can get to the entry
2976 // directly (possibly with flatmap's nth method)
2977 continue;
2978 }
2979
2980 // currentCodeIndex is within top and skip or equal to specified code
2981 // index
2982
2983 // Get the Created time from the timestamp
2984 std::string entryTimeStr;
2985 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2986 {
2987 continue;
2988 }
2989
2990 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2991 std::ostringstream hexCode;
2992 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2993 << code.second;
2994 std::ostringstream timeOffsetStr;
2995 // Set Fixed -Point Notation
2996 timeOffsetStr << std::fixed;
2997 // Set precision to 4 digits
2998 timeOffsetStr << std::setprecision(4);
2999 // Add double to stream
3000 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3001 std::vector<std::string> messageArgs = {
3002 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3003
3004 // Get MessageArgs template from message registry
3005 std::string msg;
3006 if (message != nullptr)
3007 {
3008 msg = message->message;
3009
3010 // fill in this post code value
3011 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003012 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003013 {
3014 std::string argStr = "%" + std::to_string(++i);
3015 size_t argPos = msg.find(argStr);
3016 if (argPos != std::string::npos)
3017 {
3018 msg.replace(argPos, argStr.length(), messageArg);
3019 }
3020 }
3021 }
3022
Tim Leed4342a92020-04-27 11:47:58 +08003023 // Get Severity template from message registry
3024 std::string severity;
3025 if (message != nullptr)
3026 {
3027 severity = message->severity;
3028 }
3029
ZhikuiRena3316fc2020-01-29 14:58:08 -08003030 // add to AsyncResp
3031 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003032 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003033 bmcLogEntry = {
3034 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3035 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
3036 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3037 "PostCodes/Entries/" +
3038 postcodeEntryID},
3039 {"Name", "POST Code Log Entry"},
3040 {"Id", postcodeEntryID},
3041 {"Message", std::move(msg)},
3042 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3043 {"MessageArgs", std::move(messageArgs)},
3044 {"EntryType", "Event"},
3045 {"Severity", std::move(severity)},
3046 {"Created", std::move(entryTimeStr)}};
3047 }
3048}
3049
3050static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
3051 const uint16_t bootIndex,
3052 const uint64_t codeIndex)
3053{
3054 crow::connections::systemBus->async_method_call(
3055 [aResp, bootIndex, codeIndex](
3056 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003057 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003058 if (ec)
3059 {
3060 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3061 messages::internalError(aResp->res);
3062 return;
3063 }
3064
3065 // skip the empty postcode boots
3066 if (postcode.empty())
3067 {
3068 return;
3069 }
3070
3071 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3072
3073 aResp->res.jsonValue["Members@odata.count"] =
3074 aResp->res.jsonValue["Members"].size();
3075 },
3076 "xyz.openbmc_project.State.Boot.PostCode",
3077 "/xyz/openbmc_project/State/Boot/PostCode",
3078 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3079 bootIndex);
3080}
3081
3082static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
3083 const uint16_t bootIndex,
3084 const uint16_t bootCount,
3085 const uint64_t entryCount, const uint64_t skip,
3086 const uint64_t top)
3087{
3088 crow::connections::systemBus->async_method_call(
3089 [aResp, bootIndex, bootCount, entryCount, skip,
3090 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003091 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003092 if (ec)
3093 {
3094 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3095 messages::internalError(aResp->res);
3096 return;
3097 }
3098
3099 uint64_t endCount = entryCount;
3100 if (!postcode.empty())
3101 {
3102 endCount = entryCount + postcode.size();
3103
3104 if ((skip < endCount) && ((top + skip) > entryCount))
3105 {
3106 uint64_t thisBootSkip =
3107 std::max(skip, entryCount) - entryCount;
3108 uint64_t thisBootTop =
3109 std::min(top + skip, endCount) - entryCount;
3110
3111 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3112 thisBootSkip, thisBootTop);
3113 }
3114 aResp->res.jsonValue["Members@odata.count"] = endCount;
3115 }
3116
3117 // continue to previous bootIndex
3118 if (bootIndex < bootCount)
3119 {
3120 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3121 bootCount, endCount, skip, top);
3122 }
3123 else
3124 {
3125 aResp->res.jsonValue["Members@odata.nextLink"] =
3126 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3127 "Entries?$skip=" +
3128 std::to_string(skip + top);
3129 }
3130 },
3131 "xyz.openbmc_project.State.Boot.PostCode",
3132 "/xyz/openbmc_project/State/Boot/PostCode",
3133 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3134 bootIndex);
3135}
3136
3137static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
3138 const uint64_t skip, const uint64_t top)
3139{
3140 uint64_t entryCount = 0;
3141 crow::connections::systemBus->async_method_call(
3142 [aResp, entryCount, skip,
3143 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003144 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003145 if (ec)
3146 {
3147 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3148 messages::internalError(aResp->res);
3149 return;
3150 }
3151 auto pVal = std::get_if<uint16_t>(&bootCount);
3152 if (pVal)
3153 {
3154 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3155 }
3156 else
3157 {
3158 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3159 }
3160 },
3161 "xyz.openbmc_project.State.Boot.PostCode",
3162 "/xyz/openbmc_project/State/Boot/PostCode",
3163 "org.freedesktop.DBus.Properties", "Get",
3164 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3165}
3166
3167class PostCodesEntryCollection : public Node
3168{
3169 public:
3170 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003171 PostCodesEntryCollection(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003172 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3173 {
3174 entityPrivileges = {
3175 {boost::beast::http::verb::get, {{"Login"}}},
3176 {boost::beast::http::verb::head, {{"Login"}}},
3177 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3178 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3179 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3180 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3181 }
3182
3183 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003184 void doGet(crow::Response& res, const crow::Request& req,
3185 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003186 {
3187 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3188
3189 asyncResp->res.jsonValue["@odata.type"] =
3190 "#LogEntryCollection.LogEntryCollection";
3191 asyncResp->res.jsonValue["@odata.context"] =
3192 "/redfish/v1/"
3193 "$metadata#LogEntryCollection.LogEntryCollection";
3194 asyncResp->res.jsonValue["@odata.id"] =
3195 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3196 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3197 asyncResp->res.jsonValue["Description"] =
3198 "Collection of POST Code Log Entries";
3199 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3200 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3201
3202 uint64_t skip = 0;
3203 uint64_t top = maxEntriesPerPage; // Show max entries by default
3204 if (!getSkipParam(asyncResp->res, req, skip))
3205 {
3206 return;
3207 }
3208 if (!getTopParam(asyncResp->res, req, top))
3209 {
3210 return;
3211 }
3212 getCurrentBootNumber(asyncResp, skip, top);
3213 }
3214};
3215
3216class PostCodesEntry : public Node
3217{
3218 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003219 PostCodesEntry(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003220 Node(app,
3221 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3222 std::string())
3223 {
3224 entityPrivileges = {
3225 {boost::beast::http::verb::get, {{"Login"}}},
3226 {boost::beast::http::verb::head, {{"Login"}}},
3227 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3228 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3229 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3230 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3231 }
3232
3233 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003234 void doGet(crow::Response& res, const crow::Request& req,
3235 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236 {
3237 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3238 if (params.size() != 1)
3239 {
3240 messages::internalError(asyncResp->res);
3241 return;
3242 }
3243
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003244 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003245
3246 size_t bootPos = targetID.find('B');
3247 if (bootPos == std::string::npos)
3248 {
3249 // Requested ID was not found
3250 messages::resourceMissingAtURI(asyncResp->res, targetID);
3251 return;
3252 }
3253 std::string_view bootIndexStr(targetID);
3254 bootIndexStr.remove_prefix(bootPos + 1);
3255 uint16_t bootIndex = 0;
3256 uint64_t codeIndex = 0;
3257 size_t dashPos = bootIndexStr.find('-');
3258
3259 if (dashPos == std::string::npos)
3260 {
3261 return;
3262 }
3263 std::string_view codeIndexStr(bootIndexStr);
3264 bootIndexStr.remove_suffix(dashPos);
3265 codeIndexStr.remove_prefix(dashPos + 1);
3266
3267 bootIndex = static_cast<uint16_t>(
3268 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
3269 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
3270 if (bootIndex == 0 || codeIndex == 0)
3271 {
3272 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3273 << params[0];
3274 }
3275
3276 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3277 asyncResp->res.jsonValue["@odata.context"] =
3278 "/redfish/v1/$metadata#LogEntry.LogEntry";
3279 asyncResp->res.jsonValue["@odata.id"] =
3280 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3281 "Entries";
3282 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3283 asyncResp->res.jsonValue["Description"] =
3284 "Collection of POST Code Log Entries";
3285 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3286 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3287
3288 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3289 }
3290};
3291
Ed Tanous1da66f72018-07-27 16:13:37 -07003292} // namespace redfish