blob: c9555a10ad4381ccf5b702b99e5c8ca2e60ca521 [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 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700251
252 messages::queryParameterValueTypeError(
253 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700254 return;
255 }
256 if (skip < 0)
257 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700258
259 messages::queryParameterOutOfRange(asyncResp->res,
260 std::to_string(skip),
261 "$skip", "greater than 0");
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700262 return;
263 }
264 }
265 char *topParam = req.urlParams.get("$top");
266 if (topParam != nullptr)
267 {
268 char *ptr = nullptr;
269 top = std::strtol(topParam, &ptr, 10);
270 if (*topParam == '\0' || *ptr != '\0')
271 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700272 messages::queryParameterValueTypeError(
273 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700274 return;
275 }
276 if (top < 1 || top > maxEntriesPerPage)
277 {
Jason M. Bills35a62c72018-10-09 12:45:45 -0700278
279 messages::queryParameterOutOfRange(
280 asyncResp->res, std::to_string(top), "$top",
281 "1-" + std::to_string(maxEntriesPerPage));
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700282 asyncResp->res.result(boost::beast::http::status::bad_request);
283 return;
284 }
285 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700286 // Collections don't include the static data added by SubRoute because
287 // it has a duplicate entry for members
288 asyncResp->res.jsonValue["@odata.type"] =
289 "#LogEntryCollection.LogEntryCollection";
290 asyncResp->res.jsonValue["@odata.context"] =
291 "/redfish/v1/"
292 "$metadata#LogEntryCollection.LogEntryCollection";
293 asyncResp->res.jsonValue["@odata.id"] =
294 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
295 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
296 asyncResp->res.jsonValue["Description"] =
297 "Collection of BMC Journal Entries";
298 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
299 logEntryArray = nlohmann::json::array();
300
301 // Go through the journal and use the timestamp to create a unique ID
302 // for each entry
303 sd_journal *journalTmp = nullptr;
304 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
305 if (ret < 0)
306 {
307 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700308 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700309 return;
310 }
311 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
312 journalTmp, sd_journal_close);
313 journalTmp = nullptr;
314 uint64_t prevTs = 0;
315 int index = 0;
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700316 uint64_t entryCount = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -0700317 SD_JOURNAL_FOREACH(journal.get())
318 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700319 entryCount++;
320 // Handle paging using skip (number of entries to skip from the
321 // start) and top (number of entries to display)
322 if (entryCount <= skip || entryCount > skip + top)
323 {
324 continue;
325 }
326
Jason M. Billse1f26342018-07-18 12:12:00 -0700327 // Get the entry timestamp
328 uint64_t curTs = 0;
329 ret = sd_journal_get_realtime_usec(journal.get(), &curTs);
330 if (ret < 0)
331 {
332 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
333 << strerror(-ret);
334 continue;
335 }
336 // If the timestamp isn't unique, increment the index
337 if (curTs == prevTs)
338 {
339 index++;
340 }
341 else
342 {
343 // Otherwise, reset it
344 index = 0;
345 }
346 // Save the timestamp
347 prevTs = curTs;
348
349 std::string idStr(std::to_string(curTs));
350 if (index > 0)
351 {
352 idStr += "_" + std::to_string(index);
353 }
354 logEntryArray.push_back({});
355 nlohmann::json &bmcLogEntry = logEntryArray.back();
356 if (fillBMCLogEntryJson(idStr, journal.get(), bmcLogEntry) != 0)
357 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700358 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700359 return;
360 }
361 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -0700362 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
363 if (skip + top < entryCount)
364 {
365 asyncResp->res.jsonValue["Members@odata.nextLink"] =
366 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries?$skip=" +
367 std::to_string(skip + top);
368 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700369 }
370};
371
372class BMCLogEntry : public Node
373{
374 public:
375 BMCLogEntry(CrowApp &app) :
376 Node(app, "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries/<str>/",
377 std::string())
378 {
379 entityPrivileges = {
380 {boost::beast::http::verb::get, {{"Login"}}},
381 {boost::beast::http::verb::head, {{"Login"}}},
382 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
383 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
384 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
385 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
386 }
387
388 private:
389 void doGet(crow::Response &res, const crow::Request &req,
390 const std::vector<std::string> &params) override
391 {
392 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
393 if (params.size() != 1)
394 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700395 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700396 return;
397 }
398 // Convert the unique ID back to a timestamp to find the entry
399 boost::string_view tsStr(params[0]);
400 boost::string_view indexStr(params[0]);
401 uint64_t ts = 0;
402 uint16_t index = 0;
403 auto underscorePos = tsStr.find("_");
404 if (underscorePos == tsStr.npos)
405 {
406 // Timestamp has no index
407 ts = strtoull(tsStr.data(), nullptr, 10);
408 }
409 else
410 {
411 // Timestamp has an index
412 tsStr.remove_suffix(tsStr.size() - underscorePos + 1);
413 ts = strtoull(tsStr.data(), nullptr, 10);
414 indexStr.remove_prefix(underscorePos + 1);
415 index =
416 static_cast<uint16_t>(strtoul(indexStr.data(), nullptr, 10));
417 }
418
419 sd_journal *journalTmp = nullptr;
420 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
421 if (ret < 0)
422 {
423 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -0700424 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700425 return;
426 }
427 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
428 journalTmp, sd_journal_close);
429 journalTmp = nullptr;
430 // Go to the timestamp in the log and move to the entry at the index
431 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
432 for (int i = 0; i <= index; i++)
433 {
434 sd_journal_next(journal.get());
435 }
436 if (fillBMCLogEntryJson(params[0], journal.get(),
437 asyncResp->res.jsonValue) != 0)
438 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700439 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700440 return;
441 }
442 }
443};
444
445class CPULogService : public Node
446{
447 public:
448 template <typename CrowApp>
449 CPULogService(CrowApp &app) :
450 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700451 {
452 // Set the id for SubRoute
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700453 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/CpuLog";
Ed Tanous1da66f72018-07-27 16:13:37 -0700454 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700455 {boost::beast::http::verb::get, {{"Login"}}},
456 {boost::beast::http::verb::head, {{"Login"}}},
457 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
458 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
459 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
460 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700461 }
462
463 private:
464 /**
465 * Functions triggers appropriate requests on DBus
466 */
467 void doGet(crow::Response &res, const crow::Request &req,
468 const std::vector<std::string> &params) override
469 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700470 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700471 // Copy over the static data to include the entries added by SubRoute
Jason M. Billse1f26342018-07-18 12:12:00 -0700472 asyncResp->res.jsonValue = Node::json;
473 asyncResp->res.jsonValue["@odata.type"] =
474 "#LogService.v1_1_0.LogService";
475 asyncResp->res.jsonValue["@odata.context"] =
476 "/redfish/v1/"
477 "$metadata#LogService.LogService";
478 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Service";
479 asyncResp->res.jsonValue["Description"] = "CPU Log Service";
480 asyncResp->res.jsonValue["Id"] = "CPU Log";
481 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
482 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
483 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700484 {"Oem",
485 {{"#CpuLog.Immediate",
486 {{"target",
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700487 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Ed Tanous1da66f72018-07-27 16:13:37 -0700488 "CpuLog.Immediate"}}}}}};
489
490#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -0700491 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Ed Tanous1da66f72018-07-27 16:13:37 -0700492 {"#CpuLog.SendRawPeci",
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.SendRawPeci"}}});
496#endif
Ed Tanous1da66f72018-07-27 16:13:37 -0700497 }
498};
499
Jason M. Billse1f26342018-07-18 12:12:00 -0700500class CPULogEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700501{
502 public:
503 template <typename CrowApp>
Jason M. Billse1f26342018-07-18 12:12:00 -0700504 CPULogEntryCollection(CrowApp &app) :
505 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700506 {
507 // Collections use static ID for SubRoute to add to its parent, but only
508 // load dynamic data so the duplicate static members don't get displayed
509 Node::json["@odata.id"] =
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700510 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
Ed Tanous1da66f72018-07-27 16:13:37 -0700511 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700512 {boost::beast::http::verb::get, {{"Login"}}},
513 {boost::beast::http::verb::head, {{"Login"}}},
514 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
515 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
516 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
517 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700518 }
519
520 private:
521 /**
522 * Functions triggers appropriate requests on DBus
523 */
524 void doGet(crow::Response &res, const crow::Request &req,
525 const std::vector<std::string> &params) override
526 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700527 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700528 // Collections don't include the static data added by SubRoute because
529 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -0700530 auto getLogEntriesCallback = [asyncResp](
531 const boost::system::error_code ec,
532 const std::vector<std::string> &resp) {
533 if (ec)
534 {
535 if (ec.value() !=
536 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -0700537 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700538 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
539 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700540 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700541 return;
Ed Tanous1da66f72018-07-27 16:13:37 -0700542 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700543 }
544 asyncResp->res.jsonValue["@odata.type"] =
545 "#LogEntryCollection.LogEntryCollection";
546 asyncResp->res.jsonValue["@odata.context"] =
547 "/redfish/v1/"
548 "$metadata#LogEntryCollection.LogEntryCollection";
549 asyncResp->res.jsonValue["@odata.id"] =
550 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries";
551 asyncResp->res.jsonValue["Name"] = "Open BMC CPU Log Entries";
552 asyncResp->res.jsonValue["Description"] =
553 "Collection of CPU Log Entries";
554 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
555 logEntryArray = nlohmann::json::array();
556 for (const std::string &objpath : resp)
557 {
558 // Don't list the immediate log
559 if (objpath.compare(cpuLogImmediatePath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -0700560 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700561 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -0700562 }
Jason M. Billse1f26342018-07-18 12:12:00 -0700563 std::size_t lastPos = objpath.rfind("/");
564 if (lastPos != std::string::npos)
565 {
566 logEntryArray.push_back(
567 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/"
568 "CpuLog/Entries/" +
569 objpath.substr(lastPos + 1)}});
570 }
571 }
572 asyncResp->res.jsonValue["Members@odata.count"] =
573 logEntryArray.size();
574 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700575 crow::connections::systemBus->async_method_call(
576 std::move(getLogEntriesCallback),
577 "xyz.openbmc_project.ObjectMapper",
578 "/xyz/openbmc_project/object_mapper",
579 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700580 std::array<const char *, 1>{cpuLogInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -0700581 }
582};
583
584std::string getLogCreatedTime(const nlohmann::json &cpuLog)
585{
586 nlohmann::json::const_iterator metaIt = cpuLog.find("metadata");
587 if (metaIt != cpuLog.end())
588 {
589 nlohmann::json::const_iterator tsIt = metaIt->find("timestamp");
590 if (tsIt != metaIt->end())
591 {
592 const std::string *logTime = tsIt->get_ptr<const std::string *>();
593 if (logTime != nullptr)
594 {
595 return *logTime;
596 }
597 }
598 }
599 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
600
601 return std::string();
602}
603
Jason M. Billse1f26342018-07-18 12:12:00 -0700604class CPULogEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700605{
606 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700607 CPULogEntry(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700608 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -0700609 std::string())
610 {
611 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700612 {boost::beast::http::verb::get, {{"Login"}}},
613 {boost::beast::http::verb::head, {{"Login"}}},
614 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
615 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
616 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
617 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700618 }
619
620 private:
621 void doGet(crow::Response &res, const crow::Request &req,
622 const std::vector<std::string> &params) override
623 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700624 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700625 if (params.size() != 1)
626 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700627 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700628 return;
629 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700630 const uint8_t logId = std::atoi(params[0].c_str());
Jason M. Billse1f26342018-07-18 12:12:00 -0700631 auto getStoredLogCallback =
632 [asyncResp,
633 logId](const boost::system::error_code ec,
634 const sdbusplus::message::variant<std::string> &resp) {
635 if (ec)
636 {
637 BMCWEB_LOG_DEBUG << "failed to get log ec: "
638 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700639 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700640 return;
641 }
642 const std::string *log =
643 mapbox::getPtr<const std::string>(resp);
644 if (log == nullptr)
645 {
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 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
650 if (j.is_discarded())
651 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700652 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700653 return;
654 }
655 std::string t = getLogCreatedTime(j);
656 asyncResp->res.jsonValue = {
657 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
658 {"@odata.context",
659 "/redfish/v1/$metadata#LogEntry.LogEntry"},
660 {"@odata.id",
661 "/redfish/v1/Managers/bmc/LogServices/CpuLog/Entries/" +
662 std::to_string(logId)},
663 {"Name", "CPU Debug Log"},
664 {"Id", logId},
665 {"EntryType", "Oem"},
666 {"OemRecordFormat", "Intel CPU Log"},
667 {"Oem", {{"Intel", std::move(j)}}},
668 {"Created", std::move(t)}};
669 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700670 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700671 std::move(getStoredLogCallback), cpuLogObject,
672 cpuLogPath + std::string("/") + std::to_string(logId),
673 "org.freedesktop.DBus.Properties", "Get", cpuLogInterface, "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -0700674 }
675};
676
Jason M. Billse1f26342018-07-18 12:12:00 -0700677class ImmediateCPULog : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700678{
679 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700680 ImmediateCPULog(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700681 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700682 "CpuLog.Immediate/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700683 {
684 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -0700685 {boost::beast::http::verb::get, {{"Login"}}},
686 {boost::beast::http::verb::head, {{"Login"}}},
687 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
688 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
689 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
690 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700691 }
692
693 private:
694 void doPost(crow::Response &res, const crow::Request &req,
695 const std::vector<std::string> &params) override
696 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700697 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700698 static std::unique_ptr<sdbusplus::bus::match::match>
699 immediateLogMatcher;
700
701 // Only allow one Immediate Log request at a time
702 if (immediateLogMatcher != nullptr)
703 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700704 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -0700705 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -0700706 return;
707 }
708 // Make this static so it survives outside this method
709 static boost::asio::deadline_timer timeout(*req.ioService);
710
711 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -0700712 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700713 immediateLogMatcher = nullptr;
714 if (ec)
715 {
716 // operation_aborted is expected if timer is canceled before
717 // completion.
718 if (ec != boost::asio::error::operation_aborted)
719 {
720 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
721 }
722 return;
723 }
724 BMCWEB_LOG_ERROR << "Timed out waiting for immediate log";
725
Jason M. Billsf12894f2018-10-09 12:45:45 -0700726 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700727 });
728
Jason M. Billse1f26342018-07-18 12:12:00 -0700729 auto immediateLogMatcherCallback = [asyncResp](
Ed Tanous1da66f72018-07-27 16:13:37 -0700730 sdbusplus::message::message &m) {
731 BMCWEB_LOG_DEBUG << "Immediate log available match fired";
732 boost::system::error_code ec;
733 timeout.cancel(ec);
734 if (ec)
735 {
736 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
737 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700738 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -0700739 boost::container::flat_map<
740 std::string,
741 boost::container::flat_map<
742 std::string, sdbusplus::message::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700743 interfacesAdded;
744 m.read(objPath, interfacesAdded);
Ed Tanous1da66f72018-07-27 16:13:37 -0700745 const std::string *log = mapbox::getPtr<const std::string>(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700746 interfacesAdded[cpuLogInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -0700747 if (log == nullptr)
748 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700749 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700750 // Careful with immediateLogMatcher. It is a unique_ptr to the
751 // match object inside which this lambda is executing. Once it
752 // is set to nullptr, the match object will be destroyed and the
753 // lambda will lose its context, including res, so it needs to
754 // be the last thing done.
755 immediateLogMatcher = nullptr;
756 return;
757 }
758 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
759 if (j.is_discarded())
760 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700761 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700762 // Careful with immediateLogMatcher. It is a unique_ptr to the
763 // match object inside which this lambda is executing. Once it
764 // is set to nullptr, the match object will be destroyed and the
765 // lambda will lose its context, including res, so it needs to
766 // be the last thing done.
767 immediateLogMatcher = nullptr;
768 return;
769 }
770 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -0700771 asyncResp->res.jsonValue = {
Ed Tanous1da66f72018-07-27 16:13:37 -0700772 {"@odata.type", "#LogEntry.v1_3_0.LogEntry"},
773 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
774 {"Name", "CPU Debug Log"},
775 {"EntryType", "Oem"},
776 {"OemRecordFormat", "Intel CPU Log"},
777 {"Oem", {{"Intel", std::move(j)}}},
778 {"Created", std::move(t)}};
Ed Tanous1da66f72018-07-27 16:13:37 -0700779 // Careful with immediateLogMatcher. It is a unique_ptr to the
780 // match object inside which this lambda is executing. Once it is
781 // set to nullptr, the match object will be destroyed and the lambda
782 // will lose its context, including res, so it needs to be the last
783 // thing done.
784 immediateLogMatcher = nullptr;
785 };
786 immediateLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
787 *crow::connections::systemBus,
788 sdbusplus::bus::match::rules::interfacesAdded() +
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700789 sdbusplus::bus::match::rules::argNpath(0, cpuLogImmediatePath),
Ed Tanous1da66f72018-07-27 16:13:37 -0700790 std::move(immediateLogMatcherCallback));
791
792 auto generateImmediateLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -0700793 [asyncResp](const boost::system::error_code ec,
794 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -0700795 if (ec)
796 {
797 if (ec.value() ==
798 boost::system::errc::operation_not_supported)
799 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700800 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700801 }
802 else
803 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700804 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700805 }
Ed Tanous1da66f72018-07-27 16:13:37 -0700806 boost::system::error_code timeoutec;
807 timeout.cancel(timeoutec);
808 if (timeoutec)
809 {
810 BMCWEB_LOG_ERROR << "error canceling timer "
811 << timeoutec;
812 }
813 immediateLogMatcher = nullptr;
814 return;
815 }
816 };
817 crow::connections::systemBus->async_method_call(
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700818 std::move(generateImmediateLogCallback), cpuLogObject, cpuLogPath,
819 cpuLogImmediateInterface, "GenerateImmediateLog");
Ed Tanous1da66f72018-07-27 16:13:37 -0700820 }
821};
822
Jason M. Billse1f26342018-07-18 12:12:00 -0700823class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700824{
825 public:
Jason M. Billse1f26342018-07-18 12:12:00 -0700826 SendRawPECI(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700827 Node(app, "/redfish/v1/Managers/bmc/LogServices/CpuLog/Actions/Oem/"
Jason M. Billse1f26342018-07-18 12:12:00 -0700828 "CpuLog.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -0700829 {
830 entityPrivileges = {
831 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
832 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
833 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
834 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
835 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
836 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
837 }
838
839 private:
840 void doPost(crow::Response &res, const crow::Request &req,
841 const std::vector<std::string> &params) override
842 {
Jason M. Billse1f26342018-07-18 12:12:00 -0700843 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -0700844 // Get the Raw PECI command from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700845 nlohmann::json rawPECICmd;
846 if (!json_util::processJsonFromRequest(res, req, rawPECICmd))
Ed Tanous1da66f72018-07-27 16:13:37 -0700847 {
848 return;
849 }
850 // Get the Client Address from the request
Jason M. Billse1f26342018-07-18 12:12:00 -0700851 nlohmann::json::const_iterator caIt = rawPECICmd.find("ClientAddress");
852 if (caIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700853 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700854 messages::propertyMissing(asyncResp->res, "ClientAddress",
855 "/ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700856 return;
857 }
858 const uint64_t *ca = caIt->get_ptr<const uint64_t *>();
859 if (ca == nullptr)
860 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700861 messages::propertyValueTypeError(asyncResp->res, caIt->dump(),
862 "ClientAddress", "/ClientAddress");
Ed Tanous1da66f72018-07-27 16:13:37 -0700863 return;
864 }
865 // Get the Read Length from the request
866 const uint8_t clientAddress = static_cast<uint8_t>(*ca);
Jason M. Billse1f26342018-07-18 12:12:00 -0700867 nlohmann::json::const_iterator rlIt = rawPECICmd.find("ReadLength");
868 if (rlIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700869 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700870 messages::propertyMissing(asyncResp->res, "ReadLength",
871 "/ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700872 return;
873 }
874 const uint64_t *rl = rlIt->get_ptr<const uint64_t *>();
875 if (rl == nullptr)
876 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700877 messages::propertyValueTypeError(asyncResp->res, rlIt->dump(),
878 "ReadLength", "/ReadLength");
Ed Tanous1da66f72018-07-27 16:13:37 -0700879 return;
880 }
881 // Get the PECI Command from the request
882 const uint32_t readLength = static_cast<uint32_t>(*rl);
Jason M. Billse1f26342018-07-18 12:12:00 -0700883 nlohmann::json::const_iterator pcIt = rawPECICmd.find("PECICommand");
884 if (pcIt == rawPECICmd.end())
Ed Tanous1da66f72018-07-27 16:13:37 -0700885 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700886 messages::propertyMissing(asyncResp->res, "PECICommand",
887 "/PECICommand");
Ed Tanous1da66f72018-07-27 16:13:37 -0700888 return;
889 }
890 std::vector<uint8_t> peciCommand;
891 for (auto pc : *pcIt)
892 {
893 const uint64_t *val = pc.get_ptr<const uint64_t *>();
894 if (val == nullptr)
895 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700896 messages::propertyValueTypeError(
897 asyncResp->res, pc.dump(),
898 "PECICommand/" + std::to_string(peciCommand.size()),
Ed Tanous1da66f72018-07-27 16:13:37 -0700899 "/PECICommand");
Ed Tanous1da66f72018-07-27 16:13:37 -0700900 return;
901 }
902 peciCommand.push_back(static_cast<uint8_t>(*val));
903 }
904 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -0700905 auto sendRawPECICallback =
906 [asyncResp](const boost::system::error_code ec,
907 const std::vector<uint8_t> &resp) {
908 if (ec)
909 {
910 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
911 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700912 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -0700913 return;
914 }
915 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
916 {"PECIResponse", resp}};
917 };
Ed Tanous1da66f72018-07-27 16:13:37 -0700918 // Call the SendRawPECI command with the provided data
919 crow::connections::systemBus->async_method_call(
Jason M. Billse1f26342018-07-18 12:12:00 -0700920 std::move(sendRawPECICallback), cpuLogObject, cpuLogPath,
921 cpuLogRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -0700922 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -0700923 }
924};
925
926} // namespace redfish