blob: 7167503f815de5e06ab9e83e8e4e134f6ea8d1f9 [file] [log] [blame]
/*
// Copyright (c) 2019 Intel Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#pragma once
#include "health.hpp"
#include "openbmc_dbus_rest.hpp"
#include <node.hpp>
namespace redfish
{
class StorageCollection : public Node
{
public:
StorageCollection(App& app) :
Node(app, "/redfish/v1/Systems/system/Storage/")
{
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
{boost::beast::http::verb::head, {{"Login"}}},
{boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
{boost::beast::http::verb::put, {{"ConfigureComponents"}}},
{boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
{boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
}
private:
void doGet(crow::Response& res, const crow::Request&,
const std::vector<std::string>&) override
{
res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection";
res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage";
res.jsonValue["Name"] = "Storage Collection";
res.jsonValue["Members"] = {
{{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}};
res.jsonValue["Members@odata.count"] = 1;
res.end();
}
};
class Storage : public Node
{
public:
Storage(App& app) : Node(app, "/redfish/v1/Systems/system/Storage/1/")
{
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
{boost::beast::http::verb::head, {{"Login"}}},
{boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
{boost::beast::http::verb::put, {{"ConfigureComponents"}}},
{boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
{boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
}
private:
void doGet(crow::Response& res, const crow::Request&,
const std::vector<std::string>&) override
{
res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage";
res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1";
res.jsonValue["Name"] = "Storage";
res.jsonValue["Id"] = "1";
res.jsonValue["Status"]["State"] = "Enabled";
auto asyncResp = std::make_shared<AsyncResp>(res);
auto health = std::make_shared<HealthPopulate>(asyncResp);
health->populate();
crow::connections::systemBus->async_method_call(
[asyncResp, health](const boost::system::error_code ec,
const std::vector<std::string>& storageList) {
nlohmann::json& storageArray =
asyncResp->res.jsonValue["Drives"];
storageArray = nlohmann::json::array();
auto& count = asyncResp->res.jsonValue["Drives@odata.count"];
count = 0;
if (ec)
{
BMCWEB_LOG_ERROR << "Drive mapper call error";
messages::internalError(asyncResp->res);
return;
}
health->inventory.insert(health->inventory.end(),
storageList.begin(),
storageList.end());
for (const std::string& objpath : storageList)
{
std::size_t lastPos = objpath.rfind('/');
if (lastPos == std::string::npos ||
(objpath.size() <= lastPos + 1))
{
BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath;
continue;
}
storageArray.push_back(
{{"@odata.id",
"/redfish/v1/Systems/system/Storage/1/Drives/" +
objpath.substr(lastPos + 1)}});
}
count = storageArray.size();
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
"/xyz/openbmc_project/inventory", int32_t(0),
std::array<const char*, 1>{
"xyz.openbmc_project.Inventory.Item.Drive"});
crow::connections::systemBus->async_method_call(
[asyncResp,
health](const boost::system::error_code ec,
const crow::openbmc_mapper::GetSubTreeType& subtree) {
if (ec || !subtree.size())
{
// doesn't have to be there
return;
}
nlohmann::json& root =
asyncResp->res.jsonValue["StorageControllers"];
root = nlohmann::json::array();
for (const auto& [path, interfaceDict] : subtree)
{
std::size_t lastPos = path.rfind('/');
if (lastPos == std::string::npos ||
(path.size() <= lastPos + 1))
{
BMCWEB_LOG_ERROR << "Failed to find '/' in " << path;
return;
}
if (interfaceDict.size() != 1)
{
BMCWEB_LOG_ERROR << "Connection size "
<< interfaceDict.size()
<< ", greater than 1";
messages::internalError(asyncResp->res);
return;
}
const std::string& connectionName =
interfaceDict.front().first;
size_t index = root.size();
nlohmann::json& storageController =
root.emplace_back(nlohmann::json::object());
std::string id = path.substr(lastPos + 1);
storageController["@odata.type"] =
"#Storage.v1_7_0.StorageController";
storageController["@odata.id"] =
"/redfish/v1/Systems/system/Storage/1"
"#/StorageControllers/" +
std::to_string(index);
storageController["Name"] = id;
storageController["MemberId"] = id;
storageController["Status"]["State"] = "Enabled";
crow::connections::systemBus->async_method_call(
[asyncResp, index](const boost::system::error_code ec2,
const std::variant<bool> present) {
// this interface isn't necessary, only check it if
// we get a good return
if (ec2)
{
return;
}
const bool* enabled = std::get_if<bool>(&present);
if (enabled == nullptr)
{
BMCWEB_LOG_DEBUG << "Illegal property present";
messages::internalError(asyncResp->res);
return;
}
if (!(*enabled))
{
asyncResp->res
.jsonValue["StorageControllers"][index]
["Status"]["State"] = "Disabled";
}
},
connectionName, path, "org.freedesktop.DBus.Properties",
"Get", "xyz.openbmc_project.Inventory.Item", "Present");
crow::connections::systemBus->async_method_call(
[asyncResp,
index](const boost::system::error_code ec2,
const std::vector<std::pair<
std::string,
std::variant<bool, std::string, uint64_t>>>&
propertiesList) {
if (ec2)
{
// this interface isn't necessary
return;
}
for (const std::pair<
std::string,
std::variant<bool, std::string, uint64_t>>&
property : propertiesList)
{
// Store DBus properties that are also
// Redfish properties with same name and a
// string value
const std::string& propertyName =
property.first;
nlohmann::json& object =
asyncResp->res
.jsonValue["StorageControllers"][index];
if ((propertyName == "PartNumber") ||
(propertyName == "SerialNumber") ||
(propertyName == "Manufacturer") ||
(propertyName == "Model"))
{
const std::string* value =
std::get_if<std::string>(
&property.second);
if (value == nullptr)
{
// illegal property
messages::internalError(asyncResp->res);
continue;
}
object[propertyName] = *value;
}
}
},
connectionName, path, "org.freedesktop.DBus.Properties",
"GetAll",
"xyz.openbmc_project.Inventory.Decorator.Asset");
}
// this is done after we know the json array will no longer be
// resized, as json::array uses vector underneath and we need
// references to its members that won't change
size_t count = 0;
for (const auto& [path, interfaceDict] : subtree)
{
auto subHealth = std::make_shared<HealthPopulate>(
asyncResp, root[count]["Status"]);
subHealth->inventory.emplace_back(path);
health->inventory.emplace_back(path);
health->children.emplace_back(subHealth);
count++;
}
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/inventory", int32_t(0),
std::array<const char*, 1>{
"xyz.openbmc_project.Inventory.Item.StorageController"});
}
};
class Drive : public Node
{
public:
Drive(App& app) :
Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/",
std::string())
{
entityPrivileges = {
{boost::beast::http::verb::get, {{"Login"}}},
{boost::beast::http::verb::head, {{"Login"}}},
{boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
{boost::beast::http::verb::put, {{"ConfigureComponents"}}},
{boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
{boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
}
private:
void doGet(crow::Response& res, const crow::Request&,
const std::vector<std::string>& params) override
{
auto asyncResp = std::make_shared<AsyncResp>(res);
if (params.size() != 1)
{
messages::internalError(asyncResp->res);
return;
}
const std::string& driveId = params[0];
crow::connections::systemBus->async_method_call(
[asyncResp,
driveId](const boost::system::error_code ec,
const crow::openbmc_mapper::GetSubTreeType& subtree) {
if (ec)
{
BMCWEB_LOG_ERROR << "Drive mapper call error";
messages::internalError(asyncResp->res);
return;
}
auto object2 = std::find_if(
subtree.begin(), subtree.end(), [&driveId](auto& object) {
const std::string& path = object.first;
return boost::ends_with(path, "/" + driveId);
});
if (object2 == subtree.end())
{
messages::resourceNotFound(asyncResp->res, "Drive",
driveId);
return;
}
const std::string& path = object2->first;
const std::vector<
std::pair<std::string, std::vector<std::string>>>&
connectionNames = object2->second;
asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
asyncResp->res.jsonValue["@odata.id"] =
"/redfish/v1/Systems/system/Storage/1/Drives/" + driveId;
asyncResp->res.jsonValue["Name"] = driveId;
asyncResp->res.jsonValue["Id"] = driveId;
if (connectionNames.size() != 1)
{
BMCWEB_LOG_ERROR << "Connection size "
<< connectionNames.size()
<< ", greater than 1";
messages::internalError(asyncResp->res);
return;
}
getMainChassisId(
asyncResp, [](const std::string& chassisId,
const std::shared_ptr<AsyncResp>& aRsp) {
aRsp->res.jsonValue["Links"]["Chassis"] = {
{"@odata.id", "/redfish/v1/Chassis/" + chassisId}};
});
const std::string& connectionName = connectionNames[0].first;
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec2,
const std::vector<std::pair<
std::string,
std::variant<bool, std::string, uint64_t>>>&
propertiesList) {
if (ec2)
{
// this interface isn't necessary
return;
}
for (const std::pair<std::string,
std::variant<bool, std::string,
uint64_t>>& property :
propertiesList)
{
// Store DBus properties that are also
// Redfish properties with same name and a
// string value
const std::string& propertyName = property.first;
if ((propertyName == "PartNumber") ||
(propertyName == "SerialNumber") ||
(propertyName == "Manufacturer") ||
(propertyName == "Model"))
{
const std::string* value =
std::get_if<std::string>(&property.second);
if (value == nullptr)
{
// illegal property
messages::internalError(asyncResp->res);
continue;
}
asyncResp->res.jsonValue[propertyName] = *value;
}
}
},
connectionName, path, "org.freedesktop.DBus.Properties",
"GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset");
// default it to Enabled
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
auto health = std::make_shared<HealthPopulate>(asyncResp);
health->inventory.emplace_back(path);
health->populate();
crow::connections::systemBus->async_method_call(
[asyncResp, path](const boost::system::error_code ec2,
const std::variant<bool> present) {
// this interface isn't necessary, only check it if we
// get a good return
if (ec2)
{
return;
}
const bool* enabled = std::get_if<bool>(&present);
if (enabled == nullptr)
{
BMCWEB_LOG_DEBUG << "Illegal property present";
messages::internalError(asyncResp->res);
return;
}
if (!(*enabled))
{
asyncResp->res.jsonValue["Status"]["State"] =
"Disabled";
}
},
connectionName, path, "org.freedesktop.DBus.Properties",
"Get", "xyz.openbmc_project.Inventory.Item", "Present");
crow::connections::systemBus->async_method_call(
[asyncResp](const boost::system::error_code ec2,
const std::variant<bool> rebuilding) {
// this interface isn't necessary, only check it if we
// get a good return
if (ec2)
{
return;
}
const bool* updating = std::get_if<bool>(&rebuilding);
if (updating == nullptr)
{
BMCWEB_LOG_DEBUG << "Illegal property present";
messages::internalError(asyncResp->res);
return;
}
// updating and disabled in the backend shouldn't be
// able to be set at the same time, so we don't need to
// check for the race condition of these two calls
if ((*updating))
{
asyncResp->res.jsonValue["Status"]["State"] =
"Updating";
}
},
connectionName, path, "org.freedesktop.DBus.Properties",
"Get", "xyz.openbmc_project.State.Drive", "Rebuilding");
},
"xyz.openbmc_project.ObjectMapper",
"/xyz/openbmc_project/object_mapper",
"xyz.openbmc_project.ObjectMapper", "GetSubTree",
"/xyz/openbmc_project/inventory", int32_t(0),
std::array<const char*, 1>{
"xyz.openbmc_project.Inventory.Item.Drive"});
}
};
} // namespace redfish