| #include "utils/boot_time_utils.hpp" |
| |
| #include "async_resp.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "generated/enums/google_boot_time.hpp" |
| #include "managed_store.hpp" |
| #include "utils/dbus_utils.hpp" |
| #include "utils/time_utils.hpp" |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <cstring> |
| #include <map> |
| #include <span> |
| #include <string> |
| #include <string_view> |
| #include <vector> |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace redfish::boot_time_utils |
| { |
| |
| // Logics to calculate the boot time are from |
| // google3/platforms/mosys/modules/bmc/gbmcipmi/boottime_parsing.cc |
| |
| inline void RecordDuration(std::map<std::string, int64_t>& rebootStages, |
| std::string_view name, const Checkpoint& begin, |
| const Checkpoint& end) |
| { |
| int64_t duration = end.monoTimeMs - begin.monoTimeMs; |
| if (duration < 0) |
| { |
| duration = 0; |
| } |
| rebootStages[std::string(name)] += duration; |
| } |
| |
| inline bool IsBeginningOfBootEvent(std::string_view checkpointName) |
| { |
| return checkpointName == kHostOff || checkpointName == kOSInactive; |
| } |
| |
| int64_t PowerCycleCount(std::span<const Checkpoint> checkpoints) |
| { |
| int64_t cnt = 0; |
| |
| if (checkpoints.empty()) |
| { |
| return 0; |
| } |
| if (IsBeginningOfBootEvent(checkpoints.begin()->name)) |
| { |
| cnt++; |
| } |
| for (std::span<const Checkpoint>::iterator it = checkpoints.begin() + 1; |
| it != checkpoints.end(); ++it) |
| { |
| if ((it - 1)->monoTimeMs > it->monoTimeMs) |
| { |
| cnt++; |
| } |
| } |
| return cnt; |
| } |
| |
| void FormatCheckpoints(std::vector<Checkpoint>& checkpoints) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| |
| // Remove HostState and OSState right after BMC power on so that these won't |
| // be treated as a checkpoint while they are actually just initial value, |
| // i.e. state is updated from NULL. |
| bool flag_remove = false; |
| for (auto it = checkpoints.begin() + 1; it != checkpoints.end();) |
| { |
| if ((it - 1)->monoTimeMs > it->monoTimeMs) |
| { |
| flag_remove = true; |
| } |
| if (flag_remove && (it->name == kHostOff || it->name == kOSInactive)) |
| { |
| checkpoints.erase(it); |
| continue; |
| } |
| // Reset the flag once we hit any other events. |
| flag_remove = false; |
| ++it; |
| } |
| } |
| |
| void ParseTransitionEventDurations( |
| std::map<std::string, int64_t>& rebootStages, std::string_view name, |
| std::span<const Checkpoint> checkpoints, |
| const std::vector<std::string_view>& begWords, |
| const std::vector<std::string_view>& endWords) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| for (std::span<const Checkpoint>::iterator it = checkpoints.begin() + 1; |
| it != checkpoints.end(); ++it) |
| { |
| if (std::find(begWords.begin(), begWords.end(), (it - 1)->name) != |
| begWords.end() && |
| std::find(endWords.begin(), endWords.end(), it->name) != |
| endWords.end()) |
| { |
| RecordDuration(rebootStages, name, *(it - 1), *it); |
| } |
| } |
| } |
| |
| void ParseEventDurations(std::map<std::string, int64_t>& rebootStages, |
| std::string_view name, |
| std::span<const Checkpoint> checkpoints, |
| const std::vector<std::string_view>& begWords, |
| const std::vector<std::string_view>& endWords) |
| { |
| std::span<const Checkpoint>::iterator lastMatchedWord = checkpoints.end(); |
| for (std::span<const Checkpoint>::iterator it = checkpoints.begin(); |
| it != checkpoints.end(); ++it) |
| { |
| // Keep tracking the last matched checkpoint. |
| if (std::find(begWords.begin(), begWords.end(), it->name) != |
| begWords.end()) |
| { |
| lastMatchedWord = it; |
| } |
| if (lastMatchedWord != checkpoints.end() && |
| std::find(endWords.begin(), endWords.end(), it->name) != |
| endWords.end()) |
| { |
| RecordDuration(rebootStages, name, *lastMatchedWord, *it); |
| lastMatchedWord = checkpoints.end(); |
| } |
| } |
| } |
| |
| void ParseOffDuration(std::map<std::string, int64_t>& rebootStages, |
| std::span<const Checkpoint> checkpoints) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| ParseEventDurations(rebootStages, kOff, checkpoints, |
| {kHostOff, kHostTransitioningToOff}, {kHostRunning}); |
| ParseTransitionEventDurations( |
| rebootStages, kOff, checkpoints, |
| {kHostOff, kHostTransitioningToOff, kOSInactive}, {kTrayPowerCycle}); |
| // Handle unclean power cycle conditions. This assumes that the monoTimeMs |
| // represent monotonic time from BMC power on. However, this may not be true |
| // on some platforms. |
| for (auto it = checkpoints.begin() + 1; it != checkpoints.end(); ++it) |
| { |
| if (it->name == kHostRunning && (it - 1)->monoTimeMs > it->monoTimeMs) |
| { |
| rebootStages[std::string(kOff)] += it->monoTimeMs; |
| } |
| } |
| } |
| |
| // Parses the duration from host off to power cycle. This duration means how |
| // long the time spent on BMC shutdown + IMC shutdown + S4 operation time. |
| // The returned value is duration in milliseconds. |
| static void |
| ParseOffToPowerCycleDuration(std::map<std::string, int64_t>& reboot_stages, |
| std::span<const Checkpoint> checkpoints) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| auto prev = checkpoints.begin(); |
| for (auto it = checkpoints.begin() + 1; it != checkpoints.end(); ++it) |
| { |
| // Ignore kOSInactive because it may be shown after kHostOff but the |
| // real event happens in a different order. |
| if (it->name == kOSInactive) |
| { |
| continue; |
| } |
| if (prev->name == kHostOff && it->name == kTrayPowerCycle) |
| { |
| RecordDuration(reboot_stages, "OffToPowerCycle", *prev, *it); |
| } |
| prev = it; |
| } |
| } |
| |
| void ParseBootToHostDuration(std::map<std::string, int64_t>& rebootStages, |
| std::span<const Checkpoint> checkpoints) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| for (const auto& checkpoint : checkpoints) |
| { |
| if (checkpoint.name == "HostReleased") |
| { |
| rebootStages[std::string(kBMCBootToHost)] += checkpoint.monoTimeMs; |
| } |
| } |
| } |
| |
| void ParseStageDurations(std::map<std::string, int64_t>& rebootStages, |
| std::span<const Checkpoint> checkpoints) |
| { |
| if (checkpoints.empty()) |
| { |
| return; |
| } |
| for (auto it = checkpoints.begin() + 1; it != checkpoints.end(); ++it) |
| { |
| std::string name = it->name; |
| // Skip HW events. |
| if (name.starts_with(kHostState) || name.starts_with(kOSStatus) || |
| name.starts_with(kTrayPowerCycle)) |
| { |
| continue; |
| } |
| if (name.ends_with(":BEGIN")) |
| { |
| name = "TransitioningTo" + |
| name.substr(0, name.size() - std::strlen(":BEGIN")); |
| } |
| RecordDuration(rebootStages, name, *(it - 1), *it); |
| } |
| } |
| |
| BreakdownStagesInMs |
| ParseHostRebootStages(std::span<const Checkpoint> origCheckpoints) |
| { |
| BreakdownStagesInMs rebootStages; |
| std::vector<Checkpoint> checkpoints(origCheckpoints.begin(), |
| origCheckpoints.end()); |
| |
| if (checkpoints.empty()) |
| { |
| return {}; |
| } |
| FormatCheckpoints(checkpoints); |
| |
| ParseOffDuration(rebootStages, checkpoints); |
| ParseOffToPowerCycleDuration(rebootStages, checkpoints); |
| ParseEventDurations(rebootStages, "Firmware", checkpoints, |
| {kHostRunning, kOSInactive}, {kOSStandby}); |
| ParseTransitionEventDurations(rebootStages, "TransitioningToOff", |
| checkpoints, {"Shutdown", kOSInactive}, |
| {kHostTransitioningToOff, kHostOff}); |
| ParseTransitionEventDurations(rebootStages, "TransitioningToOff", |
| checkpoints, {"Shutdown"}, {kOSInactive}); |
| ParseTransitionEventDurations(rebootStages, "TransitioningToOff", |
| checkpoints, {kHostTransitioningToOff}, |
| {kHostOff}); |
| ParseStageDurations(rebootStages, checkpoints); |
| |
| return rebootStages; |
| } |
| |
| BreakdownStagesInMs |
| ParseBMCRebootStages(std::span<const Checkpoint> checkpoints) |
| { |
| BreakdownStagesInMs rebootStages; |
| |
| if (checkpoints.empty()) |
| { |
| return {}; |
| } |
| ParseEventDurations(rebootStages, "Shutdown", checkpoints, {"RebootStart"}, |
| {kTrayPowerCycle}); |
| ParseBootToHostDuration(rebootStages, checkpoints); |
| |
| return rebootStages; |
| } |
| |
| std::vector<Checkpoint> |
| AppendPowerCycleEvent(std::span<const Checkpoint> hostCheckpoints, |
| std::span<const Checkpoint> bmcCheckpoints) |
| { |
| std::vector<Checkpoint> ret; |
| |
| if (hostCheckpoints.empty()) |
| { |
| return {}; |
| } |
| |
| std::span<const Checkpoint>::iterator it = hostCheckpoints.begin() + 1; |
| std::span<const Checkpoint>::iterator itBmc = bmcCheckpoints.begin(); |
| |
| // Skip bmc iterator if TrayPowerCycle has already been appended. |
| if (std::find_if(hostCheckpoints.begin(), hostCheckpoints.end(), |
| [](auto& x) { return x.name == kTrayPowerCycle; }) != |
| hostCheckpoints.end()) |
| { |
| itBmc = bmcCheckpoints.end(); |
| } |
| |
| ret.push_back(*hostCheckpoints.begin()); |
| for (; it != hostCheckpoints.end(); ++it) |
| { |
| // Find next TrayPowerCycle event. |
| while (itBmc != bmcCheckpoints.end() && itBmc->name != kTrayPowerCycle) |
| { |
| itBmc++; |
| } |
| // Append TrayPowerCycle event at monotonic time reset point. |
| if ((it - 1)->monoTimeMs > it->monoTimeMs && |
| itBmc != bmcCheckpoints.end()) |
| { |
| ret.push_back(*itBmc); |
| ++itBmc; |
| } |
| ret.push_back(*it); |
| } |
| return ret; |
| } |
| |
| void UpdateKernelStageDuration(std::map<std::string, int64_t>& stages, |
| std::span<const Duration> duration_list) |
| { |
| // Do nothing if it's already set. |
| if (stages.find("Kernel") != stages.end()) |
| { |
| return; |
| } |
| |
| std::span<const Duration>::iterator kernel_duration = std::find_if( |
| duration_list.begin(), duration_list.end(), |
| [](const Duration& duration) { return duration.name == "Kernel"; }); |
| if (kernel_duration != duration_list.end()) |
| { |
| // Kernel stage should always be assigned even without |
| // TransitioningToUserspace stage. |
| stages["Kernel"] = kernel_duration->durationMs; |
| if (stages.find("TransitioningToUserspace") != stages.end()) |
| { |
| // TransitioningToUserspace = TransitioningToKernel + Kernel. |
| stages["TransitioningToKernel"] = |
| stages["TransitioningToUserspace"] - stages["Kernel"]; |
| stages.erase("TransitioningToUserspace"); |
| if (stages["TransitioningToKernel"] < 0) |
| { |
| stages["TransitioningToKernel"] = 0; |
| } |
| } |
| } |
| } |
| |
| double GetTimeOffsetDuration(std::span<const Duration> durations) |
| { |
| auto timeOffset = std::find_if( |
| durations.begin(), durations.end(), |
| [](const Duration& duration) { return duration.name == "TimeOffset"; }); |
| if (timeOffset != durations.end()) |
| { |
| return static_cast<double>(timeOffset->durationMs); |
| } |
| return 0.0; |
| } |
| |
| double GetTotalRebootTime(const Checkpoint& start, const Checkpoint& end, |
| std::span<const Duration> durations) |
| { |
| double timeOffsetMs = GetTimeOffsetDuration(durations); |
| double totalMs = static_cast<double>(end.wallTimeMs - start.wallTimeMs); |
| |
| return totalMs - timeOffsetMs; |
| } |
| |
| void UpdateHostOffStageDuration(std::map<std::string, int64_t>& stages, |
| double totalTimeMs) |
| { |
| if (!stages.contains("Off")) |
| { |
| return; |
| } |
| |
| int64_t totalMs = 0; |
| for (auto const& [stage, durationMs] : stages) |
| { |
| totalMs += durationMs; |
| } |
| |
| int64_t& offStage = stages["Off"]; |
| offStage += static_cast<int64_t>(totalTimeMs) - totalMs; |
| // NOTE: In case of a warm reboot and incorrect TimeOffset, the Off stage |
| // will be a minus number after calculation. Set to zero in this case. |
| if (offStage < 0) |
| { |
| offStage = 0; |
| } |
| } |
| |
| // End of logics from |
| // google3/platforms/mosys/modules/bmc/gbmcipmi/boottime_parsing.cc |
| |
| void PopulateDefaultBootTimeProperties( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) |
| { |
| aResp->res.jsonValue["@odata.type"] = |
| "#GoogleBootTime.v1_0_0.GoogleBootTime"; |
| aResp->res.jsonValue["Name"] = "Boot time data"; |
| aResp->res.jsonValue["Id"] = "BootTimeData"; |
| aResp->res.jsonValue["Breakdown"] = nlohmann::json::array(); |
| aResp->res.jsonValue["Durations"] = nlohmann::json::array(); |
| aResp->res.jsonValue["RebootFlow"] = nlohmann::json::array(); |
| aResp->res.jsonValue["Stat"]["IsRebooting"] = false; |
| aResp->res.jsonValue["Total"] = 0; |
| } |
| |
| // Logics to calculate Boot time in metrics are from |
| // google3/platforms/mosys/modules/bmc/gbmcipmi/bmc_context.cc |
| |
| void PopulateBreakdownInResponse( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const BreakdownStagesInMs& stages) |
| { |
| for (auto const& [name, duration] : stages) |
| { |
| nlohmann::json::object_t stage; |
| stage["Stage"] = name; |
| stage["Duration"] = static_cast<double>(duration) / 1000.0; |
| aResp->res.jsonValue["Breakdown"].push_back(stage); |
| } |
| } |
| |
| void PopulateDurationInResponse( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Duration>>& durations) |
| { |
| for (auto const& duration : *durations) |
| { |
| nlohmann::json::object_t stage; |
| stage["Name"] = duration.name; |
| stage["Duration"] = static_cast<double>(duration.durationMs) / 1000.0; |
| aResp->res.jsonValue["Durations"].push_back(stage); |
| } |
| } |
| |
| void PopulateStatIsRebootingInResponse( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<bool>& isRebooting) |
| { |
| aResp->res.jsonValue["Stat"]["IsRebooting"] = *isRebooting; |
| } |
| |
| void PopulateTotalTimeInResponse( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& checkpoints, |
| const std::shared_ptr<std::vector<Duration>>& durations) |
| { |
| double total = 0; |
| if (!checkpoints->empty()) |
| { |
| total = GetTotalRebootTime(checkpoints->front(), checkpoints->back(), |
| *durations); |
| total /= 1000.0; |
| } |
| |
| aResp->res.jsonValue["Total"] = total; |
| } |
| |
| void GetHostBreakdownStages( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& hostCheckpoints, |
| const std::shared_ptr<std::vector<Checkpoint>>& bmcCheckpoints, |
| const std::shared_ptr<std::vector<Duration>>& hostDurations) |
| { |
| BreakdownStagesInMs stages = ParseHostRebootStages( |
| AppendPowerCycleEvent(*hostCheckpoints, *bmcCheckpoints)); |
| UpdateKernelStageDuration(stages, *hostDurations); |
| // Update Off stage duration by using total time. |
| double totalMs = 0.0; |
| if (!hostCheckpoints->empty()) |
| { |
| totalMs = GetTotalRebootTime(hostCheckpoints->front(), |
| hostCheckpoints->back(), *hostDurations); |
| } |
| UpdateHostOffStageDuration(stages, totalMs); |
| PopulateBreakdownInResponse(aResp, stages); |
| } |
| |
| void GetBmcBreakdownStages( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& bmcCheckpoints) |
| { |
| BreakdownStagesInMs stages = ParseBMCRebootStages(*bmcCheckpoints); |
| PopulateBreakdownInResponse(aResp, stages); |
| } |
| |
| void CalibrateWallTimeAndPopulate( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::vector<Checkpoint>& checkpoints) |
| { |
| // Add entries with calibrated wall_time. |
| int64_t curWallTime = 0; |
| for (auto it = checkpoints.begin(); it != checkpoints.end(); ++it) |
| { |
| if (it == checkpoints.begin()) |
| { |
| curWallTime = checkpoints.begin()->wallTimeMs; |
| } |
| else if ((it - 1)->monoTimeMs > it->monoTimeMs) |
| { |
| // Increment by 0.1s for correct timestamp ordering. |
| curWallTime = curWallTime + 100; |
| } |
| else |
| { |
| curWallTime += it->monoTimeMs - (it - 1)->monoTimeMs; |
| } |
| nlohmann::json::object_t stage; |
| stage["Stage"] = it->name; |
| if (curWallTime >= 0) |
| { |
| stage["StartTime"] = time_utils::getDateTimeUintMs( |
| static_cast<uint64_t>(curWallTime)); |
| } |
| stage["MonotonicStartTime"] = it->monoTimeMs; |
| aResp->res.jsonValue["RebootFlow"].push_back(stage); |
| } |
| } |
| |
| void GetHostRebootFlow( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& hostCheckpoints, |
| const std::shared_ptr<std::vector<Checkpoint>>& bmcCheckpoints) |
| { |
| std::vector<Checkpoint> mergedCheckpoints = |
| AppendPowerCycleEvent(*hostCheckpoints, *bmcCheckpoints); |
| CalibrateWallTimeAndPopulate(aResp, mergedCheckpoints); |
| } |
| |
| void GetBmcRebootFlow( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& bmcCheckpoints) |
| { |
| CalibrateWallTimeAndPopulate(aResp, *bmcCheckpoints); |
| } |
| |
| void PopulateStatPowerSourceInResponse( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::shared_ptr<std::vector<Checkpoint>>& checkpoints) |
| { |
| google_boot_time::PowerSourceTypes powerSource = |
| google_boot_time::PowerSourceTypes::Unknown; |
| if (!checkpoints->empty()) |
| { |
| if (PowerCycleCount(*checkpoints) > 0) |
| { |
| powerSource = google_boot_time::PowerSourceTypes::AC; |
| } |
| else |
| { |
| powerSource = google_boot_time::PowerSourceTypes::DC; |
| } |
| } |
| aResp->res.jsonValue["Stat"]["PowerSource"] = powerSource; |
| } |
| |
| // End of logic from |
| // google3/platforms/mosys/modules/bmc/gbmcipmi/bmc_context.cc |
| |
| void GetBootTimeCheckpoints( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& host, |
| const std::shared_ptr<std::vector<Checkpoint>>& checkpoints, |
| const std::function<void()>& successCb, |
| const std::function<void()>& errorCb) |
| { |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| managedStore::GetManagedObjectStore() |
| ->PostDbusCallToIoContextThreadSafe( |
| requestContext.GetStrand(), |
| [aResp, checkpoints, successCb, |
| errorCb](const boost::system::error_code& ec, |
| const std::vector<CheckpointTuple>& checkpointTuples) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS Boot Time checkpoints response error"; |
| messages::internalError(aResp->res); |
| errorCb(); |
| return; |
| } |
| try |
| { |
| for (const CheckpointTuple& checkpoint : checkpointTuples) |
| { |
| const auto [name, wallTime, monoTime] = checkpoint; |
| checkpoints->push_back(Checkpoint{ |
| .name = name, |
| .wallTimeMs = wallTime, |
| .monoTimeMs = monoTime, |
| }); |
| } |
| successCb(); |
| } |
| catch (...) |
| { |
| errorCb(); |
| } |
| }, |
| "com.google.gbmc.boot_time_monitor", |
| "/xyz/openbmc_project/time/boot/" + host, |
| "xyz.openbmc_project.Time.Boot.Checkpoint", "GetCheckpointList"); |
| } |
| |
| void GetBootTimeDurations( |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp, const std::string& host, |
| const std::shared_ptr<std::vector<Duration>>& durations, |
| const std::function<void()>& successCb, |
| const std::function<void()>& errorCb) |
| { |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| managedStore::GetManagedObjectStore() |
| ->PostDbusCallToIoContextThreadSafe( |
| requestContext.GetStrand(), |
| [aResp, durations, successCb, |
| errorCb](const boost::system::error_code& ec, |
| const std::vector<DurationTuple>& durationTuples) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS Boot Time durations response error"; |
| messages::internalError(aResp->res); |
| errorCb(); |
| return; |
| } |
| try |
| { |
| for (const DurationTuple& duration : durationTuples) |
| { |
| const auto [name, durationMs] = duration; |
| durations->push_back(Duration{ |
| .name = name, |
| .durationMs = durationMs, |
| }); |
| } |
| successCb(); |
| } |
| catch (...) |
| { |
| errorCb(); |
| } |
| }, |
| "com.google.gbmc.boot_time_monitor", |
| "/xyz/openbmc_project/time/boot/" + host, |
| "xyz.openbmc_project.Time.Boot.Duration", "GetAdditionalDurations"); |
| } |
| |
| void GetIsRebooting(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& host, |
| const std::shared_ptr<bool>& isRebooting, |
| const std::function<void()>& successCb, |
| const std::function<void()>& errorCb) |
| { |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| dbus_utils::getProperty<bool>( |
| "com.google.gbmc.boot_time_monitor", |
| "/xyz/openbmc_project/time/boot/" + host, |
| "xyz.openbmc_project.Time.Boot.Statistic", "IsRebooting", |
| requestContext, |
| [aResp, isRebooting, successCb, |
| errorCb](const boost::system::error_code& ec, const bool rebooting) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS Boot Time rebooting response error" << ec; |
| messages::internalError(aResp->res); |
| errorCb(); |
| return; |
| } |
| *isRebooting = rebooting; |
| successCb(); |
| }); |
| } |
| |
| } // namespace redfish::boot_time_utils |