| #include "boottime_callback.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <tuple> |
| #include <utility> |
| #include <vector> |
| |
| #include "boottime.grpc.pb.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/time/time.h" |
| #include "boottime_api/boottime_api_common.h" |
| #include "boottime_api/node_config.h" |
| #include "boottime_api/resource.h" |
| #include "grpcpp/server_context.h" |
| #include "grpcpp/support/server_callback.h" |
| #include "grpcpp/support/status.h" |
| |
| namespace blobs { |
| |
| namespace btm = boot_time_monitor; |
| |
| grpc::Status BootTimeServiceImpl::ToGrpcStatus(absl::Status s) { |
| if (s.ok()) { |
| return grpc::Status::OK; |
| } |
| // The grpc & absl code have 1:1 mapping from 0 (OK) to 15 (DATA_LOSS). |
| grpc::StatusCode code = static_cast<grpc::StatusCode>(s.code()); |
| if (code > grpc::StatusCode::DATA_LOSS) { |
| code = grpc::StatusCode::UNKNOWN; |
| } |
| return grpc::Status(static_cast<grpc::StatusCode>(s.code()), |
| std::string(s.message())); |
| } |
| |
| absl::Status BootTimeServiceImpl::RegisterSelfToApi() { |
| if (base_path_.empty()) { |
| // Using default FileResource path |
| return boottime_api_.RegisterNode(node_config_); |
| } else { |
| // Using custom base path |
| auto resource = std::make_unique<btm::resource::FileResource>( |
| node_config_.node_name, |
| base_path_ + std::string(btm::def::kDataDir)); |
| return boottime_api_.RegisterNode(node_config_, std::move(resource)); |
| } |
| } |
| |
| BootTimeServiceImpl::BootTimeServiceImpl(int instance) |
| : phosphor::boottime::grpc_gen::BootTimeService::CallbackService(), |
| instance_number_(instance), |
| base_path_(), |
| node_config_("host" + std::to_string(instance), |
| std::string(btm::kBootTimeTagHost)) { |
| RegisterSelfToApi().IgnoreError(); |
| } |
| |
| BootTimeServiceImpl::BootTimeServiceImpl(int instance, |
| const std::string& basePath) |
| : phosphor::boottime::grpc_gen::BootTimeService::CallbackService(), |
| instance_number_(instance), |
| base_path_(basePath), |
| node_config_("host" + std::to_string(instance), |
| std::string(btm::kBootTimeTagHost)), |
| boottime_api_(basePath + std::string(btm::def::kLockFile), |
| absl::Seconds(5)) { |
| auto resource = std::make_unique<btm::resource::FileResource>( |
| node_config_.node_name, |
| basePath + std::string(btm::def::kDataDir)); |
| RegisterSelfToApi().IgnoreError(); |
| } |
| |
| grpc::ServerUnaryReactor* BootTimeServiceImpl::SetCheckpoint( |
| [[maybe_unused]] grpc::CallbackServerContext* context, |
| const phosphor::boottime::SetCheckpointRequest* request, |
| [[maybe_unused]] phosphor::boottime::SetCheckpointResponse* response) { |
| LOG(INFO) << "SetCheckpoint[" << instance_number_ |
| << "]: name=" << request->name() |
| << ", timestamp=" << request->timestamp_ms() |
| << ", duration=" << request->duration_ms(); |
| |
| grpc::ServerUnaryReactor* reactor = context->DefaultReactor(); |
| // Check request content |
| // Note that timestamp or duration is optional and can be 0 |
| if (request->name().empty() || request->name().length() > 1024) { |
| reactor->Finish(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, |
| "Invalid name length")); |
| return reactor; |
| } |
| |
| absl::Status ret = boottime_api_.SetNodeCheckpoint( |
| node_config_, request->name(), request->timestamp_ms(), |
| request->duration_ms()); |
| if (ret.code() == absl::StatusCode::kNotFound) { |
| // Retry node register if the node is not found. |
| LOG(ERROR) << "Node not found. Retrying node registration."; |
| if (!(ret = RegisterSelfToApi()).ok()) { |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| ret = boottime_api_.SetNodeCheckpoint(node_config_, request->name(), |
| request->timestamp_ms(), |
| request->duration_ms()); |
| } |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| |
| grpc::ServerUnaryReactor* BootTimeServiceImpl::RebootComplete( |
| [[maybe_unused]] grpc::CallbackServerContext* context, |
| [[maybe_unused]] const phosphor::boottime::RebootCompleteRequest* request, |
| [[maybe_unused]] phosphor::boottime::RebootCompleteResponse* response) { |
| LOG(INFO) << "RebootComplete[" << instance_number_ << "]"; |
| |
| grpc::ServerUnaryReactor* reactor = context->DefaultReactor(); |
| absl::Status ret = boottime_api_.NotifyNodeComplete(node_config_); |
| |
| if (ret.code() == absl::StatusCode::kNotFound) { |
| // Retry node register if the node is not found. |
| LOG(ERROR) << "Node not found. Retrying node registration."; |
| if (!(ret = RegisterSelfToApi()).ok()) { |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| ret = boottime_api_.NotifyNodeComplete(node_config_); |
| } |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| |
| grpc::ServerUnaryReactor* BootTimeServiceImpl::SetDuration( |
| [[maybe_unused]] grpc::CallbackServerContext* context, |
| const phosphor::boottime::SetDurationRequest* request, |
| [[maybe_unused]] phosphor::boottime::SetDurationResponse* response) { |
| LOG(INFO) << "SetDuration[" << instance_number_ |
| << "]: name=" << request->name() |
| << ", duration=" << request->duration_ms(); |
| |
| grpc::ServerUnaryReactor* reactor = context->DefaultReactor(); |
| // Check request content |
| if (request->name().empty() || request->name().length() > 1024) { |
| reactor->Finish(grpc::Status(grpc::StatusCode::INVALID_ARGUMENT, |
| "Invalid name length")); |
| return reactor; |
| } |
| |
| absl::Status ret = boottime_api_.SetNodeDuration( |
| node_config_, request->name(), request->duration_ms()); |
| if (ret.code() == absl::StatusCode::kNotFound) { |
| // Retry node register if the node is not found. |
| LOG(ERROR) << "Node not found. Retrying node registration."; |
| if (!(ret = RegisterSelfToApi()).ok()) { |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| ret = boottime_api_.SetNodeDuration(node_config_, request->name(), |
| request->duration_ms()); |
| } |
| reactor->Finish(ToGrpcStatus(ret)); |
| return reactor; |
| } |
| |
| grpc::ServerUnaryReactor* BootTimeServiceImpl::GetBootTime( |
| [[maybe_unused]] grpc::CallbackServerContext* context, |
| [[maybe_unused]] const phosphor::boottime::GetBootTimeRequest* request, |
| phosphor::boottime::GetBootTimeResponse* response) { |
| LOG(INFO) << "GetBootTime[" << instance_number_ << "]"; |
| |
| grpc::ServerUnaryReactor* reactor = context->DefaultReactor(); |
| std::vector<std::tuple<std::string, int64_t, int64_t>> checkpoints = |
| boottime_api_.GetNodeCheckpointList(node_config_); |
| for (const auto& [name, wall_time_ms, mono_time_ms] : checkpoints) { |
| auto* rbf = response->add_reboot_flow(); |
| rbf->set_stage(name); |
| rbf->set_timestamp_wall_ms(wall_time_ms); |
| rbf->set_timestamp_mono_ms(mono_time_ms); |
| } |
| |
| std::vector<std::tuple<std::string, int64_t>> durations = |
| boottime_api_.GetNodeAdditionalDurations(node_config_); |
| for (const auto& [name, duration_ms] : durations) { |
| auto* d = response->add_durations(); |
| d->set_name(name); |
| d->set_duration_ms(duration_ms); |
| } |
| |
| response->mutable_stat()->set_is_rebooting( |
| boottime_api_.IsNodeRebooting(node_config_)); |
| |
| reactor->Finish(grpc::Status::OK); |
| return reactor; |
| } |
| |
| } // namespace blobs |