blob: 85ef1b0e34202647f84f61b66d51d5a8ca2941ff [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"
19
Jason M. Billse1f26342018-07-18 12:12:00 -070020#include <systemd/sd-journal.h>
21
Ed Tanous1da66f72018-07-27 16:13:37 -070022#include <boost/container/flat_map.hpp>
Jason M. Billse1f26342018-07-18 12:12:00 -070023#include <boost/utility/string_view.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070024#include <experimental/filesystem>
25
26namespace redfish
27{
28
Ed Tanous4ed77cd2018-10-15 08:08:07 -070029constexpr char const *cpuLogObject = "com.intel.CpuDebugLog";
30constexpr char const *cpuLogPath = "/com/intel/CpuDebugLog";
31constexpr char const *cpuLogImmediatePath = "/com/intel/CpuDebugLog/Immediate";
32constexpr char const *cpuLogInterface = "com.intel.CpuDebugLog";
33constexpr char const *cpuLogImmediateInterface =
Ed Tanous1da66f72018-07-27 16:13:37 -070034 "com.intel.CpuDebugLog.Immediate";
Jason M. Billse1f26342018-07-18 12:12:00 -070035constexpr char const *cpuLogRawPECIInterface =
Ed Tanous1da66f72018-07-27 16:13:37 -070036 "com.intel.CpuDebugLog.SendRawPeci";
37
38namespace fs = std::experimental::filesystem;
39
40class LogServiceCollection : public Node
41{
42 public:
43 template <typename CrowApp>
44 LogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -070045 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -070046 {
47 // Collections use static ID for SubRoute to add to its parent, but only
48 // load dynamic data so the duplicate static members don't get displayed
Ed Tanous4ed77cd2018-10-15 08:08:07 -070049 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices";
Ed Tanous1da66f72018-07-27 16:13:37 -070050 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -070051 {boost::beast::http::verb::get, {{"Login"}}},
52 {boost::beast::http::verb::head, {{"Login"}}},
53 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
54 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
55 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
56 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -070057 }
58
59 private:
60 /**
61 * Functions triggers appropriate requests on DBus
62 */
63 void doGet(crow::Response &res, const crow::Request &req,
64 const std::vector<std::string> &params) override
65 {
Jason M. Billse1f26342018-07-18 12:12:00 -070066 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -070067 // Collections don't include the static data added by SubRoute because
68 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -070069 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -070070 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -070071 asyncResp->res.jsonValue["@odata.context"] =
Ed Tanous1da66f72018-07-27 16:13:37 -070072 "/redfish/v1/"
73 "$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -070074 asyncResp->res.jsonValue["@odata.id"] =
75 "/redfish/v1/Managers/bmc/LogServices";
76 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
77 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -070078 "Collection of LogServices for this Manager";
Jason M. Billse1f26342018-07-18 12:12:00 -070079 nlohmann::json &logserviceArray = asyncResp->res.jsonValue["Members"];
Ed Tanous1da66f72018-07-27 16:13:37 -070080 logserviceArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -070081 logserviceArray.push_back(
82 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog"}});
Ed Tanous1da66f72018-07-27 16:13:37 -070083#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
84 logserviceArray.push_back(
Ed Tanous4ed77cd2018-10-15 08:08:07 -070085 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/CpuLog"}});
Ed Tanous1da66f72018-07-27 16:13:37 -070086#endif
Jason M. Billse1f26342018-07-18 12:12:00 -070087 asyncResp->res.jsonValue["Members@odata.count"] =
88 logserviceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -070089 }
90};
91
Jason M. Billse1f26342018-07-18 12:12:00 -070092class BMCLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -070093{
94 public:
95 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -070096 BMCLogService(CrowApp &app) :
97 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/")
98 {
99 // Set the id for SubRoute
100 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/BmcLog";
101 entityPrivileges = {
102 {boost::beast::http::verb::get, {{"Login"}}},
103 {boost::beast::http::verb::head, {{"Login"}}},
104 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
105 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
106 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
107 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
108 }
109
110 private:
111 void doGet(crow::Response &res, const crow::Request &req,
112 const std::vector<std::string> &params) override
113 {
114 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
115 // Copy over the static data to include the entries added by SubRoute
116 asyncResp->res.jsonValue = Node::json;
117 asyncResp->res.jsonValue["@odata.type"] =
118 "#LogService.v1_1_0.LogService";
119 asyncResp->res.jsonValue["@odata.context"] =
120 "/redfish/v1/$metadata#LogService.LogService";
121 asyncResp->res.jsonValue["Name"] = "Open BMC Log Service";
122 asyncResp->res.jsonValue["Description"] = "BMC Log Service";
123 asyncResp->res.jsonValue["Id"] = "BMC Log";
124 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
125 }
126};
127
128static int fillBMCLogEntryJson(const std::string &bmcLogEntryID,
129 sd_journal *journal,
130 nlohmann::json &bmcLogEntryJson)
131{
132 // Get the Log Entry contents
133 int ret = 0;
134 const char *data = nullptr;
135 size_t length = 0;
136
137 ret =
138 sd_journal_get_data(journal, "MESSAGE", (const void **)&data, &length);
139 if (ret < 0)
140 {
141 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
142 return 1;
143 }
144 boost::string_view msg;
145 msg = boost::string_view(data, length);
146 // Only use the content after the "=" character.
147 msg.remove_prefix(std::min(msg.find("=") + 1, msg.size()));
148
149 // Get the severity from the PRIORITY field
150 boost::string_view priority;
151 int severity = 8; // Default to an invalid priority
152 ret =
153 sd_journal_get_data(journal, "PRIORITY", (const void **)&data, &length);
154 if (ret < 0)
155 {
156 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
157 return 1;
158 }
159 priority = boost::string_view(data, length);
160 // Check length for sanity. Must be a single digit in the form
161 // "PRIORITY=[0-7]"
162 if (priority.size() > sizeof("PRIORITY=0"))
163 {
164 BMCWEB_LOG_ERROR << "Invalid PRIORITY field length";
165 return 1;
166 }
167 // Only use the content after the "=" character.
168 priority.remove_prefix(std::min(priority.find("=") + 1, priority.size()));
169 severity = strtol(priority.data(), nullptr, 10);
170
171 // Get the Created time from the timestamp
172 // Get the entry timestamp
173 uint64_t timestamp = 0;
174 ret = sd_journal_get_realtime_usec(journal, &timestamp);
175 if (ret < 0)
176 {
177 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178 << strerror(-ret);
179 }
180 time_t t =
181 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
182 struct tm *loctime = localtime(&t);
183 char entryTime[64] = {};
184 if (NULL != loctime)
185 {
186 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
187 }
188 // Insert the ':' into the timezone
189 boost::string_view t1(entryTime);
190 boost::string_view t2(entryTime);
191 if (t1.size() > 2 && t2.size() > 2)
192 {
193 t1.remove_suffix(2);
194 t2.remove_prefix(t2.size() - 2);
195 }
196 const std::string entryTimeStr(t1.to_string() + ":" + t2.to_string());
197
198 // Fill in the log entry with the gathered data
199 bmcLogEntryJson = {
200 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
201 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
202 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/" +
203 bmcLogEntryID},
204 {"Name", "BMC Journal Entry"},
205 {"Id", bmcLogEntryID},
206 {"Message", msg.to_string()},
207 {"EntryType", "Oem"},
208 {"Severity",
209 severity <= 2 ? "Critical"
210 : severity <= 4 ? "Warning" : severity <= 7 ? "OK" : ""},
211 {"OemRecordFormat", "Intel BMC Journal Entry"},
212 {"Created", std::move(entryTimeStr)}};
213 return 0;
214}
215
216class BMCLogEntryCollection : public Node
217{
218 public:
219 template <typename CrowApp>
220 BMCLogEntryCollection(CrowApp &app) :
221 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/")
222 {
223 // Collections use static ID for SubRoute to add to its parent, but only
224 // load dynamic data so the duplicate static members don't get displayed
225 Node::json["@odata.id"] =
226 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
227 entityPrivileges = {
228 {boost::beast::http::verb::get, {{"Login"}}},
229 {boost::beast::http::verb::head, {{"Login"}}},
230 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
231 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
232 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
233 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
234 }
235
236 private:
237 void doGet(crow::Response &res, const crow::Request &req,
238 const std::vector<std::string> &params) override
239 {
240 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700241 static constexpr const long maxEntriesPerPage = 1000;
242 long skip = 0;
243 long top = maxEntriesPerPage; // Show max entries by default
244 char *skipParam = req.urlParams.get("$skip");
245 if (skipParam != nullptr)
246 {
247 char *ptr = nullptr;
248 skip = std::strtol(skipParam, &ptr, 10);
249 if (*skipParam == '\0' || *ptr != '\0')
250 {
251 messages::addMessageToErrorJson(
252 asyncResp->res.jsonValue,
253 messages::queryParameterValueTypeError(
254 std::string(skipParam), "$skip"));
255 asyncResp->res.result(boost::beast::http::status::bad_request);
256 return;
257 }
258 if (skip < 0)
259 {
260 messages::addMessageToErrorJson(
261 asyncResp->res.jsonValue,
262 messages::queryParameterOutOfRange(
263 std::to_string(skip), "$skip", "greater than 0"));
264 asyncResp->res.result(boost::beast::http::status::bad_request);
265 return;
266 }
267 }
268 char *topParam = req.urlParams.get("$top");
269 if (topParam != nullptr)
270 {
271 char *ptr = nullptr;
272 top = std::strtol(topParam, &ptr, 10);
273 if (*topParam == '\0' || *ptr != '\0')
274 {
275 messages::addMessageToErrorJson(
276 asyncResp->res.jsonValue,
277 messages::queryParameterValueTypeError(
278 std::string(topParam), "$top"));
279 asyncResp->res.result(boost::beast::http::status::bad_request);
280 return;
281 }
282 if (top < 1 || top > maxEntriesPerPage)
283 {
284 messages::addMessageToErrorJson(
285 asyncResp->res.jsonValue,
286 messages::queryParameterOutOfRange(
287 std::to_string(top), "$top",
288 "1-" + std::to_string(maxEntriesPerPage)));
289 asyncResp->res.result(boost::beast::http::status::bad_request);
290 return;
291 }
292 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700293 // Collections don't include the static data added by SubRoute because
294 // it has a duplicate entry for members
295 asyncResp->res.jsonValue["@odata.type"] =
296 "#LogEntryCollection.LogEntryCollection";
297 asyncResp->res.jsonValue["@odata.context"] =
298 "/redfish/v1/"
299 "$metadata#LogEntryCollection.LogEntryCollection";
300 asyncResp->res.jsonValue["@odata.id"] =
301 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
302 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
303 asyncResp->res.jsonValue["Description"] =
304 "Collection of BMC Journal Entries";
305 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
306 logEntryArray = nlohmann::json::array();
307
308 // Go through the journal and use the timestamp to create a unique ID
309 // for each entry
310 sd_journal *journalTmp = nullptr;
311 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
312 if (ret < 0)
313 {
314 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700315 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700316 return;
317 }
318 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
319 journalTmp, sd_journal_close);
320 journalTmp = nullptr;
321 uint64_t prevTs = 0;
322 int index = 0;
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700323 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -0700324 SD_JOURNAL_FOREACH(journal.get())
325 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700326 entryCount++;
327 // Handle paging using skip (number of entries to skip from the
328 // start) and top (number of entries to display)
329 if (entryCount <= skip || entryCount > skip + top)
330 {
331 continue;
332 }
333
Jason M. Billse1f26342018-07-18 12:12:00 -0700334 // Get the entry timestamp
335 uint64_t curTs = 0;
336 ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
337 if (ret < 0)
338 {
339 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
340 << strerror(-ret);
341 continue;
342 }
343 // If the timestamp isn't unique, increment the index
344 if (curTs == prevTs)
345 {
346 index++;
347 }
348 else
349 {
350 // Otherwise, reset it
351 index = 0;
352 }
353 // Save the timestamp
354 prevTs = curTs;
355
356 std::string idStr(std::to_string(curTs));
357 if (index > 0)
358 {
359 idStr += "_" + std::to_string(index);
360 }
361 logEntryArray.push_back({});
362 nlohmann::json &bmcLogEntry = logEntryArray.back();
363 if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
364 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700365 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700366 return;
367 }
368 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700369 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
370 if (skip + top < entryCount)
371 {
372 asyncResp->res.jsonValue["Members@odata.nextLink"] =
373 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
374 std::to_string(skip + top);
375 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700376 }
377};
378
379class BMCLogEntry : public Node
380{
381 public:
382 BMCLogEntry(CrowApp &app) :
383 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
384 std::string())
385 {
386 entityPrivileges = {
387 {boost::beast::http::verb::get, {{"Login"}}},
388 {boost::beast::http::verb::head, {{"Login"}}},
389 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
390 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
391 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
392 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
393 }
394
395 private:
396 void doGet(crow::Response &res, const crow::Request &req,
397 const std::vector<std::string> &params) override
398 {
399 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
400 if (params.size() != 1)
401 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700402 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700403 return;
404 }
405 // Convert the unique ID back to a timestamp to find the entry
406 boost::string_view tsStr(params[0]);
407 boost::string_view indexStr(params[0]);
408 uint64_t ts = 0;
409 uint16_t index = 0;
410 auto underscorePos = tsStr.find("_");
411 if (underscorePos == tsStr.npos)
412 {
413 // Timestamp has no index
414 ts = strtoull(tsStr.data(), nullptr, 10);
415 }
416 else
417 {
418 // Timestamp has an index
419 tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
420 ts = strtoull(tsStr.data(), nullptr, 10);
421 indexStr.remove_prefix(underscorePos + 1);
422 index =
423 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
424 }
425
426 sd_journal *journalTmp = nullptr;
427 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
428 if (ret < 0)
429 {
430 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700431 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700432 return;
433 }
434 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
435 journalTmp, sd_journal_close);
436 journalTmp = nullptr;
437 // Go to the timestamp in the log and move to the entry at the index
438 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
439 for (int i = 0; i <= index; i++)
440 {
441 sd_journal_next(journal.get());
442 }
443 if (fillBMCLogEntryJson(params[0], journal.get(),
444 asyncResp->res.jsonValue) != 0)
445 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700446 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700447 return;
448 }
449 }
450};
451
452class CPULogService : public Node
453{
454 public:
455 template <typename CrowApp>
456 CPULogService(CrowApp &app) :
457 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700458 {
459 // Set the id for SubRoute
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700460 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
Ed Tanous1da66f72018-07-27 16:13:37 -0700461 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700462 {boost::beast::http::verb::get, {{"Login"}}},
463 {boost::beast::http::verb::head, {{"Login"}}},
464 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
465 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
466 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
467 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700468 }
469
470 private:
471 /**
472 * Functions triggers appropriate requests on DBus
473 */
474 void doGet(crow::Response &res, const crow::Request &req,
475 const std::vector<std::string> &params) override
476 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700477 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700478 // Copy over the static data to include the entries added by SubRoute
Jason M. Billse1f26342018-07-18 12:12:00 -0700479 asyncResp->res.jsonValue = Node::json;
480 asyncResp->res.jsonValue["@odata.type"] =
481 "#LogService.v1_1_0.LogService";
482 asyncResp->res.jsonValue["@odata.context"] =
483 "/redfish/v1/"
484 "$metadata#LogService.LogService";
485 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
486 asyncResp->res.jsonValue["Description"] = "CPU Log Service";
487 asyncResp->res.jsonValue["Id"] = "CPU Log";
488 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
489 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
490 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700491 {"Oem",
492 {{"#CpuLog.Immediate",
493 {{"target",
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700494 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Ed Tanous1da66f72018-07-27 16:13:37 -0700495 "CpuLog.Immediate"}}}}}};
496
497#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -0700498 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Ed Tanous1da66f72018-07-27 16:13:37 -0700499 {"#CpuLog.SendRawPeci",
500 {{"target",
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700501 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Ed Tanous1da66f72018-07-27 16:13:37 -0700502 "CpuLog.SendRawPeci"}}});
503#endif
Ed Tanous1da66f72018-07-27 16:13:37 -0700504 }
505};
506
Jason M. Billse1f26342018-07-18 12:12:00 -0700507class CPULogEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700508{
509 public:
510 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -0700511 CPULogEntryCollection(CrowApp &app) :
512 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700513 {
514 // Collections use static ID for SubRoute to add to its parent, but only
515 // load dynamic data so the duplicate static members don't get displayed
516 Node::json["@odata.id"] =
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700517 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
Ed Tanous1da66f72018-07-27 16:13:37 -0700518 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700519 {boost::beast::http::verb::get, {{"Login"}}},
520 {boost::beast::http::verb::head, {{"Login"}}},
521 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
522 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
523 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
524 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700525 }
526
527 private:
528 /**
529 * Functions triggers appropriate requests on DBus
530 */
531 void doGet(crow::Response &res, const crow::Request &req,
532 const std::vector<std::string> &params) override
533 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700534 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700535 // Collections don't include the static data added by SubRoute because
536 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700537 auto getLogEntriesCallback = [asyncResp](
538 const boost::system::error_code ec,
539 const std::vector<std::string> &resp) {
540 if (ec)
541 {
542 if (ec.value() !=
543 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -0700544 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700545 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
546 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700547 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700548 return;
Ed Tanous1da66f72018-07-27 16:13:37 -0700549 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700550 }
551 asyncResp->res.jsonValue["@odata.type"] =
552 "#LogEntryCollection.LogEntryCollection";
553 asyncResp->res.jsonValue["@odata.context"] =
554 "/redfish/v1/"
555 "$metadata#LogEntryCollection.LogEntryCollection";
556 asyncResp->res.jsonValue["@odata.id"] =
557 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
558 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
559 asyncResp->res.jsonValue["Description"] =
560 "Collection of CPU Log Entries";
561 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
562 logEntryArray = nlohmann::json::array();
563 for (const std::string &objpath : resp)
564 {
565 // Don't list the immediate log
566 if (objpath.compare(cpuLogImmediatePath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -0700567 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700568 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -0700569 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700570 std::size_t lastPos = objpath.rfind("/");
571 if (lastPos != std::string::npos)
572 {
573 logEntryArray.push_back(
574 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
575 "CpuLog/Entries/" +
576 objpath.substr(lastPos + 1)}});
577 }
578 }
579 asyncResp->res.jsonValue["Members@odata.count"] =
580 logEntryArray.size();
581 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700582 crow::connections::systemBus->async_method_call(
583 std::move(getLogEntriesCallback),
584 "xyz.openbmc_project.ObjectMapper",
585 "/xyz/openbmc_project/object_mapper",
586 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700587 std::array<const char *, 1>{cpuLogInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -0700588 }
589};
590
591std::string getLogCreatedTime(const nlohmann::json &cpuLog)
592{
593 nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
594 if (metaIt != cpuLog.end())
595 {
596 nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
597 if (tsIt != metaIt->end())
598 {
599 const std::string *logTime = tsIt->get_ptr<const std::string *>();
600 if (logTime != nullptr)
601 {
602 return *logTime;
603 }
604 }
605 }
606 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
607
608 return std::string();
609}
610
Jason M. Billse1f26342018-07-18 12:12:00 -0700611class CPULogEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700612{
613 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700614 CPULogEntry(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700615 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -0700616 std::string())
617 {
618 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700619 {boost::beast::http::verb::get, {{"Login"}}},
620 {boost::beast::http::verb::head, {{"Login"}}},
621 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
622 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
623 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
624 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700625 }
626
627 private:
628 void doGet(crow::Response &res, const crow::Request &req,
629 const std::vector<std::string> &params) override
630 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700631 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700632 if (params.size() != 1)
633 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700634 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700635 return;
636 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700637 const uint8_t logId = std::atoi(params[0].c_str());
Jason M. Billse1f26342018-07-18 12:12:00 -0700638 auto getStoredLogCallback =
639 [asyncResp,
640 logId](const boost::system::error_code ec,
641 const sdbusplus::message::variant<std::string> &resp) {
642 if (ec)
643 {
644 BMCWEB_LOG_DEBUG << "failed to get log ec: "
645 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700646 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700647 return;
648 }
649 const std::string *log =
650 mapbox::getPtr<const std::string>(resp);
651 if (log == nullptr)
652 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700653 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700654 return;
655 }
656 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
657 if (j.is_discarded())
658 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700659 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700660 return;
661 }
662 std::string t = getLogCreatedTime(j);
663 asyncResp->res.jsonValue = {
664 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
665 {"@odata.context",
666 "/redfish/v1/$metadata#LogEntry.LogEntry"},
667 {"@odata.id",
668 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
669 std::to_string(logId)},
670 {"Name", "CPU Debug Log"},
671 {"Id", logId},
672 {"EntryType", "Oem"},
673 {"OemRecordFormat", "Intel CPU Log"},
674 {"Oem", {{"Intel", std::move(j)}}},
675 {"Created", std::move(t)}};
676 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700677 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700678 std::move(getStoredLogCallback), cpuLogObject,
679 cpuLogPath + std::string("/") + std::to_string(logId),
680 "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -0700681 }
682};
683
Jason M. Billse1f26342018-07-18 12:12:00 -0700684class ImmediateCPULog : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700685{
686 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700687 ImmediateCPULog(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700688 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700689 "CpuLog.Immediate/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700690 {
691 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700692 {boost::beast::http::verb::get, {{"Login"}}},
693 {boost::beast::http::verb::head, {{"Login"}}},
694 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
695 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
696 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
697 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700698 }
699
700 private:
701 void doPost(crow::Response &res, const crow::Request &req,
702 const std::vector<std::string> &params) override
703 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700704 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700705 static std::unique_ptr<sdbusplus::bus::match::match>
706 immediateLogMatcher;
707
708 // Only allow one Immediate Log request at a time
709 if (immediateLogMatcher != nullptr)
710 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700711 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -0700712 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -0700713 return;
714 }
715 // Make this static so it survives outside this method
716 static boost::asio::deadline_timer timeout(*req.ioService);
717
718 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -0700719 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700720 immediateLogMatcher = nullptr;
721 if (ec)
722 {
723 // operation_aborted is expected if timer is canceled before
724 // completion.
725 if (ec != boost::asio::error::operation_aborted)
726 {
727 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
728 }
729 return;
730 }
731 BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
732
Jason M. Billsf12894f2018-10-09 12:45:45 -0700733 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700734 });
735
Jason M. Billse1f26342018-07-18 12:12:00 -0700736 auto immediateLogMatcherCallback = [asyncResp](
Ed Tanous1da66f72018-07-27 16:13:37 -0700737 sdbusplus::message::message &m) {
738 BMCWEB_LOG_DEBUG << "Immediate log available match fired";
739 boost::system::error_code ec;
740 timeout.cancel(ec);
741 if (ec)
742 {
743 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
744 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700745 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -0700746 boost::container::flat_map<
747 std::string,
748 boost::container::flat_map<
749 std::string, sdbusplus::message::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700750 interfacesAdded;
751 m.read(objPath, interfacesAdded);
Ed Tanous1da66f72018-07-27 16:13:37 -0700752 const std::string *log = mapbox::getPtr<const std::string>(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700753 interfacesAdded[cpuLogInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -0700754 if (log == nullptr)
755 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700756 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700757 // Careful with immediateLogMatcher. It is a unique_ptr to the
758 // match object inside which this lambda is executing. Once it
759 // is set to nullptr, the match object will be destroyed and the
760 // lambda will lose its context, including res, so it needs to
761 // be the last thing done.
762 immediateLogMatcher = nullptr;
763 return;
764 }
765 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
766 if (j.is_discarded())
767 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700768 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700769 // Careful with immediateLogMatcher. It is a unique_ptr to the
770 // match object inside which this lambda is executing. Once it
771 // is set to nullptr, the match object will be destroyed and the
772 // lambda will lose its context, including res, so it needs to
773 // be the last thing done.
774 immediateLogMatcher = nullptr;
775 return;
776 }
777 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -0700778 asyncResp->res.jsonValue = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700779 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
780 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
781 {"Name", "CPU Debug Log"},
782 {"EntryType", "Oem"},
783 {"OemRecordFormat", "Intel CPU Log"},
784 {"Oem", {{"Intel", std::move(j)}}},
785 {"Created", std::move(t)}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700786 // Careful with immediateLogMatcher. It is a unique_ptr to the
787 // match object inside which this lambda is executing. Once it is
788 // set to nullptr, the match object will be destroyed and the lambda
789 // will lose its context, including res, so it needs to be the last
790 // thing done.
791 immediateLogMatcher = nullptr;
792 };
793 immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
794 *crow::connections::systemBus,
795 sdbusplus::bus::match::rules::interfacesAdded() +
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700796 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
Ed Tanous1da66f72018-07-27 16:13:37 -0700797 std::move(immediateLogMatcherCallback));
798
799 auto generateImmediateLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -0700800 [asyncResp](const boost::system::error_code ec,
801 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700802 if (ec)
803 {
804 if (ec.value() ==
805 boost::system::errc::operation_not_supported)
806 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700807 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700808 }
809 else
810 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700811 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700812 }
Ed Tanous1da66f72018-07-27 16:13:37 -0700813 boost::system::error_code timeoutec;
814 timeout.cancel(timeoutec);
815 if (timeoutec)
816 {
817 BMCWEB_LOG_ERROR << "error canceling timer "
818 << timeoutec;
819 }
820 immediateLogMatcher = nullptr;
821 return;
822 }
823 };
824 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700825 std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
826 cpuLogImmediateInterface, "GenerateImmediateLog");
Ed Tanous1da66f72018-07-27 16:13:37 -0700827 }
828};
829
Jason M. Billse1f26342018-07-18 12:12:00 -0700830class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700831{
832 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700833 SendRawPECI(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700834 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700835 "CpuLog.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700836 {
837 entityPrivileges = {
838 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
839 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
840 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
841 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
842 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
843 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
844 }
845
846 private:
847 void doPost(crow::Response &res, const crow::Request &req,
848 const std::vector<std::string> &params) override
849 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700850 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700851 // Get the Raw PECI command from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700852 nlohmann::json rawPECICmd;
853 if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
Ed Tanous1da66f72018-07-27 16:13:37 -0700854 {
855 return;
856 }
857 // Get the Client Address from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700858 nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
859 if (caIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700860 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700861 messages::propertyMissing(asyncResp->res, "ClientAddress",
862 "/ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700863 return;
864 }
865 const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
866 if (ca == nullptr)
867 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700868 messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
869 "ClientAddress", "/ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700870 return;
871 }
872 // Get the Read Length from the request
873 const uint8_t clientAddress = static_cast<uint8_t>(*ca);
Jason M. Billse1f26342018-07-18 12:12:00 -0700874 nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
875 if (rlIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700876 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700877 messages::propertyMissing(asyncResp->res, "ReadLength",
878 "/ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700879 return;
880 }
881 const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
882 if (rl == nullptr)
883 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700884 messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
885 "ReadLength", "/ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700886 return;
887 }
888 // Get the PECI Command from the request
889 const uint32_t readLength = static_cast<uint32_t>(*rl);
Jason M. Billse1f26342018-07-18 12:12:00 -0700890 nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
891 if (pcIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700892 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700893 messages::propertyMissing(asyncResp->res, "PECICommand",
894 "/PECICommand");
Ed Tanous1da66f72018-07-27 16:13:37 -0700895 return;
896 }
897 std::vector<uint8_t> peciCommand;
898 for (auto pc : *pcIt)
899 {
900 const uint64_t *val = pc.get_ptr<const uint64_t *>();
901 if (val == nullptr)
902 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700903 messages::propertyValueTypeError(
904 asyncResp->res, pc.dump(),
905 "PECICommand/" + std::to_string(peciCommand.size()),
Ed Tanous1da66f72018-07-27 16:13:37 -0700906 "/PECICommand");
Ed Tanous1da66f72018-07-27 16:13:37 -0700907 return;
908 }
909 peciCommand.push_back(static_cast<uint8_t>(*val));
910 }
911 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -0700912 auto sendRawPECICallback =
913 [asyncResp](const boost::system::error_code ec,
914 const std::vector<uint8_t> &resp) {
915 if (ec)
916 {
917 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
918 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700919 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700920 return;
921 }
922 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
923 {"PECIResponse", resp}};
924 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700925 // Call the SendRawPECI command with the provided data
926 crow::connections::systemBus->async_method_call(
Jason M. Billse1f26342018-07-18 12:12:00 -0700927 std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
928 cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700929 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -0700930 }
931};
932
933} // namespace redfish