| #include "bmc/register_actions_bmc.h" |
| |
| #include <cstdint> |
| #include <memory> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| #include <vector> |
| |
| #include "action_context.h" |
| #include "bmc/http_connection.h" |
| #include "safepower_agent.pb.h" |
| #include "safepower_agent_config.pb.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/log/log.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_split.h" |
| #include "absl/strings/string_view.h" |
| |
| // NOLINTBEGIN(readability/boost) |
| #include "boost/asio/ip/tcp.hpp" |
| #include "boost/beast/core.hpp" |
| #include "boost/beast/http.hpp" |
| // NOLINTEND(readability/boost) |
| |
| #include "nlohmann/json.hpp" |
| #include "nlohmann/json_fwd.hpp" |
| |
| namespace safepower_agent { |
| |
| using safepower_agent_config::SafePowerAgentConfig; |
| |
| namespace beast = boost::beast; |
| namespace http = beast::http; |
| |
| // runs after the action is called, parses the response to a status |
| inline absl::Status GenericRedfishParser(absl::StatusOr<nlohmann::json> js) { |
| if (!js.ok()) { |
| LOG(ERROR) << "BMC connection failed: " << js.status(); |
| return js.status(); |
| } |
| auto extended_info = (*js)["@Message.ExtendedInfo"]; |
| if (extended_info.size() !=1){ |
| LOG(ERROR) << "redfish response has an abnormal number of message : " |
| << *js; |
| } |
| auto info = extended_info[0]; |
| auto message_id = info.find("MessageId"); |
| if (message_id == info.end()) { |
| LOG(ERROR) << "Failed to parse bmc's response to JSON: " << *js; |
| return absl::InvalidArgumentError("Failed to parse bmc's response to JSON"); |
| } |
| std::string status = *message_id; |
| std::vector<std::string> v = absl::StrSplit(status, '.'); |
| if (v.size() >= 4 && v[4] == "Success") { |
| return absl::OkStatus(); |
| } else { |
| LOG(ERROR) << "Failed to power off :" << *message_id; |
| return absl::NotFoundError("Failed to power off :" + message_id->dump()); |
| } |
| } |
| |
| // runs when then action is called, perform the action |
| inline void GenericRedfishPowerOperation( |
| absl::string_view uri, nlohmann::json body, std::string_view ip, |
| uint16_t port, absl::AnyInvocable<void(absl::Status) &&> cb, |
| const safepower_agent_proto::Action&) { |
| LOG(INFO) << "Redfish action " << ip << ":" << port << " " << |
| uri << " " << body; |
| auto connection = std::make_shared<HttpConnection>(); |
| connection->PerformConnection( |
| http::verb::post, uri, |
| [callback = std::move(cb)](absl::StatusOr<nlohmann::json> js) mutable { |
| absl::Status status = GenericRedfishParser(js); |
| std::move(callback)(status); |
| }, |
| body, ip, port); |
| } |
| |
| // runs at startup to register all actions from the config |
| absl::Status RegisterActionFromConfig( |
| ActionContextManager* action_context_manager, |
| const SafePowerAgentConfig& config) { |
| for (const auto& action_config : config.action_configs()) { |
| safepower_agent_proto::Action action_to_register = action_config.action(); |
| // The config will map actions to redfish (uris and body) |
| // or dbus (path, method, interface, value) |
| nlohmann::json body = nlohmann::json::parse( |
| action_config.redfish().json_body(), /* cb */ nullptr, |
| /* allow_exceptions */ false); |
| if (body.is_discarded()){ |
| LOG(ERROR) << "Failed to parse json body: " << |
| action_config.redfish().json_body(); |
| return absl::InvalidArgumentError("Failed to parse json body: " + |
| action_config.redfish().json_body()); |
| } |
| std::string uri = action_config.redfish().uri(); |
| uint32_t port = action_config.redfish().port(); |
| if (port > 65535) { |
| LOG(ERROR) << "Port number is invalid: " << port; |
| return absl::InvalidArgumentError("Port number is invalid: " + |
| std::to_string(port)); |
| } |
| std::string ip = action_config.redfish().ip(); |
| LOG(INFO) << "registering " << action_config.action_name() << " " |
| << ip << ":" << port << " " << uri << " " << body; |
| |
| absl::Status action_status = action_context_manager->RegisterAction( |
| action_to_register, |
| [uri, body, port, ip](const safepower_agent_proto::Action& a, |
| absl::AnyInvocable<void(absl::Status) &&> cb) { |
| GenericRedfishPowerOperation(uri, body, ip, port, std::move(cb), a); |
| }); |
| |
| if (!action_status.ok()) { |
| LOG(ERROR) << "Unable to register action :" << action_status; |
| return action_status; |
| } |
| } |
| LOG(INFO) << "All actions registered"; |
| return absl::OkStatus(); |
| } |
| } // namespace safepower_agent |