blob: 765529343008237ff8df89d26c1d589c00c32220 [file]
#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