blob: 98a08084a1cd6a72859eb594c5770a0a2fa4e80e [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050031
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039constexpr char const* crashdumpObject = "com.intel.crashdump";
40constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041constexpr char const* crashdumpInterface = "com.intel.crashdump";
42constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070043 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070048constexpr char const* crashdumpTelemetryInterface =
49 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070050
Jason M. Bills4851d452019-03-28 11:27:48 -070051namespace message_registries
52{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053static const Message* getMessageFromRegistry(
54 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070055 const boost::beast::span<const MessageEntry> registry)
56{
57 boost::beast::span<const MessageEntry>::const_iterator messageIt =
58 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050059 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070060 return !std::strcmp(messageEntry.first,
61 messageKey.c_str());
62 });
63 if (messageIt != registry.cend())
64 {
65 return &messageIt->second;
66 }
67
68 return nullptr;
69}
70
Gunnar Mills1214b7e2020-06-04 10:11:30 -050071static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070072{
73 // Redfish MessageIds are in the form
74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
75 // the right Message
76 std::vector<std::string> fields;
77 fields.reserve(4);
78 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 std::string& registryName = fields[0];
80 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070081
82 // Find the right registry and check it for the MessageKey
83 if (std::string(base::header.registryPrefix) == registryName)
84 {
85 return getMessageFromRegistry(
86 messageKey, boost::beast::span<const MessageEntry>(base::registry));
87 }
88 if (std::string(openbmc::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey,
92 boost::beast::span<const MessageEntry>(openbmc::registry));
93 }
94 return nullptr;
95}
96} // namespace message_registries
97
James Feistf6150402019-01-08 10:36:20 -080098namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070099
Andrew Geisslercb92c032018-08-17 07:56:14 -0700100using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500101 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
102 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700103
104using GetManagedObjectsType = boost::container::flat_map<
105 sdbusplus::message::object_path,
106 boost::container::flat_map<std::string, GetManagedPropertyType>>;
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
Ed Tanousd4d25792020-09-29 15:15:03 -0700110 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114 {
115 return "Critical";
116 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700117 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700120 {
121 return "OK";
122 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700123 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700124 {
125 return "Warning";
126 }
127 return "";
128}
129
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500130static int getJournalMetadata(sd_journal* journal,
131 const std::string_view& field,
132 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700133{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700135 size_t length = 0;
136 int ret = 0;
137 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700138 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 if (ret < 0)
141 {
142 return ret;
143 }
Ed Tanous39e77502019-03-04 17:35:53 -0800144 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000146 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700147 return ret;
148}
149
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150static int getJournalMetadata(sd_journal* journal,
151 const std::string_view& field, const int& base,
152 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700153{
154 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800155 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 // Get the metadata from the requested field of the journal entry
157 ret = getJournalMetadata(journal, field, metadata);
158 if (ret < 0)
159 {
160 return ret;
161 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000162 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700163 return ret;
164}
165
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800167{
168 int ret = 0;
169 uint64_t timestamp = 0;
170 ret = sd_journal_get_realtime_usec(journal, &timestamp);
171 if (ret < 0)
172 {
173 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
174 << strerror(-ret);
175 return false;
176 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500177 entryTimestamp = crow::utility::getDateTime(
178 static_cast<std::time_t>(timestamp / 1000 / 1000));
179 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800180}
181
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182static bool getSkipParam(crow::Response& res, const crow::Request& req,
183 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700184{
James Feist5a7e8772020-07-22 09:08:38 -0700185 boost::urls::url_view::params_type::iterator it =
186 req.urlParams.find("$skip");
187 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700188 {
James Feist5a7e8772020-07-22 09:08:38 -0700189 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500190 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700191 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
192 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
194
195 messages::queryParameterValueTypeError(res, std::string(skipParam),
196 "$skip");
197 return false;
198 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 }
200 return true;
201}
202
Ed Tanous271584a2019-07-09 16:24:22 -0700203static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204static bool getTopParam(crow::Response& res, const crow::Request& req,
205 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
James Feist5a7e8772020-07-22 09:08:38 -0700207 boost::urls::url_view::params_type::iterator it =
208 req.urlParams.find("$top");
209 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700210 {
James Feist5a7e8772020-07-22 09:08:38 -0700211 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500212 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700213 top = std::strtoul(topParam.c_str(), &ptr, 10);
214 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 {
216 messages::queryParameterValueTypeError(res, std::string(topParam),
217 "$top");
218 return false;
219 }
Ed Tanous271584a2019-07-09 16:24:22 -0700220 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 {
222
223 messages::queryParameterOutOfRange(
224 res, std::to_string(top), "$top",
225 "1-" + std::to_string(maxEntriesPerPage));
226 return false;
227 }
228 }
229 return true;
230}
231
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500232static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700233 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700234{
235 int ret = 0;
236 static uint64_t prevTs = 0;
237 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700238 if (firstEntry)
239 {
240 prevTs = 0;
241 }
242
Jason M. Bills16428a12018-11-02 12:42:29 -0700243 // Get the entry timestamp
244 uint64_t curTs = 0;
245 ret = sd_journal_get_realtime_usec(journal, &curTs);
246 if (ret < 0)
247 {
248 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
249 << strerror(-ret);
250 return false;
251 }
252 // If the timestamp isn't unique, increment the index
253 if (curTs == prevTs)
254 {
255 index++;
256 }
257 else
258 {
259 // Otherwise, reset it
260 index = 0;
261 }
262 // Save the timestamp
263 prevTs = curTs;
264
265 entryID = std::to_string(curTs);
266 if (index > 0)
267 {
268 entryID += "_" + std::to_string(index);
269 }
270 return true;
271}
272
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500273static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700274 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700275{
Ed Tanous271584a2019-07-09 16:24:22 -0700276 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700277 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700278 if (firstEntry)
279 {
280 prevTs = 0;
281 }
282
Jason M. Bills95820182019-04-22 16:25:34 -0700283 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700284 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700285 std::tm timeStruct = {};
286 std::istringstream entryStream(logEntry);
287 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
288 {
289 curTs = std::mktime(&timeStruct);
290 }
291 // If the timestamp isn't unique, increment the index
292 if (curTs == prevTs)
293 {
294 index++;
295 }
296 else
297 {
298 // Otherwise, reset it
299 index = 0;
300 }
301 // Save the timestamp
302 prevTs = curTs;
303
304 entryID = std::to_string(curTs);
305 if (index > 0)
306 {
307 entryID += "_" + std::to_string(index);
308 }
309 return true;
310}
311
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500312static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
313 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700314{
315 if (entryID.empty())
316 {
317 return false;
318 }
319 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800320 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700321
Ed Tanous81ce6092020-12-17 16:54:55 +0000322 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700323 if (underscorePos != tsStr.npos)
324 {
325 // Timestamp has an index
326 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800327 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700328 indexStr.remove_prefix(underscorePos + 1);
329 std::size_t pos;
330 try
331 {
Ed Tanous39e77502019-03-04 17:35:53 -0800332 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700333 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500334 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700335 {
336 messages::resourceMissingAtURI(res, entryID);
337 return false;
338 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500339 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 {
341 messages::resourceMissingAtURI(res, entryID);
342 return false;
343 }
344 if (pos != indexStr.size())
345 {
346 messages::resourceMissingAtURI(res, entryID);
347 return false;
348 }
349 }
350 // Timestamp has no index
351 std::size_t pos;
352 try
353 {
Ed Tanous39e77502019-03-04 17:35:53 -0800354 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700355 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700357 {
358 messages::resourceMissingAtURI(res, entryID);
359 return false;
360 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500361 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700362 {
363 messages::resourceMissingAtURI(res, entryID);
364 return false;
365 }
366 if (pos != tsStr.size())
367 {
368 messages::resourceMissingAtURI(res, entryID);
369 return false;
370 }
371 return true;
372}
373
Jason M. Bills95820182019-04-22 16:25:34 -0700374static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500375 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700376{
377 static const std::filesystem::path redfishLogDir = "/var/log";
378 static const std::string redfishLogFilename = "redfish";
379
380 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500381 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700382 std::filesystem::directory_iterator(redfishLogDir))
383 {
384 // If we find a redfish log file, save the path
385 std::string filename = dirEnt.path().filename();
386 if (boost::starts_with(filename, redfishLogFilename))
387 {
388 redfishLogFiles.emplace_back(redfishLogDir / filename);
389 }
390 }
391 // As the log files rotate, they are appended with a ".#" that is higher for
392 // the older logs. Since we don't expect more than 10 log files, we
393 // can just sort the list to get them in order from newest to oldest
394 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
395
396 return !redfishLogFiles.empty();
397}
398
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500399inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
400 const std::string& dumpType)
401{
402 std::string dumpPath;
403 if (dumpType == "BMC")
404 {
405 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
406 }
407 else if (dumpType == "System")
408 {
409 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
410 }
411 else
412 {
413 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
414 messages::internalError(asyncResp->res);
415 return;
416 }
417
418 crow::connections::systemBus->async_method_call(
419 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
420 GetManagedObjectsType& resp) {
421 if (ec)
422 {
423 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
424 messages::internalError(asyncResp->res);
425 return;
426 }
427
428 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
429 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500430 std::string dumpEntryPath =
431 "/xyz/openbmc_project/dump/" +
432 std::string(boost::algorithm::to_lower_copy(dumpType)) +
433 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434
435 for (auto& object : resp)
436 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500437 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500438 {
439 continue;
440 }
441 std::time_t timestamp;
442 uint64_t size = 0;
443 entriesArray.push_back({});
444 nlohmann::json& thisEntry = entriesArray.back();
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000445
446 std::string entryID = object.first.filename();
447 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500448 {
449 continue;
450 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500451
452 for (auto& interfaceMap : object.second)
453 {
454 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
455 {
456
457 for (auto& propertyMap : interfaceMap.second)
458 {
459 if (propertyMap.first == "Size")
460 {
461 auto sizePtr =
462 std::get_if<uint64_t>(&propertyMap.second);
463 if (sizePtr == nullptr)
464 {
465 messages::internalError(asyncResp->res);
466 break;
467 }
468 size = *sizePtr;
469 break;
470 }
471 }
472 }
473 else if (interfaceMap.first ==
474 "xyz.openbmc_project.Time.EpochTime")
475 {
476
477 for (auto& propertyMap : interfaceMap.second)
478 {
479 if (propertyMap.first == "Elapsed")
480 {
481 const uint64_t* usecsTimeStamp =
482 std::get_if<uint64_t>(&propertyMap.second);
483 if (usecsTimeStamp == nullptr)
484 {
485 messages::internalError(asyncResp->res);
486 break;
487 }
488 timestamp =
489 static_cast<std::time_t>(*usecsTimeStamp);
490 break;
491 }
492 }
493 }
494 }
495
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500496 thisEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500497 thisEntry["@odata.id"] = dumpPath + entryID;
498 thisEntry["Id"] = entryID;
499 thisEntry["EntryType"] = "Event";
500 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
501 thisEntry["Name"] = dumpType + " Dump Entry";
502
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500503 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500504
505 if (dumpType == "BMC")
506 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500507 thisEntry["DiagnosticDataType"] = "Manager";
508 thisEntry["AdditionalDataURI"] =
509 "/redfish/v1/Managers/bmc/LogServices/Dump/"
510 "attachment/" +
511 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500512 }
513 else if (dumpType == "System")
514 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500515 thisEntry["DiagnosticDataType"] = "OEM";
516 thisEntry["OEMDiagnosticDataType"] = "System";
517 thisEntry["AdditionalDataURI"] =
518 "/redfish/v1/Systems/system/LogServices/Dump/"
519 "attachment/" +
520 entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500521 }
522 }
523 asyncResp->res.jsonValue["Members@odata.count"] =
524 entriesArray.size();
525 },
526 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
527 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
528}
529
530inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
531 const std::string& entryID,
532 const std::string& dumpType)
533{
534 std::string dumpPath;
535 if (dumpType == "BMC")
536 {
537 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
538 }
539 else if (dumpType == "System")
540 {
541 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
542 }
543 else
544 {
545 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
546 messages::internalError(asyncResp->res);
547 return;
548 }
549
550 crow::connections::systemBus->async_method_call(
551 [asyncResp, entryID, dumpPath, dumpType](
552 const boost::system::error_code ec, GetManagedObjectsType& resp) {
553 if (ec)
554 {
555 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
556 messages::internalError(asyncResp->res);
557 return;
558 }
559
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500560 bool foundDumpEntry = false;
561 std::string dumpEntryPath =
562 "/xyz/openbmc_project/dump/" +
563 std::string(boost::algorithm::to_lower_copy(dumpType)) +
564 "/entry/";
565
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500566 for (auto& objectPath : resp)
567 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500568 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500569 {
570 continue;
571 }
572
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500573 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500574 std::time_t timestamp;
575 uint64_t size = 0;
576
577 for (auto& interfaceMap : objectPath.second)
578 {
579 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
580 {
581 for (auto& propertyMap : interfaceMap.second)
582 {
583 if (propertyMap.first == "Size")
584 {
585 auto sizePtr =
586 std::get_if<uint64_t>(&propertyMap.second);
587 if (sizePtr == nullptr)
588 {
589 messages::internalError(asyncResp->res);
590 break;
591 }
592 size = *sizePtr;
593 break;
594 }
595 }
596 }
597 else if (interfaceMap.first ==
598 "xyz.openbmc_project.Time.EpochTime")
599 {
600 for (auto& propertyMap : interfaceMap.second)
601 {
602 if (propertyMap.first == "Elapsed")
603 {
604 const uint64_t* usecsTimeStamp =
605 std::get_if<uint64_t>(&propertyMap.second);
606 if (usecsTimeStamp == nullptr)
607 {
608 messages::internalError(asyncResp->res);
609 break;
610 }
611 timestamp =
612 static_cast<std::time_t>(*usecsTimeStamp);
613 break;
614 }
615 }
616 }
617 }
618
619 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500620 "#LogEntry.v1_7_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
622 asyncResp->res.jsonValue["Id"] = entryID;
623 asyncResp->res.jsonValue["EntryType"] = "Event";
624 asyncResp->res.jsonValue["Created"] =
625 crow::utility::getDateTime(timestamp);
626 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
627
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500628 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500629
630 if (dumpType == "BMC")
631 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500632 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
633 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500634 "/redfish/v1/Managers/bmc/LogServices/Dump/"
635 "attachment/" +
636 entryID;
637 }
638 else if (dumpType == "System")
639 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500640 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
641 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500642 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500643 asyncResp->res.jsonValue["AdditionalDataURI"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500644 "/redfish/v1/Systems/system/LogServices/Dump/"
645 "attachment/" +
646 entryID;
647 }
648 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500649 if (foundDumpEntry == false)
650 {
651 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
652 messages::internalError(asyncResp->res);
653 return;
654 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 },
656 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
657 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
658}
659
Stanley Chu98782562020-11-04 16:10:24 +0800660inline void deleteDumpEntry(const std::shared_ptr<AsyncResp>& asyncResp,
661 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500662 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663{
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500664 auto respHandler = [asyncResp](const boost::system::error_code ec) {
665 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
666 if (ec)
667 {
668 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
669 << ec;
670 messages::internalError(asyncResp->res);
671 return;
672 }
673 };
674 crow::connections::systemBus->async_method_call(
675 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500676 "/xyz/openbmc_project/dump/" +
677 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
678 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500679 "xyz.openbmc_project.Object.Delete", "Delete");
680}
681
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500682inline void createDumpTaskCallback(const crow::Request& req,
Ed Tanousb5a76932020-09-29 16:16:58 -0700683 const std::shared_ptr<AsyncResp>& asyncResp,
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500684 const uint32_t& dumpId,
685 const std::string& dumpPath,
686 const std::string& dumpType)
687{
688 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500689 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500690 boost::system::error_code err, sdbusplus::message::message& m,
691 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000692 if (err)
693 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500694 BMCWEB_LOG_ERROR << "Error in creating a dump";
695 taskData->state = "Cancelled";
696 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000697 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500698 std::vector<std::pair<
699 std::string,
700 std::vector<std::pair<std::string, std::variant<std::string>>>>>
701 interfacesList;
702
703 sdbusplus::message::object_path objPath;
704
705 m.read(objPath, interfacesList);
706
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500707 if (objPath.str ==
708 "/xyz/openbmc_project/dump/" +
709 std::string(boost::algorithm::to_lower_copy(dumpType)) +
710 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500711 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500712 nlohmann::json retMessage = messages::success();
713 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500714
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500715 std::string headerLoc =
716 "Location: " + dumpPath + std::to_string(dumpId);
717 taskData->payload->httpHeaders.emplace_back(
718 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500719
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500720 taskData->state = "Completed";
721 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500723 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500724 },
725 "type='signal',interface='org.freedesktop.DBus."
726 "ObjectManager',"
727 "member='InterfacesAdded', "
728 "path='/xyz/openbmc_project/dump'");
729
730 task->startTimer(std::chrono::minutes(3));
731 task->populateResp(asyncResp->res);
732 task->payload.emplace(req);
733}
734
735inline void createDump(crow::Response& res, const crow::Request& req,
736 const std::string& dumpType)
737{
738 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
739
740 std::string dumpPath;
741 if (dumpType == "BMC")
742 {
743 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
744 }
745 else if (dumpType == "System")
746 {
747 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
748 }
749 else
750 {
751 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
752 messages::internalError(asyncResp->res);
753 return;
754 }
755
756 std::optional<std::string> diagnosticDataType;
757 std::optional<std::string> oemDiagnosticDataType;
758
759 if (!redfish::json_util::readJson(
760 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
761 "OEMDiagnosticDataType", oemDiagnosticDataType))
762 {
763 return;
764 }
765
766 if (dumpType == "System")
767 {
768 if (!oemDiagnosticDataType || !diagnosticDataType)
769 {
770 BMCWEB_LOG_ERROR << "CreateDump action parameter "
771 "'DiagnosticDataType'/"
772 "'OEMDiagnosticDataType' value not found!";
773 messages::actionParameterMissing(
774 asyncResp->res, "CollectDiagnosticData",
775 "DiagnosticDataType & OEMDiagnosticDataType");
776 return;
777 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700778 if ((*oemDiagnosticDataType != "System") ||
779 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500780 {
781 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
782 messages::invalidObject(asyncResp->res,
783 "System Dump creation parameters");
784 return;
785 }
786 }
787 else if (dumpType == "BMC")
788 {
789 if (!diagnosticDataType)
790 {
791 BMCWEB_LOG_ERROR << "CreateDump action parameter "
792 "'DiagnosticDataType' not found!";
793 messages::actionParameterMissing(
794 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
795 return;
796 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700797 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500798 {
799 BMCWEB_LOG_ERROR
800 << "Wrong parameter value passed for 'DiagnosticDataType'";
801 messages::invalidObject(asyncResp->res,
802 "BMC Dump creation parameters");
803 return;
804 }
805 }
806
807 crow::connections::systemBus->async_method_call(
808 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
809 const uint32_t& dumpId) {
810 if (ec)
811 {
812 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
813 messages::internalError(asyncResp->res);
814 return;
815 }
816 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
817
818 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
819 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500820 "xyz.openbmc_project.Dump.Manager",
821 "/xyz/openbmc_project/dump/" +
822 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500823 "xyz.openbmc_project.Dump.Create", "CreateDump");
824}
825
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500826inline void clearDump(crow::Response& res, const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500827{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500828 std::string dumpTypeLowerCopy =
829 std::string(boost::algorithm::to_lower_copy(dumpType));
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500830 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
831 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500832 [asyncResp, dumpType](const boost::system::error_code ec,
833 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500834 if (ec)
835 {
836 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
837 messages::internalError(asyncResp->res);
838 return;
839 }
840
841 for (const std::string& path : subTreePaths)
842 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000843 sdbusplus::message::object_path objPath(path);
844 std::string logID = objPath.filename();
845 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500846 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000847 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500848 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000849 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500850 }
851 },
852 "xyz.openbmc_project.ObjectMapper",
853 "/xyz/openbmc_project/object_mapper",
854 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500855 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
856 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
857 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500858}
859
Ed Tanous2c70f802020-09-28 14:29:23 -0700860static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500861 const std::vector<std::pair<std::string, VariantType>>& params,
862 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700863{
864 for (auto property : params)
865 {
866 if (property.first == "Timestamp")
867 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500868 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500869 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700870 if (value != nullptr)
871 {
872 timestamp = *value;
873 }
874 }
875 else if (property.first == "Filename")
876 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500877 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500878 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700879 if (value != nullptr)
880 {
881 filename = *value;
882 }
883 }
884 else if (property.first == "Log")
885 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500886 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500887 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700888 if (value != nullptr)
889 {
890 logfile = *value;
891 }
892 }
893 }
894}
895
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500896constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800897class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700898{
899 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700900 SystemLogServiceCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800901 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800902 {
903 entityPrivileges = {
904 {boost::beast::http::verb::get, {{"Login"}}},
905 {boost::beast::http::verb::head, {{"Login"}}},
906 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
907 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
908 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
909 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
910 }
911
912 private:
913 /**
914 * Functions triggers appropriate requests on DBus
915 */
Ed Tanouscb13a392020-07-25 19:02:03 +0000916 void doGet(crow::Response& res, const crow::Request&,
917 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800918 {
919 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800920 // Collections don't include the static data added by SubRoute because
921 // it has a duplicate entry for members
922 asyncResp->res.jsonValue["@odata.type"] =
923 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800924 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800925 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800926 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
927 asyncResp->res.jsonValue["Description"] =
928 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500929 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800930 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800931 logServiceArray.push_back(
932 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500933#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600934 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500935 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600936#endif
937
Jason M. Billsd53dd412019-02-12 17:16:22 -0800938#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
939 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500940 {{"@odata.id",
941 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800942#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800943 asyncResp->res.jsonValue["Members@odata.count"] =
944 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800945
946 crow::connections::systemBus->async_method_call(
947 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500948 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800949 if (ec)
950 {
951 BMCWEB_LOG_ERROR << ec;
952 return;
953 }
954
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500955 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800956 {
957 if (pathStr.find("PostCode") != std::string::npos)
958 {
Ed Tanous23a21a12020-07-25 04:45:05 +0000959 nlohmann::json& logServiceArrayLocal =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800960 asyncResp->res.jsonValue["Members"];
Ed Tanous23a21a12020-07-25 04:45:05 +0000961 logServiceArrayLocal.push_back(
ZhikuiRena3316fc2020-01-29 14:58:08 -0800962 {{"@odata.id", "/redfish/v1/Systems/system/"
963 "LogServices/PostCodes"}});
964 asyncResp->res.jsonValue["Members@odata.count"] =
Ed Tanous23a21a12020-07-25 04:45:05 +0000965 logServiceArrayLocal.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800966 return;
967 }
968 }
969 },
970 "xyz.openbmc_project.ObjectMapper",
971 "/xyz/openbmc_project/object_mapper",
972 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500973 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800974 }
975};
976
977class EventLogService : public Node
978{
979 public:
Ed Tanous52cc1122020-07-18 13:51:21 -0700980 EventLogService(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800981 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800982 {
983 entityPrivileges = {
984 {boost::beast::http::verb::get, {{"Login"}}},
985 {boost::beast::http::verb::head, {{"Login"}}},
986 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
987 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
988 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
989 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
990 }
991
992 private:
Ed Tanouscb13a392020-07-25 19:02:03 +0000993 void doGet(crow::Response& res, const crow::Request&,
994 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800995 {
996 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
997
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800998 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800999 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001000 asyncResp->res.jsonValue["@odata.type"] =
1001 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001002 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1003 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001004 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001005 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1006 asyncResp->res.jsonValue["Entries"] = {
1007 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001008 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001009 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1010
1011 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1012 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001013 }
1014};
1015
Tim Lee1f56a3a2019-10-09 10:17:57 +08001016class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001017{
1018 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001019 JournalEventLogClear(App& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001020 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1021 "LogService.ClearLog/")
1022 {
1023 entityPrivileges = {
1024 {boost::beast::http::verb::get, {{"Login"}}},
1025 {boost::beast::http::verb::head, {{"Login"}}},
1026 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1027 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1028 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1029 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1030 }
1031
1032 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001033 void doPost(crow::Response& res, const crow::Request&,
1034 const std::vector<std::string>&) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001035 {
1036 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1037
1038 // Clear the EventLog by deleting the log files
1039 std::vector<std::filesystem::path> redfishLogFiles;
1040 if (getRedfishLogFiles(redfishLogFiles))
1041 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001042 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001043 {
1044 std::error_code ec;
1045 std::filesystem::remove(file, ec);
1046 }
1047 }
1048
1049 // Reload rsyslog so it knows to start new log files
1050 crow::connections::systemBus->async_method_call(
1051 [asyncResp](const boost::system::error_code ec) {
1052 if (ec)
1053 {
1054 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1055 messages::internalError(asyncResp->res);
1056 return;
1057 }
1058
1059 messages::success(asyncResp->res);
1060 },
1061 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1062 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1063 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001064 }
1065};
1066
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001067static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001068 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001069 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001070{
Jason M. Bills95820182019-04-22 16:25:34 -07001071 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001072 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001073 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001074 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001075 {
1076 return 1;
1077 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001078 std::string timestamp = logEntry.substr(0, space);
1079 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001080 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001081 if (entryStart == std::string::npos)
1082 {
1083 return 1;
1084 }
1085 std::string_view entry(logEntry);
1086 entry.remove_prefix(entryStart);
1087 // Use split to separate the entry into its fields
1088 std::vector<std::string> logEntryFields;
1089 boost::split(logEntryFields, entry, boost::is_any_of(","),
1090 boost::token_compress_on);
1091 // We need at least a MessageId to be valid
1092 if (logEntryFields.size() < 1)
1093 {
1094 return 1;
1095 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001096 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001097
Jason M. Bills4851d452019-03-28 11:27:48 -07001098 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001099 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001100 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001101
Jason M. Bills4851d452019-03-28 11:27:48 -07001102 std::string msg;
1103 std::string severity;
1104 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001105 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001106 msg = message->message;
1107 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001108 }
1109
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001110 // Get the MessageArgs from the log if there are any
1111 boost::beast::span<std::string> messageArgs;
1112 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001113 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001114 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001115 // If the first string is empty, assume there are no MessageArgs
1116 std::size_t messageArgsSize = 0;
1117 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001118 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001119 messageArgsSize = logEntryFields.size() - 1;
1120 }
1121
Ed Tanous23a21a12020-07-25 04:45:05 +00001122 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001123
1124 // Fill the MessageArgs into the Message
1125 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001126 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001127 {
1128 std::string argStr = "%" + std::to_string(++i);
1129 size_t argPos = msg.find(argStr);
1130 if (argPos != std::string::npos)
1131 {
1132 msg.replace(argPos, argStr.length(), messageArg);
1133 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001134 }
1135 }
1136
Jason M. Bills95820182019-04-22 16:25:34 -07001137 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1138 // format which matches the Redfish format except for the fractional seconds
1139 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001140 std::size_t dot = timestamp.find_first_of('.');
1141 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001142 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001143 {
Jason M. Bills95820182019-04-22 16:25:34 -07001144 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001145 }
1146
1147 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001148 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001149 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001150 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001151 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001152 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001153 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001154 {"Id", logEntryID},
1155 {"Message", std::move(msg)},
1156 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001157 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001158 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001159 {"Severity", std::move(severity)},
1160 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001161 return 0;
1162}
1163
Anthony Wilson27062602019-04-22 02:10:09 -05001164class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001165{
1166 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001167 JournalEventLogEntryCollection(App& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001168 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001169 {
1170 entityPrivileges = {
1171 {boost::beast::http::verb::get, {{"Login"}}},
1172 {boost::beast::http::verb::head, {{"Login"}}},
1173 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1174 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1175 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1176 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1177 }
1178
1179 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001180 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001181 const std::vector<std::string>&) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001182 {
1183 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001184 uint64_t skip = 0;
1185 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001186 if (!getSkipParam(asyncResp->res, req, skip))
1187 {
1188 return;
1189 }
1190 if (!getTopParam(asyncResp->res, req, top))
1191 {
1192 return;
1193 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001194 // Collections don't include the static data added by SubRoute because
1195 // it has a duplicate entry for members
1196 asyncResp->res.jsonValue["@odata.type"] =
1197 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001198 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001199 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001200 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1201 asyncResp->res.jsonValue["Description"] =
1202 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001203
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001204 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001205 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001206 // Go through the log files and create a unique ID for each entry
1207 std::vector<std::filesystem::path> redfishLogFiles;
1208 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001209 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001210 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001211
1212 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001213 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1214 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001215 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001216 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001217 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001218 {
1219 continue;
1220 }
1221
Jason M. Billse85d6b12019-07-29 17:01:15 -07001222 // Reset the unique ID on the first entry
1223 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001224 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001225 {
Jason M. Bills95820182019-04-22 16:25:34 -07001226 entryCount++;
1227 // Handle paging using skip (number of entries to skip from the
1228 // start) and top (number of entries to display)
1229 if (entryCount <= skip || entryCount > skip + top)
1230 {
1231 continue;
1232 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001233
Jason M. Bills95820182019-04-22 16:25:34 -07001234 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001235 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001236 {
1237 continue;
1238 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001239
Jason M. Billse85d6b12019-07-29 17:01:15 -07001240 if (firstEntry)
1241 {
1242 firstEntry = false;
1243 }
1244
Jason M. Bills95820182019-04-22 16:25:34 -07001245 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001246 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001247 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1248 {
1249 messages::internalError(asyncResp->res);
1250 return;
1251 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001252 }
1253 }
1254 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1255 if (skip + top < entryCount)
1256 {
1257 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001258 "/redfish/v1/Systems/system/LogServices/EventLog/"
1259 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001260 std::to_string(skip + top);
1261 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001262 }
1263};
1264
Jason M. Bills897967d2019-07-29 17:05:30 -07001265class JournalEventLogEntry : public Node
1266{
1267 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001268 JournalEventLogEntry(App& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001269 Node(app,
1270 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1271 std::string())
1272 {
1273 entityPrivileges = {
1274 {boost::beast::http::verb::get, {{"Login"}}},
1275 {boost::beast::http::verb::head, {{"Login"}}},
1276 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1277 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1278 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1279 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1280 }
1281
1282 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001283 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001284 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001285 {
1286 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1287 if (params.size() != 1)
1288 {
1289 messages::internalError(asyncResp->res);
1290 return;
1291 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001292 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001293
1294 // Go through the log files and check the unique ID for each entry to
1295 // find the target entry
1296 std::vector<std::filesystem::path> redfishLogFiles;
1297 getRedfishLogFiles(redfishLogFiles);
1298 std::string logEntry;
1299
1300 // Oldest logs are in the last file, so start there and loop backwards
1301 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1302 it++)
1303 {
1304 std::ifstream logStream(*it);
1305 if (!logStream.is_open())
1306 {
1307 continue;
1308 }
1309
1310 // Reset the unique ID on the first entry
1311 bool firstEntry = true;
1312 while (std::getline(logStream, logEntry))
1313 {
1314 std::string idStr;
1315 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1316 {
1317 continue;
1318 }
1319
1320 if (firstEntry)
1321 {
1322 firstEntry = false;
1323 }
1324
1325 if (idStr == targetID)
1326 {
1327 if (fillEventLogEntryJson(idStr, logEntry,
1328 asyncResp->res.jsonValue) != 0)
1329 {
1330 messages::internalError(asyncResp->res);
1331 return;
1332 }
1333 return;
1334 }
1335 }
1336 }
1337 // Requested ID was not found
1338 messages::resourceMissingAtURI(asyncResp->res, targetID);
1339 }
1340};
1341
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001342class DBusEventLogEntryCollection : public Node
1343{
1344 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001345 DBusEventLogEntryCollection(App& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001346 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1347 {
1348 entityPrivileges = {
1349 {boost::beast::http::verb::get, {{"Login"}}},
1350 {boost::beast::http::verb::head, {{"Login"}}},
1351 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1352 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1353 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1354 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1355 }
1356
1357 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001358 void doGet(crow::Response& res, const crow::Request&,
1359 const std::vector<std::string>&) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001360 {
1361 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1362
1363 // Collections don't include the static data added by SubRoute because
1364 // it has a duplicate entry for members
1365 asyncResp->res.jsonValue["@odata.type"] =
1366 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001367 asyncResp->res.jsonValue["@odata.id"] =
1368 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1369 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1370 asyncResp->res.jsonValue["Description"] =
1371 "Collection of System Event Log Entries";
1372
Andrew Geisslercb92c032018-08-17 07:56:14 -07001373 // DBus implementation of EventLog/Entries
1374 // Make call to Logging Service to find all log entry objects
1375 crow::connections::systemBus->async_method_call(
1376 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001377 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001378 if (ec)
1379 {
1380 // TODO Handle for specific error code
1381 BMCWEB_LOG_ERROR
1382 << "getLogEntriesIfaceData resp_handler got error "
1383 << ec;
1384 messages::internalError(asyncResp->res);
1385 return;
1386 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001387 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001388 asyncResp->res.jsonValue["Members"];
1389 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001390 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001391 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001392 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001393 {
1394 if (interfaceMap.first !=
1395 "xyz.openbmc_project.Logging.Entry")
1396 {
1397 BMCWEB_LOG_DEBUG << "Bailing early on "
1398 << interfaceMap.first;
1399 continue;
1400 }
1401 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001402 nlohmann::json& thisEntry = entriesArray.back();
1403 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001404 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001405 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001406 std::string* severity = nullptr;
1407 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001408
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001409 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001410 {
1411 if (propertyMap.first == "Id")
1412 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001413 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001414 }
1415 else if (propertyMap.first == "Timestamp")
1416 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001417 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001418 std::get_if<uint64_t>(&propertyMap.second);
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001419 if (millisTimeStamp != nullptr)
George Liuebd45902020-08-26 14:21:10 +08001420 {
1421 timestamp = crow::utility::getTimestamp(
1422 *millisTimeStamp);
1423 }
George Liud139c232020-08-18 18:48:57 +08001424 }
1425 else if (propertyMap.first == "UpdateTimestamp")
1426 {
1427 const uint64_t* millisTimeStamp =
1428 std::get_if<uint64_t>(&propertyMap.second);
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001429 if (millisTimeStamp != nullptr)
George Liuebd45902020-08-26 14:21:10 +08001430 {
1431 updateTimestamp =
1432 crow::utility::getTimestamp(
1433 *millisTimeStamp);
1434 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001435 }
1436 else if (propertyMap.first == "Severity")
1437 {
1438 severity = std::get_if<std::string>(
1439 &propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001440 }
1441 else if (propertyMap.first == "Message")
1442 {
1443 message = std::get_if<std::string>(
1444 &propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001445 }
1446 }
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001447 if (id == nullptr || message == nullptr ||
1448 severity == nullptr)
1449 {
1450 messages::internalError(asyncResp->res);
1451 return;
1452 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001453 thisEntry = {
George Liud139c232020-08-18 18:48:57 +08001454 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001455 {"@odata.id",
1456 "/redfish/v1/Systems/system/LogServices/EventLog/"
1457 "Entries/" +
1458 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001459 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001460 {"Id", std::to_string(*id)},
1461 {"Message", *message},
1462 {"EntryType", "Event"},
1463 {"Severity",
1464 translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001465 {"Created", crow::utility::getDateTime(timestamp)},
1466 {"Modified",
1467 crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001468 }
1469 }
1470 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001471 [](const nlohmann::json& left,
1472 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001473 return (left["Id"] <= right["Id"]);
1474 });
1475 asyncResp->res.jsonValue["Members@odata.count"] =
1476 entriesArray.size();
1477 },
1478 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1479 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001480 }
1481};
1482
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001483class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001484{
1485 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001486 DBusEventLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001487 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001488 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1489 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001490 {
1491 entityPrivileges = {
1492 {boost::beast::http::verb::get, {{"Login"}}},
1493 {boost::beast::http::verb::head, {{"Login"}}},
1494 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1495 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1496 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1497 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1498 }
1499
1500 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001501 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001502 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001503 {
1504 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001505 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001506 {
1507 messages::internalError(asyncResp->res);
1508 return;
1509 }
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001510 std::string entryID = params[0];
1511 dbus::utility::escapePathForDbus(entryID);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001512
Andrew Geisslercb92c032018-08-17 07:56:14 -07001513 // DBus implementation of EventLog/Entries
1514 // Make call to Logging Service to find all log entry objects
1515 crow::connections::systemBus->async_method_call(
1516 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001517 GetManagedPropertyType& resp) {
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001518 if (ec.value() == EBADR)
1519 {
1520 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1521 entryID);
1522 return;
1523 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001524 if (ec)
1525 {
1526 BMCWEB_LOG_ERROR
1527 << "EventLogEntry (DBus) resp_handler got error " << ec;
1528 messages::internalError(asyncResp->res);
1529 return;
1530 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001531 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001532 std::time_t timestamp{};
George Liud139c232020-08-18 18:48:57 +08001533 std::time_t updateTimestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001534 std::string* severity = nullptr;
1535 std::string* message = nullptr;
George Liud139c232020-08-18 18:48:57 +08001536
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001537 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001538 {
1539 if (propertyMap.first == "Id")
1540 {
1541 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001542 }
1543 else if (propertyMap.first == "Timestamp")
1544 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001545 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001546 std::get_if<uint64_t>(&propertyMap.second);
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001547 if (millisTimeStamp != nullptr)
George Liuebd45902020-08-26 14:21:10 +08001548 {
1549 timestamp =
1550 crow::utility::getTimestamp(*millisTimeStamp);
1551 }
George Liud139c232020-08-18 18:48:57 +08001552 }
1553 else if (propertyMap.first == "UpdateTimestamp")
1554 {
1555 const uint64_t* millisTimeStamp =
1556 std::get_if<uint64_t>(&propertyMap.second);
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001557 if (millisTimeStamp != nullptr)
George Liuebd45902020-08-26 14:21:10 +08001558 {
1559 updateTimestamp =
1560 crow::utility::getTimestamp(*millisTimeStamp);
1561 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001562 }
1563 else if (propertyMap.first == "Severity")
1564 {
1565 severity =
1566 std::get_if<std::string>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001567 }
1568 else if (propertyMap.first == "Message")
1569 {
1570 message = std::get_if<std::string>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001571 }
1572 }
Ed Tanous271584a2019-07-09 16:24:22 -07001573 if (id == nullptr || message == nullptr || severity == nullptr)
1574 {
Adriana Kobylakae34c8e2021-02-11 09:33:10 -06001575 messages::internalError(asyncResp->res);
Ed Tanous271584a2019-07-09 16:24:22 -07001576 return;
1577 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001578 asyncResp->res.jsonValue = {
George Liud139c232020-08-18 18:48:57 +08001579 {"@odata.type", "#LogEntry.v1_6_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001580 {"@odata.id",
1581 "/redfish/v1/Systems/system/LogServices/EventLog/"
1582 "Entries/" +
1583 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001584 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001585 {"Id", std::to_string(*id)},
1586 {"Message", *message},
1587 {"EntryType", "Event"},
1588 {"Severity", translateSeverityDbusToRedfish(*severity)},
George Liud139c232020-08-18 18:48:57 +08001589 {"Created", crow::utility::getDateTime(timestamp)},
1590 {"Modified", crow::utility::getDateTime(updateTimestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001591 },
1592 "xyz.openbmc_project.Logging",
1593 "/xyz/openbmc_project/logging/entry/" + entryID,
1594 "org.freedesktop.DBus.Properties", "GetAll",
1595 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001596 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001597
Ed Tanouscb13a392020-07-25 19:02:03 +00001598 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001599 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001600 {
1601
1602 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1603
1604 auto asyncResp = std::make_shared<AsyncResp>(res);
1605
1606 if (params.size() != 1)
1607 {
1608 messages::internalError(asyncResp->res);
1609 return;
1610 }
1611 std::string entryID = params[0];
1612
1613 dbus::utility::escapePathForDbus(entryID);
1614
1615 // Process response from Logging service.
1616 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1617 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1618 if (ec)
1619 {
1620 // TODO Handle for specific error code
1621 BMCWEB_LOG_ERROR
1622 << "EventLogEntry (DBus) doDelete respHandler got error "
1623 << ec;
1624 asyncResp->res.result(
1625 boost::beast::http::status::internal_server_error);
1626 return;
1627 }
1628
1629 asyncResp->res.result(boost::beast::http::status::ok);
1630 };
1631
1632 // Make call to Logging service to request Delete Log
1633 crow::connections::systemBus->async_method_call(
1634 respHandler, "xyz.openbmc_project.Logging",
1635 "/xyz/openbmc_project/logging/entry/" + entryID,
1636 "xyz.openbmc_project.Object.Delete", "Delete");
1637 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001638};
1639
1640class BMCLogServiceCollection : public Node
1641{
1642 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001643 BMCLogServiceCollection(App& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001644 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001645 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001646 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001647 {boost::beast::http::verb::get, {{"Login"}}},
1648 {boost::beast::http::verb::head, {{"Login"}}},
1649 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1650 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1651 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1652 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001653 }
1654
1655 private:
1656 /**
1657 * Functions triggers appropriate requests on DBus
1658 */
Ed Tanouscb13a392020-07-25 19:02:03 +00001659 void doGet(crow::Response& res, const crow::Request&,
1660 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001661 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001662 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001663 // Collections don't include the static data added by SubRoute because
1664 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001665 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001666 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001667 asyncResp->res.jsonValue["@odata.id"] =
1668 "/redfish/v1/Managers/bmc/LogServices";
1669 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1670 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001671 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001672 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001673 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001674#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1675 logServiceArray.push_back(
1676 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1677#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001678#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1679 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001680 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001681#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001682 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001683 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001684 }
1685};
1686
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001687class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001688{
1689 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001690 BMCJournalLogService(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001691 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001692 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001693 entityPrivileges = {
1694 {boost::beast::http::verb::get, {{"Login"}}},
1695 {boost::beast::http::verb::head, {{"Login"}}},
1696 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1697 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1698 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1699 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1700 }
1701
1702 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001703 void doGet(crow::Response& res, const crow::Request&,
1704 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001705 {
1706 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001707 asyncResp->res.jsonValue["@odata.type"] =
1708 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001709 asyncResp->res.jsonValue["@odata.id"] =
1710 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001711 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1712 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1713 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001714 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001715 asyncResp->res.jsonValue["Entries"] = {
1716 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001717 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001718 }
1719};
1720
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001721static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1722 sd_journal* journal,
1723 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001724{
1725 // Get the Log Entry contents
1726 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001727
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001728 std::string message;
1729 std::string_view syslogID;
1730 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1731 if (ret < 0)
1732 {
1733 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1734 << strerror(-ret);
1735 }
1736 if (!syslogID.empty())
1737 {
1738 message += std::string(syslogID) + ": ";
1739 }
1740
Ed Tanous39e77502019-03-04 17:35:53 -08001741 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001742 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001743 if (ret < 0)
1744 {
1745 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1746 return 1;
1747 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001748 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001749
1750 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001751 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001752 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001753 if (ret < 0)
1754 {
1755 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001756 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001757
1758 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001759 std::string entryTimeStr;
1760 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001761 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001762 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001763 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001764
1765 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001766 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001767 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001768 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1769 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001770 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001771 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001772 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001773 {"EntryType", "Oem"},
1774 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001775 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001776 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001777 {"Created", std::move(entryTimeStr)}};
1778 return 0;
1779}
1780
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001781class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001782{
1783 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001784 BMCJournalLogEntryCollection(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001785 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001786 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001787 entityPrivileges = {
1788 {boost::beast::http::verb::get, {{"Login"}}},
1789 {boost::beast::http::verb::head, {{"Login"}}},
1790 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1791 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1792 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1793 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1794 }
1795
1796 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001797 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00001798 const std::vector<std::string>&) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001799 {
1800 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001801 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001802 uint64_t skip = 0;
1803 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001804 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001805 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001806 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001807 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001808 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001809 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001810 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001811 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001812 // Collections don't include the static data added by SubRoute because
1813 // it has a duplicate entry for members
1814 asyncResp->res.jsonValue["@odata.type"] =
1815 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001816 asyncResp->res.jsonValue["@odata.id"] =
1817 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001818 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001819 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001820 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1821 asyncResp->res.jsonValue["Description"] =
1822 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001823 asyncResp->res.jsonValue["@odata.id"] =
1824 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001825 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001826 logEntryArray = nlohmann::json::array();
1827
1828 // Go through the journal and use the timestamp to create a unique ID
1829 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001830 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001831 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1832 if (ret < 0)
1833 {
1834 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001835 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001836 return;
1837 }
1838 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1839 journalTmp, sd_journal_close);
1840 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001841 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001842 // Reset the unique ID on the first entry
1843 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001844 SD_JOURNAL_FOREACH(journal.get())
1845 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001846 entryCount++;
1847 // Handle paging using skip (number of entries to skip from the
1848 // start) and top (number of entries to display)
1849 if (entryCount <= skip || entryCount > skip + top)
1850 {
1851 continue;
1852 }
1853
Jason M. Bills16428a12018-11-02 12:42:29 -07001854 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001855 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001856 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001857 continue;
1858 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001859
Jason M. Billse85d6b12019-07-29 17:01:15 -07001860 if (firstEntry)
1861 {
1862 firstEntry = false;
1863 }
1864
Jason M. Billse1f26342018-07-18 12:12:00 -07001865 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001866 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001867 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1868 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001869 {
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 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001874 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1875 if (skip + top < entryCount)
1876 {
1877 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001878 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001879 std::to_string(skip + top);
1880 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001881 }
1882};
1883
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001884class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001885{
1886 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001887 BMCJournalLogEntry(App& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001888 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001889 std::string())
1890 {
1891 entityPrivileges = {
1892 {boost::beast::http::verb::get, {{"Login"}}},
1893 {boost::beast::http::verb::head, {{"Login"}}},
1894 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1895 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1896 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1897 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1898 }
1899
1900 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001901 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001902 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001903 {
1904 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1905 if (params.size() != 1)
1906 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001907 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001908 return;
1909 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001910 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001911 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001912 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001913 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001914 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001915 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001916 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001917 }
1918
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001919 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001920 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1921 if (ret < 0)
1922 {
1923 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001924 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001925 return;
1926 }
1927 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1928 journalTmp, sd_journal_close);
1929 journalTmp = nullptr;
1930 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001931 // tracking the unique ID
1932 std::string idStr;
1933 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001934 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301935 if (ret < 0)
1936 {
1937 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1938 << strerror(-ret);
1939 messages::internalError(asyncResp->res);
1940 return;
1941 }
Ed Tanous271584a2019-07-09 16:24:22 -07001942 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001943 {
1944 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001945 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1946 {
1947 messages::internalError(asyncResp->res);
1948 return;
1949 }
1950 if (firstEntry)
1951 {
1952 firstEntry = false;
1953 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001955 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001956 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001957 {
1958 messages::resourceMissingAtURI(asyncResp->res, entryID);
1959 return;
1960 }
1961
1962 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1963 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001964 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001965 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001966 return;
1967 }
1968 }
1969};
1970
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001971class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001972{
1973 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07001974 BMCDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001975 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001976 {
1977 entityPrivileges = {
1978 {boost::beast::http::verb::get, {{"Login"}}},
1979 {boost::beast::http::verb::head, {{"Login"}}},
1980 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1981 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1982 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1983 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1984 }
1985
1986 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00001987 void doGet(crow::Response& res, const crow::Request&,
1988 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001989 {
1990 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1991
1992 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001993 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06001994 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05001995 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001996 asyncResp->res.jsonValue["Name"] = "Dump LogService";
1997 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
1998 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06001999 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002000 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002001 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2002 asyncResp->res.jsonValue["Actions"] = {
2003 {"#LogService.ClearLog",
2004 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2005 "Actions/LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002006 {"#LogService.CollectDiagnosticData",
2007 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2008 "Actions/LogService.CollectDiagnosticData"}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002009 }
2010};
2011
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002012class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002013{
2014 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002015 BMCDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002016 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002017 {
2018 entityPrivileges = {
2019 {boost::beast::http::verb::get, {{"Login"}}},
2020 {boost::beast::http::verb::head, {{"Login"}}},
2021 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2022 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2023 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2024 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2025 }
2026
2027 private:
2028 /**
2029 * Functions triggers appropriate requests on DBus
2030 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002031 void doGet(crow::Response& res, const crow::Request&,
2032 const std::vector<std::string>&) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002033 {
2034 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2035
2036 asyncResp->res.jsonValue["@odata.type"] =
2037 "#LogEntryCollection.LogEntryCollection";
2038 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002039 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2040 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002041 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002042 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002043
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002044 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002045 }
2046};
2047
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002048class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002049{
2050 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002051 BMCDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002052 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002053 std::string())
2054 {
2055 entityPrivileges = {
2056 {boost::beast::http::verb::get, {{"Login"}}},
2057 {boost::beast::http::verb::head, {{"Login"}}},
2058 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2059 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2060 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2061 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2062 }
2063
2064 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002065 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002066 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002067 {
2068 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2069 if (params.size() != 1)
2070 {
2071 messages::internalError(asyncResp->res);
2072 return;
2073 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002074 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002075 }
2076
Ed Tanouscb13a392020-07-25 19:02:03 +00002077 void doDelete(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002078 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002079 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002080 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002081 if (params.size() != 1)
2082 {
2083 messages::internalError(asyncResp->res);
2084 return;
2085 }
Stanley Chu98782562020-11-04 16:10:24 +08002086 deleteDumpEntry(asyncResp, params[0], "bmc");
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002087 }
2088};
raviteja-bc9bb6862020-02-03 11:53:32 -06002089
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002090class BMCDumpCreate : public Node
2091{
2092 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002093 BMCDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002094 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002095 "Actions/"
2096 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002097 {
2098 entityPrivileges = {
2099 {boost::beast::http::verb::get, {{"Login"}}},
2100 {boost::beast::http::verb::head, {{"Login"}}},
2101 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2102 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2103 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2104 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2105 }
2106
2107 private:
2108 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002109 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002110 {
2111 createDump(res, req, "BMC");
2112 }
2113};
2114
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002115class BMCDumpClear : public Node
2116{
2117 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002118 BMCDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002119 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2120 "Actions/"
2121 "LogService.ClearLog/")
2122 {
2123 entityPrivileges = {
2124 {boost::beast::http::verb::get, {{"Login"}}},
2125 {boost::beast::http::verb::head, {{"Login"}}},
2126 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2127 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2128 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2129 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2130 }
2131
2132 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002133 void doPost(crow::Response& res, const crow::Request&,
2134 const std::vector<std::string>&) override
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002135 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002136 clearDump(res, "BMC");
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002137 }
2138};
2139
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002140class SystemDumpService : public Node
2141{
2142 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002143 SystemDumpService(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002144 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2145 {
2146 entityPrivileges = {
2147 {boost::beast::http::verb::get, {{"Login"}}},
2148 {boost::beast::http::verb::head, {{"Login"}}},
2149 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2150 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2151 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2152 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2153 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002154
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002155 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002156 void doGet(crow::Response& res, const crow::Request&,
2157 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002158 {
2159 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002160
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002161 asyncResp->res.jsonValue["@odata.id"] =
2162 "/redfish/v1/Systems/system/LogServices/Dump";
2163 asyncResp->res.jsonValue["@odata.type"] =
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002164 "#LogService.v1_2_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002165 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2166 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2167 asyncResp->res.jsonValue["Id"] = "Dump";
2168 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2169 asyncResp->res.jsonValue["Entries"] = {
2170 {"@odata.id",
2171 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2172 asyncResp->res.jsonValue["Actions"] = {
2173 {"#LogService.ClearLog",
2174 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2175 "LogService.ClearLog"}}},
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002176 {"#LogService.CollectDiagnosticData",
2177 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2178 "LogService.CollectDiagnosticData"}}}};
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002179 }
2180};
2181
2182class SystemDumpEntryCollection : public Node
2183{
2184 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002185 SystemDumpEntryCollection(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002186 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2187 {
2188 entityPrivileges = {
2189 {boost::beast::http::verb::get, {{"Login"}}},
2190 {boost::beast::http::verb::head, {{"Login"}}},
2191 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2192 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2193 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2194 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2195 }
2196
2197 private:
2198 /**
2199 * Functions triggers appropriate requests on DBus
2200 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002201 void doGet(crow::Response& res, const crow::Request&,
2202 const std::vector<std::string>&) override
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002203 {
2204 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2205
2206 asyncResp->res.jsonValue["@odata.type"] =
2207 "#LogEntryCollection.LogEntryCollection";
2208 asyncResp->res.jsonValue["@odata.id"] =
2209 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2210 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2211 asyncResp->res.jsonValue["Description"] =
2212 "Collection of System Dump Entries";
2213
2214 getDumpEntryCollection(asyncResp, "System");
2215 }
2216};
2217
2218class SystemDumpEntry : public Node
2219{
2220 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002221 SystemDumpEntry(App& app) :
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002222 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2223 std::string())
2224 {
2225 entityPrivileges = {
2226 {boost::beast::http::verb::get, {{"Login"}}},
2227 {boost::beast::http::verb::head, {{"Login"}}},
2228 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2229 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2230 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2231 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2232 }
2233
2234 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002235 void doGet(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002236 const std::vector<std::string>& params) override
2237 {
2238 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2239 if (params.size() != 1)
2240 {
2241 messages::internalError(asyncResp->res);
2242 return;
2243 }
2244 getDumpEntryById(asyncResp, params[0], "System");
2245 }
2246
Ed Tanouscb13a392020-07-25 19:02:03 +00002247 void doDelete(crow::Response& res, const crow::Request&,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002248 const std::vector<std::string>& params) override
2249 {
2250 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2251 if (params.size() != 1)
2252 {
2253 messages::internalError(asyncResp->res);
2254 return;
2255 }
Stanley Chu98782562020-11-04 16:10:24 +08002256 deleteDumpEntry(asyncResp, params[0], "system");
raviteja-bc9bb6862020-02-03 11:53:32 -06002257 }
2258};
2259
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002260class SystemDumpCreate : public Node
2261{
2262 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002263 SystemDumpCreate(App& app) :
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002264 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
Asmitha Karunanithid337bb72020-09-21 10:34:02 -05002265 "Actions/"
2266 "LogService.CollectDiagnosticData/")
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002267 {
2268 entityPrivileges = {
2269 {boost::beast::http::verb::get, {{"Login"}}},
2270 {boost::beast::http::verb::head, {{"Login"}}},
2271 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2272 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2273 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2274 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2275 }
2276
2277 private:
2278 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002279 const std::vector<std::string>&) override
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002280 {
2281 createDump(res, req, "System");
2282 }
2283};
2284
raviteja-b013487e2020-03-03 03:20:48 -06002285class SystemDumpClear : public Node
2286{
2287 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002288 SystemDumpClear(App& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002289 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002290 "Actions/"
2291 "LogService.ClearLog/")
2292 {
2293 entityPrivileges = {
2294 {boost::beast::http::verb::get, {{"Login"}}},
2295 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002296 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2297 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2298 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002299 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2300 }
2301
2302 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002303 void doPost(crow::Response& res, const crow::Request&,
2304 const std::vector<std::string>&) override
raviteja-b013487e2020-03-03 03:20:48 -06002305 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -05002306 clearDump(res, "System");
raviteja-b013487e2020-03-03 03:20:48 -06002307 }
2308};
2309
Jason M. Bills424c4172019-03-21 13:50:33 -07002310class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002311{
2312 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002313 CrashdumpService(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002314 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002315 {
AppaRao Puli39460282020-04-07 17:03:04 +05302316 // Note: Deviated from redfish privilege registry for GET & HEAD
2317 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002318 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302319 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2320 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002321 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2322 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2323 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2324 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002325 }
2326
2327 private:
2328 /**
2329 * Functions triggers appropriate requests on DBus
2330 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002331 void doGet(crow::Response& res, const crow::Request&,
2332 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002333 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002334 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002335 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002336 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002337 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002338 asyncResp->res.jsonValue["@odata.type"] =
2339 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002340 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2341 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2342 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002343 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2344 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002345 asyncResp->res.jsonValue["Entries"] = {
2346 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002347 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002348 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002349 {"#LogService.ClearLog",
2350 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2351 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002352 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002353 {{"#Crashdump.OnDemand",
2354 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002355 "Actions/Oem/Crashdump.OnDemand"}}},
2356 {"#Crashdump.Telemetry",
2357 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2358 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002359
2360#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002361 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002362 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002363 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2364 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002365#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002366 }
2367};
2368
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002369class CrashdumpClear : public Node
2370{
2371 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002372 CrashdumpClear(App& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002373 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2374 "LogService.ClearLog/")
2375 {
AppaRao Puli39460282020-04-07 17:03:04 +05302376 // Note: Deviated from redfish privilege registry for GET & HEAD
2377 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002378 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302379 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2380 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002381 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2382 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2383 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2384 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2385 }
2386
2387 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002388 void doPost(crow::Response& res, const crow::Request&,
2389 const std::vector<std::string>&) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002390 {
2391 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2392
2393 crow::connections::systemBus->async_method_call(
2394 [asyncResp](const boost::system::error_code ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002395 const std::string&) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002396 if (ec)
2397 {
2398 messages::internalError(asyncResp->res);
2399 return;
2400 }
2401 messages::success(asyncResp->res);
2402 },
2403 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2404 }
2405};
2406
Ed Tanousb5a76932020-09-29 16:16:58 -07002407static void logCrashdumpEntry(const std::shared_ptr<AsyncResp>& asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002408 const std::string& logID,
2409 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002410{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002411 auto getStoredLogCallback =
2412 [asyncResp, logID, &logEntryJson](
2413 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002414 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002415 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002416 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002417 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2418 if (ec.value() ==
2419 boost::system::linux_error::bad_request_descriptor)
2420 {
2421 messages::resourceNotFound(asyncResp->res, "LogEntry",
2422 logID);
2423 }
2424 else
2425 {
2426 messages::internalError(asyncResp->res);
2427 }
2428 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002429 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002430
Johnathan Mantey043a0532020-03-10 17:15:28 -07002431 std::string timestamp{};
2432 std::string filename{};
2433 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002434 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002435
2436 if (filename.empty() || timestamp.empty())
2437 {
2438 messages::resourceMissingAtURI(asyncResp->res, logID);
2439 return;
2440 }
2441
2442 std::string crashdumpURI =
2443 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2444 logID + "/" + filename;
2445 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2446 {"@odata.id", "/redfish/v1/Systems/system/"
2447 "LogServices/Crashdump/Entries/" +
2448 logID},
2449 {"Name", "CPU Crashdump"},
2450 {"Id", logID},
2451 {"EntryType", "Oem"},
2452 {"OemRecordFormat", "Crashdump URI"},
2453 {"Message", std::move(crashdumpURI)},
2454 {"Created", std::move(timestamp)}};
2455 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002456 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002457 std::move(getStoredLogCallback), crashdumpObject,
2458 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002459 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002460}
2461
Jason M. Bills424c4172019-03-21 13:50:33 -07002462class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002463{
2464 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002465 CrashdumpEntryCollection(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002466 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002467 {
AppaRao Puli39460282020-04-07 17:03:04 +05302468 // Note: Deviated from redfish privilege registry for GET & HEAD
2469 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002470 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302471 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2472 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002473 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2474 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2475 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2476 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002477 }
2478
2479 private:
2480 /**
2481 * Functions triggers appropriate requests on DBus
2482 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002483 void doGet(crow::Response& res, const crow::Request&,
2484 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002485 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002486 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002487 // Collections don't include the static data added by SubRoute because
2488 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002489 auto getLogEntriesCallback = [asyncResp](
2490 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002491 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002492 if (ec)
2493 {
2494 if (ec.value() !=
2495 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002496 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002497 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2498 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002499 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002500 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002501 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002502 }
2503 asyncResp->res.jsonValue["@odata.type"] =
2504 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002505 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002506 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002507 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002508 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002509 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002510 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002511 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002512 std::vector<std::string> logIDs;
2513 // Get the list of log entries and build up an empty array big
2514 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002515 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002516 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002517 // Get the log ID
Ed Tanousf23b7292020-10-15 09:41:17 -07002518 std::size_t lastPos = objpath.rfind('/');
Jason M. Billse855dd22019-10-08 11:37:48 -07002519 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002520 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002521 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002522 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002523 logIDs.emplace_back(objpath.substr(lastPos + 1));
2524
2525 // Add a space for the log entry to the array
2526 logEntryArray.push_back({});
2527 }
2528 // Now go through and set up async calls to fill in the entries
2529 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002530 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002531 {
2532 // Add the log entry to the array
2533 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002534 }
2535 asyncResp->res.jsonValue["Members@odata.count"] =
2536 logEntryArray.size();
2537 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002538 crow::connections::systemBus->async_method_call(
2539 std::move(getLogEntriesCallback),
2540 "xyz.openbmc_project.ObjectMapper",
2541 "/xyz/openbmc_project/object_mapper",
2542 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002543 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002544 }
2545};
2546
Jason M. Bills424c4172019-03-21 13:50:33 -07002547class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002548{
2549 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002550 CrashdumpEntry(App& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002551 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002552 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002553 std::string())
2554 {
AppaRao Puli39460282020-04-07 17:03:04 +05302555 // Note: Deviated from redfish privilege registry for GET & HEAD
2556 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002557 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302558 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2559 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002560 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2561 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2562 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2563 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002564 }
2565
2566 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002567 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002568 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002569 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002570 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002571 if (params.size() != 1)
2572 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002573 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002574 return;
2575 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002576 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002577 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2578 }
2579};
2580
2581class CrashdumpFile : public Node
2582{
2583 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002584 CrashdumpFile(App& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002585 Node(app,
2586 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2587 "<str>/",
2588 std::string(), 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.
Jason M. Billse855dd22019-10-08 11:37:48 -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. Billse855dd22019-10-08 11:37:48 -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"}}}};
2599 }
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
Jason M. Billse855dd22019-10-08 11:37:48 -07002604 {
2605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2606 if (params.size() != 2)
2607 {
2608 messages::internalError(asyncResp->res);
2609 return;
2610 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002611 const std::string& logID = params[0];
2612 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002613
Johnathan Mantey043a0532020-03-10 17:15:28 -07002614 auto getStoredLogCallback =
2615 [asyncResp, logID, fileName](
2616 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002617 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002618 if (ec)
2619 {
2620 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2621 << ec.message();
2622 messages::internalError(asyncResp->res);
2623 return;
2624 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002625
Johnathan Mantey043a0532020-03-10 17:15:28 -07002626 std::string dbusFilename{};
2627 std::string dbusTimestamp{};
2628 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002629
Ed Tanous2c70f802020-09-28 14:29:23 -07002630 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002631 dbusFilepath);
2632
2633 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2634 dbusFilepath.empty())
2635 {
2636 messages::resourceMissingAtURI(asyncResp->res, fileName);
2637 return;
2638 }
2639
2640 // Verify the file name parameter is correct
2641 if (fileName != dbusFilename)
2642 {
2643 messages::resourceMissingAtURI(asyncResp->res, fileName);
2644 return;
2645 }
2646
2647 if (!std::filesystem::exists(dbusFilepath))
2648 {
2649 messages::resourceMissingAtURI(asyncResp->res, fileName);
2650 return;
2651 }
2652 std::ifstream ifs(dbusFilepath, std::ios::in |
2653 std::ios::binary |
2654 std::ios::ate);
2655 std::ifstream::pos_type fileSize = ifs.tellg();
2656 if (fileSize < 0)
2657 {
2658 messages::generalError(asyncResp->res);
2659 return;
2660 }
2661 ifs.seekg(0, std::ios::beg);
2662
2663 auto crashData = std::make_unique<char[]>(
2664 static_cast<unsigned int>(fileSize));
2665
2666 ifs.read(crashData.get(), static_cast<int>(fileSize));
2667
2668 // The cast to std::string is intentional in order to use the
2669 // assign() that applies move mechanics
2670 asyncResp->res.body().assign(
2671 static_cast<std::string>(crashData.get()));
2672
2673 // Configure this to be a file download when accessed from
2674 // a browser
2675 asyncResp->res.addHeader("Content-Disposition", "attachment");
2676 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002677 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002678 std::move(getStoredLogCallback), crashdumpObject,
2679 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002680 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002681 }
2682};
2683
Jason M. Bills424c4172019-03-21 13:50:33 -07002684class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002685{
2686 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002687 OnDemandCrashdump(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002688 Node(app,
2689 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2690 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002691 {
AppaRao Puli39460282020-04-07 17:03:04 +05302692 // Note: Deviated from redfish privilege registry for GET & HEAD
2693 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002694 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302695 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2696 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2697 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2698 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2699 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2700 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002701 }
2702
2703 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002704 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002705 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002706 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002707 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002708
James Feistfe306722020-03-12 16:32:08 -07002709 auto generateonDemandLogCallback = [asyncResp,
2710 req](const boost::system::error_code
2711 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002712 const std::string&) {
James Feist46229572020-02-19 15:11:58 -08002713 if (ec)
2714 {
2715 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002716 {
James Feist46229572020-02-19 15:11:58 -08002717 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002718 }
James Feist46229572020-02-19 15:11:58 -08002719 else if (ec.value() ==
2720 boost::system::errc::device_or_resource_busy)
2721 {
2722 messages::serviceTemporarilyUnavailable(asyncResp->res,
2723 "60");
2724 }
2725 else
2726 {
2727 messages::internalError(asyncResp->res);
2728 }
2729 return;
2730 }
2731 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002732 [](boost::system::error_code err, sdbusplus::message::message&,
2733 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002734 if (!err)
2735 {
James Feiste5d50062020-05-11 17:29:00 -07002736 taskData->messages.emplace_back(
2737 messages::taskCompletedOK(
2738 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002739 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002740 }
James Feist32898ce2020-03-10 16:16:52 -07002741 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002742 },
James Feist46229572020-02-19 15:11:58 -08002743 "type='signal',interface='org.freedesktop.DBus.Properties',"
2744 "member='PropertiesChanged',arg0namespace='com.intel."
2745 "crashdump'");
2746 task->startTimer(std::chrono::minutes(5));
2747 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002748 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002749 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002750 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002751 std::move(generateonDemandLogCallback), crashdumpObject,
2752 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002753 }
2754};
2755
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002756class TelemetryCrashdump : public Node
2757{
2758 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002759 TelemetryCrashdump(App& app) :
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002760 Node(app,
2761 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2762 "Crashdump.Telemetry/")
2763 {
2764 // Note: Deviated from redfish privilege registry for GET & HEAD
2765 // method for security reasons.
2766 entityPrivileges = {
2767 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2768 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2769 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2770 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2771 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2772 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2773 }
2774
2775 private:
2776 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002777 const std::vector<std::string>&) override
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002778 {
2779 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2780
2781 auto generateTelemetryLogCallback = [asyncResp, req](
2782 const boost::system::error_code
2783 ec,
Ed Tanouscb13a392020-07-25 19:02:03 +00002784 const std::string&) {
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002785 if (ec)
2786 {
2787 if (ec.value() == boost::system::errc::operation_not_supported)
2788 {
2789 messages::resourceInStandby(asyncResp->res);
2790 }
2791 else if (ec.value() ==
2792 boost::system::errc::device_or_resource_busy)
2793 {
2794 messages::serviceTemporarilyUnavailable(asyncResp->res,
2795 "60");
2796 }
2797 else
2798 {
2799 messages::internalError(asyncResp->res);
2800 }
2801 return;
2802 }
2803 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2804 [](boost::system::error_code err, sdbusplus::message::message&,
2805 const std::shared_ptr<task::TaskData>& taskData) {
2806 if (!err)
2807 {
2808 taskData->messages.emplace_back(
2809 messages::taskCompletedOK(
2810 std::to_string(taskData->index)));
2811 taskData->state = "Completed";
2812 }
2813 return task::completed;
2814 },
2815 "type='signal',interface='org.freedesktop.DBus.Properties',"
2816 "member='PropertiesChanged',arg0namespace='com.intel."
2817 "crashdump'");
2818 task->startTimer(std::chrono::minutes(5));
2819 task->populateResp(asyncResp->res);
2820 task->payload.emplace(req);
2821 };
2822 crow::connections::systemBus->async_method_call(
2823 std::move(generateTelemetryLogCallback), crashdumpObject,
2824 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2825 }
2826};
2827
Jason M. Billse1f26342018-07-18 12:12:00 -07002828class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002829{
2830 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002831 SendRawPECI(App& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002832 Node(app,
2833 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2834 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002835 {
AppaRao Puli39460282020-04-07 17:03:04 +05302836 // Note: Deviated from redfish privilege registry for GET & HEAD
2837 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002838 entityPrivileges = {
2839 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2840 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2841 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2842 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2843 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2844 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2845 }
2846
2847 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002848 void doPost(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00002849 const std::vector<std::string>&) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002850 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002851 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002852 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002853
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002854 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002855 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002856 return;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002857 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002858 uint32_t idx = 0;
2859 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002860 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002861 if (cmd.size() < 3)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002862 {
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002863 std::string s("[");
2864 for (auto const& val : cmd)
2865 {
2866 if (val != *cmd.begin())
2867 {
2868 s += ",";
2869 }
2870 s += std::to_string(val);
2871 }
2872 s += "]";
2873 messages::actionParameterValueFormatError(
2874 res, s, "PECICommands[" + std::to_string(idx) + "]",
2875 "SendRawPeci");
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002876 return;
2877 }
Karthick Sundarrajanf0b6ae02020-01-17 13:32:58 -08002878 idx++;
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002879 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002880 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002881 auto sendRawPECICallback =
2882 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002883 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002884 if (ec)
2885 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002886 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002887 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002888 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002889 return;
2890 }
2891 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2892 {"PECIResponse", resp}};
2893 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002894 // Call the SendRawPECI command with the provided data
2895 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002896 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002897 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002898 }
2899};
2900
Andrew Geisslercb92c032018-08-17 07:56:14 -07002901/**
2902 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2903 */
2904class DBusLogServiceActionsClear : public Node
2905{
2906 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002907 DBusLogServiceActionsClear(App& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002908 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002909 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002910 {
2911 entityPrivileges = {
2912 {boost::beast::http::verb::get, {{"Login"}}},
2913 {boost::beast::http::verb::head, {{"Login"}}},
2914 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2915 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2916 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2917 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2918 }
2919
2920 private:
2921 /**
2922 * Function handles POST method request.
2923 * The Clear Log actions does not require any parameter.The action deletes
2924 * all entries found in the Entries collection for this Log Service.
2925 */
Ed Tanouscb13a392020-07-25 19:02:03 +00002926 void doPost(crow::Response& res, const crow::Request&,
2927 const std::vector<std::string>&) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002928 {
2929 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2930
2931 auto asyncResp = std::make_shared<AsyncResp>(res);
2932 // Process response from Logging service.
Ed Tanous2c70f802020-09-28 14:29:23 -07002933 auto respHandler = [asyncResp](const boost::system::error_code ec) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07002934 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2935 if (ec)
2936 {
2937 // TODO Handle for specific error code
2938 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2939 asyncResp->res.result(
2940 boost::beast::http::status::internal_server_error);
2941 return;
2942 }
2943
2944 asyncResp->res.result(boost::beast::http::status::no_content);
2945 };
2946
2947 // Make call to Logging service to request Clear Log
2948 crow::connections::systemBus->async_method_call(
Ed Tanous2c70f802020-09-28 14:29:23 -07002949 respHandler, "xyz.openbmc_project.Logging",
Andrew Geisslercb92c032018-08-17 07:56:14 -07002950 "/xyz/openbmc_project/logging",
2951 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2952 }
2953};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002954
2955/****************************************************
2956 * Redfish PostCode interfaces
2957 * using DBUS interface: getPostCodesTS
2958 ******************************************************/
2959class PostCodesLogService : public Node
2960{
2961 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002962 PostCodesLogService(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002963 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2964 {
2965 entityPrivileges = {
2966 {boost::beast::http::verb::get, {{"Login"}}},
2967 {boost::beast::http::verb::head, {{"Login"}}},
2968 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2969 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2970 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2971 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2972 }
2973
2974 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00002975 void doGet(crow::Response& res, const crow::Request&,
2976 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002977 {
2978 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2979
2980 asyncResp->res.jsonValue = {
2981 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2982 {"@odata.type", "#LogService.v1_1_0.LogService"},
ZhikuiRena3316fc2020-01-29 14:58:08 -08002983 {"Name", "POST Code Log Service"},
2984 {"Description", "POST Code Log Service"},
2985 {"Id", "BIOS POST Code Log"},
2986 {"OverWritePolicy", "WrapsWhenFull"},
2987 {"Entries",
2988 {{"@odata.id",
2989 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2990 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2991 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2992 "Actions/LogService.ClearLog"}};
2993 }
2994};
2995
2996class PostCodesClear : public Node
2997{
2998 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07002999 PostCodesClear(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003000 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3001 "LogService.ClearLog/")
3002 {
3003 entityPrivileges = {
3004 {boost::beast::http::verb::get, {{"Login"}}},
3005 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303006 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3007 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3008 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3009 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003010 }
3011
3012 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003013 void doPost(crow::Response& res, const crow::Request&,
3014 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003015 {
3016 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3017
3018 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3019 // Make call to post-code service to request clear all
3020 crow::connections::systemBus->async_method_call(
3021 [asyncResp](const boost::system::error_code ec) {
3022 if (ec)
3023 {
3024 // TODO Handle for specific error code
3025 BMCWEB_LOG_ERROR
3026 << "doClearPostCodes resp_handler got error " << ec;
3027 asyncResp->res.result(
3028 boost::beast::http::status::internal_server_error);
3029 messages::internalError(asyncResp->res);
3030 return;
3031 }
3032 },
3033 "xyz.openbmc_project.State.Boot.PostCode",
3034 "/xyz/openbmc_project/State/Boot/PostCode",
3035 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3036 }
3037};
3038
3039static void fillPostCodeEntry(
Ed Tanousb5a76932020-09-29 16:16:58 -07003040 const std::shared_ptr<AsyncResp>& aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003041 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003042 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3043 const uint64_t skip = 0, const uint64_t top = 0)
3044{
3045 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003046 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003047 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003048
3049 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003050 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003051
3052 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003053 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003054 {
3055 currentCodeIndex++;
3056 std::string postcodeEntryID =
3057 "B" + std::to_string(bootIndex) + "-" +
3058 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3059
3060 uint64_t usecSinceEpoch = code.first;
3061 uint64_t usTimeOffset = 0;
3062
3063 if (1 == currentCodeIndex)
3064 { // already incremented
3065 firstCodeTimeUs = code.first;
3066 }
3067 else
3068 {
3069 usTimeOffset = code.first - firstCodeTimeUs;
3070 }
3071
3072 // skip if no specific codeIndex is specified and currentCodeIndex does
3073 // not fall between top and skip
3074 if ((codeIndex == 0) &&
3075 (currentCodeIndex <= skip || currentCodeIndex > top))
3076 {
3077 continue;
3078 }
3079
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003080 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003081 // currentIndex
3082 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3083 {
3084 // This is done for simplicity. 1st entry is needed to calculate
3085 // time offset. To improve efficiency, one can get to the entry
3086 // directly (possibly with flatmap's nth method)
3087 continue;
3088 }
3089
3090 // currentCodeIndex is within top and skip or equal to specified code
3091 // index
3092
3093 // Get the Created time from the timestamp
3094 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003095 entryTimeStr = crow::utility::getDateTime(
3096 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003097
3098 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3099 std::ostringstream hexCode;
3100 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3101 << code.second;
3102 std::ostringstream timeOffsetStr;
3103 // Set Fixed -Point Notation
3104 timeOffsetStr << std::fixed;
3105 // Set precision to 4 digits
3106 timeOffsetStr << std::setprecision(4);
3107 // Add double to stream
3108 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3109 std::vector<std::string> messageArgs = {
3110 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3111
3112 // Get MessageArgs template from message registry
3113 std::string msg;
3114 if (message != nullptr)
3115 {
3116 msg = message->message;
3117
3118 // fill in this post code value
3119 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003120 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003121 {
3122 std::string argStr = "%" + std::to_string(++i);
3123 size_t argPos = msg.find(argStr);
3124 if (argPos != std::string::npos)
3125 {
3126 msg.replace(argPos, argStr.length(), messageArg);
3127 }
3128 }
3129 }
3130
Tim Leed4342a92020-04-27 11:47:58 +08003131 // Get Severity template from message registry
3132 std::string severity;
3133 if (message != nullptr)
3134 {
3135 severity = message->severity;
3136 }
3137
ZhikuiRena3316fc2020-01-29 14:58:08 -08003138 // add to AsyncResp
3139 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003140 nlohmann::json& bmcLogEntry = logEntryArray.back();
Gunnar Mills743e9a12020-10-26 12:44:53 -05003141 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3142 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3143 "PostCodes/Entries/" +
3144 postcodeEntryID},
3145 {"Name", "POST Code Log Entry"},
3146 {"Id", postcodeEntryID},
3147 {"Message", std::move(msg)},
3148 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3149 {"MessageArgs", std::move(messageArgs)},
3150 {"EntryType", "Event"},
3151 {"Severity", std::move(severity)},
3152 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003153 }
3154}
3155
Ed Tanousb5a76932020-09-29 16:16:58 -07003156static void getPostCodeForEntry(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003157 const uint16_t bootIndex,
3158 const uint64_t codeIndex)
3159{
3160 crow::connections::systemBus->async_method_call(
3161 [aResp, bootIndex, codeIndex](
3162 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003163 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003164 if (ec)
3165 {
3166 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3167 messages::internalError(aResp->res);
3168 return;
3169 }
3170
3171 // skip the empty postcode boots
3172 if (postcode.empty())
3173 {
3174 return;
3175 }
3176
3177 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3178
3179 aResp->res.jsonValue["Members@odata.count"] =
3180 aResp->res.jsonValue["Members"].size();
3181 },
3182 "xyz.openbmc_project.State.Boot.PostCode",
3183 "/xyz/openbmc_project/State/Boot/PostCode",
3184 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3185 bootIndex);
3186}
3187
Ed Tanousb5a76932020-09-29 16:16:58 -07003188static void getPostCodeForBoot(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003189 const uint16_t bootIndex,
3190 const uint16_t bootCount,
3191 const uint64_t entryCount, const uint64_t skip,
3192 const uint64_t top)
3193{
3194 crow::connections::systemBus->async_method_call(
3195 [aResp, bootIndex, bootCount, entryCount, skip,
3196 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003197 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003198 if (ec)
3199 {
3200 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3201 messages::internalError(aResp->res);
3202 return;
3203 }
3204
3205 uint64_t endCount = entryCount;
3206 if (!postcode.empty())
3207 {
3208 endCount = entryCount + postcode.size();
3209
3210 if ((skip < endCount) && ((top + skip) > entryCount))
3211 {
3212 uint64_t thisBootSkip =
3213 std::max(skip, entryCount) - entryCount;
3214 uint64_t thisBootTop =
3215 std::min(top + skip, endCount) - entryCount;
3216
3217 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3218 thisBootSkip, thisBootTop);
3219 }
3220 aResp->res.jsonValue["Members@odata.count"] = endCount;
3221 }
3222
3223 // continue to previous bootIndex
3224 if (bootIndex < bootCount)
3225 {
3226 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3227 bootCount, endCount, skip, top);
3228 }
3229 else
3230 {
3231 aResp->res.jsonValue["Members@odata.nextLink"] =
3232 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3233 "Entries?$skip=" +
3234 std::to_string(skip + top);
3235 }
3236 },
3237 "xyz.openbmc_project.State.Boot.PostCode",
3238 "/xyz/openbmc_project/State/Boot/PostCode",
3239 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3240 bootIndex);
3241}
3242
Ed Tanousb5a76932020-09-29 16:16:58 -07003243static void getCurrentBootNumber(const std::shared_ptr<AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003244 const uint64_t skip, const uint64_t top)
3245{
3246 uint64_t entryCount = 0;
3247 crow::connections::systemBus->async_method_call(
3248 [aResp, entryCount, skip,
3249 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003250 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003251 if (ec)
3252 {
3253 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3254 messages::internalError(aResp->res);
3255 return;
3256 }
3257 auto pVal = std::get_if<uint16_t>(&bootCount);
3258 if (pVal)
3259 {
3260 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3261 }
3262 else
3263 {
3264 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3265 }
3266 },
3267 "xyz.openbmc_project.State.Boot.PostCode",
3268 "/xyz/openbmc_project/State/Boot/PostCode",
3269 "org.freedesktop.DBus.Properties", "Get",
3270 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3271}
3272
3273class PostCodesEntryCollection : public Node
3274{
3275 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003276 PostCodesEntryCollection(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003277 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3278 {
3279 entityPrivileges = {
3280 {boost::beast::http::verb::get, {{"Login"}}},
3281 {boost::beast::http::verb::head, {{"Login"}}},
3282 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3283 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3284 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3285 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3286 }
3287
3288 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003289 void doGet(crow::Response& res, const crow::Request& req,
Ed Tanouscb13a392020-07-25 19:02:03 +00003290 const std::vector<std::string>&) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003291 {
3292 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3293
3294 asyncResp->res.jsonValue["@odata.type"] =
3295 "#LogEntryCollection.LogEntryCollection";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003296 asyncResp->res.jsonValue["@odata.id"] =
3297 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3298 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3299 asyncResp->res.jsonValue["Description"] =
3300 "Collection of POST Code Log Entries";
3301 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3302 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3303
3304 uint64_t skip = 0;
3305 uint64_t top = maxEntriesPerPage; // Show max entries by default
3306 if (!getSkipParam(asyncResp->res, req, skip))
3307 {
3308 return;
3309 }
3310 if (!getTopParam(asyncResp->res, req, top))
3311 {
3312 return;
3313 }
3314 getCurrentBootNumber(asyncResp, skip, top);
3315 }
3316};
3317
3318class PostCodesEntry : public Node
3319{
3320 public:
Ed Tanous52cc1122020-07-18 13:51:21 -07003321 PostCodesEntry(App& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003322 Node(app,
3323 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3324 std::string())
3325 {
3326 entityPrivileges = {
3327 {boost::beast::http::verb::get, {{"Login"}}},
3328 {boost::beast::http::verb::head, {{"Login"}}},
3329 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3330 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3331 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3332 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3333 }
3334
3335 private:
Ed Tanouscb13a392020-07-25 19:02:03 +00003336 void doGet(crow::Response& res, const crow::Request&,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003337 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003338 {
3339 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3340 if (params.size() != 1)
3341 {
3342 messages::internalError(asyncResp->res);
3343 return;
3344 }
3345
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003346 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003347
3348 size_t bootPos = targetID.find('B');
3349 if (bootPos == std::string::npos)
3350 {
3351 // Requested ID was not found
3352 messages::resourceMissingAtURI(asyncResp->res, targetID);
3353 return;
3354 }
3355 std::string_view bootIndexStr(targetID);
3356 bootIndexStr.remove_prefix(bootPos + 1);
3357 uint16_t bootIndex = 0;
3358 uint64_t codeIndex = 0;
3359 size_t dashPos = bootIndexStr.find('-');
3360
3361 if (dashPos == std::string::npos)
3362 {
3363 return;
3364 }
3365 std::string_view codeIndexStr(bootIndexStr);
3366 bootIndexStr.remove_suffix(dashPos);
3367 codeIndexStr.remove_prefix(dashPos + 1);
3368
3369 bootIndex = static_cast<uint16_t>(
Ed Tanous23a21a12020-07-25 04:45:05 +00003370 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3371 codeIndex = strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003372 if (bootIndex == 0 || codeIndex == 0)
3373 {
3374 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3375 << params[0];
3376 }
3377
3378 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003379 asyncResp->res.jsonValue["@odata.id"] =
3380 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3381 "Entries";
3382 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3383 asyncResp->res.jsonValue["Description"] =
3384 "Collection of POST Code Log Entries";
3385 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3386 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3387
3388 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3389 }
3390};
3391
Ed Tanous1da66f72018-07-27 16:13:37 -07003392} // namespace redfish