1 /* 2 // Copyright (c) 2019 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "health.hpp" 19 20 #include <node.hpp> 21 22 namespace redfish 23 { 24 class StorageCollection : public Node 25 { 26 public: 27 StorageCollection(CrowApp &app) : 28 Node(app, "/redfish/v1/Systems/system/Storage/") 29 { 30 entityPrivileges = { 31 {boost::beast::http::verb::get, {{"Login"}}}, 32 {boost::beast::http::verb::head, {{"Login"}}}, 33 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 34 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 35 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 36 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 37 } 38 39 private: 40 void doGet(crow::Response &res, const crow::Request &req, 41 const std::vector<std::string> ¶ms) override 42 { 43 res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection"; 44 res.jsonValue["@odata.context"] = 45 "/redfish/v1/$metadata#StorageCollection.StorageCollection"; 46 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage"; 47 res.jsonValue["Name"] = "Storage Collection"; 48 res.jsonValue["Members"] = { 49 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}}; 50 res.jsonValue["Members@odata.count"] = 1; 51 res.end(); 52 } 53 }; 54 55 class Storage : public Node 56 { 57 public: 58 Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1") 59 { 60 entityPrivileges = { 61 {boost::beast::http::verb::get, {{"Login"}}}, 62 {boost::beast::http::verb::head, {{"Login"}}}, 63 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 64 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 65 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 66 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 67 } 68 69 private: 70 void doGet(crow::Response &res, const crow::Request &req, 71 const std::vector<std::string> ¶ms) override 72 { 73 res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage"; 74 res.jsonValue["@odata.context"] = 75 "/redfish/v1/$metadata#Storage.Storage"; 76 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1"; 77 res.jsonValue["Name"] = "Storage Controller"; 78 res.jsonValue["Id"] = "1"; 79 res.jsonValue["Status"]["State"] = "Enabled"; 80 81 auto asyncResp = std::make_shared<AsyncResp>(res); 82 crow::connections::systemBus->async_method_call( 83 [asyncResp](const boost::system::error_code ec, 84 const std::vector<std::string> &storageList) { 85 nlohmann::json &storageArray = 86 asyncResp->res.jsonValue["Drives"]; 87 storageArray = nlohmann::json::array(); 88 asyncResp->res.jsonValue["Drives@odata.count"] = 0; 89 auto health = std::make_shared<HealthPopulate>(asyncResp); 90 91 if (ec) 92 { 93 BMCWEB_LOG_ERROR << "Drive mapper call error"; 94 messages::internalError(asyncResp->res); 95 return; 96 } 97 98 health->inventory = storageList; 99 health->populate(); 100 101 for (const std::string &objpath : storageList) 102 { 103 std::size_t lastPos = objpath.rfind("/"); 104 if (lastPos == std::string::npos || 105 (objpath.size() <= lastPos + 1)) 106 { 107 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 108 continue; 109 } 110 111 storageArray.push_back( 112 {{"@odata.id", 113 "/redfish/v1/Systems/system/Storage/1/Drives/" + 114 objpath.substr(lastPos + 1)}}); 115 } 116 117 asyncResp->res.jsonValue["Drives@odata.count"] = 118 storageArray.size(); 119 }, 120 "xyz.openbmc_project.ObjectMapper", 121 "/xyz/openbmc_project/object_mapper", 122 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 123 "/xyz/openbmc_project/inventory", int32_t(0), 124 std::array<const char *, 1>{ 125 "xyz.openbmc_project.Inventory.Item.Drive"}); 126 } 127 }; 128 129 class Drive : public Node 130 { 131 public: 132 Drive(CrowApp &app) : 133 Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/", 134 std::string()) 135 { 136 entityPrivileges = { 137 {boost::beast::http::verb::get, {{"Login"}}}, 138 {boost::beast::http::verb::head, {{"Login"}}}, 139 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 140 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 141 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 142 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 143 } 144 145 private: 146 void doGet(crow::Response &res, const crow::Request &req, 147 const std::vector<std::string> ¶ms) override 148 { 149 const std::string &driveId = params[0]; 150 151 auto asyncResp = std::make_shared<AsyncResp>(res); 152 153 crow::connections::systemBus->async_method_call( 154 [asyncResp, driveId]( 155 const boost::system::error_code ec, 156 const std::vector<std::pair< 157 std::string, std::vector<std::pair< 158 std::string, std::vector<std::string>>>>> 159 &subtree) { 160 if (ec) 161 { 162 BMCWEB_LOG_ERROR << "Drive mapper call error"; 163 messages::internalError(asyncResp->res); 164 return; 165 } 166 167 auto object = std::find_if( 168 subtree.begin(), subtree.end(), [&driveId](auto &object) { 169 const std::string &path = object.first; 170 return boost::ends_with(path, "/" + driveId); 171 }); 172 173 if (object == subtree.end()) 174 { 175 messages::resourceNotFound(asyncResp->res, "Drive", 176 driveId); 177 return; 178 } 179 180 const std::string &path = object->first; 181 const std::vector< 182 std::pair<std::string, std::vector<std::string>>> 183 &connectionNames = object->second; 184 185 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 186 asyncResp->res.jsonValue["@odata.context"] = 187 "/redfish/v1/$metadata#Drive.Drive"; 188 asyncResp->res.jsonValue["@odata.id"] = 189 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId; 190 asyncResp->res.jsonValue["Name"] = driveId; 191 asyncResp->res.jsonValue["Id"] = driveId; 192 193 if (connectionNames.size() != 1) 194 { 195 BMCWEB_LOG_ERROR << "Connection size " 196 << connectionNames.size() 197 << ", greater than 1"; 198 messages::internalError(asyncResp->res); 199 return; 200 } 201 202 getMainChassisId( 203 asyncResp, [](const std::string &chassisId, 204 std::shared_ptr<AsyncResp> aRsp) { 205 aRsp->res.jsonValue["Links"]["Chassis"] = { 206 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}}; 207 }); 208 209 const std::string &connectionName = connectionNames[0].first; 210 crow::connections::systemBus->async_method_call( 211 [asyncResp](const boost::system::error_code ec, 212 const std::vector<std::pair< 213 std::string, 214 std::variant<bool, std::string, uint64_t>>> 215 &propertiesList) { 216 if (ec) 217 { 218 // this interface isn't necessary 219 return; 220 } 221 for (const std::pair<std::string, 222 std::variant<bool, std::string, 223 uint64_t>> &property : 224 propertiesList) 225 { 226 // Store DBus properties that are also 227 // Redfish properties with same name and a 228 // string value 229 const std::string &propertyName = property.first; 230 if ((propertyName == "PartNumber") || 231 (propertyName == "SerialNumber") || 232 (propertyName == "Manufacturer") || 233 (propertyName == "Model")) 234 { 235 const std::string *value = 236 std::get_if<std::string>(&property.second); 237 if (value == nullptr) 238 { 239 // illegal property 240 messages::internalError(asyncResp->res); 241 continue; 242 } 243 asyncResp->res.jsonValue[propertyName] = *value; 244 } 245 } 246 }, 247 connectionName, path, "org.freedesktop.DBus.Properties", 248 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); 249 250 // default it to Enabled 251 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 252 253 auto health = std::make_shared<HealthPopulate>(asyncResp); 254 health->inventory = std::vector<std::string>{path}; 255 256 health->populate(); 257 258 crow::connections::systemBus->async_method_call( 259 [asyncResp, path](const boost::system::error_code ec, 260 const std::variant<bool> present) { 261 // this interface isn't necessary, only check it if we 262 // get a good return 263 if (ec) 264 { 265 return; 266 } 267 const bool *enabled = std::get_if<bool>(&present); 268 if (enabled == nullptr) 269 { 270 BMCWEB_LOG_DEBUG << "Illegal property present"; 271 messages::internalError(asyncResp->res); 272 return; 273 } 274 if (!(*enabled)) 275 { 276 asyncResp->res.jsonValue["Status"]["State"] = 277 "Disabled"; 278 } 279 }, 280 connectionName, path, "org.freedesktop.DBus.Properties", 281 "Get", "xyz.openbmc_project.Inventory.Item", "Present"); 282 283 crow::connections::systemBus->async_method_call( 284 [asyncResp](const boost::system::error_code ec, 285 const std::variant<bool> rebuilding) { 286 // this interface isn't necessary, only check it if we 287 // get a good return 288 if (ec) 289 { 290 return; 291 } 292 const bool *updating = std::get_if<bool>(&rebuilding); 293 if (updating == nullptr) 294 { 295 BMCWEB_LOG_DEBUG << "Illegal property present"; 296 messages::internalError(asyncResp->res); 297 return; 298 } 299 300 // updating and disabled in the backend shouldn't be 301 // able to be set at the same time, so we don't need to 302 // check for the race condition of these two calls 303 if ((*updating)) 304 { 305 asyncResp->res.jsonValue["Status"]["State"] = 306 "Updating"; 307 } 308 }, 309 connectionName, path, "org.freedesktop.DBus.Properties", 310 "Get", "xyz.openbmc_project.State.Drive", "Rebuilding"); 311 }, 312 "xyz.openbmc_project.ObjectMapper", 313 "/xyz/openbmc_project/object_mapper", 314 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 315 "/xyz/openbmc_project/inventory", int32_t(0), 316 std::array<const char *, 1>{ 317 "xyz.openbmc_project.Inventory.Item.Drive"}); 318 } 319 }; 320 } // namespace redfish 321