11da66f75SEd Tanous /* 21da66f75SEd Tanous // Copyright (c) 2018 Intel Corporation 31da66f75SEd Tanous // 41da66f75SEd Tanous // Licensed under the Apache License, Version 2.0 (the "License"); 51da66f75SEd Tanous // you may not use this file except in compliance with the License. 61da66f75SEd Tanous // You may obtain a copy of the License at 71da66f75SEd Tanous // 81da66f75SEd Tanous // http://www.apache.org/licenses/LICENSE-2.0 91da66f75SEd Tanous // 101da66f75SEd Tanous // Unless required by applicable law or agreed to in writing, software 111da66f75SEd Tanous // distributed under the License is distributed on an "AS IS" BASIS, 121da66f75SEd Tanous // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131da66f75SEd Tanous // See the License for the specific language governing permissions and 141da66f75SEd Tanous // limitations under the License. 151da66f75SEd Tanous */ 161da66f75SEd Tanous #pragma once 171da66f75SEd Tanous 181da66f75SEd Tanous #include "node.hpp" 194851d45dSJason M. Bills #include "registries.hpp" 204851d45dSJason M. Bills #include "registries/base_message_registry.hpp" 214851d45dSJason M. Bills #include "registries/openbmc_message_registry.hpp" 2246229577SJames Feist #include "task.hpp" 231da66f75SEd Tanous 24e1f26343SJason M. Bills #include <systemd/sd-journal.h> 25e1f26343SJason M. Bills 264851d45dSJason M. Bills #include <boost/algorithm/string/split.hpp> 274851d45dSJason M. Bills #include <boost/beast/core/span.hpp> 281da66f75SEd Tanous #include <boost/container/flat_map.hpp> 291ddcf01aSJason M. Bills #include <boost/system/linux_error.hpp> 30cb92c03bSAndrew Geissler #include <error_messages.hpp> 314418c7f0SJames Feist #include <filesystem> 32cd225da8SJason M. Bills #include <string_view> 33abf2add6SEd Tanous #include <variant> 341da66f75SEd Tanous 351da66f75SEd Tanous namespace redfish 361da66f75SEd Tanous { 371da66f75SEd Tanous 385b61b5e8SJason M. Bills constexpr char const *crashdumpObject = "com.intel.crashdump"; 395b61b5e8SJason M. Bills constexpr char const *crashdumpPath = "/com/intel/crashdump"; 405b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand"; 415b61b5e8SJason M. Bills constexpr char const *crashdumpInterface = "com.intel.crashdump"; 425b61b5e8SJason M. Bills constexpr char const *deleteAllInterface = 435b61b5e8SJason M. Bills "xyz.openbmc_project.Collection.DeleteAll"; 445b61b5e8SJason M. Bills constexpr char const *crashdumpOnDemandInterface = 45424c4176SJason M. Bills "com.intel.crashdump.OnDemand"; 465b61b5e8SJason M. Bills constexpr char const *crashdumpRawPECIInterface = 47424c4176SJason M. Bills "com.intel.crashdump.SendRawPeci"; 481da66f75SEd Tanous 494851d45dSJason M. Bills namespace message_registries 504851d45dSJason M. Bills { 514851d45dSJason M. Bills static const Message *getMessageFromRegistry( 524851d45dSJason M. Bills const std::string &messageKey, 534851d45dSJason M. Bills const boost::beast::span<const MessageEntry> registry) 544851d45dSJason M. Bills { 554851d45dSJason M. Bills boost::beast::span<const MessageEntry>::const_iterator messageIt = 564851d45dSJason M. Bills std::find_if(registry.cbegin(), registry.cend(), 574851d45dSJason M. Bills [&messageKey](const MessageEntry &messageEntry) { 584851d45dSJason M. Bills return !std::strcmp(messageEntry.first, 594851d45dSJason M. Bills messageKey.c_str()); 604851d45dSJason M. Bills }); 614851d45dSJason M. Bills if (messageIt != registry.cend()) 624851d45dSJason M. Bills { 634851d45dSJason M. Bills return &messageIt->second; 644851d45dSJason M. Bills } 654851d45dSJason M. Bills 664851d45dSJason M. Bills return nullptr; 674851d45dSJason M. Bills } 684851d45dSJason M. Bills 694851d45dSJason M. Bills static const Message *getMessage(const std::string_view &messageID) 704851d45dSJason M. Bills { 714851d45dSJason M. Bills // Redfish MessageIds are in the form 724851d45dSJason M. Bills // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find 734851d45dSJason M. Bills // the right Message 744851d45dSJason M. Bills std::vector<std::string> fields; 754851d45dSJason M. Bills fields.reserve(4); 764851d45dSJason M. Bills boost::split(fields, messageID, boost::is_any_of(".")); 774851d45dSJason M. Bills std::string ®istryName = fields[0]; 784851d45dSJason M. Bills std::string &messageKey = fields[3]; 794851d45dSJason M. Bills 804851d45dSJason M. Bills // Find the right registry and check it for the MessageKey 814851d45dSJason M. Bills if (std::string(base::header.registryPrefix) == registryName) 824851d45dSJason M. Bills { 834851d45dSJason M. Bills return getMessageFromRegistry( 844851d45dSJason M. Bills messageKey, boost::beast::span<const MessageEntry>(base::registry)); 854851d45dSJason M. Bills } 864851d45dSJason M. Bills if (std::string(openbmc::header.registryPrefix) == registryName) 874851d45dSJason M. Bills { 884851d45dSJason M. Bills return getMessageFromRegistry( 894851d45dSJason M. Bills messageKey, 904851d45dSJason M. Bills boost::beast::span<const MessageEntry>(openbmc::registry)); 914851d45dSJason M. Bills } 924851d45dSJason M. Bills return nullptr; 934851d45dSJason M. Bills } 944851d45dSJason M. Bills } // namespace message_registries 954851d45dSJason M. Bills 96f6150403SJames Feist namespace fs = std::filesystem; 971da66f75SEd Tanous 98cb92c03bSAndrew Geissler using GetManagedPropertyType = boost::container::flat_map< 99cb92c03bSAndrew Geissler std::string, 100cb92c03bSAndrew Geissler sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, 101cb92c03bSAndrew Geissler int32_t, uint32_t, int64_t, uint64_t, double>>; 102cb92c03bSAndrew Geissler 103cb92c03bSAndrew Geissler using GetManagedObjectsType = boost::container::flat_map< 104cb92c03bSAndrew Geissler sdbusplus::message::object_path, 105cb92c03bSAndrew Geissler boost::container::flat_map<std::string, GetManagedPropertyType>>; 106cb92c03bSAndrew Geissler 107cb92c03bSAndrew Geissler inline std::string translateSeverityDbusToRedfish(const std::string &s) 108cb92c03bSAndrew Geissler { 109cb92c03bSAndrew Geissler if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert") 110cb92c03bSAndrew Geissler { 111cb92c03bSAndrew Geissler return "Critical"; 112cb92c03bSAndrew Geissler } 113cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") 114cb92c03bSAndrew Geissler { 115cb92c03bSAndrew Geissler return "Critical"; 116cb92c03bSAndrew Geissler } 117cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug") 118cb92c03bSAndrew Geissler { 119cb92c03bSAndrew Geissler return "OK"; 120cb92c03bSAndrew Geissler } 121cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") 122cb92c03bSAndrew Geissler { 123cb92c03bSAndrew Geissler return "Critical"; 124cb92c03bSAndrew Geissler } 125cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error") 126cb92c03bSAndrew Geissler { 127cb92c03bSAndrew Geissler return "Critical"; 128cb92c03bSAndrew Geissler } 129cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") 130cb92c03bSAndrew Geissler { 131cb92c03bSAndrew Geissler return "OK"; 132cb92c03bSAndrew Geissler } 133cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice") 134cb92c03bSAndrew Geissler { 135cb92c03bSAndrew Geissler return "OK"; 136cb92c03bSAndrew Geissler } 137cb92c03bSAndrew Geissler else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning") 138cb92c03bSAndrew Geissler { 139cb92c03bSAndrew Geissler return "Warning"; 140cb92c03bSAndrew Geissler } 141cb92c03bSAndrew Geissler return ""; 142cb92c03bSAndrew Geissler } 143cb92c03bSAndrew Geissler 14416428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 14539e77504SEd Tanous const std::string_view &field, 14639e77504SEd Tanous std::string_view &contents) 14716428a1aSJason M. Bills { 14816428a1aSJason M. Bills const char *data = nullptr; 14916428a1aSJason M. Bills size_t length = 0; 15016428a1aSJason M. Bills int ret = 0; 15116428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 152271584abSEd Tanous ret = sd_journal_get_data(journal, field.data(), 153271584abSEd Tanous reinterpret_cast<const void **>(&data), &length); 15416428a1aSJason M. Bills if (ret < 0) 15516428a1aSJason M. Bills { 15616428a1aSJason M. Bills return ret; 15716428a1aSJason M. Bills } 15839e77504SEd Tanous contents = std::string_view(data, length); 15916428a1aSJason M. Bills // Only use the content after the "=" character. 16016428a1aSJason M. Bills contents.remove_prefix(std::min(contents.find("=") + 1, contents.size())); 16116428a1aSJason M. Bills return ret; 16216428a1aSJason M. Bills } 16316428a1aSJason M. Bills 16416428a1aSJason M. Bills static int getJournalMetadata(sd_journal *journal, 16539e77504SEd Tanous const std::string_view &field, const int &base, 166271584abSEd Tanous long int &contents) 16716428a1aSJason M. Bills { 16816428a1aSJason M. Bills int ret = 0; 16939e77504SEd Tanous std::string_view metadata; 17016428a1aSJason M. Bills // Get the metadata from the requested field of the journal entry 17116428a1aSJason M. Bills ret = getJournalMetadata(journal, field, metadata); 17216428a1aSJason M. Bills if (ret < 0) 17316428a1aSJason M. Bills { 17416428a1aSJason M. Bills return ret; 17516428a1aSJason M. Bills } 176b01bf299SEd Tanous contents = strtol(metadata.data(), nullptr, base); 17716428a1aSJason M. Bills return ret; 17816428a1aSJason M. Bills } 17916428a1aSJason M. Bills 180*a3316fc6SZhikuiRen static bool getTimestampStr(const uint64_t usecSinceEpoch, 181*a3316fc6SZhikuiRen std::string &entryTimestamp) 18216428a1aSJason M. Bills { 183*a3316fc6SZhikuiRen time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000); 18416428a1aSJason M. Bills struct tm *loctime = localtime(&t); 18516428a1aSJason M. Bills char entryTime[64] = {}; 18699131cd0SEd Tanous if (nullptr != loctime) 18716428a1aSJason M. Bills { 18816428a1aSJason M. Bills strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime); 18916428a1aSJason M. Bills } 19016428a1aSJason M. Bills // Insert the ':' into the timezone 19139e77504SEd Tanous std::string_view t1(entryTime); 19239e77504SEd Tanous std::string_view t2(entryTime); 19316428a1aSJason M. Bills if (t1.size() > 2 && t2.size() > 2) 19416428a1aSJason M. Bills { 19516428a1aSJason M. Bills t1.remove_suffix(2); 19616428a1aSJason M. Bills t2.remove_prefix(t2.size() - 2); 19716428a1aSJason M. Bills } 19839e77504SEd Tanous entryTimestamp = std::string(t1) + ":" + std::string(t2); 19916428a1aSJason M. Bills return true; 20016428a1aSJason M. Bills } 20116428a1aSJason M. Bills 202*a3316fc6SZhikuiRen static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp) 203*a3316fc6SZhikuiRen { 204*a3316fc6SZhikuiRen int ret = 0; 205*a3316fc6SZhikuiRen uint64_t timestamp = 0; 206*a3316fc6SZhikuiRen ret = sd_journal_get_realtime_usec(journal, ×tamp); 207*a3316fc6SZhikuiRen if (ret < 0) 208*a3316fc6SZhikuiRen { 209*a3316fc6SZhikuiRen BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 210*a3316fc6SZhikuiRen << strerror(-ret); 211*a3316fc6SZhikuiRen return false; 212*a3316fc6SZhikuiRen } 213*a3316fc6SZhikuiRen return getTimestampStr(timestamp, entryTimestamp); 214*a3316fc6SZhikuiRen } 215*a3316fc6SZhikuiRen 21616428a1aSJason M. Bills static bool getSkipParam(crow::Response &res, const crow::Request &req, 217271584abSEd Tanous uint64_t &skip) 21816428a1aSJason M. Bills { 21916428a1aSJason M. Bills char *skipParam = req.urlParams.get("$skip"); 22016428a1aSJason M. Bills if (skipParam != nullptr) 22116428a1aSJason M. Bills { 22216428a1aSJason M. Bills char *ptr = nullptr; 223271584abSEd Tanous skip = std::strtoul(skipParam, &ptr, 10); 22416428a1aSJason M. Bills if (*skipParam == '\0' || *ptr != '\0') 22516428a1aSJason M. Bills { 22616428a1aSJason M. Bills 22716428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(skipParam), 22816428a1aSJason M. Bills "$skip"); 22916428a1aSJason M. Bills return false; 23016428a1aSJason M. Bills } 23116428a1aSJason M. Bills } 23216428a1aSJason M. Bills return true; 23316428a1aSJason M. Bills } 23416428a1aSJason M. Bills 235271584abSEd Tanous static constexpr const uint64_t maxEntriesPerPage = 1000; 23616428a1aSJason M. Bills static bool getTopParam(crow::Response &res, const crow::Request &req, 237271584abSEd Tanous uint64_t &top) 23816428a1aSJason M. Bills { 23916428a1aSJason M. Bills char *topParam = req.urlParams.get("$top"); 24016428a1aSJason M. Bills if (topParam != nullptr) 24116428a1aSJason M. Bills { 24216428a1aSJason M. Bills char *ptr = nullptr; 243271584abSEd Tanous top = std::strtoul(topParam, &ptr, 10); 24416428a1aSJason M. Bills if (*topParam == '\0' || *ptr != '\0') 24516428a1aSJason M. Bills { 24616428a1aSJason M. Bills messages::queryParameterValueTypeError(res, std::string(topParam), 24716428a1aSJason M. Bills "$top"); 24816428a1aSJason M. Bills return false; 24916428a1aSJason M. Bills } 250271584abSEd Tanous if (top < 1U || top > maxEntriesPerPage) 25116428a1aSJason M. Bills { 25216428a1aSJason M. Bills 25316428a1aSJason M. Bills messages::queryParameterOutOfRange( 25416428a1aSJason M. Bills res, std::to_string(top), "$top", 25516428a1aSJason M. Bills "1-" + std::to_string(maxEntriesPerPage)); 25616428a1aSJason M. Bills return false; 25716428a1aSJason M. Bills } 25816428a1aSJason M. Bills } 25916428a1aSJason M. Bills return true; 26016428a1aSJason M. Bills } 26116428a1aSJason M. Bills 262e85d6b16SJason M. Bills static bool getUniqueEntryID(sd_journal *journal, std::string &entryID, 263e85d6b16SJason M. Bills const bool firstEntry = true) 26416428a1aSJason M. Bills { 26516428a1aSJason M. Bills int ret = 0; 26616428a1aSJason M. Bills static uint64_t prevTs = 0; 26716428a1aSJason M. Bills static int index = 0; 268e85d6b16SJason M. Bills if (firstEntry) 269e85d6b16SJason M. Bills { 270e85d6b16SJason M. Bills prevTs = 0; 271e85d6b16SJason M. Bills } 272e85d6b16SJason M. Bills 27316428a1aSJason M. Bills // Get the entry timestamp 27416428a1aSJason M. Bills uint64_t curTs = 0; 27516428a1aSJason M. Bills ret = sd_journal_get_realtime_usec(journal, &curTs); 27616428a1aSJason M. Bills if (ret < 0) 27716428a1aSJason M. Bills { 27816428a1aSJason M. Bills BMCWEB_LOG_ERROR << "Failed to read entry timestamp: " 27916428a1aSJason M. Bills << strerror(-ret); 28016428a1aSJason M. Bills return false; 28116428a1aSJason M. Bills } 28216428a1aSJason M. Bills // If the timestamp isn't unique, increment the index 28316428a1aSJason M. Bills if (curTs == prevTs) 28416428a1aSJason M. Bills { 28516428a1aSJason M. Bills index++; 28616428a1aSJason M. Bills } 28716428a1aSJason M. Bills else 28816428a1aSJason M. Bills { 28916428a1aSJason M. Bills // Otherwise, reset it 29016428a1aSJason M. Bills index = 0; 29116428a1aSJason M. Bills } 29216428a1aSJason M. Bills // Save the timestamp 29316428a1aSJason M. Bills prevTs = curTs; 29416428a1aSJason M. Bills 29516428a1aSJason M. Bills entryID = std::to_string(curTs); 29616428a1aSJason M. Bills if (index > 0) 29716428a1aSJason M. Bills { 29816428a1aSJason M. Bills entryID += "_" + std::to_string(index); 29916428a1aSJason M. Bills } 30016428a1aSJason M. Bills return true; 30116428a1aSJason M. Bills } 30216428a1aSJason M. Bills 303e85d6b16SJason M. Bills static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID, 304e85d6b16SJason M. Bills const bool firstEntry = true) 30595820184SJason M. Bills { 306271584abSEd Tanous static time_t prevTs = 0; 30795820184SJason M. Bills static int index = 0; 308e85d6b16SJason M. Bills if (firstEntry) 309e85d6b16SJason M. Bills { 310e85d6b16SJason M. Bills prevTs = 0; 311e85d6b16SJason M. Bills } 312e85d6b16SJason M. Bills 31395820184SJason M. Bills // Get the entry timestamp 314271584abSEd Tanous std::time_t curTs = 0; 31595820184SJason M. Bills std::tm timeStruct = {}; 31695820184SJason M. Bills std::istringstream entryStream(logEntry); 31795820184SJason M. Bills if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 31895820184SJason M. Bills { 31995820184SJason M. Bills curTs = std::mktime(&timeStruct); 32095820184SJason M. Bills } 32195820184SJason M. Bills // If the timestamp isn't unique, increment the index 32295820184SJason M. Bills if (curTs == prevTs) 32395820184SJason M. Bills { 32495820184SJason M. Bills index++; 32595820184SJason M. Bills } 32695820184SJason M. Bills else 32795820184SJason M. Bills { 32895820184SJason M. Bills // Otherwise, reset it 32995820184SJason M. Bills index = 0; 33095820184SJason M. Bills } 33195820184SJason M. Bills // Save the timestamp 33295820184SJason M. Bills prevTs = curTs; 33395820184SJason M. Bills 33495820184SJason M. Bills entryID = std::to_string(curTs); 33595820184SJason M. Bills if (index > 0) 33695820184SJason M. Bills { 33795820184SJason M. Bills entryID += "_" + std::to_string(index); 33895820184SJason M. Bills } 33995820184SJason M. Bills return true; 34095820184SJason M. Bills } 34195820184SJason M. Bills 34216428a1aSJason M. Bills static bool getTimestampFromID(crow::Response &res, const std::string &entryID, 343271584abSEd Tanous uint64_t ×tamp, uint64_t &index) 34416428a1aSJason M. Bills { 34516428a1aSJason M. Bills if (entryID.empty()) 34616428a1aSJason M. Bills { 34716428a1aSJason M. Bills return false; 34816428a1aSJason M. Bills } 34916428a1aSJason M. Bills // Convert the unique ID back to a timestamp to find the entry 35039e77504SEd Tanous std::string_view tsStr(entryID); 35116428a1aSJason M. Bills 35216428a1aSJason M. Bills auto underscorePos = tsStr.find("_"); 35316428a1aSJason M. Bills if (underscorePos != tsStr.npos) 35416428a1aSJason M. Bills { 35516428a1aSJason M. Bills // Timestamp has an index 35616428a1aSJason M. Bills tsStr.remove_suffix(tsStr.size() - underscorePos); 35739e77504SEd Tanous std::string_view indexStr(entryID); 35816428a1aSJason M. Bills indexStr.remove_prefix(underscorePos + 1); 35916428a1aSJason M. Bills std::size_t pos; 36016428a1aSJason M. Bills try 36116428a1aSJason M. Bills { 36239e77504SEd Tanous index = std::stoul(std::string(indexStr), &pos); 36316428a1aSJason M. Bills } 364271584abSEd Tanous catch (std::invalid_argument &) 36516428a1aSJason M. Bills { 36616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 36716428a1aSJason M. Bills return false; 36816428a1aSJason M. Bills } 369271584abSEd Tanous catch (std::out_of_range &) 37016428a1aSJason M. Bills { 37116428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37216428a1aSJason M. Bills return false; 37316428a1aSJason M. Bills } 37416428a1aSJason M. Bills if (pos != indexStr.size()) 37516428a1aSJason M. Bills { 37616428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 37716428a1aSJason M. Bills return false; 37816428a1aSJason M. Bills } 37916428a1aSJason M. Bills } 38016428a1aSJason M. Bills // Timestamp has no index 38116428a1aSJason M. Bills std::size_t pos; 38216428a1aSJason M. Bills try 38316428a1aSJason M. Bills { 38439e77504SEd Tanous timestamp = std::stoull(std::string(tsStr), &pos); 38516428a1aSJason M. Bills } 386271584abSEd Tanous catch (std::invalid_argument &) 38716428a1aSJason M. Bills { 38816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 38916428a1aSJason M. Bills return false; 39016428a1aSJason M. Bills } 391271584abSEd Tanous catch (std::out_of_range &) 39216428a1aSJason M. Bills { 39316428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39416428a1aSJason M. Bills return false; 39516428a1aSJason M. Bills } 39616428a1aSJason M. Bills if (pos != tsStr.size()) 39716428a1aSJason M. Bills { 39816428a1aSJason M. Bills messages::resourceMissingAtURI(res, entryID); 39916428a1aSJason M. Bills return false; 40016428a1aSJason M. Bills } 40116428a1aSJason M. Bills return true; 40216428a1aSJason M. Bills } 40316428a1aSJason M. Bills 40495820184SJason M. Bills static bool 40595820184SJason M. Bills getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles) 40695820184SJason M. Bills { 40795820184SJason M. Bills static const std::filesystem::path redfishLogDir = "/var/log"; 40895820184SJason M. Bills static const std::string redfishLogFilename = "redfish"; 40995820184SJason M. Bills 41095820184SJason M. Bills // Loop through the directory looking for redfish log files 41195820184SJason M. Bills for (const std::filesystem::directory_entry &dirEnt : 41295820184SJason M. Bills std::filesystem::directory_iterator(redfishLogDir)) 41395820184SJason M. Bills { 41495820184SJason M. Bills // If we find a redfish log file, save the path 41595820184SJason M. Bills std::string filename = dirEnt.path().filename(); 41695820184SJason M. Bills if (boost::starts_with(filename, redfishLogFilename)) 41795820184SJason M. Bills { 41895820184SJason M. Bills redfishLogFiles.emplace_back(redfishLogDir / filename); 41995820184SJason M. Bills } 42095820184SJason M. Bills } 42195820184SJason M. Bills // As the log files rotate, they are appended with a ".#" that is higher for 42295820184SJason M. Bills // the older logs. Since we don't expect more than 10 log files, we 42395820184SJason M. Bills // can just sort the list to get them in order from newest to oldest 42495820184SJason M. Bills std::sort(redfishLogFiles.begin(), redfishLogFiles.end()); 42595820184SJason M. Bills 42695820184SJason M. Bills return !redfishLogFiles.empty(); 42795820184SJason M. Bills } 42895820184SJason M. Bills 429*a3316fc6SZhikuiRen constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode"; 430c4bf6374SJason M. Bills class SystemLogServiceCollection : public Node 4311da66f75SEd Tanous { 4321da66f75SEd Tanous public: 4331da66f75SEd Tanous template <typename CrowApp> 434c4bf6374SJason M. Bills SystemLogServiceCollection(CrowApp &app) : 435029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/") 436c4bf6374SJason M. Bills { 437c4bf6374SJason M. Bills entityPrivileges = { 438c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 439c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 440c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 441c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 442c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 443c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 444c4bf6374SJason M. Bills } 445c4bf6374SJason M. Bills 446c4bf6374SJason M. Bills private: 447c4bf6374SJason M. Bills /** 448c4bf6374SJason M. Bills * Functions triggers appropriate requests on DBus 449c4bf6374SJason M. Bills */ 450c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 451c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 452c4bf6374SJason M. Bills { 453c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 454c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 455c4bf6374SJason M. Bills // it has a duplicate entry for members 456c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 457c4bf6374SJason M. Bills "#LogServiceCollection.LogServiceCollection"; 458c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 459029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices"; 460c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Log Services Collection"; 461c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 462c4bf6374SJason M. Bills "Collection of LogServices for this Computer System"; 463c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 464c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 465029573d4SEd Tanous logServiceArray.push_back( 466029573d4SEd Tanous {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}}); 467d53dd41fSJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG 468d53dd41fSJason M. Bills logServiceArray.push_back( 469cb92c03bSAndrew Geissler {{"@odata.id", 470424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"}}); 471d53dd41fSJason M. Bills #endif 472c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 473c4bf6374SJason M. Bills logServiceArray.size(); 474*a3316fc6SZhikuiRen 475*a3316fc6SZhikuiRen crow::connections::systemBus->async_method_call( 476*a3316fc6SZhikuiRen [asyncResp](const boost::system::error_code ec, 477*a3316fc6SZhikuiRen const std::vector<std::string> &subtreePath) { 478*a3316fc6SZhikuiRen if (ec) 479*a3316fc6SZhikuiRen { 480*a3316fc6SZhikuiRen BMCWEB_LOG_ERROR << ec; 481*a3316fc6SZhikuiRen return; 482*a3316fc6SZhikuiRen } 483*a3316fc6SZhikuiRen 484*a3316fc6SZhikuiRen for (auto &pathStr : subtreePath) 485*a3316fc6SZhikuiRen { 486*a3316fc6SZhikuiRen if (pathStr.find("PostCode") != std::string::npos) 487*a3316fc6SZhikuiRen { 488*a3316fc6SZhikuiRen nlohmann::json &logServiceArray = 489*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members"]; 490*a3316fc6SZhikuiRen logServiceArray.push_back( 491*a3316fc6SZhikuiRen {{"@odata.id", "/redfish/v1/Systems/system/" 492*a3316fc6SZhikuiRen "LogServices/PostCodes"}}); 493*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members@odata.count"] = 494*a3316fc6SZhikuiRen logServiceArray.size(); 495*a3316fc6SZhikuiRen return; 496*a3316fc6SZhikuiRen } 497*a3316fc6SZhikuiRen } 498*a3316fc6SZhikuiRen }, 499*a3316fc6SZhikuiRen "xyz.openbmc_project.ObjectMapper", 500*a3316fc6SZhikuiRen "/xyz/openbmc_project/object_mapper", 501*a3316fc6SZhikuiRen "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0, 502*a3316fc6SZhikuiRen std::array<const char *, 1>{postCodeIface}); 503c4bf6374SJason M. Bills } 504c4bf6374SJason M. Bills }; 505c4bf6374SJason M. Bills 506c4bf6374SJason M. Bills class EventLogService : public Node 507c4bf6374SJason M. Bills { 508c4bf6374SJason M. Bills public: 509c4bf6374SJason M. Bills template <typename CrowApp> 510c4bf6374SJason M. Bills EventLogService(CrowApp &app) : 511029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/") 512c4bf6374SJason M. Bills { 513c4bf6374SJason M. Bills entityPrivileges = { 514c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 515c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 516c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 517c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 518c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 519c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 520c4bf6374SJason M. Bills } 521c4bf6374SJason M. Bills 522c4bf6374SJason M. Bills private: 523c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 524c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 525c4bf6374SJason M. Bills { 526c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 527c4bf6374SJason M. Bills 528c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 529029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog"; 530c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 531c4bf6374SJason M. Bills "#LogService.v1_1_0.LogService"; 532c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Event Log Service"; 533c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "System Event Log Service"; 534c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "Event Log"; 535c4bf6374SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 536c4bf6374SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 537c4bf6374SJason M. Bills {"@odata.id", 538029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}}; 539e7d6c8b2SGunnar Mills asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = { 540e7d6c8b2SGunnar Mills 541e7d6c8b2SGunnar Mills {"target", "/redfish/v1/Systems/system/LogServices/EventLog/" 542e7d6c8b2SGunnar Mills "Actions/LogService.ClearLog"}}; 543489640c6SJason M. Bills } 544489640c6SJason M. Bills }; 545489640c6SJason M. Bills 5461f56a3a6STim Lee class JournalEventLogClear : public Node 547489640c6SJason M. Bills { 548489640c6SJason M. Bills public: 5491f56a3a6STim Lee JournalEventLogClear(CrowApp &app) : 550489640c6SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 551489640c6SJason M. Bills "LogService.ClearLog/") 552489640c6SJason M. Bills { 553489640c6SJason M. Bills entityPrivileges = { 554489640c6SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 555489640c6SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 556489640c6SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 557489640c6SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 558489640c6SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 559489640c6SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 560489640c6SJason M. Bills } 561489640c6SJason M. Bills 562489640c6SJason M. Bills private: 563489640c6SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 564489640c6SJason M. Bills const std::vector<std::string> ¶ms) override 565489640c6SJason M. Bills { 566489640c6SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 567489640c6SJason M. Bills 568489640c6SJason M. Bills // Clear the EventLog by deleting the log files 569489640c6SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 570489640c6SJason M. Bills if (getRedfishLogFiles(redfishLogFiles)) 571489640c6SJason M. Bills { 572489640c6SJason M. Bills for (const std::filesystem::path &file : redfishLogFiles) 573489640c6SJason M. Bills { 574489640c6SJason M. Bills std::error_code ec; 575489640c6SJason M. Bills std::filesystem::remove(file, ec); 576489640c6SJason M. Bills } 577489640c6SJason M. Bills } 578489640c6SJason M. Bills 579489640c6SJason M. Bills // Reload rsyslog so it knows to start new log files 580489640c6SJason M. Bills crow::connections::systemBus->async_method_call( 581489640c6SJason M. Bills [asyncResp](const boost::system::error_code ec) { 582489640c6SJason M. Bills if (ec) 583489640c6SJason M. Bills { 584489640c6SJason M. Bills BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec; 585489640c6SJason M. Bills messages::internalError(asyncResp->res); 586489640c6SJason M. Bills return; 587489640c6SJason M. Bills } 588489640c6SJason M. Bills 589489640c6SJason M. Bills messages::success(asyncResp->res); 590489640c6SJason M. Bills }, 591489640c6SJason M. Bills "org.freedesktop.systemd1", "/org/freedesktop/systemd1", 592489640c6SJason M. Bills "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service", 593489640c6SJason M. Bills "replace"); 594c4bf6374SJason M. Bills } 595c4bf6374SJason M. Bills }; 596c4bf6374SJason M. Bills 59795820184SJason M. Bills static int fillEventLogEntryJson(const std::string &logEntryID, 59895820184SJason M. Bills const std::string logEntry, 59995820184SJason M. Bills nlohmann::json &logEntryJson) 600c4bf6374SJason M. Bills { 60195820184SJason M. Bills // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 602cd225da8SJason M. Bills // First get the Timestamp 603cd225da8SJason M. Bills size_t space = logEntry.find_first_of(" "); 604cd225da8SJason M. Bills if (space == std::string::npos) 60595820184SJason M. Bills { 60695820184SJason M. Bills return 1; 60795820184SJason M. Bills } 608cd225da8SJason M. Bills std::string timestamp = logEntry.substr(0, space); 609cd225da8SJason M. Bills // Then get the log contents 610cd225da8SJason M. Bills size_t entryStart = logEntry.find_first_not_of(" ", space); 611cd225da8SJason M. Bills if (entryStart == std::string::npos) 612cd225da8SJason M. Bills { 613cd225da8SJason M. Bills return 1; 614cd225da8SJason M. Bills } 615cd225da8SJason M. Bills std::string_view entry(logEntry); 616cd225da8SJason M. Bills entry.remove_prefix(entryStart); 617cd225da8SJason M. Bills // Use split to separate the entry into its fields 618cd225da8SJason M. Bills std::vector<std::string> logEntryFields; 619cd225da8SJason M. Bills boost::split(logEntryFields, entry, boost::is_any_of(","), 620cd225da8SJason M. Bills boost::token_compress_on); 621cd225da8SJason M. Bills // We need at least a MessageId to be valid 622cd225da8SJason M. Bills if (logEntryFields.size() < 1) 623cd225da8SJason M. Bills { 624cd225da8SJason M. Bills return 1; 625cd225da8SJason M. Bills } 626cd225da8SJason M. Bills std::string &messageID = logEntryFields[0]; 62795820184SJason M. Bills 6284851d45dSJason M. Bills // Get the Message from the MessageRegistry 6294851d45dSJason M. Bills const message_registries::Message *message = 6304851d45dSJason M. Bills message_registries::getMessage(messageID); 631c4bf6374SJason M. Bills 6324851d45dSJason M. Bills std::string msg; 6334851d45dSJason M. Bills std::string severity; 6344851d45dSJason M. Bills if (message != nullptr) 635c4bf6374SJason M. Bills { 6364851d45dSJason M. Bills msg = message->message; 6374851d45dSJason M. Bills severity = message->severity; 638c4bf6374SJason M. Bills } 639c4bf6374SJason M. Bills 64015a86ff6SJason M. Bills // Get the MessageArgs from the log if there are any 64115a86ff6SJason M. Bills boost::beast::span<std::string> messageArgs; 64215a86ff6SJason M. Bills if (logEntryFields.size() > 1) 64315a86ff6SJason M. Bills { 64415a86ff6SJason M. Bills std::string &messageArgsStart = logEntryFields[1]; 64515a86ff6SJason M. Bills // If the first string is empty, assume there are no MessageArgs 64615a86ff6SJason M. Bills std::size_t messageArgsSize = 0; 64715a86ff6SJason M. Bills if (!messageArgsStart.empty()) 64815a86ff6SJason M. Bills { 64915a86ff6SJason M. Bills messageArgsSize = logEntryFields.size() - 1; 65015a86ff6SJason M. Bills } 65115a86ff6SJason M. Bills 65215a86ff6SJason M. Bills messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize); 653c4bf6374SJason M. Bills 6544851d45dSJason M. Bills // Fill the MessageArgs into the Message 65595820184SJason M. Bills int i = 0; 65695820184SJason M. Bills for (const std::string &messageArg : messageArgs) 6574851d45dSJason M. Bills { 65895820184SJason M. Bills std::string argStr = "%" + std::to_string(++i); 6594851d45dSJason M. Bills size_t argPos = msg.find(argStr); 6604851d45dSJason M. Bills if (argPos != std::string::npos) 6614851d45dSJason M. Bills { 66295820184SJason M. Bills msg.replace(argPos, argStr.length(), messageArg); 6634851d45dSJason M. Bills } 6644851d45dSJason M. Bills } 66515a86ff6SJason M. Bills } 6664851d45dSJason M. Bills 66795820184SJason M. Bills // Get the Created time from the timestamp. The log timestamp is in RFC3339 66895820184SJason M. Bills // format which matches the Redfish format except for the fractional seconds 66995820184SJason M. Bills // between the '.' and the '+', so just remove them. 67095820184SJason M. Bills std::size_t dot = timestamp.find_first_of("."); 67195820184SJason M. Bills std::size_t plus = timestamp.find_first_of("+"); 67295820184SJason M. Bills if (dot != std::string::npos && plus != std::string::npos) 673c4bf6374SJason M. Bills { 67495820184SJason M. Bills timestamp.erase(dot, plus - dot); 675c4bf6374SJason M. Bills } 676c4bf6374SJason M. Bills 677c4bf6374SJason M. Bills // Fill in the log entry with the gathered data 67895820184SJason M. Bills logEntryJson = { 679cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 680029573d4SEd Tanous {"@odata.id", 681897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + 68295820184SJason M. Bills logEntryID}, 683c4bf6374SJason M. Bills {"Name", "System Event Log Entry"}, 68495820184SJason M. Bills {"Id", logEntryID}, 68595820184SJason M. Bills {"Message", std::move(msg)}, 68695820184SJason M. Bills {"MessageId", std::move(messageID)}, 687c4bf6374SJason M. Bills {"MessageArgs", std::move(messageArgs)}, 688c4bf6374SJason M. Bills {"EntryType", "Event"}, 68995820184SJason M. Bills {"Severity", std::move(severity)}, 69095820184SJason M. Bills {"Created", std::move(timestamp)}}; 691c4bf6374SJason M. Bills return 0; 692c4bf6374SJason M. Bills } 693c4bf6374SJason M. Bills 69427062605SAnthony Wilson class JournalEventLogEntryCollection : public Node 695c4bf6374SJason M. Bills { 696c4bf6374SJason M. Bills public: 697c4bf6374SJason M. Bills template <typename CrowApp> 69827062605SAnthony Wilson JournalEventLogEntryCollection(CrowApp &app) : 699029573d4SEd Tanous Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 700c4bf6374SJason M. Bills { 701c4bf6374SJason M. Bills entityPrivileges = { 702c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 703c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 704c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 705c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 706c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 707c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 708c4bf6374SJason M. Bills } 709c4bf6374SJason M. Bills 710c4bf6374SJason M. Bills private: 711c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 712c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 713c4bf6374SJason M. Bills { 714c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 715271584abSEd Tanous uint64_t skip = 0; 716271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 717c4bf6374SJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 718c4bf6374SJason M. Bills { 719c4bf6374SJason M. Bills return; 720c4bf6374SJason M. Bills } 721c4bf6374SJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 722c4bf6374SJason M. Bills { 723c4bf6374SJason M. Bills return; 724c4bf6374SJason M. Bills } 725c4bf6374SJason M. Bills // Collections don't include the static data added by SubRoute because 726c4bf6374SJason M. Bills // it has a duplicate entry for members 727c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 728c4bf6374SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 729c4bf6374SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 730029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 731c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 732c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = 733c4bf6374SJason M. Bills "Collection of System Event Log Entries"; 734cb92c03bSAndrew Geissler 735c4bf6374SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 736c4bf6374SJason M. Bills logEntryArray = nlohmann::json::array(); 73795820184SJason M. Bills // Go through the log files and create a unique ID for each entry 73895820184SJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 73995820184SJason M. Bills getRedfishLogFiles(redfishLogFiles); 740b01bf299SEd Tanous uint64_t entryCount = 0; 741cd225da8SJason M. Bills std::string logEntry; 74295820184SJason M. Bills 74395820184SJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 744cd225da8SJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 745cd225da8SJason M. Bills it++) 746c4bf6374SJason M. Bills { 747cd225da8SJason M. Bills std::ifstream logStream(*it); 74895820184SJason M. Bills if (!logStream.is_open()) 749c4bf6374SJason M. Bills { 750c4bf6374SJason M. Bills continue; 751c4bf6374SJason M. Bills } 752c4bf6374SJason M. Bills 753e85d6b16SJason M. Bills // Reset the unique ID on the first entry 754e85d6b16SJason M. Bills bool firstEntry = true; 75595820184SJason M. Bills while (std::getline(logStream, logEntry)) 75695820184SJason M. Bills { 757c4bf6374SJason M. Bills entryCount++; 758c4bf6374SJason M. Bills // Handle paging using skip (number of entries to skip from the 759c4bf6374SJason M. Bills // start) and top (number of entries to display) 760c4bf6374SJason M. Bills if (entryCount <= skip || entryCount > skip + top) 761c4bf6374SJason M. Bills { 762c4bf6374SJason M. Bills continue; 763c4bf6374SJason M. Bills } 764c4bf6374SJason M. Bills 765c4bf6374SJason M. Bills std::string idStr; 766e85d6b16SJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 767c4bf6374SJason M. Bills { 768c4bf6374SJason M. Bills continue; 769c4bf6374SJason M. Bills } 770c4bf6374SJason M. Bills 771e85d6b16SJason M. Bills if (firstEntry) 772e85d6b16SJason M. Bills { 773e85d6b16SJason M. Bills firstEntry = false; 774e85d6b16SJason M. Bills } 775e85d6b16SJason M. Bills 776c4bf6374SJason M. Bills logEntryArray.push_back({}); 777c4bf6374SJason M. Bills nlohmann::json &bmcLogEntry = logEntryArray.back(); 77895820184SJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0) 779c4bf6374SJason M. Bills { 780c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 781c4bf6374SJason M. Bills return; 782c4bf6374SJason M. Bills } 783c4bf6374SJason M. Bills } 78495820184SJason M. Bills } 785c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 786c4bf6374SJason M. Bills if (skip + top < entryCount) 787c4bf6374SJason M. Bills { 788c4bf6374SJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 78995820184SJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/" 79095820184SJason M. Bills "Entries?$skip=" + 791c4bf6374SJason M. Bills std::to_string(skip + top); 792c4bf6374SJason M. Bills } 79308a4e4b5SAnthony Wilson } 79408a4e4b5SAnthony Wilson }; 79508a4e4b5SAnthony Wilson 796897967deSJason M. Bills class JournalEventLogEntry : public Node 797897967deSJason M. Bills { 798897967deSJason M. Bills public: 799897967deSJason M. Bills JournalEventLogEntry(CrowApp &app) : 800897967deSJason M. Bills Node(app, 801897967deSJason M. Bills "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 802897967deSJason M. Bills std::string()) 803897967deSJason M. Bills { 804897967deSJason M. Bills entityPrivileges = { 805897967deSJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 806897967deSJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 807897967deSJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 808897967deSJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 809897967deSJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 810897967deSJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 811897967deSJason M. Bills } 812897967deSJason M. Bills 813897967deSJason M. Bills private: 814897967deSJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 815897967deSJason M. Bills const std::vector<std::string> ¶ms) override 816897967deSJason M. Bills { 817897967deSJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 818897967deSJason M. Bills if (params.size() != 1) 819897967deSJason M. Bills { 820897967deSJason M. Bills messages::internalError(asyncResp->res); 821897967deSJason M. Bills return; 822897967deSJason M. Bills } 823897967deSJason M. Bills const std::string &targetID = params[0]; 824897967deSJason M. Bills 825897967deSJason M. Bills // Go through the log files and check the unique ID for each entry to 826897967deSJason M. Bills // find the target entry 827897967deSJason M. Bills std::vector<std::filesystem::path> redfishLogFiles; 828897967deSJason M. Bills getRedfishLogFiles(redfishLogFiles); 829897967deSJason M. Bills std::string logEntry; 830897967deSJason M. Bills 831897967deSJason M. Bills // Oldest logs are in the last file, so start there and loop backwards 832897967deSJason M. Bills for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); 833897967deSJason M. Bills it++) 834897967deSJason M. Bills { 835897967deSJason M. Bills std::ifstream logStream(*it); 836897967deSJason M. Bills if (!logStream.is_open()) 837897967deSJason M. Bills { 838897967deSJason M. Bills continue; 839897967deSJason M. Bills } 840897967deSJason M. Bills 841897967deSJason M. Bills // Reset the unique ID on the first entry 842897967deSJason M. Bills bool firstEntry = true; 843897967deSJason M. Bills while (std::getline(logStream, logEntry)) 844897967deSJason M. Bills { 845897967deSJason M. Bills std::string idStr; 846897967deSJason M. Bills if (!getUniqueEntryID(logEntry, idStr, firstEntry)) 847897967deSJason M. Bills { 848897967deSJason M. Bills continue; 849897967deSJason M. Bills } 850897967deSJason M. Bills 851897967deSJason M. Bills if (firstEntry) 852897967deSJason M. Bills { 853897967deSJason M. Bills firstEntry = false; 854897967deSJason M. Bills } 855897967deSJason M. Bills 856897967deSJason M. Bills if (idStr == targetID) 857897967deSJason M. Bills { 858897967deSJason M. Bills if (fillEventLogEntryJson(idStr, logEntry, 859897967deSJason M. Bills asyncResp->res.jsonValue) != 0) 860897967deSJason M. Bills { 861897967deSJason M. Bills messages::internalError(asyncResp->res); 862897967deSJason M. Bills return; 863897967deSJason M. Bills } 864897967deSJason M. Bills return; 865897967deSJason M. Bills } 866897967deSJason M. Bills } 867897967deSJason M. Bills } 868897967deSJason M. Bills // Requested ID was not found 869897967deSJason M. Bills messages::resourceMissingAtURI(asyncResp->res, targetID); 870897967deSJason M. Bills } 871897967deSJason M. Bills }; 872897967deSJason M. Bills 87308a4e4b5SAnthony Wilson class DBusEventLogEntryCollection : public Node 87408a4e4b5SAnthony Wilson { 87508a4e4b5SAnthony Wilson public: 87608a4e4b5SAnthony Wilson template <typename CrowApp> 87708a4e4b5SAnthony Wilson DBusEventLogEntryCollection(CrowApp &app) : 87808a4e4b5SAnthony Wilson Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/") 87908a4e4b5SAnthony Wilson { 88008a4e4b5SAnthony Wilson entityPrivileges = { 88108a4e4b5SAnthony Wilson {boost::beast::http::verb::get, {{"Login"}}}, 88208a4e4b5SAnthony Wilson {boost::beast::http::verb::head, {{"Login"}}}, 88308a4e4b5SAnthony Wilson {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 88408a4e4b5SAnthony Wilson {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 88508a4e4b5SAnthony Wilson {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 88608a4e4b5SAnthony Wilson {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 88708a4e4b5SAnthony Wilson } 88808a4e4b5SAnthony Wilson 88908a4e4b5SAnthony Wilson private: 89008a4e4b5SAnthony Wilson void doGet(crow::Response &res, const crow::Request &req, 89108a4e4b5SAnthony Wilson const std::vector<std::string> ¶ms) override 89208a4e4b5SAnthony Wilson { 89308a4e4b5SAnthony Wilson std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 89408a4e4b5SAnthony Wilson 89508a4e4b5SAnthony Wilson // Collections don't include the static data added by SubRoute because 89608a4e4b5SAnthony Wilson // it has a duplicate entry for members 89708a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.type"] = 89808a4e4b5SAnthony Wilson "#LogEntryCollection.LogEntryCollection"; 89908a4e4b5SAnthony Wilson asyncResp->res.jsonValue["@odata.id"] = 90008a4e4b5SAnthony Wilson "/redfish/v1/Systems/system/LogServices/EventLog/Entries"; 90108a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Name"] = "System Event Log Entries"; 90208a4e4b5SAnthony Wilson asyncResp->res.jsonValue["Description"] = 90308a4e4b5SAnthony Wilson "Collection of System Event Log Entries"; 90408a4e4b5SAnthony Wilson 905cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 906cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 907cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 908cb92c03bSAndrew Geissler [asyncResp](const boost::system::error_code ec, 909cb92c03bSAndrew Geissler GetManagedObjectsType &resp) { 910cb92c03bSAndrew Geissler if (ec) 911cb92c03bSAndrew Geissler { 912cb92c03bSAndrew Geissler // TODO Handle for specific error code 913cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 914cb92c03bSAndrew Geissler << "getLogEntriesIfaceData resp_handler got error " 915cb92c03bSAndrew Geissler << ec; 916cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 917cb92c03bSAndrew Geissler return; 918cb92c03bSAndrew Geissler } 919cb92c03bSAndrew Geissler nlohmann::json &entriesArray = 920cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members"]; 921cb92c03bSAndrew Geissler entriesArray = nlohmann::json::array(); 922cb92c03bSAndrew Geissler for (auto &objectPath : resp) 923cb92c03bSAndrew Geissler { 924cb92c03bSAndrew Geissler for (auto &interfaceMap : objectPath.second) 925cb92c03bSAndrew Geissler { 926cb92c03bSAndrew Geissler if (interfaceMap.first != 927cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry") 928cb92c03bSAndrew Geissler { 929cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Bailing early on " 930cb92c03bSAndrew Geissler << interfaceMap.first; 931cb92c03bSAndrew Geissler continue; 932cb92c03bSAndrew Geissler } 933cb92c03bSAndrew Geissler entriesArray.push_back({}); 934cb92c03bSAndrew Geissler nlohmann::json &thisEntry = entriesArray.back(); 93566664f25SEd Tanous uint32_t *id = nullptr; 93666664f25SEd Tanous std::time_t timestamp{}; 93766664f25SEd Tanous std::string *severity = nullptr; 93866664f25SEd Tanous std::string *message = nullptr; 939cb92c03bSAndrew Geissler for (auto &propertyMap : interfaceMap.second) 940cb92c03bSAndrew Geissler { 941cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 942cb92c03bSAndrew Geissler { 943cb92c03bSAndrew Geissler id = sdbusplus::message::variant_ns::get_if< 944cb92c03bSAndrew Geissler uint32_t>(&propertyMap.second); 945cb92c03bSAndrew Geissler if (id == nullptr) 946cb92c03bSAndrew Geissler { 947cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 948cb92c03bSAndrew Geissler "Id"); 949cb92c03bSAndrew Geissler } 950cb92c03bSAndrew Geissler } 951cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 952cb92c03bSAndrew Geissler { 953cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 954cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 955cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 956cb92c03bSAndrew Geissler { 957cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 958cb92c03bSAndrew Geissler "Timestamp"); 959271584abSEd Tanous continue; 960cb92c03bSAndrew Geissler } 961cb92c03bSAndrew Geissler // Retrieve Created property with format: 962cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 963cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 964cb92c03bSAndrew Geissler *millisTimeStamp); 965271584abSEd Tanous timestamp = std::chrono::duration_cast< 966271584abSEd Tanous std::chrono::duration<int>>( 967271584abSEd Tanous chronoTimeStamp) 968cb92c03bSAndrew Geissler .count(); 969cb92c03bSAndrew Geissler } 970cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 971cb92c03bSAndrew Geissler { 972cb92c03bSAndrew Geissler severity = std::get_if<std::string>( 973cb92c03bSAndrew Geissler &propertyMap.second); 974cb92c03bSAndrew Geissler if (severity == nullptr) 975cb92c03bSAndrew Geissler { 976cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 977cb92c03bSAndrew Geissler "Severity"); 978cb92c03bSAndrew Geissler } 979cb92c03bSAndrew Geissler } 980cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 981cb92c03bSAndrew Geissler { 982cb92c03bSAndrew Geissler message = std::get_if<std::string>( 983cb92c03bSAndrew Geissler &propertyMap.second); 984cb92c03bSAndrew Geissler if (message == nullptr) 985cb92c03bSAndrew Geissler { 986cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 987cb92c03bSAndrew Geissler "Message"); 988cb92c03bSAndrew Geissler } 989cb92c03bSAndrew Geissler } 990cb92c03bSAndrew Geissler } 991cb92c03bSAndrew Geissler thisEntry = { 992cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 993cb92c03bSAndrew Geissler {"@odata.id", 994cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 995cb92c03bSAndrew Geissler "Entries/" + 996cb92c03bSAndrew Geissler std::to_string(*id)}, 99727062605SAnthony Wilson {"Name", "System Event Log Entry"}, 998cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 999cb92c03bSAndrew Geissler {"Message", *message}, 1000cb92c03bSAndrew Geissler {"EntryType", "Event"}, 1001cb92c03bSAndrew Geissler {"Severity", 1002cb92c03bSAndrew Geissler translateSeverityDbusToRedfish(*severity)}, 1003cb92c03bSAndrew Geissler {"Created", crow::utility::getDateTime(timestamp)}}; 1004cb92c03bSAndrew Geissler } 1005cb92c03bSAndrew Geissler } 1006cb92c03bSAndrew Geissler std::sort(entriesArray.begin(), entriesArray.end(), 1007cb92c03bSAndrew Geissler [](const nlohmann::json &left, 1008cb92c03bSAndrew Geissler const nlohmann::json &right) { 1009cb92c03bSAndrew Geissler return (left["Id"] <= right["Id"]); 1010cb92c03bSAndrew Geissler }); 1011cb92c03bSAndrew Geissler asyncResp->res.jsonValue["Members@odata.count"] = 1012cb92c03bSAndrew Geissler entriesArray.size(); 1013cb92c03bSAndrew Geissler }, 1014cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging", 1015cb92c03bSAndrew Geissler "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 1016c4bf6374SJason M. Bills } 1017c4bf6374SJason M. Bills }; 1018c4bf6374SJason M. Bills 101908a4e4b5SAnthony Wilson class DBusEventLogEntry : public Node 1020c4bf6374SJason M. Bills { 1021c4bf6374SJason M. Bills public: 102208a4e4b5SAnthony Wilson DBusEventLogEntry(CrowApp &app) : 1023c4bf6374SJason M. Bills Node(app, 1024029573d4SEd Tanous "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/", 1025029573d4SEd Tanous std::string()) 1026c4bf6374SJason M. Bills { 1027c4bf6374SJason M. Bills entityPrivileges = { 1028c4bf6374SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1029c4bf6374SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1030c4bf6374SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1031c4bf6374SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1032c4bf6374SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1033c4bf6374SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1034c4bf6374SJason M. Bills } 1035c4bf6374SJason M. Bills 1036c4bf6374SJason M. Bills private: 1037c4bf6374SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1038c4bf6374SJason M. Bills const std::vector<std::string> ¶ms) override 1039c4bf6374SJason M. Bills { 1040c4bf6374SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1041029573d4SEd Tanous if (params.size() != 1) 1042c4bf6374SJason M. Bills { 1043c4bf6374SJason M. Bills messages::internalError(asyncResp->res); 1044c4bf6374SJason M. Bills return; 1045c4bf6374SJason M. Bills } 1046029573d4SEd Tanous const std::string &entryID = params[0]; 1047cb92c03bSAndrew Geissler 1048cb92c03bSAndrew Geissler // DBus implementation of EventLog/Entries 1049cb92c03bSAndrew Geissler // Make call to Logging Service to find all log entry objects 1050cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 1051cb92c03bSAndrew Geissler [asyncResp, entryID](const boost::system::error_code ec, 1052cb92c03bSAndrew Geissler GetManagedPropertyType &resp) { 1053cb92c03bSAndrew Geissler if (ec) 1054cb92c03bSAndrew Geissler { 1055cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR 1056cb92c03bSAndrew Geissler << "EventLogEntry (DBus) resp_handler got error " << ec; 1057cb92c03bSAndrew Geissler messages::internalError(asyncResp->res); 1058cb92c03bSAndrew Geissler return; 1059cb92c03bSAndrew Geissler } 106066664f25SEd Tanous uint32_t *id = nullptr; 106166664f25SEd Tanous std::time_t timestamp{}; 106266664f25SEd Tanous std::string *severity = nullptr; 106366664f25SEd Tanous std::string *message = nullptr; 1064cb92c03bSAndrew Geissler for (auto &propertyMap : resp) 1065cb92c03bSAndrew Geissler { 1066cb92c03bSAndrew Geissler if (propertyMap.first == "Id") 1067cb92c03bSAndrew Geissler { 1068cb92c03bSAndrew Geissler id = std::get_if<uint32_t>(&propertyMap.second); 1069cb92c03bSAndrew Geissler if (id == nullptr) 1070cb92c03bSAndrew Geissler { 1071cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, "Id"); 1072cb92c03bSAndrew Geissler } 1073cb92c03bSAndrew Geissler } 1074cb92c03bSAndrew Geissler else if (propertyMap.first == "Timestamp") 1075cb92c03bSAndrew Geissler { 1076cb92c03bSAndrew Geissler const uint64_t *millisTimeStamp = 1077cb92c03bSAndrew Geissler std::get_if<uint64_t>(&propertyMap.second); 1078cb92c03bSAndrew Geissler if (millisTimeStamp == nullptr) 1079cb92c03bSAndrew Geissler { 1080cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1081cb92c03bSAndrew Geissler "Timestamp"); 1082271584abSEd Tanous continue; 1083cb92c03bSAndrew Geissler } 1084cb92c03bSAndrew Geissler // Retrieve Created property with format: 1085cb92c03bSAndrew Geissler // yyyy-mm-ddThh:mm:ss 1086cb92c03bSAndrew Geissler std::chrono::milliseconds chronoTimeStamp( 1087cb92c03bSAndrew Geissler *millisTimeStamp); 1088cb92c03bSAndrew Geissler timestamp = 1089271584abSEd Tanous std::chrono::duration_cast< 1090271584abSEd Tanous std::chrono::duration<int>>(chronoTimeStamp) 1091cb92c03bSAndrew Geissler .count(); 1092cb92c03bSAndrew Geissler } 1093cb92c03bSAndrew Geissler else if (propertyMap.first == "Severity") 1094cb92c03bSAndrew Geissler { 1095cb92c03bSAndrew Geissler severity = 1096cb92c03bSAndrew Geissler std::get_if<std::string>(&propertyMap.second); 1097cb92c03bSAndrew Geissler if (severity == nullptr) 1098cb92c03bSAndrew Geissler { 1099cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1100cb92c03bSAndrew Geissler "Severity"); 1101cb92c03bSAndrew Geissler } 1102cb92c03bSAndrew Geissler } 1103cb92c03bSAndrew Geissler else if (propertyMap.first == "Message") 1104cb92c03bSAndrew Geissler { 1105cb92c03bSAndrew Geissler message = std::get_if<std::string>(&propertyMap.second); 1106cb92c03bSAndrew Geissler if (message == nullptr) 1107cb92c03bSAndrew Geissler { 1108cb92c03bSAndrew Geissler messages::propertyMissing(asyncResp->res, 1109cb92c03bSAndrew Geissler "Message"); 1110cb92c03bSAndrew Geissler } 1111cb92c03bSAndrew Geissler } 1112cb92c03bSAndrew Geissler } 1113271584abSEd Tanous if (id == nullptr || message == nullptr || severity == nullptr) 1114271584abSEd Tanous { 1115271584abSEd Tanous return; 1116271584abSEd Tanous } 1117cb92c03bSAndrew Geissler asyncResp->res.jsonValue = { 1118cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1119cb92c03bSAndrew Geissler {"@odata.id", 1120cb92c03bSAndrew Geissler "/redfish/v1/Systems/system/LogServices/EventLog/" 1121cb92c03bSAndrew Geissler "Entries/" + 1122cb92c03bSAndrew Geissler std::to_string(*id)}, 112327062605SAnthony Wilson {"Name", "System Event Log Entry"}, 1124cb92c03bSAndrew Geissler {"Id", std::to_string(*id)}, 1125cb92c03bSAndrew Geissler {"Message", *message}, 1126cb92c03bSAndrew Geissler {"EntryType", "Event"}, 1127cb92c03bSAndrew Geissler {"Severity", translateSeverityDbusToRedfish(*severity)}, 112808a4e4b5SAnthony Wilson {"Created", crow::utility::getDateTime(timestamp)}}; 1129cb92c03bSAndrew Geissler }, 1130cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging", 1131cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging/entry/" + entryID, 1132cb92c03bSAndrew Geissler "org.freedesktop.DBus.Properties", "GetAll", 1133cb92c03bSAndrew Geissler "xyz.openbmc_project.Logging.Entry"); 1134c4bf6374SJason M. Bills } 1135336e96c6SChicago Duan 1136336e96c6SChicago Duan void doDelete(crow::Response &res, const crow::Request &req, 1137336e96c6SChicago Duan const std::vector<std::string> ¶ms) override 1138336e96c6SChicago Duan { 1139336e96c6SChicago Duan 1140336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "Do delete single event entries."; 1141336e96c6SChicago Duan 1142336e96c6SChicago Duan auto asyncResp = std::make_shared<AsyncResp>(res); 1143336e96c6SChicago Duan 1144336e96c6SChicago Duan if (params.size() != 1) 1145336e96c6SChicago Duan { 1146336e96c6SChicago Duan messages::internalError(asyncResp->res); 1147336e96c6SChicago Duan return; 1148336e96c6SChicago Duan } 1149336e96c6SChicago Duan std::string entryID = params[0]; 1150336e96c6SChicago Duan 1151336e96c6SChicago Duan dbus::utility::escapePathForDbus(entryID); 1152336e96c6SChicago Duan 1153336e96c6SChicago Duan // Process response from Logging service. 1154336e96c6SChicago Duan auto respHandler = [asyncResp](const boost::system::error_code ec) { 1155336e96c6SChicago Duan BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done"; 1156336e96c6SChicago Duan if (ec) 1157336e96c6SChicago Duan { 1158336e96c6SChicago Duan // TODO Handle for specific error code 1159336e96c6SChicago Duan BMCWEB_LOG_ERROR 1160336e96c6SChicago Duan << "EventLogEntry (DBus) doDelete respHandler got error " 1161336e96c6SChicago Duan << ec; 1162336e96c6SChicago Duan asyncResp->res.result( 1163336e96c6SChicago Duan boost::beast::http::status::internal_server_error); 1164336e96c6SChicago Duan return; 1165336e96c6SChicago Duan } 1166336e96c6SChicago Duan 1167336e96c6SChicago Duan asyncResp->res.result(boost::beast::http::status::ok); 1168336e96c6SChicago Duan }; 1169336e96c6SChicago Duan 1170336e96c6SChicago Duan // Make call to Logging service to request Delete Log 1171336e96c6SChicago Duan crow::connections::systemBus->async_method_call( 1172336e96c6SChicago Duan respHandler, "xyz.openbmc_project.Logging", 1173336e96c6SChicago Duan "/xyz/openbmc_project/logging/entry/" + entryID, 1174336e96c6SChicago Duan "xyz.openbmc_project.Object.Delete", "Delete"); 1175336e96c6SChicago Duan } 1176c4bf6374SJason M. Bills }; 1177c4bf6374SJason M. Bills 1178c4bf6374SJason M. Bills class BMCLogServiceCollection : public Node 1179c4bf6374SJason M. Bills { 1180c4bf6374SJason M. Bills public: 1181c4bf6374SJason M. Bills template <typename CrowApp> 1182c4bf6374SJason M. Bills BMCLogServiceCollection(CrowApp &app) : 11834ed77cd5SEd Tanous Node(app, "/redfish/v1/Managers/bmc/LogServices/") 11841da66f75SEd Tanous { 11851da66f75SEd Tanous entityPrivileges = { 1186e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1187e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1188e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1189e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1190e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1191e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 11921da66f75SEd Tanous } 11931da66f75SEd Tanous 11941da66f75SEd Tanous private: 11951da66f75SEd Tanous /** 11961da66f75SEd Tanous * Functions triggers appropriate requests on DBus 11971da66f75SEd Tanous */ 11981da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 11991da66f75SEd Tanous const std::vector<std::string> ¶ms) override 12001da66f75SEd Tanous { 1201e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 12021da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 12031da66f75SEd Tanous // it has a duplicate entry for members 1204e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 12051da66f75SEd Tanous "#LogServiceCollection.LogServiceCollection"; 1206e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1207e1f26343SJason M. Bills "/redfish/v1/Managers/bmc/LogServices"; 1208e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection"; 1209e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 12101da66f75SEd Tanous "Collection of LogServices for this Manager"; 1211c4bf6374SJason M. Bills nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"]; 1212c4bf6374SJason M. Bills logServiceArray = nlohmann::json::array(); 1213c4bf6374SJason M. Bills #ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL 1214c4bf6374SJason M. Bills logServiceArray.push_back( 121508a4e4b5SAnthony Wilson {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}}); 1216c4bf6374SJason M. Bills #endif 1217e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1218c4bf6374SJason M. Bills logServiceArray.size(); 12191da66f75SEd Tanous } 12201da66f75SEd Tanous }; 12211da66f75SEd Tanous 1222c4bf6374SJason M. Bills class BMCJournalLogService : public Node 12231da66f75SEd Tanous { 12241da66f75SEd Tanous public: 12251da66f75SEd Tanous template <typename CrowApp> 1226c4bf6374SJason M. Bills BMCJournalLogService(CrowApp &app) : 1227c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/") 1228e1f26343SJason M. Bills { 1229e1f26343SJason M. Bills entityPrivileges = { 1230e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1231e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1232e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1233e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1234e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1235e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1236e1f26343SJason M. Bills } 1237e1f26343SJason M. Bills 1238e1f26343SJason M. Bills private: 1239e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1240e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1241e1f26343SJason M. Bills { 1242e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1243e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1244e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 12450f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 12460f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal"; 1247c4bf6374SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service"; 1248c4bf6374SJason M. Bills asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service"; 1249c4bf6374SJason M. Bills asyncResp->res.jsonValue["Id"] = "BMC Journal"; 1250e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1251cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1252cd50aa42SJason M. Bills {"@odata.id", 1253086be238SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}}; 1254e1f26343SJason M. Bills } 1255e1f26343SJason M. Bills }; 1256e1f26343SJason M. Bills 1257c4bf6374SJason M. Bills static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID, 1258e1f26343SJason M. Bills sd_journal *journal, 1259c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntryJson) 1260e1f26343SJason M. Bills { 1261e1f26343SJason M. Bills // Get the Log Entry contents 1262e1f26343SJason M. Bills int ret = 0; 1263e1f26343SJason M. Bills 126439e77504SEd Tanous std::string_view msg; 126516428a1aSJason M. Bills ret = getJournalMetadata(journal, "MESSAGE", msg); 1266e1f26343SJason M. Bills if (ret < 0) 1267e1f26343SJason M. Bills { 1268e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret); 1269e1f26343SJason M. Bills return 1; 1270e1f26343SJason M. Bills } 1271e1f26343SJason M. Bills 1272e1f26343SJason M. Bills // Get the severity from the PRIORITY field 1273271584abSEd Tanous long int severity = 8; // Default to an invalid priority 127416428a1aSJason M. Bills ret = getJournalMetadata(journal, "PRIORITY", 10, severity); 1275e1f26343SJason M. Bills if (ret < 0) 1276e1f26343SJason M. Bills { 1277e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret); 1278e1f26343SJason M. Bills } 1279e1f26343SJason M. Bills 1280e1f26343SJason M. Bills // Get the Created time from the timestamp 128116428a1aSJason M. Bills std::string entryTimeStr; 128216428a1aSJason M. Bills if (!getEntryTimestamp(journal, entryTimeStr)) 1283e1f26343SJason M. Bills { 128416428a1aSJason M. Bills return 1; 1285e1f26343SJason M. Bills } 1286e1f26343SJason M. Bills 1287e1f26343SJason M. Bills // Fill in the log entry with the gathered data 1288c4bf6374SJason M. Bills bmcJournalLogEntryJson = { 1289cb92c03bSAndrew Geissler {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1290c4bf6374SJason M. Bills {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" + 1291c4bf6374SJason M. Bills bmcJournalLogEntryID}, 1292e1f26343SJason M. Bills {"Name", "BMC Journal Entry"}, 1293c4bf6374SJason M. Bills {"Id", bmcJournalLogEntryID}, 129416428a1aSJason M. Bills {"Message", msg}, 1295e1f26343SJason M. Bills {"EntryType", "Oem"}, 1296e1f26343SJason M. Bills {"Severity", 1297b6a61a5eSJason M. Bills severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"}, 1298086be238SEd Tanous {"OemRecordFormat", "BMC Journal Entry"}, 1299e1f26343SJason M. Bills {"Created", std::move(entryTimeStr)}}; 1300e1f26343SJason M. Bills return 0; 1301e1f26343SJason M. Bills } 1302e1f26343SJason M. Bills 1303c4bf6374SJason M. Bills class BMCJournalLogEntryCollection : public Node 1304e1f26343SJason M. Bills { 1305e1f26343SJason M. Bills public: 1306e1f26343SJason M. Bills template <typename CrowApp> 1307c4bf6374SJason M. Bills BMCJournalLogEntryCollection(CrowApp &app) : 1308c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/") 1309e1f26343SJason M. Bills { 1310e1f26343SJason M. Bills entityPrivileges = { 1311e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1312e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1313e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1314e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1315e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1316e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1317e1f26343SJason M. Bills } 1318e1f26343SJason M. Bills 1319e1f26343SJason M. Bills private: 1320e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1321e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1322e1f26343SJason M. Bills { 1323e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1324193ad2faSJason M. Bills static constexpr const long maxEntriesPerPage = 1000; 1325271584abSEd Tanous uint64_t skip = 0; 1326271584abSEd Tanous uint64_t top = maxEntriesPerPage; // Show max entries by default 132716428a1aSJason M. Bills if (!getSkipParam(asyncResp->res, req, skip)) 1328193ad2faSJason M. Bills { 1329193ad2faSJason M. Bills return; 1330193ad2faSJason M. Bills } 133116428a1aSJason M. Bills if (!getTopParam(asyncResp->res, req, top)) 1332193ad2faSJason M. Bills { 1333193ad2faSJason M. Bills return; 1334193ad2faSJason M. Bills } 1335e1f26343SJason M. Bills // Collections don't include the static data added by SubRoute because 1336e1f26343SJason M. Bills // it has a duplicate entry for members 1337e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1338e1f26343SJason M. Bills "#LogEntryCollection.LogEntryCollection"; 13390f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13400f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1341e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.id"] = 1342c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"; 1343e1f26343SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries"; 1344e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1345e1f26343SJason M. Bills "Collection of BMC Journal Entries"; 13460f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 13470f74e643SEd Tanous "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries"; 1348e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1349e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1350e1f26343SJason M. Bills 1351e1f26343SJason M. Bills // Go through the journal and use the timestamp to create a unique ID 1352e1f26343SJason M. Bills // for each entry 1353e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1354e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1355e1f26343SJason M. Bills if (ret < 0) 1356e1f26343SJason M. Bills { 1357e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1358f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1359e1f26343SJason M. Bills return; 1360e1f26343SJason M. Bills } 1361e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1362e1f26343SJason M. Bills journalTmp, sd_journal_close); 1363e1f26343SJason M. Bills journalTmp = nullptr; 1364b01bf299SEd Tanous uint64_t entryCount = 0; 1365e85d6b16SJason M. Bills // Reset the unique ID on the first entry 1366e85d6b16SJason M. Bills bool firstEntry = true; 1367e1f26343SJason M. Bills SD_JOURNAL_FOREACH(journal.get()) 1368e1f26343SJason M. Bills { 1369193ad2faSJason M. Bills entryCount++; 1370193ad2faSJason M. Bills // Handle paging using skip (number of entries to skip from the 1371193ad2faSJason M. Bills // start) and top (number of entries to display) 1372193ad2faSJason M. Bills if (entryCount <= skip || entryCount > skip + top) 1373193ad2faSJason M. Bills { 1374193ad2faSJason M. Bills continue; 1375193ad2faSJason M. Bills } 1376193ad2faSJason M. Bills 137716428a1aSJason M. Bills std::string idStr; 1378e85d6b16SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1379e1f26343SJason M. Bills { 1380e1f26343SJason M. Bills continue; 1381e1f26343SJason M. Bills } 1382e1f26343SJason M. Bills 1383e85d6b16SJason M. Bills if (firstEntry) 1384e85d6b16SJason M. Bills { 1385e85d6b16SJason M. Bills firstEntry = false; 1386e85d6b16SJason M. Bills } 1387e85d6b16SJason M. Bills 1388e1f26343SJason M. Bills logEntryArray.push_back({}); 1389c4bf6374SJason M. Bills nlohmann::json &bmcJournalLogEntry = logEntryArray.back(); 1390c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(idStr, journal.get(), 1391c4bf6374SJason M. Bills bmcJournalLogEntry) != 0) 1392e1f26343SJason M. Bills { 1393f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1394e1f26343SJason M. Bills return; 1395e1f26343SJason M. Bills } 1396e1f26343SJason M. Bills } 1397193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = entryCount; 1398193ad2faSJason M. Bills if (skip + top < entryCount) 1399193ad2faSJason M. Bills { 1400193ad2faSJason M. Bills asyncResp->res.jsonValue["Members@odata.nextLink"] = 1401c4bf6374SJason M. Bills "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" + 1402193ad2faSJason M. Bills std::to_string(skip + top); 1403193ad2faSJason M. Bills } 1404e1f26343SJason M. Bills } 1405e1f26343SJason M. Bills }; 1406e1f26343SJason M. Bills 1407c4bf6374SJason M. Bills class BMCJournalLogEntry : public Node 1408e1f26343SJason M. Bills { 1409e1f26343SJason M. Bills public: 1410c4bf6374SJason M. Bills BMCJournalLogEntry(CrowApp &app) : 1411c4bf6374SJason M. Bills Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/", 1412e1f26343SJason M. Bills std::string()) 1413e1f26343SJason M. Bills { 1414e1f26343SJason M. Bills entityPrivileges = { 1415e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1416e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1417e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1418e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1419e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1420e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1421e1f26343SJason M. Bills } 1422e1f26343SJason M. Bills 1423e1f26343SJason M. Bills private: 1424e1f26343SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1425e1f26343SJason M. Bills const std::vector<std::string> ¶ms) override 1426e1f26343SJason M. Bills { 1427e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1428e1f26343SJason M. Bills if (params.size() != 1) 1429e1f26343SJason M. Bills { 1430f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1431e1f26343SJason M. Bills return; 1432e1f26343SJason M. Bills } 143316428a1aSJason M. Bills const std::string &entryID = params[0]; 1434e1f26343SJason M. Bills // Convert the unique ID back to a timestamp to find the entry 1435e1f26343SJason M. Bills uint64_t ts = 0; 1436271584abSEd Tanous uint64_t index = 0; 143716428a1aSJason M. Bills if (!getTimestampFromID(asyncResp->res, entryID, ts, index)) 1438e1f26343SJason M. Bills { 143916428a1aSJason M. Bills return; 1440e1f26343SJason M. Bills } 1441e1f26343SJason M. Bills 1442e1f26343SJason M. Bills sd_journal *journalTmp = nullptr; 1443e1f26343SJason M. Bills int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY); 1444e1f26343SJason M. Bills if (ret < 0) 1445e1f26343SJason M. Bills { 1446e1f26343SJason M. Bills BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret); 1447f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1448e1f26343SJason M. Bills return; 1449e1f26343SJason M. Bills } 1450e1f26343SJason M. Bills std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal( 1451e1f26343SJason M. Bills journalTmp, sd_journal_close); 1452e1f26343SJason M. Bills journalTmp = nullptr; 1453e1f26343SJason M. Bills // Go to the timestamp in the log and move to the entry at the index 1454af07e3f5SJason M. Bills // tracking the unique ID 1455af07e3f5SJason M. Bills std::string idStr; 1456af07e3f5SJason M. Bills bool firstEntry = true; 1457e1f26343SJason M. Bills ret = sd_journal_seek_realtime_usec(journal.get(), ts); 1458271584abSEd Tanous for (uint64_t i = 0; i <= index; i++) 1459e1f26343SJason M. Bills { 1460e1f26343SJason M. Bills sd_journal_next(journal.get()); 1461af07e3f5SJason M. Bills if (!getUniqueEntryID(journal.get(), idStr, firstEntry)) 1462af07e3f5SJason M. Bills { 1463af07e3f5SJason M. Bills messages::internalError(asyncResp->res); 1464af07e3f5SJason M. Bills return; 1465af07e3f5SJason M. Bills } 1466af07e3f5SJason M. Bills if (firstEntry) 1467af07e3f5SJason M. Bills { 1468af07e3f5SJason M. Bills firstEntry = false; 1469af07e3f5SJason M. Bills } 1470e1f26343SJason M. Bills } 1471c4bf6374SJason M. Bills // Confirm that the entry ID matches what was requested 1472af07e3f5SJason M. Bills if (idStr != entryID) 1473c4bf6374SJason M. Bills { 1474c4bf6374SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, entryID); 1475c4bf6374SJason M. Bills return; 1476c4bf6374SJason M. Bills } 1477c4bf6374SJason M. Bills 1478c4bf6374SJason M. Bills if (fillBMCJournalLogEntryJson(entryID, journal.get(), 1479e1f26343SJason M. Bills asyncResp->res.jsonValue) != 0) 1480e1f26343SJason M. Bills { 1481f12894f8SJason M. Bills messages::internalError(asyncResp->res); 1482e1f26343SJason M. Bills return; 1483e1f26343SJason M. Bills } 1484e1f26343SJason M. Bills } 1485e1f26343SJason M. Bills }; 1486e1f26343SJason M. Bills 1487424c4176SJason M. Bills class CrashdumpService : public Node 1488e1f26343SJason M. Bills { 1489e1f26343SJason M. Bills public: 1490e1f26343SJason M. Bills template <typename CrowApp> 1491424c4176SJason M. Bills CrashdumpService(CrowApp &app) : 1492424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/") 14931da66f75SEd Tanous { 14941da66f75SEd Tanous entityPrivileges = { 1495e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1496e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1497e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1498e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1499e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1500e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 15011da66f75SEd Tanous } 15021da66f75SEd Tanous 15031da66f75SEd Tanous private: 15041da66f75SEd Tanous /** 15051da66f75SEd Tanous * Functions triggers appropriate requests on DBus 15061da66f75SEd Tanous */ 15071da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 15081da66f75SEd Tanous const std::vector<std::string> ¶ms) override 15091da66f75SEd Tanous { 1510e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 15111da66f75SEd Tanous // Copy over the static data to include the entries added by SubRoute 15120f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1513424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump"; 1514e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 1515e1f26343SJason M. Bills "#LogService.v1_1_0.LogService"; 15164f50ae4bSGunnar Mills asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service"; 15174f50ae4bSGunnar Mills asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service"; 15184f50ae4bSGunnar Mills asyncResp->res.jsonValue["Id"] = "Oem Crashdump"; 1519e1f26343SJason M. Bills asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull"; 1520e1f26343SJason M. Bills asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3; 1521cd50aa42SJason M. Bills asyncResp->res.jsonValue["Entries"] = { 1522cd50aa42SJason M. Bills {"@odata.id", 1523424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}}; 1524e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"] = { 15255b61b5e8SJason M. Bills {"#LogService.ClearLog", 15265b61b5e8SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 15275b61b5e8SJason M. Bills "Actions/LogService.ClearLog"}}}, 15281da66f75SEd Tanous {"Oem", 1529424c4176SJason M. Bills {{"#Crashdump.OnDemand", 1530424c4176SJason M. Bills {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1531424c4176SJason M. Bills "Actions/Oem/Crashdump.OnDemand"}}}}}}; 15321da66f75SEd Tanous 15331da66f75SEd Tanous #ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI 1534e1f26343SJason M. Bills asyncResp->res.jsonValue["Actions"]["Oem"].push_back( 1535424c4176SJason M. Bills {"#Crashdump.SendRawPeci", 153608a4e4b5SAnthony Wilson {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/" 1537424c4176SJason M. Bills "Actions/Oem/Crashdump.SendRawPeci"}}}); 15381da66f75SEd Tanous #endif 15391da66f75SEd Tanous } 15401da66f75SEd Tanous }; 15411da66f75SEd Tanous 15425b61b5e8SJason M. Bills class CrashdumpClear : public Node 15435b61b5e8SJason M. Bills { 15445b61b5e8SJason M. Bills public: 15455b61b5e8SJason M. Bills CrashdumpClear(CrowApp &app) : 15465b61b5e8SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/" 15475b61b5e8SJason M. Bills "LogService.ClearLog/") 15485b61b5e8SJason M. Bills { 15495b61b5e8SJason M. Bills entityPrivileges = { 15505b61b5e8SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 15515b61b5e8SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 15525b61b5e8SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 15535b61b5e8SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 15545b61b5e8SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 15555b61b5e8SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 15565b61b5e8SJason M. Bills } 15575b61b5e8SJason M. Bills 15585b61b5e8SJason M. Bills private: 15595b61b5e8SJason M. Bills void doPost(crow::Response &res, const crow::Request &req, 15605b61b5e8SJason M. Bills const std::vector<std::string> ¶ms) override 15615b61b5e8SJason M. Bills { 15625b61b5e8SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 15635b61b5e8SJason M. Bills 15645b61b5e8SJason M. Bills crow::connections::systemBus->async_method_call( 15655b61b5e8SJason M. Bills [asyncResp](const boost::system::error_code ec, 15665b61b5e8SJason M. Bills const std::string &resp) { 15675b61b5e8SJason M. Bills if (ec) 15685b61b5e8SJason M. Bills { 15695b61b5e8SJason M. Bills messages::internalError(asyncResp->res); 15705b61b5e8SJason M. Bills return; 15715b61b5e8SJason M. Bills } 15725b61b5e8SJason M. Bills messages::success(asyncResp->res); 15735b61b5e8SJason M. Bills }, 15745b61b5e8SJason M. Bills crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll"); 15755b61b5e8SJason M. Bills } 15765b61b5e8SJason M. Bills }; 15775b61b5e8SJason M. Bills 1578e855dd28SJason M. Bills std::string getLogCreatedTime(const std::string &crashdump) 1579e855dd28SJason M. Bills { 1580e855dd28SJason M. Bills nlohmann::json crashdumpJson = 1581e855dd28SJason M. Bills nlohmann::json::parse(crashdump, nullptr, false); 1582e855dd28SJason M. Bills if (crashdumpJson.is_discarded()) 1583e855dd28SJason M. Bills { 1584e855dd28SJason M. Bills return std::string(); 1585e855dd28SJason M. Bills } 1586e855dd28SJason M. Bills 1587e855dd28SJason M. Bills nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data"); 1588e855dd28SJason M. Bills if (cdIt == crashdumpJson.end()) 1589e855dd28SJason M. Bills { 1590e855dd28SJason M. Bills return std::string(); 1591e855dd28SJason M. Bills } 1592e855dd28SJason M. Bills 1593e855dd28SJason M. Bills nlohmann::json::const_iterator siIt = cdIt->find("METADATA"); 1594e855dd28SJason M. Bills if (siIt == cdIt->end()) 1595e855dd28SJason M. Bills { 1596e855dd28SJason M. Bills return std::string(); 1597e855dd28SJason M. Bills } 1598e855dd28SJason M. Bills 1599e855dd28SJason M. Bills nlohmann::json::const_iterator tsIt = siIt->find("timestamp"); 1600e855dd28SJason M. Bills if (tsIt == siIt->end()) 1601e855dd28SJason M. Bills { 1602e855dd28SJason M. Bills return std::string(); 1603e855dd28SJason M. Bills } 1604e855dd28SJason M. Bills 1605e855dd28SJason M. Bills const std::string *logTime = tsIt->get_ptr<const std::string *>(); 1606e855dd28SJason M. Bills if (logTime == nullptr) 1607e855dd28SJason M. Bills { 1608e855dd28SJason M. Bills return std::string(); 1609e855dd28SJason M. Bills } 1610e855dd28SJason M. Bills 1611e855dd28SJason M. Bills std::string redfishDateTime = *logTime; 1612e855dd28SJason M. Bills if (redfishDateTime.length() > 2) 1613e855dd28SJason M. Bills { 1614e855dd28SJason M. Bills redfishDateTime.insert(redfishDateTime.end() - 2, ':'); 1615e855dd28SJason M. Bills } 1616e855dd28SJason M. Bills 1617e855dd28SJason M. Bills return redfishDateTime; 1618e855dd28SJason M. Bills } 1619e855dd28SJason M. Bills 1620e855dd28SJason M. Bills std::string getLogFileName(const std::string &logTime) 1621e855dd28SJason M. Bills { 1622e855dd28SJason M. Bills // Set the crashdump file name to "crashdump_<logTime>.json" using the 1623e855dd28SJason M. Bills // created time without the timezone info 1624e855dd28SJason M. Bills std::string fileTime = logTime; 1625e855dd28SJason M. Bills size_t plusPos = fileTime.rfind('+'); 1626e855dd28SJason M. Bills if (plusPos != std::string::npos) 1627e855dd28SJason M. Bills { 1628e855dd28SJason M. Bills fileTime.erase(plusPos); 1629e855dd28SJason M. Bills } 1630e855dd28SJason M. Bills return "crashdump_" + fileTime + ".json"; 1631e855dd28SJason M. Bills } 1632e855dd28SJason M. Bills 1633e855dd28SJason M. Bills static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp, 1634e855dd28SJason M. Bills const std::string &logID, 1635e855dd28SJason M. Bills nlohmann::json &logEntryJson) 1636e855dd28SJason M. Bills { 1637e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, &logEntryJson]( 1638e855dd28SJason M. Bills const boost::system::error_code ec, 1639e855dd28SJason M. Bills const std::variant<std::string> &resp) { 1640e855dd28SJason M. Bills if (ec) 1641e855dd28SJason M. Bills { 1642e855dd28SJason M. Bills BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 16431ddcf01aSJason M. Bills if (ec.value() == 16441ddcf01aSJason M. Bills boost::system::linux_error::bad_request_descriptor) 16451ddcf01aSJason M. Bills { 16461ddcf01aSJason M. Bills messages::resourceNotFound(asyncResp->res, "LogEntry", logID); 16471ddcf01aSJason M. Bills } 16481ddcf01aSJason M. Bills else 16491ddcf01aSJason M. Bills { 1650e855dd28SJason M. Bills messages::internalError(asyncResp->res); 16511ddcf01aSJason M. Bills } 1652e855dd28SJason M. Bills return; 1653e855dd28SJason M. Bills } 1654e855dd28SJason M. Bills const std::string *log = std::get_if<std::string>(&resp); 1655e855dd28SJason M. Bills if (log == nullptr) 1656e855dd28SJason M. Bills { 1657e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1658e855dd28SJason M. Bills return; 1659e855dd28SJason M. Bills } 1660e855dd28SJason M. Bills std::string logTime = getLogCreatedTime(*log); 1661e855dd28SJason M. Bills std::string fileName = getLogFileName(logTime); 1662e855dd28SJason M. Bills 1663e855dd28SJason M. Bills logEntryJson = { 1664e855dd28SJason M. Bills {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 1665e855dd28SJason M. Bills {"@odata.id", 1666e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1667e855dd28SJason M. Bills logID}, 1668e855dd28SJason M. Bills {"Name", "CPU Crashdump"}, 1669e855dd28SJason M. Bills {"Id", logID}, 1670e855dd28SJason M. Bills {"EntryType", "Oem"}, 1671e855dd28SJason M. Bills {"OemRecordFormat", "Crashdump URI"}, 1672e855dd28SJason M. Bills {"Message", 1673e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + 1674e855dd28SJason M. Bills logID + "/" + fileName}, 1675e855dd28SJason M. Bills {"Created", std::move(logTime)}}; 1676e855dd28SJason M. Bills }; 1677e855dd28SJason M. Bills crow::connections::systemBus->async_method_call( 16785b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 16795b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 16805b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log"); 1681e855dd28SJason M. Bills } 1682e855dd28SJason M. Bills 1683424c4176SJason M. Bills class CrashdumpEntryCollection : public Node 16841da66f75SEd Tanous { 16851da66f75SEd Tanous public: 16861da66f75SEd Tanous template <typename CrowApp> 1687424c4176SJason M. Bills CrashdumpEntryCollection(CrowApp &app) : 1688424c4176SJason M. Bills Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/") 16891da66f75SEd Tanous { 16901da66f75SEd Tanous entityPrivileges = { 1691e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1692e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1693e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1694e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1695e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1696e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 16971da66f75SEd Tanous } 16981da66f75SEd Tanous 16991da66f75SEd Tanous private: 17001da66f75SEd Tanous /** 17011da66f75SEd Tanous * Functions triggers appropriate requests on DBus 17021da66f75SEd Tanous */ 17031da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 17041da66f75SEd Tanous const std::vector<std::string> ¶ms) override 17051da66f75SEd Tanous { 1706e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 17071da66f75SEd Tanous // Collections don't include the static data added by SubRoute because 17081da66f75SEd Tanous // it has a duplicate entry for members 1709e1f26343SJason M. Bills auto getLogEntriesCallback = [asyncResp]( 1710e1f26343SJason M. Bills const boost::system::error_code ec, 17111da66f75SEd Tanous const std::vector<std::string> &resp) { 17121da66f75SEd Tanous if (ec) 17131da66f75SEd Tanous { 17141da66f75SEd Tanous if (ec.value() != 17151da66f75SEd Tanous boost::system::errc::no_such_file_or_directory) 17161da66f75SEd Tanous { 17171da66f75SEd Tanous BMCWEB_LOG_DEBUG << "failed to get entries ec: " 17181da66f75SEd Tanous << ec.message(); 1719f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17201da66f75SEd Tanous return; 17211da66f75SEd Tanous } 17221da66f75SEd Tanous } 1723e1f26343SJason M. Bills asyncResp->res.jsonValue["@odata.type"] = 17241da66f75SEd Tanous "#LogEntryCollection.LogEntryCollection"; 17250f74e643SEd Tanous asyncResp->res.jsonValue["@odata.id"] = 1726424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"; 1727424c4176SJason M. Bills asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries"; 1728e1f26343SJason M. Bills asyncResp->res.jsonValue["Description"] = 1729424c4176SJason M. Bills "Collection of Crashdump Entries"; 1730e1f26343SJason M. Bills nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"]; 1731e1f26343SJason M. Bills logEntryArray = nlohmann::json::array(); 1732e855dd28SJason M. Bills std::vector<std::string> logIDs; 1733e855dd28SJason M. Bills // Get the list of log entries and build up an empty array big 1734e855dd28SJason M. Bills // enough to hold them 17351da66f75SEd Tanous for (const std::string &objpath : resp) 17361da66f75SEd Tanous { 1737e855dd28SJason M. Bills // Get the log ID 17384ed77cd5SEd Tanous std::size_t lastPos = objpath.rfind("/"); 1739e855dd28SJason M. Bills if (lastPos == std::string::npos) 17401da66f75SEd Tanous { 1741e855dd28SJason M. Bills continue; 17421da66f75SEd Tanous } 1743e855dd28SJason M. Bills logIDs.emplace_back(objpath.substr(lastPos + 1)); 1744e855dd28SJason M. Bills 1745e855dd28SJason M. Bills // Add a space for the log entry to the array 1746e855dd28SJason M. Bills logEntryArray.push_back({}); 1747e855dd28SJason M. Bills } 1748e855dd28SJason M. Bills // Now go through and set up async calls to fill in the entries 1749e855dd28SJason M. Bills size_t index = 0; 1750e855dd28SJason M. Bills for (const std::string &logID : logIDs) 1751e855dd28SJason M. Bills { 1752e855dd28SJason M. Bills // Add the log entry to the array 1753e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]); 17541da66f75SEd Tanous } 1755e1f26343SJason M. Bills asyncResp->res.jsonValue["Members@odata.count"] = 1756e1f26343SJason M. Bills logEntryArray.size(); 17571da66f75SEd Tanous }; 17581da66f75SEd Tanous crow::connections::systemBus->async_method_call( 17591da66f75SEd Tanous std::move(getLogEntriesCallback), 17601da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", 17611da66f75SEd Tanous "/xyz/openbmc_project/object_mapper", 17621da66f75SEd Tanous "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0, 17635b61b5e8SJason M. Bills std::array<const char *, 1>{crashdumpInterface}); 17641da66f75SEd Tanous } 17651da66f75SEd Tanous }; 17661da66f75SEd Tanous 1767424c4176SJason M. Bills class CrashdumpEntry : public Node 17681da66f75SEd Tanous { 17691da66f75SEd Tanous public: 1770424c4176SJason M. Bills CrashdumpEntry(CrowApp &app) : 1771d53dd41fSJason M. Bills Node(app, 1772424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/", 17731da66f75SEd Tanous std::string()) 17741da66f75SEd Tanous { 17751da66f75SEd Tanous entityPrivileges = { 1776e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1777e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1778e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1779e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1780e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1781e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 17821da66f75SEd Tanous } 17831da66f75SEd Tanous 17841da66f75SEd Tanous private: 17851da66f75SEd Tanous void doGet(crow::Response &res, const crow::Request &req, 17861da66f75SEd Tanous const std::vector<std::string> ¶ms) override 17871da66f75SEd Tanous { 1788e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 17891da66f75SEd Tanous if (params.size() != 1) 17901da66f75SEd Tanous { 1791f12894f8SJason M. Bills messages::internalError(asyncResp->res); 17921da66f75SEd Tanous return; 17931da66f75SEd Tanous } 1794e855dd28SJason M. Bills const std::string &logID = params[0]; 1795e855dd28SJason M. Bills logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue); 1796e855dd28SJason M. Bills } 1797e855dd28SJason M. Bills }; 1798e855dd28SJason M. Bills 1799e855dd28SJason M. Bills class CrashdumpFile : public Node 1800e855dd28SJason M. Bills { 1801e855dd28SJason M. Bills public: 1802e855dd28SJason M. Bills CrashdumpFile(CrowApp &app) : 1803e855dd28SJason M. Bills Node(app, 1804e855dd28SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/" 1805e855dd28SJason M. Bills "<str>/", 1806e855dd28SJason M. Bills std::string(), std::string()) 1807e855dd28SJason M. Bills { 1808e855dd28SJason M. Bills entityPrivileges = { 1809e855dd28SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1810e855dd28SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1811e855dd28SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1812e855dd28SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1813e855dd28SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1814e855dd28SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 1815e855dd28SJason M. Bills } 1816e855dd28SJason M. Bills 1817e855dd28SJason M. Bills private: 1818e855dd28SJason M. Bills void doGet(crow::Response &res, const crow::Request &req, 1819e855dd28SJason M. Bills const std::vector<std::string> ¶ms) override 1820e855dd28SJason M. Bills { 1821e855dd28SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1822e855dd28SJason M. Bills if (params.size() != 2) 1823e855dd28SJason M. Bills { 1824e855dd28SJason M. Bills messages::internalError(asyncResp->res); 1825e855dd28SJason M. Bills return; 1826e855dd28SJason M. Bills } 1827e855dd28SJason M. Bills const std::string &logID = params[0]; 1828e855dd28SJason M. Bills const std::string &fileName = params[1]; 1829e855dd28SJason M. Bills 1830e855dd28SJason M. Bills auto getStoredLogCallback = [asyncResp, logID, fileName]( 1831abf2add6SEd Tanous const boost::system::error_code ec, 1832abf2add6SEd Tanous const std::variant<std::string> &resp) { 18331da66f75SEd Tanous if (ec) 18341da66f75SEd Tanous { 1835abf2add6SEd Tanous BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message(); 1836f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18371da66f75SEd Tanous return; 18381da66f75SEd Tanous } 1839abf2add6SEd Tanous const std::string *log = std::get_if<std::string>(&resp); 18401da66f75SEd Tanous if (log == nullptr) 18411da66f75SEd Tanous { 1842f12894f8SJason M. Bills messages::internalError(asyncResp->res); 18431da66f75SEd Tanous return; 18441da66f75SEd Tanous } 1845e855dd28SJason M. Bills 1846e855dd28SJason M. Bills // Verify the file name parameter is correct 1847e855dd28SJason M. Bills if (fileName != getLogFileName(getLogCreatedTime(*log))) 18481da66f75SEd Tanous { 1849e855dd28SJason M. Bills messages::resourceMissingAtURI(asyncResp->res, fileName); 18501da66f75SEd Tanous return; 18511da66f75SEd Tanous } 1852e855dd28SJason M. Bills 1853e855dd28SJason M. Bills // Configure this to be a file download when accessed from a browser 1854e855dd28SJason M. Bills asyncResp->res.addHeader("Content-Disposition", "attachment"); 1855e855dd28SJason M. Bills asyncResp->res.body() = *log; 18561da66f75SEd Tanous }; 18571da66f75SEd Tanous crow::connections::systemBus->async_method_call( 18585b61b5e8SJason M. Bills std::move(getStoredLogCallback), crashdumpObject, 18595b61b5e8SJason M. Bills crashdumpPath + std::string("/") + logID, 18605b61b5e8SJason M. Bills "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, 1861424c4176SJason M. Bills "Log"); 18621da66f75SEd Tanous } 18631da66f75SEd Tanous }; 18641da66f75SEd Tanous 1865424c4176SJason M. Bills class OnDemandCrashdump : public Node 18661da66f75SEd Tanous { 18671da66f75SEd Tanous public: 1868424c4176SJason M. Bills OnDemandCrashdump(CrowApp &app) : 1869424c4176SJason M. Bills Node(app, 1870424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1871424c4176SJason M. Bills "Crashdump.OnDemand/") 18721da66f75SEd Tanous { 18731da66f75SEd Tanous entityPrivileges = { 1874e1f26343SJason M. Bills {boost::beast::http::verb::get, {{"Login"}}}, 1875e1f26343SJason M. Bills {boost::beast::http::verb::head, {{"Login"}}}, 1876e1f26343SJason M. Bills {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 1877e1f26343SJason M. Bills {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 1878e1f26343SJason M. Bills {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 1879e1f26343SJason M. Bills {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 18801da66f75SEd Tanous } 18811da66f75SEd Tanous 18821da66f75SEd Tanous private: 18831da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 18841da66f75SEd Tanous const std::vector<std::string> ¶ms) override 18851da66f75SEd Tanous { 1886e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 18871da66f75SEd Tanous 1888fe306728SJames Feist auto generateonDemandLogCallback = [asyncResp, 1889fe306728SJames Feist req](const boost::system::error_code 189046229577SJames Feist ec, 18911da66f75SEd Tanous const std::string &resp) { 18921da66f75SEd Tanous if (ec) 18931da66f75SEd Tanous { 189446229577SJames Feist if (ec.value() == boost::system::errc::operation_not_supported) 18951da66f75SEd Tanous { 1896f12894f8SJason M. Bills messages::resourceInStandby(asyncResp->res); 18971da66f75SEd Tanous } 18984363d3b2SJason M. Bills else if (ec.value() == 18994363d3b2SJason M. Bills boost::system::errc::device_or_resource_busy) 19004363d3b2SJason M. Bills { 19014363d3b2SJason M. Bills messages::serviceTemporarilyUnavailable(asyncResp->res, 19024363d3b2SJason M. Bills "60"); 19034363d3b2SJason M. Bills } 19041da66f75SEd Tanous else 19051da66f75SEd Tanous { 1906f12894f8SJason M. Bills messages::internalError(asyncResp->res); 19071da66f75SEd Tanous } 19081da66f75SEd Tanous return; 19091da66f75SEd Tanous } 191046229577SJames Feist std::shared_ptr<task::TaskData> task = task::TaskData::createTask( 191166afe4faSJames Feist [](boost::system::error_code err, sdbusplus::message::message &, 191266afe4faSJames Feist const std::shared_ptr<task::TaskData> &taskData) { 191366afe4faSJames Feist if (!err) 191466afe4faSJames Feist { 191566afe4faSJames Feist taskData->messages.emplace_back(messages::success()); 1916831d6b09SJames Feist taskData->state = "Completed"; 191766afe4faSJames Feist } 191832898ceaSJames Feist return task::completed; 191966afe4faSJames Feist }, 192046229577SJames Feist "type='signal',interface='org.freedesktop.DBus.Properties'," 192146229577SJames Feist "member='PropertiesChanged',arg0namespace='com.intel." 192246229577SJames Feist "crashdump'"); 192346229577SJames Feist task->startTimer(std::chrono::minutes(5)); 192446229577SJames Feist task->populateResp(asyncResp->res); 1925fe306728SJames Feist task->payload.emplace(req); 19261da66f75SEd Tanous }; 19271da66f75SEd Tanous crow::connections::systemBus->async_method_call( 19285b61b5e8SJason M. Bills std::move(generateonDemandLogCallback), crashdumpObject, 19295b61b5e8SJason M. Bills crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog"); 19301da66f75SEd Tanous } 19311da66f75SEd Tanous }; 19321da66f75SEd Tanous 1933e1f26343SJason M. Bills class SendRawPECI : public Node 19341da66f75SEd Tanous { 19351da66f75SEd Tanous public: 1936e1f26343SJason M. Bills SendRawPECI(CrowApp &app) : 1937424c4176SJason M. Bills Node(app, 1938424c4176SJason M. Bills "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/" 1939424c4176SJason M. Bills "Crashdump.SendRawPeci/") 19401da66f75SEd Tanous { 19411da66f75SEd Tanous entityPrivileges = { 19421da66f75SEd Tanous {boost::beast::http::verb::get, {{"ConfigureComponents"}}}, 19431da66f75SEd Tanous {boost::beast::http::verb::head, {{"ConfigureComponents"}}}, 19441da66f75SEd Tanous {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 19451da66f75SEd Tanous {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 19461da66f75SEd Tanous {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 19471da66f75SEd Tanous {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 19481da66f75SEd Tanous } 19491da66f75SEd Tanous 19501da66f75SEd Tanous private: 19511da66f75SEd Tanous void doPost(crow::Response &res, const crow::Request &req, 19521da66f75SEd Tanous const std::vector<std::string> ¶ms) override 19531da66f75SEd Tanous { 1954e1f26343SJason M. Bills std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 19558724c297SKarthick Sundarrajan std::vector<std::vector<uint8_t>> peciCommands; 19568724c297SKarthick Sundarrajan 19578724c297SKarthick Sundarrajan nlohmann::json reqJson = 19588724c297SKarthick Sundarrajan nlohmann::json::parse(req.body, nullptr, false); 19598724c297SKarthick Sundarrajan if (reqJson.find("PECICommands") != reqJson.end()) 19608724c297SKarthick Sundarrajan { 19618724c297SKarthick Sundarrajan if (!json_util::readJson(req, res, "PECICommands", peciCommands)) 19628724c297SKarthick Sundarrajan { 19638724c297SKarthick Sundarrajan return; 19648724c297SKarthick Sundarrajan } 19658724c297SKarthick Sundarrajan uint32_t idx = 0; 19668724c297SKarthick Sundarrajan for (auto const &cmd : peciCommands) 19678724c297SKarthick Sundarrajan { 19688724c297SKarthick Sundarrajan if (cmd.size() < 3) 19698724c297SKarthick Sundarrajan { 19708724c297SKarthick Sundarrajan std::string s("["); 19718724c297SKarthick Sundarrajan for (auto const &val : cmd) 19728724c297SKarthick Sundarrajan { 19738724c297SKarthick Sundarrajan if (val != *cmd.begin()) 19748724c297SKarthick Sundarrajan { 19758724c297SKarthick Sundarrajan s += ","; 19768724c297SKarthick Sundarrajan } 19778724c297SKarthick Sundarrajan s += std::to_string(val); 19788724c297SKarthick Sundarrajan } 19798724c297SKarthick Sundarrajan s += "]"; 19808724c297SKarthick Sundarrajan messages::actionParameterValueFormatError( 19818724c297SKarthick Sundarrajan res, s, "PECICommands[" + std::to_string(idx) + "]", 19828724c297SKarthick Sundarrajan "SendRawPeci"); 19838724c297SKarthick Sundarrajan return; 19848724c297SKarthick Sundarrajan } 19858724c297SKarthick Sundarrajan idx++; 19868724c297SKarthick Sundarrajan } 19878724c297SKarthick Sundarrajan } 19888724c297SKarthick Sundarrajan else 19898724c297SKarthick Sundarrajan { 19908724c297SKarthick Sundarrajan /* This interface is deprecated */ 1991b1556427SEd Tanous uint8_t clientAddress = 0; 1992b1556427SEd Tanous uint8_t readLength = 0; 19931da66f75SEd Tanous std::vector<uint8_t> peciCommand; 1994b1556427SEd Tanous if (!json_util::readJson(req, res, "ClientAddress", clientAddress, 1995b1556427SEd Tanous "ReadLength", readLength, "PECICommand", 1996b1556427SEd Tanous peciCommand)) 19971da66f75SEd Tanous { 19981da66f75SEd Tanous return; 19991da66f75SEd Tanous } 20008724c297SKarthick Sundarrajan peciCommands.push_back({clientAddress, 0, readLength}); 20018724c297SKarthick Sundarrajan peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(), 20028724c297SKarthick Sundarrajan peciCommand.end()); 20038724c297SKarthick Sundarrajan } 20041da66f75SEd Tanous // Callback to return the Raw PECI response 2005e1f26343SJason M. Bills auto sendRawPECICallback = 2006e1f26343SJason M. Bills [asyncResp](const boost::system::error_code ec, 20078724c297SKarthick Sundarrajan const std::vector<std::vector<uint8_t>> &resp) { 20081da66f75SEd Tanous if (ec) 20091da66f75SEd Tanous { 20108724c297SKarthick Sundarrajan BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: " 20111da66f75SEd Tanous << ec.message(); 2012f12894f8SJason M. Bills messages::internalError(asyncResp->res); 20131da66f75SEd Tanous return; 20141da66f75SEd Tanous } 2015e1f26343SJason M. Bills asyncResp->res.jsonValue = {{"Name", "PECI Command Response"}, 20161da66f75SEd Tanous {"PECIResponse", resp}}; 20171da66f75SEd Tanous }; 20181da66f75SEd Tanous // Call the SendRawPECI command with the provided data 20191da66f75SEd Tanous crow::connections::systemBus->async_method_call( 20205b61b5e8SJason M. Bills std::move(sendRawPECICallback), crashdumpObject, crashdumpPath, 20218724c297SKarthick Sundarrajan crashdumpRawPECIInterface, "SendRawPeci", peciCommands); 20221da66f75SEd Tanous } 20231da66f75SEd Tanous }; 20241da66f75SEd Tanous 2025cb92c03bSAndrew Geissler /** 2026cb92c03bSAndrew Geissler * DBusLogServiceActionsClear class supports POST method for ClearLog action. 2027cb92c03bSAndrew Geissler */ 2028cb92c03bSAndrew Geissler class DBusLogServiceActionsClear : public Node 2029cb92c03bSAndrew Geissler { 2030cb92c03bSAndrew Geissler public: 2031cb92c03bSAndrew Geissler DBusLogServiceActionsClear(CrowApp &app) : 2032cb92c03bSAndrew Geissler Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/" 20331f56a3a6STim Lee "LogService.ClearLog") 2034cb92c03bSAndrew Geissler { 2035cb92c03bSAndrew Geissler entityPrivileges = { 2036cb92c03bSAndrew Geissler {boost::beast::http::verb::get, {{"Login"}}}, 2037cb92c03bSAndrew Geissler {boost::beast::http::verb::head, {{"Login"}}}, 2038cb92c03bSAndrew Geissler {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2039cb92c03bSAndrew Geissler {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2040cb92c03bSAndrew Geissler {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2041cb92c03bSAndrew Geissler {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2042cb92c03bSAndrew Geissler } 2043cb92c03bSAndrew Geissler 2044cb92c03bSAndrew Geissler private: 2045cb92c03bSAndrew Geissler /** 2046cb92c03bSAndrew Geissler * Function handles POST method request. 2047cb92c03bSAndrew Geissler * The Clear Log actions does not require any parameter.The action deletes 2048cb92c03bSAndrew Geissler * all entries found in the Entries collection for this Log Service. 2049cb92c03bSAndrew Geissler */ 2050cb92c03bSAndrew Geissler void doPost(crow::Response &res, const crow::Request &req, 2051cb92c03bSAndrew Geissler const std::vector<std::string> ¶ms) override 2052cb92c03bSAndrew Geissler { 2053cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "Do delete all entries."; 2054cb92c03bSAndrew Geissler 2055cb92c03bSAndrew Geissler auto asyncResp = std::make_shared<AsyncResp>(res); 2056cb92c03bSAndrew Geissler // Process response from Logging service. 2057cb92c03bSAndrew Geissler auto resp_handler = [asyncResp](const boost::system::error_code ec) { 2058cb92c03bSAndrew Geissler BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done"; 2059cb92c03bSAndrew Geissler if (ec) 2060cb92c03bSAndrew Geissler { 2061cb92c03bSAndrew Geissler // TODO Handle for specific error code 2062cb92c03bSAndrew Geissler BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec; 2063cb92c03bSAndrew Geissler asyncResp->res.result( 2064cb92c03bSAndrew Geissler boost::beast::http::status::internal_server_error); 2065cb92c03bSAndrew Geissler return; 2066cb92c03bSAndrew Geissler } 2067cb92c03bSAndrew Geissler 2068cb92c03bSAndrew Geissler asyncResp->res.result(boost::beast::http::status::no_content); 2069cb92c03bSAndrew Geissler }; 2070cb92c03bSAndrew Geissler 2071cb92c03bSAndrew Geissler // Make call to Logging service to request Clear Log 2072cb92c03bSAndrew Geissler crow::connections::systemBus->async_method_call( 2073cb92c03bSAndrew Geissler resp_handler, "xyz.openbmc_project.Logging", 2074cb92c03bSAndrew Geissler "/xyz/openbmc_project/logging", 2075cb92c03bSAndrew Geissler "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 2076cb92c03bSAndrew Geissler } 2077cb92c03bSAndrew Geissler }; 2078*a3316fc6SZhikuiRen 2079*a3316fc6SZhikuiRen /**************************************************** 2080*a3316fc6SZhikuiRen * Redfish PostCode interfaces 2081*a3316fc6SZhikuiRen * using DBUS interface: getPostCodesTS 2082*a3316fc6SZhikuiRen ******************************************************/ 2083*a3316fc6SZhikuiRen class PostCodesLogService : public Node 2084*a3316fc6SZhikuiRen { 2085*a3316fc6SZhikuiRen public: 2086*a3316fc6SZhikuiRen PostCodesLogService(CrowApp &app) : 2087*a3316fc6SZhikuiRen Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/") 2088*a3316fc6SZhikuiRen { 2089*a3316fc6SZhikuiRen entityPrivileges = { 2090*a3316fc6SZhikuiRen {boost::beast::http::verb::get, {{"Login"}}}, 2091*a3316fc6SZhikuiRen {boost::beast::http::verb::head, {{"Login"}}}, 2092*a3316fc6SZhikuiRen {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2093*a3316fc6SZhikuiRen {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2094*a3316fc6SZhikuiRen {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2095*a3316fc6SZhikuiRen {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2096*a3316fc6SZhikuiRen } 2097*a3316fc6SZhikuiRen 2098*a3316fc6SZhikuiRen private: 2099*a3316fc6SZhikuiRen void doGet(crow::Response &res, const crow::Request &req, 2100*a3316fc6SZhikuiRen const std::vector<std::string> ¶ms) override 2101*a3316fc6SZhikuiRen { 2102*a3316fc6SZhikuiRen std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2103*a3316fc6SZhikuiRen 2104*a3316fc6SZhikuiRen asyncResp->res.jsonValue = { 2105*a3316fc6SZhikuiRen {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"}, 2106*a3316fc6SZhikuiRen {"@odata.type", "#LogService.v1_1_0.LogService"}, 2107*a3316fc6SZhikuiRen {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"}, 2108*a3316fc6SZhikuiRen {"Name", "POST Code Log Service"}, 2109*a3316fc6SZhikuiRen {"Description", "POST Code Log Service"}, 2110*a3316fc6SZhikuiRen {"Id", "BIOS POST Code Log"}, 2111*a3316fc6SZhikuiRen {"OverWritePolicy", "WrapsWhenFull"}, 2112*a3316fc6SZhikuiRen {"Entries", 2113*a3316fc6SZhikuiRen {{"@odata.id", 2114*a3316fc6SZhikuiRen "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}}; 2115*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = { 2116*a3316fc6SZhikuiRen {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/" 2117*a3316fc6SZhikuiRen "Actions/LogService.ClearLog"}}; 2118*a3316fc6SZhikuiRen } 2119*a3316fc6SZhikuiRen }; 2120*a3316fc6SZhikuiRen 2121*a3316fc6SZhikuiRen class PostCodesClear : public Node 2122*a3316fc6SZhikuiRen { 2123*a3316fc6SZhikuiRen public: 2124*a3316fc6SZhikuiRen PostCodesClear(CrowApp &app) : 2125*a3316fc6SZhikuiRen Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/" 2126*a3316fc6SZhikuiRen "LogService.ClearLog/") 2127*a3316fc6SZhikuiRen { 2128*a3316fc6SZhikuiRen entityPrivileges = { 2129*a3316fc6SZhikuiRen {boost::beast::http::verb::get, {{"Login"}}}, 2130*a3316fc6SZhikuiRen {boost::beast::http::verb::head, {{"Login"}}}, 2131*a3316fc6SZhikuiRen {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2132*a3316fc6SZhikuiRen {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2133*a3316fc6SZhikuiRen {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2134*a3316fc6SZhikuiRen {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2135*a3316fc6SZhikuiRen } 2136*a3316fc6SZhikuiRen 2137*a3316fc6SZhikuiRen private: 2138*a3316fc6SZhikuiRen void doPost(crow::Response &res, const crow::Request &req, 2139*a3316fc6SZhikuiRen const std::vector<std::string> ¶ms) override 2140*a3316fc6SZhikuiRen { 2141*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "Do delete all postcodes entries."; 2142*a3316fc6SZhikuiRen 2143*a3316fc6SZhikuiRen std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2144*a3316fc6SZhikuiRen // Make call to post-code service to request clear all 2145*a3316fc6SZhikuiRen crow::connections::systemBus->async_method_call( 2146*a3316fc6SZhikuiRen [asyncResp](const boost::system::error_code ec) { 2147*a3316fc6SZhikuiRen if (ec) 2148*a3316fc6SZhikuiRen { 2149*a3316fc6SZhikuiRen // TODO Handle for specific error code 2150*a3316fc6SZhikuiRen BMCWEB_LOG_ERROR 2151*a3316fc6SZhikuiRen << "doClearPostCodes resp_handler got error " << ec; 2152*a3316fc6SZhikuiRen asyncResp->res.result( 2153*a3316fc6SZhikuiRen boost::beast::http::status::internal_server_error); 2154*a3316fc6SZhikuiRen messages::internalError(asyncResp->res); 2155*a3316fc6SZhikuiRen return; 2156*a3316fc6SZhikuiRen } 2157*a3316fc6SZhikuiRen }, 2158*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", 2159*a3316fc6SZhikuiRen "/xyz/openbmc_project/State/Boot/PostCode", 2160*a3316fc6SZhikuiRen "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll"); 2161*a3316fc6SZhikuiRen } 2162*a3316fc6SZhikuiRen }; 2163*a3316fc6SZhikuiRen 2164*a3316fc6SZhikuiRen static void fillPostCodeEntry( 2165*a3316fc6SZhikuiRen std::shared_ptr<AsyncResp> aResp, 2166*a3316fc6SZhikuiRen const boost::container::flat_map<uint64_t, uint64_t> &postcode, 2167*a3316fc6SZhikuiRen const uint16_t bootIndex, const uint64_t codeIndex = 0, 2168*a3316fc6SZhikuiRen const uint64_t skip = 0, const uint64_t top = 0) 2169*a3316fc6SZhikuiRen { 2170*a3316fc6SZhikuiRen // Get the Message from the MessageRegistry 2171*a3316fc6SZhikuiRen const message_registries::Message *message = 2172*a3316fc6SZhikuiRen message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode"); 2173*a3316fc6SZhikuiRen std::string severity; 2174*a3316fc6SZhikuiRen if (message != nullptr) 2175*a3316fc6SZhikuiRen { 2176*a3316fc6SZhikuiRen severity = message->severity; 2177*a3316fc6SZhikuiRen } 2178*a3316fc6SZhikuiRen 2179*a3316fc6SZhikuiRen uint64_t currentCodeIndex = 0; 2180*a3316fc6SZhikuiRen nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"]; 2181*a3316fc6SZhikuiRen 2182*a3316fc6SZhikuiRen uint64_t firstCodeTimeUs = 0; 2183*a3316fc6SZhikuiRen for (const std::pair<uint64_t, uint64_t> &code : postcode) 2184*a3316fc6SZhikuiRen { 2185*a3316fc6SZhikuiRen currentCodeIndex++; 2186*a3316fc6SZhikuiRen std::string postcodeEntryID = 2187*a3316fc6SZhikuiRen "B" + std::to_string(bootIndex) + "-" + 2188*a3316fc6SZhikuiRen std::to_string(currentCodeIndex); // 1 based index in EntryID string 2189*a3316fc6SZhikuiRen 2190*a3316fc6SZhikuiRen uint64_t usecSinceEpoch = code.first; 2191*a3316fc6SZhikuiRen uint64_t usTimeOffset = 0; 2192*a3316fc6SZhikuiRen 2193*a3316fc6SZhikuiRen if (1 == currentCodeIndex) 2194*a3316fc6SZhikuiRen { // already incremented 2195*a3316fc6SZhikuiRen firstCodeTimeUs = code.first; 2196*a3316fc6SZhikuiRen } 2197*a3316fc6SZhikuiRen else 2198*a3316fc6SZhikuiRen { 2199*a3316fc6SZhikuiRen usTimeOffset = code.first - firstCodeTimeUs; 2200*a3316fc6SZhikuiRen } 2201*a3316fc6SZhikuiRen 2202*a3316fc6SZhikuiRen // skip if no specific codeIndex is specified and currentCodeIndex does 2203*a3316fc6SZhikuiRen // not fall between top and skip 2204*a3316fc6SZhikuiRen if ((codeIndex == 0) && 2205*a3316fc6SZhikuiRen (currentCodeIndex <= skip || currentCodeIndex > top)) 2206*a3316fc6SZhikuiRen { 2207*a3316fc6SZhikuiRen continue; 2208*a3316fc6SZhikuiRen } 2209*a3316fc6SZhikuiRen 2210*a3316fc6SZhikuiRen // skip if a sepcific codeIndex is specified and does not match the 2211*a3316fc6SZhikuiRen // currentIndex 2212*a3316fc6SZhikuiRen if ((codeIndex > 0) && (currentCodeIndex != codeIndex)) 2213*a3316fc6SZhikuiRen { 2214*a3316fc6SZhikuiRen // This is done for simplicity. 1st entry is needed to calculate 2215*a3316fc6SZhikuiRen // time offset. To improve efficiency, one can get to the entry 2216*a3316fc6SZhikuiRen // directly (possibly with flatmap's nth method) 2217*a3316fc6SZhikuiRen continue; 2218*a3316fc6SZhikuiRen } 2219*a3316fc6SZhikuiRen 2220*a3316fc6SZhikuiRen // currentCodeIndex is within top and skip or equal to specified code 2221*a3316fc6SZhikuiRen // index 2222*a3316fc6SZhikuiRen 2223*a3316fc6SZhikuiRen // Get the Created time from the timestamp 2224*a3316fc6SZhikuiRen std::string entryTimeStr; 2225*a3316fc6SZhikuiRen if (!getTimestampStr(usecSinceEpoch, entryTimeStr)) 2226*a3316fc6SZhikuiRen { 2227*a3316fc6SZhikuiRen continue; 2228*a3316fc6SZhikuiRen } 2229*a3316fc6SZhikuiRen 2230*a3316fc6SZhikuiRen // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex) 2231*a3316fc6SZhikuiRen std::ostringstream hexCode; 2232*a3316fc6SZhikuiRen hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex 2233*a3316fc6SZhikuiRen << code.second; 2234*a3316fc6SZhikuiRen std::ostringstream timeOffsetStr; 2235*a3316fc6SZhikuiRen // Set Fixed -Point Notation 2236*a3316fc6SZhikuiRen timeOffsetStr << std::fixed; 2237*a3316fc6SZhikuiRen // Set precision to 4 digits 2238*a3316fc6SZhikuiRen timeOffsetStr << std::setprecision(4); 2239*a3316fc6SZhikuiRen // Add double to stream 2240*a3316fc6SZhikuiRen timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000; 2241*a3316fc6SZhikuiRen std::vector<std::string> messageArgs = { 2242*a3316fc6SZhikuiRen std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()}; 2243*a3316fc6SZhikuiRen 2244*a3316fc6SZhikuiRen // Get MessageArgs template from message registry 2245*a3316fc6SZhikuiRen std::string msg; 2246*a3316fc6SZhikuiRen if (message != nullptr) 2247*a3316fc6SZhikuiRen { 2248*a3316fc6SZhikuiRen msg = message->message; 2249*a3316fc6SZhikuiRen 2250*a3316fc6SZhikuiRen // fill in this post code value 2251*a3316fc6SZhikuiRen int i = 0; 2252*a3316fc6SZhikuiRen for (const std::string &messageArg : messageArgs) 2253*a3316fc6SZhikuiRen { 2254*a3316fc6SZhikuiRen std::string argStr = "%" + std::to_string(++i); 2255*a3316fc6SZhikuiRen size_t argPos = msg.find(argStr); 2256*a3316fc6SZhikuiRen if (argPos != std::string::npos) 2257*a3316fc6SZhikuiRen { 2258*a3316fc6SZhikuiRen msg.replace(argPos, argStr.length(), messageArg); 2259*a3316fc6SZhikuiRen } 2260*a3316fc6SZhikuiRen } 2261*a3316fc6SZhikuiRen } 2262*a3316fc6SZhikuiRen 2263*a3316fc6SZhikuiRen // add to AsyncResp 2264*a3316fc6SZhikuiRen logEntryArray.push_back({}); 2265*a3316fc6SZhikuiRen nlohmann::json &bmcLogEntry = logEntryArray.back(); 2266*a3316fc6SZhikuiRen bmcLogEntry = { 2267*a3316fc6SZhikuiRen {"@odata.type", "#LogEntry.v1_4_0.LogEntry"}, 2268*a3316fc6SZhikuiRen {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"}, 2269*a3316fc6SZhikuiRen {"@odata.id", "/redfish/v1/Systems/system/LogServices/" 2270*a3316fc6SZhikuiRen "PostCodes/Entries/" + 2271*a3316fc6SZhikuiRen postcodeEntryID}, 2272*a3316fc6SZhikuiRen {"Name", "POST Code Log Entry"}, 2273*a3316fc6SZhikuiRen {"Id", postcodeEntryID}, 2274*a3316fc6SZhikuiRen {"Message", std::move(msg)}, 2275*a3316fc6SZhikuiRen {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"}, 2276*a3316fc6SZhikuiRen {"MessageArgs", std::move(messageArgs)}, 2277*a3316fc6SZhikuiRen {"EntryType", "Event"}, 2278*a3316fc6SZhikuiRen {"Severity", std::move(severity)}, 2279*a3316fc6SZhikuiRen {"Created", std::move(entryTimeStr)}}; 2280*a3316fc6SZhikuiRen } 2281*a3316fc6SZhikuiRen } 2282*a3316fc6SZhikuiRen 2283*a3316fc6SZhikuiRen static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp, 2284*a3316fc6SZhikuiRen const uint16_t bootIndex, 2285*a3316fc6SZhikuiRen const uint64_t codeIndex) 2286*a3316fc6SZhikuiRen { 2287*a3316fc6SZhikuiRen crow::connections::systemBus->async_method_call( 2288*a3316fc6SZhikuiRen [aResp, bootIndex, codeIndex]( 2289*a3316fc6SZhikuiRen const boost::system::error_code ec, 2290*a3316fc6SZhikuiRen const boost::container::flat_map<uint64_t, uint64_t> &postcode) { 2291*a3316fc6SZhikuiRen if (ec) 2292*a3316fc6SZhikuiRen { 2293*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error"; 2294*a3316fc6SZhikuiRen messages::internalError(aResp->res); 2295*a3316fc6SZhikuiRen return; 2296*a3316fc6SZhikuiRen } 2297*a3316fc6SZhikuiRen 2298*a3316fc6SZhikuiRen // skip the empty postcode boots 2299*a3316fc6SZhikuiRen if (postcode.empty()) 2300*a3316fc6SZhikuiRen { 2301*a3316fc6SZhikuiRen return; 2302*a3316fc6SZhikuiRen } 2303*a3316fc6SZhikuiRen 2304*a3316fc6SZhikuiRen fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex); 2305*a3316fc6SZhikuiRen 2306*a3316fc6SZhikuiRen aResp->res.jsonValue["Members@odata.count"] = 2307*a3316fc6SZhikuiRen aResp->res.jsonValue["Members"].size(); 2308*a3316fc6SZhikuiRen }, 2309*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", 2310*a3316fc6SZhikuiRen "/xyz/openbmc_project/State/Boot/PostCode", 2311*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 2312*a3316fc6SZhikuiRen bootIndex); 2313*a3316fc6SZhikuiRen } 2314*a3316fc6SZhikuiRen 2315*a3316fc6SZhikuiRen static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp, 2316*a3316fc6SZhikuiRen const uint16_t bootIndex, 2317*a3316fc6SZhikuiRen const uint16_t bootCount, 2318*a3316fc6SZhikuiRen const uint64_t entryCount, const uint64_t skip, 2319*a3316fc6SZhikuiRen const uint64_t top) 2320*a3316fc6SZhikuiRen { 2321*a3316fc6SZhikuiRen crow::connections::systemBus->async_method_call( 2322*a3316fc6SZhikuiRen [aResp, bootIndex, bootCount, entryCount, skip, 2323*a3316fc6SZhikuiRen top](const boost::system::error_code ec, 2324*a3316fc6SZhikuiRen const boost::container::flat_map<uint64_t, uint64_t> &postcode) { 2325*a3316fc6SZhikuiRen if (ec) 2326*a3316fc6SZhikuiRen { 2327*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error"; 2328*a3316fc6SZhikuiRen messages::internalError(aResp->res); 2329*a3316fc6SZhikuiRen return; 2330*a3316fc6SZhikuiRen } 2331*a3316fc6SZhikuiRen 2332*a3316fc6SZhikuiRen uint64_t endCount = entryCount; 2333*a3316fc6SZhikuiRen if (!postcode.empty()) 2334*a3316fc6SZhikuiRen { 2335*a3316fc6SZhikuiRen endCount = entryCount + postcode.size(); 2336*a3316fc6SZhikuiRen 2337*a3316fc6SZhikuiRen if ((skip < endCount) && ((top + skip) > entryCount)) 2338*a3316fc6SZhikuiRen { 2339*a3316fc6SZhikuiRen uint64_t thisBootSkip = 2340*a3316fc6SZhikuiRen std::max(skip, entryCount) - entryCount; 2341*a3316fc6SZhikuiRen uint64_t thisBootTop = 2342*a3316fc6SZhikuiRen std::min(top + skip, endCount) - entryCount; 2343*a3316fc6SZhikuiRen 2344*a3316fc6SZhikuiRen fillPostCodeEntry(aResp, postcode, bootIndex, 0, 2345*a3316fc6SZhikuiRen thisBootSkip, thisBootTop); 2346*a3316fc6SZhikuiRen } 2347*a3316fc6SZhikuiRen aResp->res.jsonValue["Members@odata.count"] = endCount; 2348*a3316fc6SZhikuiRen } 2349*a3316fc6SZhikuiRen 2350*a3316fc6SZhikuiRen // continue to previous bootIndex 2351*a3316fc6SZhikuiRen if (bootIndex < bootCount) 2352*a3316fc6SZhikuiRen { 2353*a3316fc6SZhikuiRen getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1), 2354*a3316fc6SZhikuiRen bootCount, endCount, skip, top); 2355*a3316fc6SZhikuiRen } 2356*a3316fc6SZhikuiRen else 2357*a3316fc6SZhikuiRen { 2358*a3316fc6SZhikuiRen aResp->res.jsonValue["Members@odata.nextLink"] = 2359*a3316fc6SZhikuiRen "/redfish/v1/Systems/system/LogServices/PostCodes/" 2360*a3316fc6SZhikuiRen "Entries?$skip=" + 2361*a3316fc6SZhikuiRen std::to_string(skip + top); 2362*a3316fc6SZhikuiRen } 2363*a3316fc6SZhikuiRen }, 2364*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", 2365*a3316fc6SZhikuiRen "/xyz/openbmc_project/State/Boot/PostCode", 2366*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp", 2367*a3316fc6SZhikuiRen bootIndex); 2368*a3316fc6SZhikuiRen } 2369*a3316fc6SZhikuiRen 2370*a3316fc6SZhikuiRen static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp, 2371*a3316fc6SZhikuiRen const uint64_t skip, const uint64_t top) 2372*a3316fc6SZhikuiRen { 2373*a3316fc6SZhikuiRen uint64_t entryCount = 0; 2374*a3316fc6SZhikuiRen crow::connections::systemBus->async_method_call( 2375*a3316fc6SZhikuiRen [aResp, entryCount, skip, 2376*a3316fc6SZhikuiRen top](const boost::system::error_code ec, 2377*a3316fc6SZhikuiRen const std::variant<uint16_t> &bootCount) { 2378*a3316fc6SZhikuiRen if (ec) 2379*a3316fc6SZhikuiRen { 2380*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2381*a3316fc6SZhikuiRen messages::internalError(aResp->res); 2382*a3316fc6SZhikuiRen return; 2383*a3316fc6SZhikuiRen } 2384*a3316fc6SZhikuiRen auto pVal = std::get_if<uint16_t>(&bootCount); 2385*a3316fc6SZhikuiRen if (pVal) 2386*a3316fc6SZhikuiRen { 2387*a3316fc6SZhikuiRen getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top); 2388*a3316fc6SZhikuiRen } 2389*a3316fc6SZhikuiRen else 2390*a3316fc6SZhikuiRen { 2391*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "Post code boot index failed."; 2392*a3316fc6SZhikuiRen } 2393*a3316fc6SZhikuiRen }, 2394*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", 2395*a3316fc6SZhikuiRen "/xyz/openbmc_project/State/Boot/PostCode", 2396*a3316fc6SZhikuiRen "org.freedesktop.DBus.Properties", "Get", 2397*a3316fc6SZhikuiRen "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount"); 2398*a3316fc6SZhikuiRen } 2399*a3316fc6SZhikuiRen 2400*a3316fc6SZhikuiRen class PostCodesEntryCollection : public Node 2401*a3316fc6SZhikuiRen { 2402*a3316fc6SZhikuiRen public: 2403*a3316fc6SZhikuiRen template <typename CrowApp> 2404*a3316fc6SZhikuiRen PostCodesEntryCollection(CrowApp &app) : 2405*a3316fc6SZhikuiRen Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/") 2406*a3316fc6SZhikuiRen { 2407*a3316fc6SZhikuiRen entityPrivileges = { 2408*a3316fc6SZhikuiRen {boost::beast::http::verb::get, {{"Login"}}}, 2409*a3316fc6SZhikuiRen {boost::beast::http::verb::head, {{"Login"}}}, 2410*a3316fc6SZhikuiRen {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2411*a3316fc6SZhikuiRen {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2412*a3316fc6SZhikuiRen {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2413*a3316fc6SZhikuiRen {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2414*a3316fc6SZhikuiRen } 2415*a3316fc6SZhikuiRen 2416*a3316fc6SZhikuiRen private: 2417*a3316fc6SZhikuiRen void doGet(crow::Response &res, const crow::Request &req, 2418*a3316fc6SZhikuiRen const std::vector<std::string> ¶ms) override 2419*a3316fc6SZhikuiRen { 2420*a3316fc6SZhikuiRen std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2421*a3316fc6SZhikuiRen 2422*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.type"] = 2423*a3316fc6SZhikuiRen "#LogEntryCollection.LogEntryCollection"; 2424*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.context"] = 2425*a3316fc6SZhikuiRen "/redfish/v1/" 2426*a3316fc6SZhikuiRen "$metadata#LogEntryCollection.LogEntryCollection"; 2427*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.id"] = 2428*a3316fc6SZhikuiRen "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"; 2429*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; 2430*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Description"] = 2431*a3316fc6SZhikuiRen "Collection of POST Code Log Entries"; 2432*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 2433*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members@odata.count"] = 0; 2434*a3316fc6SZhikuiRen 2435*a3316fc6SZhikuiRen uint64_t skip = 0; 2436*a3316fc6SZhikuiRen uint64_t top = maxEntriesPerPage; // Show max entries by default 2437*a3316fc6SZhikuiRen if (!getSkipParam(asyncResp->res, req, skip)) 2438*a3316fc6SZhikuiRen { 2439*a3316fc6SZhikuiRen return; 2440*a3316fc6SZhikuiRen } 2441*a3316fc6SZhikuiRen if (!getTopParam(asyncResp->res, req, top)) 2442*a3316fc6SZhikuiRen { 2443*a3316fc6SZhikuiRen return; 2444*a3316fc6SZhikuiRen } 2445*a3316fc6SZhikuiRen getCurrentBootNumber(asyncResp, skip, top); 2446*a3316fc6SZhikuiRen } 2447*a3316fc6SZhikuiRen }; 2448*a3316fc6SZhikuiRen 2449*a3316fc6SZhikuiRen class PostCodesEntry : public Node 2450*a3316fc6SZhikuiRen { 2451*a3316fc6SZhikuiRen public: 2452*a3316fc6SZhikuiRen PostCodesEntry(CrowApp &app) : 2453*a3316fc6SZhikuiRen Node(app, 2454*a3316fc6SZhikuiRen "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/", 2455*a3316fc6SZhikuiRen std::string()) 2456*a3316fc6SZhikuiRen { 2457*a3316fc6SZhikuiRen entityPrivileges = { 2458*a3316fc6SZhikuiRen {boost::beast::http::verb::get, {{"Login"}}}, 2459*a3316fc6SZhikuiRen {boost::beast::http::verb::head, {{"Login"}}}, 2460*a3316fc6SZhikuiRen {boost::beast::http::verb::patch, {{"ConfigureManager"}}}, 2461*a3316fc6SZhikuiRen {boost::beast::http::verb::put, {{"ConfigureManager"}}}, 2462*a3316fc6SZhikuiRen {boost::beast::http::verb::delete_, {{"ConfigureManager"}}}, 2463*a3316fc6SZhikuiRen {boost::beast::http::verb::post, {{"ConfigureManager"}}}}; 2464*a3316fc6SZhikuiRen } 2465*a3316fc6SZhikuiRen 2466*a3316fc6SZhikuiRen private: 2467*a3316fc6SZhikuiRen void doGet(crow::Response &res, const crow::Request &req, 2468*a3316fc6SZhikuiRen const std::vector<std::string> ¶ms) override 2469*a3316fc6SZhikuiRen { 2470*a3316fc6SZhikuiRen std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 2471*a3316fc6SZhikuiRen if (params.size() != 1) 2472*a3316fc6SZhikuiRen { 2473*a3316fc6SZhikuiRen messages::internalError(asyncResp->res); 2474*a3316fc6SZhikuiRen return; 2475*a3316fc6SZhikuiRen } 2476*a3316fc6SZhikuiRen 2477*a3316fc6SZhikuiRen const std::string &targetID = params[0]; 2478*a3316fc6SZhikuiRen 2479*a3316fc6SZhikuiRen size_t bootPos = targetID.find('B'); 2480*a3316fc6SZhikuiRen if (bootPos == std::string::npos) 2481*a3316fc6SZhikuiRen { 2482*a3316fc6SZhikuiRen // Requested ID was not found 2483*a3316fc6SZhikuiRen messages::resourceMissingAtURI(asyncResp->res, targetID); 2484*a3316fc6SZhikuiRen return; 2485*a3316fc6SZhikuiRen } 2486*a3316fc6SZhikuiRen std::string_view bootIndexStr(targetID); 2487*a3316fc6SZhikuiRen bootIndexStr.remove_prefix(bootPos + 1); 2488*a3316fc6SZhikuiRen uint16_t bootIndex = 0; 2489*a3316fc6SZhikuiRen uint64_t codeIndex = 0; 2490*a3316fc6SZhikuiRen size_t dashPos = bootIndexStr.find('-'); 2491*a3316fc6SZhikuiRen 2492*a3316fc6SZhikuiRen if (dashPos == std::string::npos) 2493*a3316fc6SZhikuiRen { 2494*a3316fc6SZhikuiRen return; 2495*a3316fc6SZhikuiRen } 2496*a3316fc6SZhikuiRen std::string_view codeIndexStr(bootIndexStr); 2497*a3316fc6SZhikuiRen bootIndexStr.remove_suffix(dashPos); 2498*a3316fc6SZhikuiRen codeIndexStr.remove_prefix(dashPos + 1); 2499*a3316fc6SZhikuiRen 2500*a3316fc6SZhikuiRen bootIndex = static_cast<uint16_t>( 2501*a3316fc6SZhikuiRen strtoul(std::string(bootIndexStr).c_str(), NULL, 0)); 2502*a3316fc6SZhikuiRen codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0); 2503*a3316fc6SZhikuiRen if (bootIndex == 0 || codeIndex == 0) 2504*a3316fc6SZhikuiRen { 2505*a3316fc6SZhikuiRen BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string " 2506*a3316fc6SZhikuiRen << params[0]; 2507*a3316fc6SZhikuiRen } 2508*a3316fc6SZhikuiRen 2509*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry"; 2510*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.context"] = 2511*a3316fc6SZhikuiRen "/redfish/v1/$metadata#LogEntry.LogEntry"; 2512*a3316fc6SZhikuiRen asyncResp->res.jsonValue["@odata.id"] = 2513*a3316fc6SZhikuiRen "/redfish/v1/Systems/system/LogServices/PostCodes/" 2514*a3316fc6SZhikuiRen "Entries"; 2515*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries"; 2516*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Description"] = 2517*a3316fc6SZhikuiRen "Collection of POST Code Log Entries"; 2518*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members"] = nlohmann::json::array(); 2519*a3316fc6SZhikuiRen asyncResp->res.jsonValue["Members@odata.count"] = 0; 2520*a3316fc6SZhikuiRen 2521*a3316fc6SZhikuiRen getPostCodeForEntry(asyncResp, bootIndex, codeIndex); 2522*a3316fc6SZhikuiRen } 2523*a3316fc6SZhikuiRen }; 2524*a3316fc6SZhikuiRen 25251da66f75SEd Tanous } // namespace redfish 2526