blob: 5c16cadc9ebb13e679a19b04be17b90c5974df48 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* crashdumpObject = "com.intel.crashdump";
40constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* crashdumpInterface = "com.intel.crashdump";
42constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070043 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070048constexpr char const* crashdumpTelemetryInterface =
49 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070050
Jason M. Bills4851d452019-03-28 11:27:48 -070051namespace message_registries
52{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053static const Message* getMessageFromRegistry(
54 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070055 const boost::beast::span<const MessageEntry> registry)
56{
57 boost::beast::span<const MessageEntry>::const_iterator messageIt =
58 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050059 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070060 return !std::strcmp(messageEntry.first,
61 messageKey.c_str());
62 });
63 if (messageIt != registry.cend())
64 {
65 return &messageIt->second;
66 }
67
68 return nullptr;
69}
70
Gunnar Mills1214b7e2020-06-04 10:11:30 -050071static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070072{
73 // Redfish MessageIds are in the form
74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
75 // the right Message
76 std::vector<std::string> fields;
77 fields.reserve(4);
78 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 std::string& registryName = fields[0];
80 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070081
82 // Find the right registry and check it for the MessageKey
83 if (std::string(base::header.registryPrefix) == registryName)
84 {
85 return getMessageFromRegistry(
86 messageKey, boost::beast::span<const MessageEntry>(base::registry));
87 }
88 if (std::string(openbmc::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey,
92 boost::beast::span<const MessageEntry>(openbmc::registry));
93 }
94 return nullptr;
95}
96} // namespace message_registries
97
James Feistf6150402019-01-08 10:36:20 -080098namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070099
Andrew Geisslercb92c032018-08-17 07:56:14 -0700100using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500101 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
102 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700103
104using GetManagedObjectsType = boost::container::flat_map<
105 sdbusplus::message::object_path,
106 boost::container::flat_map<std::string, GetManagedPropertyType>>;
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
110 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
111 {
112 return "Critical";
113 }
114 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
115 {
116 return "Critical";
117 }
118 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
119 {
120 return "OK";
121 }
122 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
123 {
124 return "Critical";
125 }
126 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
127 {
128 return "Critical";
129 }
130 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
131 {
132 return "OK";
133 }
134 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
135 {
136 return "OK";
137 }
138 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
139 {
140 return "Warning";
141 }
142 return "";
143}
144
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500145static int getJournalMetadata(sd_journal* journal,
146 const std::string_view& field,
147 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700148{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500149 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 size_t length = 0;
151 int ret = 0;
152 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700153 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500154 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700155 if (ret < 0)
156 {
157 return ret;
158 }
Ed Tanous39e77502019-03-04 17:35:53 -0800159 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700160 // Only use the content after the "=" character.
161 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
162 return ret;
163}
164
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500165static int getJournalMetadata(sd_journal* journal,
166 const std::string_view& field, const int& base,
167 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700168{
169 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800170 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700171 // Get the metadata from the requested field of the journal entry
172 ret = getJournalMetadata(journal, field, metadata);
173 if (ret < 0)
174 {
175 return ret;
176 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000177 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700178 return ret;
179}
180
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500181static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800182{
183 int ret = 0;
184 uint64_t timestamp = 0;
185 ret = sd_journal_get_realtime_usec(journal, &timestamp);
186 if (ret < 0)
187 {
188 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
189 << strerror(-ret);
190 return false;
191 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500192 entryTimestamp = crow::utility::getDateTime(
193 static_cast<std::time_t>(timestamp / 1000 / 1000));
194 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800195}
196
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500197static bool getSkipParam(crow::Response& res, const crow::Request& req,
198 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700199{
James Feist5a7e8772020-07-22 09:08:38 -0700200 boost::urls::url_view::params_type::iterator it =
201 req.urlParams.find("$skip");
202 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700203 {
James Feist5a7e8772020-07-22 09:08:38 -0700204 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700206 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
207 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700208 {
209
210 messages::queryParameterValueTypeError(res, std::string(skipParam),
211 "$skip");
212 return false;
213 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700214 }
215 return true;
216}
217
Ed Tanous271584a2019-07-09 16:24:22 -0700218static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500219static bool getTopParam(crow::Response& res, const crow::Request& req,
220 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700221{
James Feist5a7e8772020-07-22 09:08:38 -0700222 boost::urls::url_view::params_type::iterator it =
223 req.urlParams.find("$top");
224 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700225 {
James Feist5a7e8772020-07-22 09:08:38 -0700226 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500227 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700228 top = std::strtoul(topParam.c_str(), &ptr, 10);
229 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700230 {
231 messages::queryParameterValueTypeError(res, std::string(topParam),
232 "$top");
233 return false;
234 }
Ed Tanous271584a2019-07-09 16:24:22 -0700235 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700236 {
237
238 messages::queryParameterOutOfRange(
239 res, std::to_string(top), "$top",
240 "1-" + std::to_string(maxEntriesPerPage));
241 return false;
242 }
243 }
244 return true;
245}
246
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500247static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700248 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700249{
250 int ret = 0;
251 static uint64_t prevTs = 0;
252 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700253 if (firstEntry)
254 {
255 prevTs = 0;
256 }
257
Jason M. Bills16428a12018-11-02 12:42:29 -0700258 // Get the entry timestamp
259 uint64_t curTs = 0;
260 ret = sd_journal_get_realtime_usec(journal, &curTs);
261 if (ret < 0)
262 {
263 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
264 << strerror(-ret);
265 return false;
266 }
267 // If the timestamp isn't unique, increment the index
268 if (curTs == prevTs)
269 {
270 index++;
271 }
272 else
273 {
274 // Otherwise, reset it
275 index = 0;
276 }
277 // Save the timestamp
278 prevTs = curTs;
279
280 entryID = std::to_string(curTs);
281 if (index > 0)
282 {
283 entryID += "_" + std::to_string(index);
284 }
285 return true;
286}
287
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500288static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700289 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700290{
Ed Tanous271584a2019-07-09 16:24:22 -0700291 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700292 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700293 if (firstEntry)
294 {
295 prevTs = 0;
296 }
297
Jason M. Bills95820182019-04-22 16:25:34 -0700298 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700299 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700300 std::tm timeStruct = {};
301 std::istringstream entryStream(logEntry);
302 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
303 {
304 curTs = std::mktime(&timeStruct);
305 }
306 // If the timestamp isn't unique, increment the index
307 if (curTs == prevTs)
308 {
309 index++;
310 }
311 else
312 {
313 // Otherwise, reset it
314 index = 0;
315 }
316 // Save the timestamp
317 prevTs = curTs;
318
319 entryID = std::to_string(curTs);
320 if (index > 0)
321 {
322 entryID += "_" + std::to_string(index);
323 }
324 return true;
325}
326
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500327static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
328 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700329{
330 if (entryID.empty())
331 {
332 return false;
333 }
334 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800335 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700336
337 auto underscorePos = tsStr.find("_");
338 if (underscorePos != tsStr.npos)
339 {
340 // Timestamp has an index
341 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800342 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700343 indexStr.remove_prefix(underscorePos + 1);
344 std::size_t pos;
345 try
346 {
Ed Tanous39e77502019-03-04 17:35:53 -0800347 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700348 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500349 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700350 {
351 messages::resourceMissingAtURI(res, entryID);
352 return false;
353 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500354 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700355 {
356 messages::resourceMissingAtURI(res, entryID);
357 return false;
358 }
359 if (pos != indexStr.size())
360 {
361 messages::resourceMissingAtURI(res, entryID);
362 return false;
363 }
364 }
365 // Timestamp has no index
366 std::size_t pos;
367 try
368 {
Ed Tanous39e77502019-03-04 17:35:53 -0800369 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700370 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500371 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700372 {
373 messages::resourceMissingAtURI(res, entryID);
374 return false;
375 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500376 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700377 {
378 messages::resourceMissingAtURI(res, entryID);
379 return false;
380 }
381 if (pos != tsStr.size())
382 {
383 messages::resourceMissingAtURI(res, entryID);
384 return false;
385 }
386 return true;
387}
388
Jason M. Bills95820182019-04-22 16:25:34 -0700389static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500390 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700391{
392 static const std::filesystem::path redfishLogDir = "/var/log";
393 static const std::string redfishLogFilename = "redfish";
394
395 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500396 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700397 std::filesystem::directory_iterator(redfishLogDir))
398 {
399 // If we find a redfish log file, save the path
400 std::string filename = dirEnt.path().filename();
401 if (boost::starts_with(filename, redfishLogFilename))
402 {
403 redfishLogFiles.emplace_back(redfishLogDir / filename);
404 }
405 }
406 // As the log files rotate, they are appended with a ".#" that is higher for
407 // the older logs. Since we don't expect more than 10 log files, we
408 // can just sort the list to get them in order from newest to oldest
409 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
410
411 return !redfishLogFiles.empty();
412}
413
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500414inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
415 const std::string& dumpType)
416{
417 std::string dumpPath;
418 if (dumpType == "BMC")
419 {
420 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
421 }
422 else if (dumpType == "System")
423 {
424 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
425 }
426 else
427 {
428 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
429 messages::internalError(asyncResp->res);
430 return;
431 }
432
433 crow::connections::systemBus->async_method_call(
434 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
435 GetManagedObjectsType& resp) {
436 if (ec)
437 {
438 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
439 messages::internalError(asyncResp->res);
440 return;
441 }
442
443 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
444 entriesArray = nlohmann::json::array();
445
446 for (auto& object : resp)
447 {
448 bool foundDumpEntry = false;
449 for (auto& interfaceMap : object.second)
450 {
451 if (interfaceMap.first ==
452 ("xyz.openbmc_project.Dump.Entry." + dumpType))
453 {
454 foundDumpEntry = true;
455 break;
456 }
457 }
458
459 if (foundDumpEntry == false)
460 {
461 continue;
462 }
463 std::time_t timestamp;
464 uint64_t size = 0;
465 entriesArray.push_back({});
466 nlohmann::json& thisEntry = entriesArray.back();
467 const std::string& path =
468 static_cast<const std::string&>(object.first);
469 std::size_t lastPos = path.rfind("/");
470 if (lastPos == std::string::npos)
471 {
472 continue;
473 }
474 std::string entryID = path.substr(lastPos + 1);
475
476 for (auto& interfaceMap : object.second)
477 {
478 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
479 {
480
481 for (auto& propertyMap : interfaceMap.second)
482 {
483 if (propertyMap.first == "Size")
484 {
485 auto sizePtr =
486 std::get_if<uint64_t>(&propertyMap.second);
487 if (sizePtr == nullptr)
488 {
489 messages::internalError(asyncResp->res);
490 break;
491 }
492 size = *sizePtr;
493 break;
494 }
495 }
496 }
497 else if (interfaceMap.first ==
498 "xyz.openbmc_project.Time.EpochTime")
499 {
500
501 for (auto& propertyMap : interfaceMap.second)
502 {
503 if (propertyMap.first == "Elapsed")
504 {
505 const uint64_t* usecsTimeStamp =
506 std::get_if<uint64_t>(&propertyMap.second);
507 if (usecsTimeStamp == nullptr)
508 {
509 messages::internalError(asyncResp->res);
510 break;
511 }
512 timestamp =
513 static_cast<std::time_t>(*usecsTimeStamp);
514 break;
515 }
516 }
517 }
518 }
519
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500520 thisEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500521 thisEntry["@odata.id"] = dumpPath + entryID;
522 thisEntry["Id"] = entryID;
523 thisEntry["EntryType"] = "Event";
524 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
525 thisEntry["Name"] = dumpType + " Dump Entry";
526
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500527 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500528
529 if (dumpType == "BMC")
530 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500531 thisEntry["DiagnosticDataType"] = "Manager";
532 thisEntry["AdditionalDataURI"] =
533 "/redfish/v1/Managers/bmc/LogServices/Dump/"
534 "attachment/" +
535 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500536 }
537 else if (dumpType == "System")
538 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500539 thisEntry["DiagnosticDataType"] = "OEM";
540 thisEntry["OEMDiagnosticDataType"] = "System";
541 thisEntry["AdditionalDataURI"] =
542 "/redfish/v1/Systems/system/LogServices/Dump/"
543 "attachment/" +
544 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500545 }
546 }
547 asyncResp->res.jsonValue["Members@odata.count"] =
548 entriesArray.size();
549 },
550 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
551 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
552}
553
554inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
555 const std::string& entryID,
556 const std::string& dumpType)
557{
558 std::string dumpPath;
559 if (dumpType == "BMC")
560 {
561 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
562 }
563 else if (dumpType == "System")
564 {
565 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
566 }
567 else
568 {
569 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
570 messages::internalError(asyncResp->res);
571 return;
572 }
573
574 crow::connections::systemBus->async_method_call(
575 [asyncResp, entryID, dumpPath, dumpType](
576 const boost::system::error_code ec, GetManagedObjectsType& resp) {
577 if (ec)
578 {
579 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
580 messages::internalError(asyncResp->res);
581 return;
582 }
583
584 for (auto& objectPath : resp)
585 {
586 if (objectPath.first.str.find(
587 "/xyz/openbmc_project/dump/entry/" + entryID) ==
588 std::string::npos)
589 {
590 continue;
591 }
592
593 bool foundDumpEntry = false;
594 for (auto& interfaceMap : objectPath.second)
595 {
596 if (interfaceMap.first ==
597 ("xyz.openbmc_project.Dump.Entry." + dumpType))
598 {
599 foundDumpEntry = true;
600 break;
601 }
602 }
603 if (foundDumpEntry == false)
604 {
605 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
606 messages::internalError(asyncResp->res);
607 return;
608 }
609
610 std::time_t timestamp;
611 uint64_t size = 0;
612
613 for (auto& interfaceMap : objectPath.second)
614 {
615 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
616 {
617 for (auto& propertyMap : interfaceMap.second)
618 {
619 if (propertyMap.first == "Size")
620 {
621 auto sizePtr =
622 std::get_if<uint64_t>(&propertyMap.second);
623 if (sizePtr == nullptr)
624 {
625 messages::internalError(asyncResp->res);
626 break;
627 }
628 size = *sizePtr;
629 break;
630 }
631 }
632 }
633 else if (interfaceMap.first ==
634 "xyz.openbmc_project.Time.EpochTime")
635 {
636 for (auto& propertyMap : interfaceMap.second)
637 {
638 if (propertyMap.first == "Elapsed")
639 {
640 const uint64_t* usecsTimeStamp =
641 std::get_if<uint64_t>(&propertyMap.second);
642 if (usecsTimeStamp == nullptr)
643 {
644 messages::internalError(asyncResp->res);
645 break;
646 }
647 timestamp =
648 static_cast<std::time_t>(*usecsTimeStamp);
649 break;
650 }
651 }
652 }
653 }
654
655 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500656 "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500657 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
658 asyncResp->res.jsonValue["Id"] = entryID;
659 asyncResp->res.jsonValue["EntryType"] = "Event";
660 asyncResp->res.jsonValue["Created"] =
661 crow::utility::getDateTime(timestamp);
662 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
663
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500664 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500665
666 if (dumpType == "BMC")
667 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500668 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
669 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500670 "/redfish/v1/Managers/bmc/LogServices/Dump/"
671 "attachment/" +
672 entryID;
673 }
674 else if (dumpType == "System")
675 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500676 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
677 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500678 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500679 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500680 "/redfish/v1/Systems/system/LogServices/Dump/"
681 "attachment/" +
682 entryID;
683 }
684 }
685 },
686 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
687 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
688}
689
690inline void deleteDumpEntry(crow::Response& res, const std::string& entryID)
691{
692 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
693
694 auto respHandler = [asyncResp](const boost::system::error_code ec) {
695 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
696 if (ec)
697 {
698 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
699 << ec;
700 messages::internalError(asyncResp->res);
701 return;
702 }
703 };
704 crow::connections::systemBus->async_method_call(
705 respHandler, "xyz.openbmc_project.Dump.Manager",
706 "/xyz/openbmc_project/dump/entry/" + entryID,
707 "xyz.openbmc_project.Object.Delete", "Delete");
708}
709
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500710inline void createDumpTaskCallback(const crow::Request& req,
711 std::shared_ptr<AsyncResp> asyncResp,
712 const uint32_t& dumpId,
713 const std::string& dumpPath,
714 const std::string& dumpType)
715{
716 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500717 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500718 boost::system::error_code err, sdbusplus::message::message& m,
719 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000720 if (err)
721 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500722 BMCWEB_LOG_ERROR << "Error in creating a dump";
723 taskData->state = "Cancelled";
724 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000725 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 std::vector<std::pair<
727 std::string,
728 std::vector<std::pair<std::string, std::variant<std::string>>>>>
729 interfacesList;
730
731 sdbusplus::message::object_path objPath;
732
733 m.read(objPath, interfacesList);
734
735 for (auto& interface : interfacesList)
736 {
737 if (interface.first ==
738 ("xyz.openbmc_project.Dump.Entry." + dumpType))
739 {
740 nlohmann::json retMessage = messages::success();
741 taskData->messages.emplace_back(retMessage);
742
743 std::string headerLoc =
744 "Location: " + dumpPath + std::to_string(dumpId);
745 taskData->payload->httpHeaders.emplace_back(
746 std::move(headerLoc));
747
748 taskData->state = "Completed";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500749 break;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500750 }
751 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500752 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500753 },
754 "type='signal',interface='org.freedesktop.DBus."
755 "ObjectManager',"
756 "member='InterfacesAdded', "
757 "path='/xyz/openbmc_project/dump'");
758
759 task->startTimer(std::chrono::minutes(3));
760 task->populateResp(asyncResp->res);
761 task->payload.emplace(req);
762}
763
764inline void createDump(crow::Response& res, const crow::Request& req,
765 const std::string& dumpType)
766{
767 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
768
769 std::string dumpPath;
770 if (dumpType == "BMC")
771 {
772 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
773 }
774 else if (dumpType == "System")
775 {
776 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
777 }
778 else
779 {
780 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
781 messages::internalError(asyncResp->res);
782 return;
783 }
784
785 std::optional<std::string> diagnosticDataType;
786 std::optional<std::string> oemDiagnosticDataType;
787
788 if (!redfish::json_util::readJson(
789 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
790 "OEMDiagnosticDataType", oemDiagnosticDataType))
791 {
792 return;
793 }
794
795 if (dumpType == "System")
796 {
797 if (!oemDiagnosticDataType || !diagnosticDataType)
798 {
799 BMCWEB_LOG_ERROR << "CreateDump action parameter "
800 "'DiagnosticDataType'/"
801 "'OEMDiagnosticDataType' value not found!";
802 messages::actionParameterMissing(
803 asyncResp->res, "CollectDiagnosticData",
804 "DiagnosticDataType & OEMDiagnosticDataType");
805 return;
806 }
807 else if ((*oemDiagnosticDataType != "System") ||
808 (*diagnosticDataType != "OEM"))
809 {
810 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
811 messages::invalidObject(asyncResp->res,
812 "System Dump creation parameters");
813 return;
814 }
815 }
816 else if (dumpType == "BMC")
817 {
818 if (!diagnosticDataType)
819 {
820 BMCWEB_LOG_ERROR << "CreateDump action parameter "
821 "'DiagnosticDataType' not found!";
822 messages::actionParameterMissing(
823 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
824 return;
825 }
826 else if (*diagnosticDataType != "Manager")
827 {
828 BMCWEB_LOG_ERROR
829 << "Wrong parameter value passed for 'DiagnosticDataType'";
830 messages::invalidObject(asyncResp->res,
831 "BMC Dump creation parameters");
832 return;
833 }
834 }
835
836 crow::connections::systemBus->async_method_call(
837 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
838 const uint32_t& dumpId) {
839 if (ec)
840 {
841 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
842 messages::internalError(asyncResp->res);
843 return;
844 }
845 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
846
847 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
848 },
849 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
850 "xyz.openbmc_project.Dump.Create", "CreateDump");
851}
852
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500853inline void clearDump(crow::Response& res, const std::string& dumpInterface)
854{
855 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
856 crow::connections::systemBus->async_method_call(
857 [asyncResp](const boost::system::error_code ec,
858 const std::vector<std::string>& subTreePaths) {
859 if (ec)
860 {
861 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
862 messages::internalError(asyncResp->res);
863 return;
864 }
865
866 for (const std::string& path : subTreePaths)
867 {
868 std::size_t pos = path.rfind("/");
869 if (pos != std::string::npos)
870 {
871 std::string logID = path.substr(pos + 1);
872 deleteDumpEntry(asyncResp->res, logID);
873 }
874 }
875 },
876 "xyz.openbmc_project.ObjectMapper",
877 "/xyz/openbmc_project/object_mapper",
878 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
879 "/xyz/openbmc_project/dump", 0,
880 std::array<std::string, 1>{dumpInterface});
881}
882
Johnathan Mantey043a0532020-03-10 17:15:28 -0700883static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500884 const std::vector<std::pair<std::string, VariantType>>& params,
885 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700886{
887 for (auto property : params)
888 {
889 if (property.first == "Timestamp")
890 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500891 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500892 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700893 if (value != nullptr)
894 {
895 timestamp = *value;
896 }
897 }
898 else if (property.first == "Filename")
899 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500900 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500901 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700902 if (value != nullptr)
903 {
904 filename = *value;
905 }
906 }
907 else if (property.first == "Log")
908 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500909 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500910 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700911 if (value != nullptr)
912 {
913 logfile = *value;
914 }
915 }
916 }
917}
918
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500919constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800920class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700921{
922 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700923 SystemLogServiceCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800924 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800925 {
926 entityPrivileges = {
927 {boost::beast::http::verb::get, {{"Login"}}},
928 {boost::beast::http::verb::head, {{"Login"}}},
929 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
930 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
931 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
932 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
933 }
934
935 private:
936 /**
937 * Functions triggers appropriate requests on DBus
938 */
Ed Tanouscb13a392020-07-25 19:02:03 +0000939 void doGet(crow::Response& res, const crow::Request&,
940 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800941 {
942 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800943 // Collections don't include the static data added by SubRoute because
944 // it has a duplicate entry for members
945 asyncResp->res.jsonValue["@odata.type"] =
946 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800947 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800948 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800949 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
950 asyncResp->res.jsonValue["Description"] =
951 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500952 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800953 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800954 logServiceArray.push_back(
955 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500956#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600957 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500958 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600959#endif
960
Jason M. Billsd53dd412019-02-12 17:16:22 -0800961#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
962 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500963 {{"@odata.id",
964 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800965#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800966 asyncResp->res.jsonValue["Members@odata.count"] =
967 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800968
969 crow::connections::systemBus->async_method_call(
970 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500971 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800972 if (ec)
973 {
974 BMCWEB_LOG_ERROR << ec;
975 return;
976 }
977
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500978 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800979 {
980 if (pathStr.find("PostCode") != std::string::npos)
981 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000982 nlohmann::json& logServiceArrayLocal =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800983 asyncResp->res.jsonValue["Members"];
Ed Tanous23a21a12020-07-25 04:45:05 +0000984 logServiceArrayLocal.push_back(
ZhikuiRena3316fc2020-01-29 14:58:08 -0800985 {{"@odata.id", "/redfish/v1/Systems/system/"
986 "LogServices/PostCodes"}});
987 asyncResp->res.jsonValue["Members@odata.count"] =
Ed Tanous23a21a12020-07-25 04:45:05 +0000988 logServiceArrayLocal.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800989 return;
990 }
991 }
992 },
993 "xyz.openbmc_project.ObjectMapper",
994 "/xyz/openbmc_project/object_mapper",
995 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500996 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800997 }
998};
999
1000class EventLogService : public Node
1001{
1002 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001003 EventLogService(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001004 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001005 {
1006 entityPrivileges = {
1007 {boost::beast::http::verb::get, {{"Login"}}},
1008 {boost::beast::http::verb::head, {{"Login"}}},
1009 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1010 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1011 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1012 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1013 }
1014
1015 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001016 void doGet(crow::Response& res, const crow::Request&,
1017 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001018 {
1019 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1020
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001021 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001022 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001023 asyncResp->res.jsonValue["@odata.type"] =
1024 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001025 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1026 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001027 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001028 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1029 asyncResp->res.jsonValue["Entries"] = {
1030 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001031 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001032 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1033
1034 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1035 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001036 }
1037};
1038
Tim Lee1f56a3a2019-10-09 10:17:57 +08001039class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001040{
1041 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001042 JournalEventLogClear(App& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001043 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1044 "LogService.ClearLog/")
1045 {
1046 entityPrivileges = {
1047 {boost::beast::http::verb::get, {{"Login"}}},
1048 {boost::beast::http::verb::head, {{"Login"}}},
1049 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1050 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1051 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1052 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1053 }
1054
1055 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001056 void doPost(crow::Response& res, const crow::Request&,
1057 const std::vector<std::string>&) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001058 {
1059 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1060
1061 // Clear the EventLog by deleting the log files
1062 std::vector<std::filesystem::path> redfishLogFiles;
1063 if (getRedfishLogFiles(redfishLogFiles))
1064 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001065 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001066 {
1067 std::error_code ec;
1068 std::filesystem::remove(file, ec);
1069 }
1070 }
1071
1072 // Reload rsyslog so it knows to start new log files
1073 crow::connections::systemBus->async_method_call(
1074 [asyncResp](const boost::system::error_code ec) {
1075 if (ec)
1076 {
1077 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1078 messages::internalError(asyncResp->res);
1079 return;
1080 }
1081
1082 messages::success(asyncResp->res);
1083 },
1084 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1085 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1086 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 }
1088};
1089
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001090static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -07001091 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001092 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001093{
Jason M. Bills95820182019-04-22 16:25:34 -07001094 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001095 // First get the Timestamp
1096 size_t space = logEntry.find_first_of(" ");
1097 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001098 {
1099 return 1;
1100 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001101 std::string timestamp = logEntry.substr(0, space);
1102 // Then get the log contents
1103 size_t entryStart = logEntry.find_first_not_of(" ", space);
1104 if (entryStart == std::string::npos)
1105 {
1106 return 1;
1107 }
1108 std::string_view entry(logEntry);
1109 entry.remove_prefix(entryStart);
1110 // Use split to separate the entry into its fields
1111 std::vector<std::string> logEntryFields;
1112 boost::split(logEntryFields, entry, boost::is_any_of(","),
1113 boost::token_compress_on);
1114 // We need at least a MessageId to be valid
1115 if (logEntryFields.size() < 1)
1116 {
1117 return 1;
1118 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001119 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001120
Jason M. Bills4851d452019-03-28 11:27:48 -07001121 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001122 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001123 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001124
Jason M. Bills4851d452019-03-28 11:27:48 -07001125 std::string msg;
1126 std::string severity;
1127 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001128 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001129 msg = message->message;
1130 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001131 }
1132
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001133 // Get the MessageArgs from the log if there are any
1134 boost::beast::span<std::string> messageArgs;
1135 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001136 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001137 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001138 // If the first string is empty, assume there are no MessageArgs
1139 std::size_t messageArgsSize = 0;
1140 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001141 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001142 messageArgsSize = logEntryFields.size() - 1;
1143 }
1144
Ed Tanous23a21a12020-07-25 04:45:05 +00001145 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001146
1147 // Fill the MessageArgs into the Message
1148 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001149 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001150 {
1151 std::string argStr = "%" + std::to_string(++i);
1152 size_t argPos = msg.find(argStr);
1153 if (argPos != std::string::npos)
1154 {
1155 msg.replace(argPos, argStr.length(), messageArg);
1156 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001157 }
1158 }
1159
Jason M. Bills95820182019-04-22 16:25:34 -07001160 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1161 // format which matches the Redfish format except for the fractional seconds
1162 // between the '.' and the '+', so just remove them.
1163 std::size_t dot = timestamp.find_first_of(".");
1164 std::size_t plus = timestamp.find_first_of("+");
1165 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001166 {
Jason M. Bills95820182019-04-22 16:25:34 -07001167 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001168 }
1169
1170 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001171 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001172 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001173 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001174 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001175 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001176 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001177 {"Id", logEntryID},
1178 {"Message", std::move(msg)},
1179 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001180 {"MessageArgs", std::move(messageArgs)},
1181 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001182 {"Severity", std::move(severity)},
1183 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001184 return 0;
1185}
1186
Anthony Wilson27062602019-04-22 02:10:09 -05001187class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001188{
1189 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001190 JournalEventLogEntryCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001191 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001192 {
1193 entityPrivileges = {
1194 {boost::beast::http::verb::get, {{"Login"}}},
1195 {boost::beast::http::verb::head, {{"Login"}}},
1196 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1197 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1198 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1199 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1200 }
1201
1202 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001203 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001204 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001205 {
1206 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001207 uint64_t skip = 0;
1208 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001209 if (!getSkipParam(asyncResp->res, req, skip))
1210 {
1211 return;
1212 }
1213 if (!getTopParam(asyncResp->res, req, top))
1214 {
1215 return;
1216 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001217 // Collections don't include the static data added by SubRoute because
1218 // it has a duplicate entry for members
1219 asyncResp->res.jsonValue["@odata.type"] =
1220 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001221 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001222 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001223 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1224 asyncResp->res.jsonValue["Description"] =
1225 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001226
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001227 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001228 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001229 // Go through the log files and create a unique ID for each entry
1230 std::vector<std::filesystem::path> redfishLogFiles;
1231 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001232 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001233 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001234
1235 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001236 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1237 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001238 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001239 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001240 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001241 {
1242 continue;
1243 }
1244
Jason M. Billse85d6b12019-07-29 17:01:15 -07001245 // Reset the unique ID on the first entry
1246 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001247 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001248 {
Jason M. Bills95820182019-04-22 16:25:34 -07001249 entryCount++;
1250 // Handle paging using skip (number of entries to skip from the
1251 // start) and top (number of entries to display)
1252 if (entryCount <= skip || entryCount > skip + top)
1253 {
1254 continue;
1255 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001256
Jason M. Bills95820182019-04-22 16:25:34 -07001257 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001258 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001259 {
1260 continue;
1261 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001262
Jason M. Billse85d6b12019-07-29 17:01:15 -07001263 if (firstEntry)
1264 {
1265 firstEntry = false;
1266 }
1267
Jason M. Bills95820182019-04-22 16:25:34 -07001268 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001269 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001270 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1271 {
1272 messages::internalError(asyncResp->res);
1273 return;
1274 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001275 }
1276 }
1277 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1278 if (skip + top < entryCount)
1279 {
1280 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001281 "/redfish/v1/Systems/system/LogServices/EventLog/"
1282 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001283 std::to_string(skip + top);
1284 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001285 }
1286};
1287
Jason M. Bills897967d2019-07-29 17:05:30 -07001288class JournalEventLogEntry : public Node
1289{
1290 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001291 JournalEventLogEntry(App& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001292 Node(app,
1293 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1294 std::string())
1295 {
1296 entityPrivileges = {
1297 {boost::beast::http::verb::get, {{"Login"}}},
1298 {boost::beast::http::verb::head, {{"Login"}}},
1299 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1300 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1301 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1302 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1303 }
1304
1305 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001306 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001307 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001308 {
1309 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1310 if (params.size() != 1)
1311 {
1312 messages::internalError(asyncResp->res);
1313 return;
1314 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001315 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001316
1317 // Go through the log files and check the unique ID for each entry to
1318 // find the target entry
1319 std::vector<std::filesystem::path> redfishLogFiles;
1320 getRedfishLogFiles(redfishLogFiles);
1321 std::string logEntry;
1322
1323 // Oldest logs are in the last file, so start there and loop backwards
1324 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1325 it++)
1326 {
1327 std::ifstream logStream(*it);
1328 if (!logStream.is_open())
1329 {
1330 continue;
1331 }
1332
1333 // Reset the unique ID on the first entry
1334 bool firstEntry = true;
1335 while (std::getline(logStream, logEntry))
1336 {
1337 std::string idStr;
1338 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1339 {
1340 continue;
1341 }
1342
1343 if (firstEntry)
1344 {
1345 firstEntry = false;
1346 }
1347
1348 if (idStr == targetID)
1349 {
1350 if (fillEventLogEntryJson(idStr, logEntry,
1351 asyncResp->res.jsonValue) != 0)
1352 {
1353 messages::internalError(asyncResp->res);
1354 return;
1355 }
1356 return;
1357 }
1358 }
1359 }
1360 // Requested ID was not found
1361 messages::resourceMissingAtURI(asyncResp->res, targetID);
1362 }
1363};
1364
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001365class DBusEventLogEntryCollection : public Node
1366{
1367 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001368 DBusEventLogEntryCollection(App& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001369 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1370 {
1371 entityPrivileges = {
1372 {boost::beast::http::verb::get, {{"Login"}}},
1373 {boost::beast::http::verb::head, {{"Login"}}},
1374 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1375 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1376 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1377 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1378 }
1379
1380 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001381 void doGet(crow::Response& res, const crow::Request&,
1382 const std::vector<std::string>&) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001383 {
1384 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1385
1386 // Collections don't include the static data added by SubRoute because
1387 // it has a duplicate entry for members
1388 asyncResp->res.jsonValue["@odata.type"] =
1389 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001390 asyncResp->res.jsonValue["@odata.id"] =
1391 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1392 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1393 asyncResp->res.jsonValue["Description"] =
1394 "Collection of System Event Log Entries";
1395
Andrew Geisslercb92c032018-08-17 07:56:14 -07001396 // DBus implementation of EventLog/Entries
1397 // Make call to Logging Service to find all log entry objects
1398 crow::connections::systemBus->async_method_call(
1399 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001400 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001401 if (ec)
1402 {
1403 // TODO Handle for specific error code
1404 BMCWEB_LOG_ERROR
1405 << "getLogEntriesIfaceData resp_handler got error "
1406 << ec;
1407 messages::internalError(asyncResp->res);
1408 return;
1409 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001410 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001411 asyncResp->res.jsonValue["Members"];
1412 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001413 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001414 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001415 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001416 {
1417 if (interfaceMap.first !=
1418 "xyz.openbmc_project.Logging.Entry")
1419 {
1420 BMCWEB_LOG_DEBUG << "Bailing early on "
1421 << interfaceMap.first;
1422 continue;
1423 }
1424 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001425 nlohmann::json& thisEntry = entriesArray.back();
1426 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001427 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001428 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001429 std::string* severity = nullptr;
1430 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001431
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001432 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001433 {
1434 if (propertyMap.first == "Id")
1435 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001436 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001437 if (id == nullptr)
1438 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001439 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001440 }
1441 }
1442 else if (propertyMap.first == "Timestamp")
1443 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001444 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001445 std::get_if<uint64_t>(&propertyMap.second);
1446 if (millisTimeStamp == nullptr)
1447 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001448 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001449 }
George Liuebd45902020-08-26 14:21:10 +08001450 else
1451 {
1452 timestamp = crow::utility::getTimestamp(
1453 *millisTimeStamp);
1454 }
George Liud139c232020-08-18 18:48:57 +08001455 }
1456 else if (propertyMap.first == "UpdateTimestamp")
1457 {
1458 const uint64_t* millisTimeStamp =
1459 std::get_if<uint64_t>(&propertyMap.second);
1460 if (millisTimeStamp == nullptr)
1461 {
1462 messages::internalError(asyncResp->res);
1463 }
George Liuebd45902020-08-26 14:21:10 +08001464 else
1465 {
1466 updateTimestamp =
1467 crow::utility::getTimestamp(
1468 *millisTimeStamp);
1469 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001470 }
1471 else if (propertyMap.first == "Severity")
1472 {
1473 severity = std::get_if<std::string>(
1474 &propertyMap.second);
1475 if (severity == nullptr)
1476 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001477 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001478 }
1479 }
1480 else if (propertyMap.first == "Message")
1481 {
1482 message = std::get_if<std::string>(
1483 &propertyMap.second);
1484 if (message == nullptr)
1485 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001486 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001487 }
1488 }
1489 }
1490 thisEntry = {
George Liud139c232020-08-18 18:48:57 +08001491 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001492 {"@odata.id",
1493 "/redfish/v1/Systems/system/LogServices/EventLog/"
1494 "Entries/" +
1495 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001496 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001497 {"Id", std::to_string(*id)},
1498 {"Message", *message},
1499 {"EntryType", "Event"},
1500 {"Severity",
1501 translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001502 {"Created", crow::utility::getDateTime(timestamp)},
1503 {"Modified",
1504 crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001505 }
1506 }
1507 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001508 [](const nlohmann::json& left,
1509 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001510 return (left["Id"] <= right["Id"]);
1511 });
1512 asyncResp->res.jsonValue["Members@odata.count"] =
1513 entriesArray.size();
1514 },
1515 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1516 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001517 }
1518};
1519
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001520class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001521{
1522 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001523 DBusEventLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001524 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001525 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1526 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001527 {
1528 entityPrivileges = {
1529 {boost::beast::http::verb::get, {{"Login"}}},
1530 {boost::beast::http::verb::head, {{"Login"}}},
1531 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1532 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1533 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1534 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1535 }
1536
1537 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001538 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001539 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001540 {
1541 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001542 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001543 {
1544 messages::internalError(asyncResp->res);
1545 return;
1546 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001547 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001548
Andrew Geisslercb92c032018-08-17 07:56:14 -07001549 // DBus implementation of EventLog/Entries
1550 // Make call to Logging Service to find all log entry objects
1551 crow::connections::systemBus->async_method_call(
1552 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001553 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001554 if (ec)
1555 {
1556 BMCWEB_LOG_ERROR
1557 << "EventLogEntry (DBus) resp_handler got error " << ec;
1558 messages::internalError(asyncResp->res);
1559 return;
1560 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001561 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001562 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001563 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001564 std::string* severity = nullptr;
1565 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001566
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001567 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001568 {
1569 if (propertyMap.first == "Id")
1570 {
1571 id = std::get_if<uint32_t>(&propertyMap.second);
1572 if (id == nullptr)
1573 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001574 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001575 }
1576 }
1577 else if (propertyMap.first == "Timestamp")
1578 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001579 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001580 std::get_if<uint64_t>(&propertyMap.second);
1581 if (millisTimeStamp == nullptr)
1582 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001583 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001584 }
George Liuebd45902020-08-26 14:21:10 +08001585 else
1586 {
1587 timestamp =
1588 crow::utility::getTimestamp(*millisTimeStamp);
1589 }
George Liud139c232020-08-18 18:48:57 +08001590 }
1591 else if (propertyMap.first == "UpdateTimestamp")
1592 {
1593 const uint64_t* millisTimeStamp =
1594 std::get_if<uint64_t>(&propertyMap.second);
1595 if (millisTimeStamp == nullptr)
1596 {
1597 messages::internalError(asyncResp->res);
1598 }
George Liuebd45902020-08-26 14:21:10 +08001599 else
1600 {
1601 updateTimestamp =
1602 crow::utility::getTimestamp(*millisTimeStamp);
1603 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001604 }
1605 else if (propertyMap.first == "Severity")
1606 {
1607 severity =
1608 std::get_if<std::string>(&propertyMap.second);
1609 if (severity == nullptr)
1610 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001611 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001612 }
1613 }
1614 else if (propertyMap.first == "Message")
1615 {
1616 message = std::get_if<std::string>(&propertyMap.second);
1617 if (message == nullptr)
1618 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001619 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001620 }
1621 }
1622 }
Ed Tanous271584a2019-07-09 16:24:22 -07001623 if (id == nullptr || message == nullptr || severity == nullptr)
1624 {
1625 return;
1626 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001627 asyncResp->res.jsonValue = {
George Liud139c232020-08-18 18:48:57 +08001628 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001629 {"@odata.id",
1630 "/redfish/v1/Systems/system/LogServices/EventLog/"
1631 "Entries/" +
1632 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001633 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001634 {"Id", std::to_string(*id)},
1635 {"Message", *message},
1636 {"EntryType", "Event"},
1637 {"Severity", translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001638 {"Created", crow::utility::getDateTime(timestamp)},
1639 {"Modified", crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001640 },
1641 "xyz.openbmc_project.Logging",
1642 "/xyz/openbmc_project/logging/entry/" + entryID,
1643 "org.freedesktop.DBus.Properties", "GetAll",
1644 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001645 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001646
Ed Tanouscb13a392020-07-25 19:02:03 +00001647 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001648 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001649 {
1650
1651 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1652
1653 auto asyncResp = std::make_shared<AsyncResp>(res);
1654
1655 if (params.size() != 1)
1656 {
1657 messages::internalError(asyncResp->res);
1658 return;
1659 }
1660 std::string entryID = params[0];
1661
1662 dbus::utility::escapePathForDbus(entryID);
1663
1664 // Process response from Logging service.
1665 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1666 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1667 if (ec)
1668 {
1669 // TODO Handle for specific error code
1670 BMCWEB_LOG_ERROR
1671 << "EventLogEntry (DBus) doDelete respHandler got error "
1672 << ec;
1673 asyncResp->res.result(
1674 boost::beast::http::status::internal_server_error);
1675 return;
1676 }
1677
1678 asyncResp->res.result(boost::beast::http::status::ok);
1679 };
1680
1681 // Make call to Logging service to request Delete Log
1682 crow::connections::systemBus->async_method_call(
1683 respHandler, "xyz.openbmc_project.Logging",
1684 "/xyz/openbmc_project/logging/entry/" + entryID,
1685 "xyz.openbmc_project.Object.Delete", "Delete");
1686 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001687};
1688
1689class BMCLogServiceCollection : public Node
1690{
1691 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001692 BMCLogServiceCollection(App& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001693 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001694 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001695 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001696 {boost::beast::http::verb::get, {{"Login"}}},
1697 {boost::beast::http::verb::head, {{"Login"}}},
1698 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1699 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1700 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1701 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001702 }
1703
1704 private:
1705 /**
1706 * Functions triggers appropriate requests on DBus
1707 */
Ed Tanouscb13a392020-07-25 19:02:03 +00001708 void doGet(crow::Response& res, const crow::Request&,
1709 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001710 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001711 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001712 // Collections don't include the static data added by SubRoute because
1713 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001714 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001715 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001716 asyncResp->res.jsonValue["@odata.id"] =
1717 "/redfish/v1/Managers/bmc/LogServices";
1718 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1719 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001720 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001721 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001722 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001723#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1724 logServiceArray.push_back(
1725 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1726#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001727#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1728 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001729 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001730#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001731 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001732 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001733 }
1734};
1735
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001736class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001737{
1738 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001739 BMCJournalLogService(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001740 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001741 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001742 entityPrivileges = {
1743 {boost::beast::http::verb::get, {{"Login"}}},
1744 {boost::beast::http::verb::head, {{"Login"}}},
1745 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1746 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1747 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1748 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1749 }
1750
1751 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001752 void doGet(crow::Response& res, const crow::Request&,
1753 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001754 {
1755 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001756 asyncResp->res.jsonValue["@odata.type"] =
1757 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001758 asyncResp->res.jsonValue["@odata.id"] =
1759 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001760 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1761 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1762 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001763 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001764 asyncResp->res.jsonValue["Entries"] = {
1765 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001766 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001767 }
1768};
1769
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001770static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1771 sd_journal* journal,
1772 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001773{
1774 // Get the Log Entry contents
1775 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001776
Ed Tanous39e77502019-03-04 17:35:53 -08001777 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001778 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001779 if (ret < 0)
1780 {
1781 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1782 return 1;
1783 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001784
1785 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001786 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001787 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001788 if (ret < 0)
1789 {
1790 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001791 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001792
1793 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001794 std::string entryTimeStr;
1795 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001796 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001797 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001798 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001799
1800 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001801 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001802 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001803 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1804 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001805 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001806 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001807 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001808 {"EntryType", "Oem"},
1809 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001810 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001811 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001812 {"Created", std::move(entryTimeStr)}};
1813 return 0;
1814}
1815
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001816class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001817{
1818 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001819 BMCJournalLogEntryCollection(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001820 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001821 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001822 entityPrivileges = {
1823 {boost::beast::http::verb::get, {{"Login"}}},
1824 {boost::beast::http::verb::head, {{"Login"}}},
1825 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1826 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1827 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1828 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1829 }
1830
1831 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001832 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001833 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001834 {
1835 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001836 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001837 uint64_t skip = 0;
1838 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001839 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001840 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001841 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001842 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001843 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001844 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001845 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001846 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001847 // Collections don't include the static data added by SubRoute because
1848 // it has a duplicate entry for members
1849 asyncResp->res.jsonValue["@odata.type"] =
1850 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001851 asyncResp->res.jsonValue["@odata.id"] =
1852 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001853 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001854 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001855 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1856 asyncResp->res.jsonValue["Description"] =
1857 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001858 asyncResp->res.jsonValue["@odata.id"] =
1859 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001860 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001861 logEntryArray = nlohmann::json::array();
1862
1863 // Go through the journal and use the timestamp to create a unique ID
1864 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001865 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001866 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1867 if (ret < 0)
1868 {
1869 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001870 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001871 return;
1872 }
1873 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1874 journalTmp, sd_journal_close);
1875 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001876 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001877 // Reset the unique ID on the first entry
1878 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001879 SD_JOURNAL_FOREACH(journal.get())
1880 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001881 entryCount++;
1882 // Handle paging using skip (number of entries to skip from the
1883 // start) and top (number of entries to display)
1884 if (entryCount <= skip || entryCount > skip + top)
1885 {
1886 continue;
1887 }
1888
Jason M. Bills16428a12018-11-02 12:42:29 -07001889 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001890 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001892 continue;
1893 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001894
Jason M. Billse85d6b12019-07-29 17:01:15 -07001895 if (firstEntry)
1896 {
1897 firstEntry = false;
1898 }
1899
Jason M. Billse1f26342018-07-18 12:12:00 -07001900 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001901 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001902 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1903 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001904 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001905 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001906 return;
1907 }
1908 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001909 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1910 if (skip + top < entryCount)
1911 {
1912 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001913 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001914 std::to_string(skip + top);
1915 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001916 }
1917};
1918
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001919class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001920{
1921 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001922 BMCJournalLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001923 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001924 std::string())
1925 {
1926 entityPrivileges = {
1927 {boost::beast::http::verb::get, {{"Login"}}},
1928 {boost::beast::http::verb::head, {{"Login"}}},
1929 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1930 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1931 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1932 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1933 }
1934
1935 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001936 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001937 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001938 {
1939 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1940 if (params.size() != 1)
1941 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001942 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001943 return;
1944 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001945 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001946 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001947 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001948 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001949 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001950 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001951 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001952 }
1953
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001954 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001955 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1956 if (ret < 0)
1957 {
1958 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001959 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001960 return;
1961 }
1962 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1963 journalTmp, sd_journal_close);
1964 journalTmp = nullptr;
1965 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001966 // tracking the unique ID
1967 std::string idStr;
1968 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001969 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301970 if (ret < 0)
1971 {
1972 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1973 << strerror(-ret);
1974 messages::internalError(asyncResp->res);
1975 return;
1976 }
Ed Tanous271584a2019-07-09 16:24:22 -07001977 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001978 {
1979 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001980 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1981 {
1982 messages::internalError(asyncResp->res);
1983 return;
1984 }
1985 if (firstEntry)
1986 {
1987 firstEntry = false;
1988 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001989 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001990 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001991 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001992 {
1993 messages::resourceMissingAtURI(asyncResp->res, entryID);
1994 return;
1995 }
1996
1997 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1998 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001999 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002000 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002001 return;
2002 }
2003 }
2004};
2005
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002006class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002007{
2008 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002009 BMCDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002010 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002011 {
2012 entityPrivileges = {
2013 {boost::beast::http::verb::get, {{"Login"}}},
2014 {boost::beast::http::verb::head, {{"Login"}}},
2015 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2016 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2017 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2018 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2019 }
2020
2021 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002022 void doGet(crow::Response& res, const crow::Request&,
2023 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002024 {
2025 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2026
2027 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002028 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002029 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002030 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002031 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2032 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2033 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002034 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002035 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002036 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2037 asyncResp->res.jsonValue["Actions"] = {
2038 {"#LogService.ClearLog",
2039 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2040 "Actions/LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002041 {"#LogService.CollectDiagnosticData",
2042 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2043 "Actions/LogService.CollectDiagnosticData"}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002044 }
2045};
2046
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002047class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002048{
2049 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002050 BMCDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002051 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002052 {
2053 entityPrivileges = {
2054 {boost::beast::http::verb::get, {{"Login"}}},
2055 {boost::beast::http::verb::head, {{"Login"}}},
2056 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2057 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2058 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2059 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2060 }
2061
2062 private:
2063 /**
2064 * Functions triggers appropriate requests on DBus
2065 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002066 void doGet(crow::Response& res, const crow::Request&,
2067 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002068 {
2069 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2070
2071 asyncResp->res.jsonValue["@odata.type"] =
2072 "#LogEntryCollection.LogEntryCollection";
2073 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002074 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2075 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002076 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002077 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002078
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002079 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002080 }
2081};
2082
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002083class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002084{
2085 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002086 BMCDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002087 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002088 std::string())
2089 {
2090 entityPrivileges = {
2091 {boost::beast::http::verb::get, {{"Login"}}},
2092 {boost::beast::http::verb::head, {{"Login"}}},
2093 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2094 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2095 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2096 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2097 }
2098
2099 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002100 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002101 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002102 {
2103 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2104 if (params.size() != 1)
2105 {
2106 messages::internalError(asyncResp->res);
2107 return;
2108 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002109 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002110 }
2111
Ed Tanouscb13a392020-07-25 19:02:03 +00002112 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002113 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002114 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002115 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002116 if (params.size() != 1)
2117 {
2118 messages::internalError(asyncResp->res);
2119 return;
2120 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002121 deleteDumpEntry(asyncResp->res, params[0]);
2122 }
2123};
raviteja-bc9bb6862020-02-03 11:53:32 -06002124
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002125class BMCDumpCreate : public Node
2126{
2127 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002128 BMCDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002129 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002130 "Actions/"
2131 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002132 {
2133 entityPrivileges = {
2134 {boost::beast::http::verb::get, {{"Login"}}},
2135 {boost::beast::http::verb::head, {{"Login"}}},
2136 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2137 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2138 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2139 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2140 }
2141
2142 private:
2143 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002144 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002145 {
2146 createDump(res, req, "BMC");
2147 }
2148};
2149
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002150class BMCDumpClear : public Node
2151{
2152 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002153 BMCDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002154 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2155 "Actions/"
2156 "LogService.ClearLog/")
2157 {
2158 entityPrivileges = {
2159 {boost::beast::http::verb::get, {{"Login"}}},
2160 {boost::beast::http::verb::head, {{"Login"}}},
2161 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2162 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2163 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2164 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2165 }
2166
2167 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002168 void doPost(crow::Response& res, const crow::Request&,
2169 const std::vector<std::string>&) override
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002170 {
2171 clearDump(res, "xyz.openbmc_project.Dump.Entry.BMC");
2172 }
2173};
2174
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002175class SystemDumpService : public Node
2176{
2177 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002178 SystemDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002179 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2180 {
2181 entityPrivileges = {
2182 {boost::beast::http::verb::get, {{"Login"}}},
2183 {boost::beast::http::verb::head, {{"Login"}}},
2184 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2185 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2186 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2187 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2188 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002189
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002190 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002191 void doGet(crow::Response& res, const crow::Request&,
2192 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002193 {
2194 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002195
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002196 asyncResp->res.jsonValue["@odata.id"] =
2197 "/redfish/v1/Systems/system/LogServices/Dump";
2198 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002199 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002200 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2201 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2202 asyncResp->res.jsonValue["Id"] = "Dump";
2203 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2204 asyncResp->res.jsonValue["Entries"] = {
2205 {"@odata.id",
2206 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2207 asyncResp->res.jsonValue["Actions"] = {
2208 {"#LogService.ClearLog",
2209 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2210 "LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002211 {"#LogService.CollectDiagnosticData",
2212 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2213 "LogService.CollectDiagnosticData"}}}};
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002214 }
2215};
2216
2217class SystemDumpEntryCollection : public Node
2218{
2219 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002220 SystemDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002221 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2222 {
2223 entityPrivileges = {
2224 {boost::beast::http::verb::get, {{"Login"}}},
2225 {boost::beast::http::verb::head, {{"Login"}}},
2226 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2227 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2228 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2229 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2230 }
2231
2232 private:
2233 /**
2234 * Functions triggers appropriate requests on DBus
2235 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002236 void doGet(crow::Response& res, const crow::Request&,
2237 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002238 {
2239 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2240
2241 asyncResp->res.jsonValue["@odata.type"] =
2242 "#LogEntryCollection.LogEntryCollection";
2243 asyncResp->res.jsonValue["@odata.id"] =
2244 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2245 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2246 asyncResp->res.jsonValue["Description"] =
2247 "Collection of System Dump Entries";
2248
2249 getDumpEntryCollection(asyncResp, "System");
2250 }
2251};
2252
2253class SystemDumpEntry : public Node
2254{
2255 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002256 SystemDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002257 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2258 std::string())
2259 {
2260 entityPrivileges = {
2261 {boost::beast::http::verb::get, {{"Login"}}},
2262 {boost::beast::http::verb::head, {{"Login"}}},
2263 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2264 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2265 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2266 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2267 }
2268
2269 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002270 void doGet(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002271 const std::vector<std::string>& params) override
2272 {
2273 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2274 if (params.size() != 1)
2275 {
2276 messages::internalError(asyncResp->res);
2277 return;
2278 }
2279 getDumpEntryById(asyncResp, params[0], "System");
2280 }
2281
Ed Tanouscb13a392020-07-25 19:02:03 +00002282 void doDelete(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002283 const std::vector<std::string>& params) override
2284 {
2285 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2286 if (params.size() != 1)
2287 {
2288 messages::internalError(asyncResp->res);
2289 return;
2290 }
2291 deleteDumpEntry(asyncResp->res, params[0]);
raviteja-bc9bb6862020-02-03 11:53:32 -06002292 }
2293};
2294
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002295class SystemDumpCreate : public Node
2296{
2297 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002298 SystemDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002299 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002300 "Actions/"
2301 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002302 {
2303 entityPrivileges = {
2304 {boost::beast::http::verb::get, {{"Login"}}},
2305 {boost::beast::http::verb::head, {{"Login"}}},
2306 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2307 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2308 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2309 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2310 }
2311
2312 private:
2313 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002314 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002315 {
2316 createDump(res, req, "System");
2317 }
2318};
2319
raviteja-b013487e2020-03-03 03:20:48 -06002320class SystemDumpClear : public Node
2321{
2322 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002323 SystemDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002324 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002325 "Actions/"
2326 "LogService.ClearLog/")
2327 {
2328 entityPrivileges = {
2329 {boost::beast::http::verb::get, {{"Login"}}},
2330 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002331 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2332 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2333 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002334 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2335 }
2336
2337 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002338 void doPost(crow::Response& res, const crow::Request&,
2339 const std::vector<std::string>&) override
raviteja-b013487e2020-03-03 03:20:48 -06002340 {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002341 clearDump(res, "xyz.openbmc_project.Dump.Entry.System");
raviteja-b013487e2020-03-03 03:20:48 -06002342 }
2343};
2344
Jason M. Bills424c4172019-03-21 13:50:33 -07002345class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002346{
2347 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002348 CrashdumpService(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002349 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002350 {
AppaRao Puli39460282020-04-07 17:03:04 +05302351 // Note: Deviated from redfish privilege registry for GET & HEAD
2352 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002353 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302354 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2355 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002356 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2357 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2358 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2359 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002360 }
2361
2362 private:
2363 /**
2364 * Functions triggers appropriate requests on DBus
2365 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002366 void doGet(crow::Response& res, const crow::Request&,
2367 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002368 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002369 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002370 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002371 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002372 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002373 asyncResp->res.jsonValue["@odata.type"] =
2374 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002375 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2376 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2377 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002378 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2379 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002380 asyncResp->res.jsonValue["Entries"] = {
2381 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002382 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002383 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002384 {"#LogService.ClearLog",
2385 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2386 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002387 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002388 {{"#Crashdump.OnDemand",
2389 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002390 "Actions/Oem/Crashdump.OnDemand"}}},
2391 {"#Crashdump.Telemetry",
2392 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2393 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002394
2395#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002396 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002397 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002398 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2399 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002400#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002401 }
2402};
2403
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002404class CrashdumpClear : public Node
2405{
2406 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002407 CrashdumpClear(App& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002408 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2409 "LogService.ClearLog/")
2410 {
AppaRao Puli39460282020-04-07 17:03:04 +05302411 // Note: Deviated from redfish privilege registry for GET & HEAD
2412 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002413 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302414 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2415 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002416 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2417 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2418 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2419 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2420 }
2421
2422 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002423 void doPost(crow::Response& res, const crow::Request&,
2424 const std::vector<std::string>&) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002425 {
2426 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2427
2428 crow::connections::systemBus->async_method_call(
2429 [asyncResp](const boost::system::error_code ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002430 const std::string&) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002431 if (ec)
2432 {
2433 messages::internalError(asyncResp->res);
2434 return;
2435 }
2436 messages::success(asyncResp->res);
2437 },
2438 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2439 }
2440};
2441
Jason M. Billse855dd22019-10-08 11:37:48 -07002442static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002443 const std::string& logID,
2444 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002445{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002446 auto getStoredLogCallback =
2447 [asyncResp, logID, &logEntryJson](
2448 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002449 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002450 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002451 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002452 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2453 if (ec.value() ==
2454 boost::system::linux_error::bad_request_descriptor)
2455 {
2456 messages::resourceNotFound(asyncResp->res, "LogEntry",
2457 logID);
2458 }
2459 else
2460 {
2461 messages::internalError(asyncResp->res);
2462 }
2463 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002464 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002465
Johnathan Mantey043a0532020-03-10 17:15:28 -07002466 std::string timestamp{};
2467 std::string filename{};
2468 std::string logfile{};
2469 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2470
2471 if (filename.empty() || timestamp.empty())
2472 {
2473 messages::resourceMissingAtURI(asyncResp->res, logID);
2474 return;
2475 }
2476
2477 std::string crashdumpURI =
2478 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2479 logID + "/" + filename;
2480 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2481 {"@odata.id", "/redfish/v1/Systems/system/"
2482 "LogServices/Crashdump/Entries/" +
2483 logID},
2484 {"Name", "CPU Crashdump"},
2485 {"Id", logID},
2486 {"EntryType", "Oem"},
2487 {"OemRecordFormat", "Crashdump URI"},
2488 {"Message", std::move(crashdumpURI)},
2489 {"Created", std::move(timestamp)}};
2490 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002491 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002492 std::move(getStoredLogCallback), crashdumpObject,
2493 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002494 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002495}
2496
Jason M. Bills424c4172019-03-21 13:50:33 -07002497class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002498{
2499 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002500 CrashdumpEntryCollection(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002501 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002502 {
AppaRao Puli39460282020-04-07 17:03:04 +05302503 // Note: Deviated from redfish privilege registry for GET & HEAD
2504 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002505 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302506 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2507 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002508 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2509 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2510 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2511 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002512 }
2513
2514 private:
2515 /**
2516 * Functions triggers appropriate requests on DBus
2517 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002518 void doGet(crow::Response& res, const crow::Request&,
2519 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002520 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002521 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002522 // Collections don't include the static data added by SubRoute because
2523 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002524 auto getLogEntriesCallback = [asyncResp](
2525 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002526 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002527 if (ec)
2528 {
2529 if (ec.value() !=
2530 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002531 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002532 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2533 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002534 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002535 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002536 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002537 }
2538 asyncResp->res.jsonValue["@odata.type"] =
2539 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002540 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002541 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002542 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002543 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002544 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002545 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002546 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002547 std::vector<std::string> logIDs;
2548 // Get the list of log entries and build up an empty array big
2549 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002550 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002551 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002552 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002553 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002554 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002555 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002556 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002557 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002558 logIDs.emplace_back(objpath.substr(lastPos + 1));
2559
2560 // Add a space for the log entry to the array
2561 logEntryArray.push_back({});
2562 }
2563 // Now go through and set up async calls to fill in the entries
2564 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002565 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002566 {
2567 // Add the log entry to the array
2568 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002569 }
2570 asyncResp->res.jsonValue["Members@odata.count"] =
2571 logEntryArray.size();
2572 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002573 crow::connections::systemBus->async_method_call(
2574 std::move(getLogEntriesCallback),
2575 "xyz.openbmc_project.ObjectMapper",
2576 "/xyz/openbmc_project/object_mapper",
2577 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002578 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002579 }
2580};
2581
Jason M. Bills424c4172019-03-21 13:50:33 -07002582class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002583{
2584 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002585 CrashdumpEntry(App& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002586 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002587 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002588 std::string())
2589 {
AppaRao Puli39460282020-04-07 17:03:04 +05302590 // Note: Deviated from redfish privilege registry for GET & HEAD
2591 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002592 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302593 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2594 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002595 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2596 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2597 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2598 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002599 }
2600
2601 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002602 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002603 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002604 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002606 if (params.size() != 1)
2607 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002608 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002609 return;
2610 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002611 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002612 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2613 }
2614};
2615
2616class CrashdumpFile : public Node
2617{
2618 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002619 CrashdumpFile(App& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002620 Node(app,
2621 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2622 "<str>/",
2623 std::string(), std::string())
2624 {
AppaRao Puli39460282020-04-07 17:03:04 +05302625 // Note: Deviated from redfish privilege registry for GET & HEAD
2626 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002627 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302628 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2629 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002630 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2631 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2632 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2633 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2634 }
2635
2636 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002637 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002638 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002639 {
2640 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2641 if (params.size() != 2)
2642 {
2643 messages::internalError(asyncResp->res);
2644 return;
2645 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002646 const std::string& logID = params[0];
2647 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002648
Johnathan Mantey043a0532020-03-10 17:15:28 -07002649 auto getStoredLogCallback =
2650 [asyncResp, logID, fileName](
2651 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002652 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002653 if (ec)
2654 {
2655 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2656 << ec.message();
2657 messages::internalError(asyncResp->res);
2658 return;
2659 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002660
Johnathan Mantey043a0532020-03-10 17:15:28 -07002661 std::string dbusFilename{};
2662 std::string dbusTimestamp{};
2663 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002664
Johnathan Mantey043a0532020-03-10 17:15:28 -07002665 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2666 dbusFilepath);
2667
2668 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2669 dbusFilepath.empty())
2670 {
2671 messages::resourceMissingAtURI(asyncResp->res, fileName);
2672 return;
2673 }
2674
2675 // Verify the file name parameter is correct
2676 if (fileName != dbusFilename)
2677 {
2678 messages::resourceMissingAtURI(asyncResp->res, fileName);
2679 return;
2680 }
2681
2682 if (!std::filesystem::exists(dbusFilepath))
2683 {
2684 messages::resourceMissingAtURI(asyncResp->res, fileName);
2685 return;
2686 }
2687 std::ifstream ifs(dbusFilepath, std::ios::in |
2688 std::ios::binary |
2689 std::ios::ate);
2690 std::ifstream::pos_type fileSize = ifs.tellg();
2691 if (fileSize < 0)
2692 {
2693 messages::generalError(asyncResp->res);
2694 return;
2695 }
2696 ifs.seekg(0, std::ios::beg);
2697
2698 auto crashData = std::make_unique<char[]>(
2699 static_cast<unsigned int>(fileSize));
2700
2701 ifs.read(crashData.get(), static_cast<int>(fileSize));
2702
2703 // The cast to std::string is intentional in order to use the
2704 // assign() that applies move mechanics
2705 asyncResp->res.body().assign(
2706 static_cast<std::string>(crashData.get()));
2707
2708 // Configure this to be a file download when accessed from
2709 // a browser
2710 asyncResp->res.addHeader("Content-Disposition", "attachment");
2711 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002712 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002713 std::move(getStoredLogCallback), crashdumpObject,
2714 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002715 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002716 }
2717};
2718
Jason M. Bills424c4172019-03-21 13:50:33 -07002719class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002720{
2721 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002722 OnDemandCrashdump(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002723 Node(app,
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2725 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002726 {
AppaRao Puli39460282020-04-07 17:03:04 +05302727 // Note: Deviated from redfish privilege registry for GET & HEAD
2728 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002729 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302730 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2731 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2732 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2733 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2734 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2735 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002736 }
2737
2738 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002739 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002740 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002741 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002742 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002743
James Feistfe306722020-03-12 16:32:08 -07002744 auto generateonDemandLogCallback = [asyncResp,
2745 req](const boost::system::error_code
2746 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002747 const std::string&) {
James Feist46229572020-02-19 15:11:58 -08002748 if (ec)
2749 {
2750 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002751 {
James Feist46229572020-02-19 15:11:58 -08002752 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002753 }
James Feist46229572020-02-19 15:11:58 -08002754 else if (ec.value() ==
2755 boost::system::errc::device_or_resource_busy)
2756 {
2757 messages::serviceTemporarilyUnavailable(asyncResp->res,
2758 "60");
2759 }
2760 else
2761 {
2762 messages::internalError(asyncResp->res);
2763 }
2764 return;
2765 }
2766 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002767 [](boost::system::error_code err, sdbusplus::message::message&,
2768 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002769 if (!err)
2770 {
James Feiste5d50062020-05-11 17:29:00 -07002771 taskData->messages.emplace_back(
2772 messages::taskCompletedOK(
2773 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002774 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002775 }
James Feist32898ce2020-03-10 16:16:52 -07002776 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002777 },
James Feist46229572020-02-19 15:11:58 -08002778 "type='signal',interface='org.freedesktop.DBus.Properties',"
2779 "member='PropertiesChanged',arg0namespace='com.intel."
2780 "crashdump'");
2781 task->startTimer(std::chrono::minutes(5));
2782 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002783 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002784 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002785 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002786 std::move(generateonDemandLogCallback), crashdumpObject,
2787 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002788 }
2789};
2790
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002791class TelemetryCrashdump : public Node
2792{
2793 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002794 TelemetryCrashdump(App& app) :
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002795 Node(app,
2796 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2797 "Crashdump.Telemetry/")
2798 {
2799 // Note: Deviated from redfish privilege registry for GET & HEAD
2800 // method for security reasons.
2801 entityPrivileges = {
2802 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2803 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2804 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2805 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2806 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2807 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2808 }
2809
2810 private:
2811 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002812 const std::vector<std::string>&) override
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002813 {
2814 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2815
2816 auto generateTelemetryLogCallback = [asyncResp, req](
2817 const boost::system::error_code
2818 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002819 const std::string&) {
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002820 if (ec)
2821 {
2822 if (ec.value() == boost::system::errc::operation_not_supported)
2823 {
2824 messages::resourceInStandby(asyncResp->res);
2825 }
2826 else if (ec.value() ==
2827 boost::system::errc::device_or_resource_busy)
2828 {
2829 messages::serviceTemporarilyUnavailable(asyncResp->res,
2830 "60");
2831 }
2832 else
2833 {
2834 messages::internalError(asyncResp->res);
2835 }
2836 return;
2837 }
2838 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2839 [](boost::system::error_code err, sdbusplus::message::message&,
2840 const std::shared_ptr<task::TaskData>& taskData) {
2841 if (!err)
2842 {
2843 taskData->messages.emplace_back(
2844 messages::taskCompletedOK(
2845 std::to_string(taskData->index)));
2846 taskData->state = "Completed";
2847 }
2848 return task::completed;
2849 },
2850 "type='signal',interface='org.freedesktop.DBus.Properties',"
2851 "member='PropertiesChanged',arg0namespace='com.intel."
2852 "crashdump'");
2853 task->startTimer(std::chrono::minutes(5));
2854 task->populateResp(asyncResp->res);
2855 task->payload.emplace(req);
2856 };
2857 crow::connections::systemBus->async_method_call(
2858 std::move(generateTelemetryLogCallback), crashdumpObject,
2859 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2860 }
2861};
2862
Jason M. Billse1f26342018-07-18 12:12:00 -07002863class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002864{
2865 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002866 SendRawPECI(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002867 Node(app,
2868 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2869 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002870 {
AppaRao Puli39460282020-04-07 17:03:04 +05302871 // Note: Deviated from redfish privilege registry for GET & HEAD
2872 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002873 entityPrivileges = {
2874 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2875 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2876 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2877 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2878 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2879 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2880 }
2881
2882 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002883 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002884 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002885 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002886 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002887 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002888
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002889 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002890 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002891 return;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002892 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002893 uint32_t idx = 0;
2894 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002895 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002896 if (cmd.size() < 3)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002897 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002898 std::string s("[");
2899 for (auto const& val : cmd)
2900 {
2901 if (val != *cmd.begin())
2902 {
2903 s += ",";
2904 }
2905 s += std::to_string(val);
2906 }
2907 s += "]";
2908 messages::actionParameterValueFormatError(
2909 res, s, "PECICommands[" + std::to_string(idx) + "]",
2910 "SendRawPeci");
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002911 return;
2912 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002913 idx++;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002914 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002915 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002916 auto sendRawPECICallback =
2917 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002918 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002919 if (ec)
2920 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002921 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002922 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002923 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002924 return;
2925 }
2926 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2927 {"PECIResponse", resp}};
2928 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002929 // Call the SendRawPECI command with the provided data
2930 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002931 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002932 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002933 }
2934};
2935
Andrew Geisslercb92c032018-08-17 07:56:14 -07002936/**
2937 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2938 */
2939class DBusLogServiceActionsClear : public Node
2940{
2941 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002942 DBusLogServiceActionsClear(App& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002943 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002944 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002945 {
2946 entityPrivileges = {
2947 {boost::beast::http::verb::get, {{"Login"}}},
2948 {boost::beast::http::verb::head, {{"Login"}}},
2949 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2950 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2951 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2952 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2953 }
2954
2955 private:
2956 /**
2957 * Function handles POST method request.
2958 * The Clear Log actions does not require any parameter.The action deletes
2959 * all entries found in the Entries collection for this Log Service.
2960 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002961 void doPost(crow::Response& res, const crow::Request&,
2962 const std::vector<std::string>&) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002963 {
2964 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2965
2966 auto asyncResp = std::make_shared<AsyncResp>(res);
2967 // Process response from Logging service.
2968 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2969 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2970 if (ec)
2971 {
2972 // TODO Handle for specific error code
2973 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2974 asyncResp->res.result(
2975 boost::beast::http::status::internal_server_error);
2976 return;
2977 }
2978
2979 asyncResp->res.result(boost::beast::http::status::no_content);
2980 };
2981
2982 // Make call to Logging service to request Clear Log
2983 crow::connections::systemBus->async_method_call(
2984 resp_handler, "xyz.openbmc_project.Logging",
2985 "/xyz/openbmc_project/logging",
2986 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2987 }
2988};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002989
2990/****************************************************
2991 * Redfish PostCode interfaces
2992 * using DBUS interface: getPostCodesTS
2993 ******************************************************/
2994class PostCodesLogService : public Node
2995{
2996 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002997 PostCodesLogService(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002998 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2999 {
3000 entityPrivileges = {
3001 {boost::beast::http::verb::get, {{"Login"}}},
3002 {boost::beast::http::verb::head, {{"Login"}}},
3003 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3004 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3005 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3006 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3007 }
3008
3009 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003010 void doGet(crow::Response& res, const crow::Request&,
3011 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003012 {
3013 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3014
3015 asyncResp->res.jsonValue = {
3016 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
3017 {"@odata.type", "#LogService.v1_1_0.LogService"},
3018 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
3019 {"Name", "POST Code Log Service"},
3020 {"Description", "POST Code Log Service"},
3021 {"Id", "BIOS POST Code Log"},
3022 {"OverWritePolicy", "WrapsWhenFull"},
3023 {"Entries",
3024 {{"@odata.id",
3025 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3026 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3027 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3028 "Actions/LogService.ClearLog"}};
3029 }
3030};
3031
3032class PostCodesClear : public Node
3033{
3034 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003035 PostCodesClear(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003036 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3037 "LogService.ClearLog/")
3038 {
3039 entityPrivileges = {
3040 {boost::beast::http::verb::get, {{"Login"}}},
3041 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303042 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3043 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3044 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3045 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003046 }
3047
3048 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003049 void doPost(crow::Response& res, const crow::Request&,
3050 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003051 {
3052 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3053
3054 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3055 // Make call to post-code service to request clear all
3056 crow::connections::systemBus->async_method_call(
3057 [asyncResp](const boost::system::error_code ec) {
3058 if (ec)
3059 {
3060 // TODO Handle for specific error code
3061 BMCWEB_LOG_ERROR
3062 << "doClearPostCodes resp_handler got error " << ec;
3063 asyncResp->res.result(
3064 boost::beast::http::status::internal_server_error);
3065 messages::internalError(asyncResp->res);
3066 return;
3067 }
3068 },
3069 "xyz.openbmc_project.State.Boot.PostCode",
3070 "/xyz/openbmc_project/State/Boot/PostCode",
3071 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3072 }
3073};
3074
3075static void fillPostCodeEntry(
3076 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003077 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003078 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3079 const uint64_t skip = 0, const uint64_t top = 0)
3080{
3081 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003082 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003083 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003084
3085 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003086 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003087
3088 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003089 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003090 {
3091 currentCodeIndex++;
3092 std::string postcodeEntryID =
3093 "B" + std::to_string(bootIndex) + "-" +
3094 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3095
3096 uint64_t usecSinceEpoch = code.first;
3097 uint64_t usTimeOffset = 0;
3098
3099 if (1 == currentCodeIndex)
3100 { // already incremented
3101 firstCodeTimeUs = code.first;
3102 }
3103 else
3104 {
3105 usTimeOffset = code.first - firstCodeTimeUs;
3106 }
3107
3108 // skip if no specific codeIndex is specified and currentCodeIndex does
3109 // not fall between top and skip
3110 if ((codeIndex == 0) &&
3111 (currentCodeIndex <= skip || currentCodeIndex > top))
3112 {
3113 continue;
3114 }
3115
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003116 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003117 // currentIndex
3118 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3119 {
3120 // This is done for simplicity. 1st entry is needed to calculate
3121 // time offset. To improve efficiency, one can get to the entry
3122 // directly (possibly with flatmap's nth method)
3123 continue;
3124 }
3125
3126 // currentCodeIndex is within top and skip or equal to specified code
3127 // index
3128
3129 // Get the Created time from the timestamp
3130 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003131 entryTimeStr = crow::utility::getDateTime(
3132 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003133
3134 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3135 std::ostringstream hexCode;
3136 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3137 << code.second;
3138 std::ostringstream timeOffsetStr;
3139 // Set Fixed -Point Notation
3140 timeOffsetStr << std::fixed;
3141 // Set precision to 4 digits
3142 timeOffsetStr << std::setprecision(4);
3143 // Add double to stream
3144 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3145 std::vector<std::string> messageArgs = {
3146 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3147
3148 // Get MessageArgs template from message registry
3149 std::string msg;
3150 if (message != nullptr)
3151 {
3152 msg = message->message;
3153
3154 // fill in this post code value
3155 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003156 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003157 {
3158 std::string argStr = "%" + std::to_string(++i);
3159 size_t argPos = msg.find(argStr);
3160 if (argPos != std::string::npos)
3161 {
3162 msg.replace(argPos, argStr.length(), messageArg);
3163 }
3164 }
3165 }
3166
Tim Leed4342a92020-04-27 11:47:58 +08003167 // Get Severity template from message registry
3168 std::string severity;
3169 if (message != nullptr)
3170 {
3171 severity = message->severity;
3172 }
3173
ZhikuiRena3316fc2020-01-29 14:58:08 -08003174 // add to AsyncResp
3175 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003176 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003177 bmcLogEntry = {
3178 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3179 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
3180 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3181 "PostCodes/Entries/" +
3182 postcodeEntryID},
3183 {"Name", "POST Code Log Entry"},
3184 {"Id", postcodeEntryID},
3185 {"Message", std::move(msg)},
3186 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3187 {"MessageArgs", std::move(messageArgs)},
3188 {"EntryType", "Event"},
3189 {"Severity", std::move(severity)},
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003190 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003191 }
3192}
3193
3194static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
3195 const uint16_t bootIndex,
3196 const uint64_t codeIndex)
3197{
3198 crow::connections::systemBus->async_method_call(
3199 [aResp, bootIndex, codeIndex](
3200 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003201 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003202 if (ec)
3203 {
3204 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3205 messages::internalError(aResp->res);
3206 return;
3207 }
3208
3209 // skip the empty postcode boots
3210 if (postcode.empty())
3211 {
3212 return;
3213 }
3214
3215 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3216
3217 aResp->res.jsonValue["Members@odata.count"] =
3218 aResp->res.jsonValue["Members"].size();
3219 },
3220 "xyz.openbmc_project.State.Boot.PostCode",
3221 "/xyz/openbmc_project/State/Boot/PostCode",
3222 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3223 bootIndex);
3224}
3225
3226static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
3227 const uint16_t bootIndex,
3228 const uint16_t bootCount,
3229 const uint64_t entryCount, const uint64_t skip,
3230 const uint64_t top)
3231{
3232 crow::connections::systemBus->async_method_call(
3233 [aResp, bootIndex, bootCount, entryCount, skip,
3234 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003235 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236 if (ec)
3237 {
3238 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3239 messages::internalError(aResp->res);
3240 return;
3241 }
3242
3243 uint64_t endCount = entryCount;
3244 if (!postcode.empty())
3245 {
3246 endCount = entryCount + postcode.size();
3247
3248 if ((skip < endCount) && ((top + skip) > entryCount))
3249 {
3250 uint64_t thisBootSkip =
3251 std::max(skip, entryCount) - entryCount;
3252 uint64_t thisBootTop =
3253 std::min(top + skip, endCount) - entryCount;
3254
3255 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3256 thisBootSkip, thisBootTop);
3257 }
3258 aResp->res.jsonValue["Members@odata.count"] = endCount;
3259 }
3260
3261 // continue to previous bootIndex
3262 if (bootIndex < bootCount)
3263 {
3264 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3265 bootCount, endCount, skip, top);
3266 }
3267 else
3268 {
3269 aResp->res.jsonValue["Members@odata.nextLink"] =
3270 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3271 "Entries?$skip=" +
3272 std::to_string(skip + top);
3273 }
3274 },
3275 "xyz.openbmc_project.State.Boot.PostCode",
3276 "/xyz/openbmc_project/State/Boot/PostCode",
3277 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3278 bootIndex);
3279}
3280
3281static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
3282 const uint64_t skip, const uint64_t top)
3283{
3284 uint64_t entryCount = 0;
3285 crow::connections::systemBus->async_method_call(
3286 [aResp, entryCount, skip,
3287 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003288 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003289 if (ec)
3290 {
3291 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3292 messages::internalError(aResp->res);
3293 return;
3294 }
3295 auto pVal = std::get_if<uint16_t>(&bootCount);
3296 if (pVal)
3297 {
3298 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3299 }
3300 else
3301 {
3302 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3303 }
3304 },
3305 "xyz.openbmc_project.State.Boot.PostCode",
3306 "/xyz/openbmc_project/State/Boot/PostCode",
3307 "org.freedesktop.DBus.Properties", "Get",
3308 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3309}
3310
3311class PostCodesEntryCollection : public Node
3312{
3313 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003314 PostCodesEntryCollection(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003315 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3316 {
3317 entityPrivileges = {
3318 {boost::beast::http::verb::get, {{"Login"}}},
3319 {boost::beast::http::verb::head, {{"Login"}}},
3320 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3321 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3322 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3323 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3324 }
3325
3326 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003327 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00003328 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003329 {
3330 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3331
3332 asyncResp->res.jsonValue["@odata.type"] =
3333 "#LogEntryCollection.LogEntryCollection";
3334 asyncResp->res.jsonValue["@odata.context"] =
3335 "/redfish/v1/"
3336 "$metadata#LogEntryCollection.LogEntryCollection";
3337 asyncResp->res.jsonValue["@odata.id"] =
3338 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3339 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3340 asyncResp->res.jsonValue["Description"] =
3341 "Collection of POST Code Log Entries";
3342 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3343 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3344
3345 uint64_t skip = 0;
3346 uint64_t top = maxEntriesPerPage; // Show max entries by default
3347 if (!getSkipParam(asyncResp->res, req, skip))
3348 {
3349 return;
3350 }
3351 if (!getTopParam(asyncResp->res, req, top))
3352 {
3353 return;
3354 }
3355 getCurrentBootNumber(asyncResp, skip, top);
3356 }
3357};
3358
3359class PostCodesEntry : public Node
3360{
3361 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003362 PostCodesEntry(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003363 Node(app,
3364 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3365 std::string())
3366 {
3367 entityPrivileges = {
3368 {boost::beast::http::verb::get, {{"Login"}}},
3369 {boost::beast::http::verb::head, {{"Login"}}},
3370 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3371 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3372 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3373 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3374 }
3375
3376 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003377 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003378 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003379 {
3380 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3381 if (params.size() != 1)
3382 {
3383 messages::internalError(asyncResp->res);
3384 return;
3385 }
3386
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003387 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003388
3389 size_t bootPos = targetID.find('B');
3390 if (bootPos == std::string::npos)
3391 {
3392 // Requested ID was not found
3393 messages::resourceMissingAtURI(asyncResp->res, targetID);
3394 return;
3395 }
3396 std::string_view bootIndexStr(targetID);
3397 bootIndexStr.remove_prefix(bootPos + 1);
3398 uint16_t bootIndex = 0;
3399 uint64_t codeIndex = 0;
3400 size_t dashPos = bootIndexStr.find('-');
3401
3402 if (dashPos == std::string::npos)
3403 {
3404 return;
3405 }
3406 std::string_view codeIndexStr(bootIndexStr);
3407 bootIndexStr.remove_suffix(dashPos);
3408 codeIndexStr.remove_prefix(dashPos + 1);
3409
3410 bootIndex = static_cast<uint16_t>(
Ed Tanous23a21a12020-07-25 04:45:05 +00003411 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3412 codeIndex = strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003413 if (bootIndex == 0 || codeIndex == 0)
3414 {
3415 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3416 << params[0];
3417 }
3418
3419 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3420 asyncResp->res.jsonValue["@odata.context"] =
3421 "/redfish/v1/$metadata#LogEntry.LogEntry";
3422 asyncResp->res.jsonValue["@odata.id"] =
3423 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3424 "Entries";
3425 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3426 asyncResp->res.jsonValue["Description"] =
3427 "Collection of POST Code Log Entries";
3428 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3429 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3430
3431 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3432 }
3433};
3434
Ed Tanous1da66f72018-07-27 16:13:37 -07003435} // namespace redfish