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 #include "openbmc_dbus_rest.hpp" 20 21 #include <node.hpp> 22 23 namespace redfish 24 { 25 class StorageCollection : public Node 26 { 27 public: 28 StorageCollection(CrowApp &app) : 29 Node(app, "/redfish/v1/Systems/system/Storage/") 30 { 31 entityPrivileges = { 32 {boost::beast::http::verb::get, {{"Login"}}}, 33 {boost::beast::http::verb::head, {{"Login"}}}, 34 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 35 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 36 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 37 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 38 } 39 40 private: 41 void doGet(crow::Response &res, const crow::Request &req, 42 const std::vector<std::string> ¶ms) override 43 { 44 res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection"; 45 res.jsonValue["@odata.context"] = 46 "/redfish/v1/$metadata#StorageCollection.StorageCollection"; 47 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage"; 48 res.jsonValue["Name"] = "Storage Collection"; 49 res.jsonValue["Members"] = { 50 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}}; 51 res.jsonValue["Members@odata.count"] = 1; 52 res.end(); 53 } 54 }; 55 56 class Storage : public Node 57 { 58 public: 59 Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1") 60 { 61 entityPrivileges = { 62 {boost::beast::http::verb::get, {{"Login"}}}, 63 {boost::beast::http::verb::head, {{"Login"}}}, 64 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 65 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 66 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 67 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 68 } 69 70 private: 71 void doGet(crow::Response &res, const crow::Request &req, 72 const std::vector<std::string> ¶ms) override 73 { 74 res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage"; 75 res.jsonValue["@odata.context"] = 76 "/redfish/v1/$metadata#Storage.Storage"; 77 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1"; 78 res.jsonValue["Name"] = "Storage"; 79 res.jsonValue["Id"] = "1"; 80 res.jsonValue["Status"]["State"] = "Enabled"; 81 82 auto asyncResp = std::make_shared<AsyncResp>(res); 83 auto health = std::make_shared<HealthPopulate>(asyncResp); 84 health->populate(); 85 86 crow::connections::systemBus->async_method_call( 87 [asyncResp, health](const boost::system::error_code ec, 88 const std::vector<std::string> &storageList) { 89 nlohmann::json &storageArray = 90 asyncResp->res.jsonValue["Drives"]; 91 storageArray = nlohmann::json::array(); 92 auto &count = asyncResp->res.jsonValue["Drives@odata.count"]; 93 count = 0; 94 95 if (ec) 96 { 97 BMCWEB_LOG_ERROR << "Drive mapper call error"; 98 messages::internalError(asyncResp->res); 99 return; 100 } 101 102 health->inventory.insert(health->inventory.end(), 103 storageList.begin(), 104 storageList.end()); 105 106 for (const std::string &objpath : storageList) 107 { 108 std::size_t lastPos = objpath.rfind("/"); 109 if (lastPos == std::string::npos || 110 (objpath.size() <= lastPos + 1)) 111 { 112 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 113 continue; 114 } 115 116 storageArray.push_back( 117 {{"@odata.id", 118 "/redfish/v1/Systems/system/Storage/1/Drives/" + 119 objpath.substr(lastPos + 1)}}); 120 } 121 122 count = storageArray.size(); 123 }, 124 "xyz.openbmc_project.ObjectMapper", 125 "/xyz/openbmc_project/object_mapper", 126 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 127 "/xyz/openbmc_project/inventory", int32_t(0), 128 std::array<const char *, 1>{ 129 "xyz.openbmc_project.Inventory.Item.Drive"}); 130 131 crow::connections::systemBus->async_method_call( 132 [asyncResp, 133 health](const boost::system::error_code ec, 134 const crow::openbmc_mapper::GetSubTreeType &subtree) { 135 if (ec || !subtree.size()) 136 { 137 // doesn't have to be there 138 return; 139 } 140 141 nlohmann::json &root = 142 asyncResp->res.jsonValue["StorageControllers"]; 143 root = nlohmann::json::array(); 144 for (const auto &[path, interfaceDict] : subtree) 145 { 146 std::size_t lastPos = path.rfind("/"); 147 if (lastPos == std::string::npos || 148 (path.size() <= lastPos + 1)) 149 { 150 BMCWEB_LOG_ERROR << "Failed to find '/' in " << path; 151 return; 152 } 153 154 if (interfaceDict.size() != 1) 155 { 156 BMCWEB_LOG_ERROR << "Connection size " 157 << interfaceDict.size() 158 << ", greater than 1"; 159 messages::internalError(asyncResp->res); 160 return; 161 } 162 163 const std::string &connectionName = 164 interfaceDict.front().first; 165 166 size_t index = root.size(); 167 nlohmann::json &storageController = 168 root.emplace_back(nlohmann::json::object()); 169 170 std::string id = path.substr(lastPos + 1); 171 172 storageController["@odata.type"] = 173 "#Storage.v1_7_0.StorageController"; 174 storageController["@odata.context"] = 175 "/redfish/v1/$metadata#Storage.StorageController"; 176 storageController["@odata.id"] = 177 "/redfish/v1/Systems/system/Storage/1" 178 "#/StorageControllers/" + 179 std::to_string(index); 180 storageController["Name"] = id; 181 storageController["MemberId"] = id; 182 storageController["Status"]["State"] = "Enabled"; 183 184 crow::connections::systemBus->async_method_call( 185 [asyncResp, index](const boost::system::error_code ec, 186 const std::variant<bool> present) { 187 // this interface isn't necessary, only check it if 188 // we get a good return 189 if (ec) 190 { 191 return; 192 } 193 const bool *enabled = std::get_if<bool>(&present); 194 if (enabled == nullptr) 195 { 196 BMCWEB_LOG_DEBUG << "Illegal property present"; 197 messages::internalError(asyncResp->res); 198 return; 199 } 200 if (!(*enabled)) 201 { 202 asyncResp->res 203 .jsonValue["StorageControllers"][index] 204 ["Status"]["State"] = "Disabled"; 205 } 206 }, 207 connectionName, path, "org.freedesktop.DBus.Properties", 208 "Get", "xyz.openbmc_project.Inventory.Item", "Present"); 209 210 crow::connections::systemBus->async_method_call( 211 [asyncResp, 212 index](const boost::system::error_code ec, 213 const std::vector<std::pair< 214 std::string, 215 std::variant<bool, std::string, uint64_t>>> 216 &propertiesList) { 217 if (ec) 218 { 219 // this interface isn't necessary 220 return; 221 } 222 for (const std::pair< 223 std::string, 224 std::variant<bool, std::string, uint64_t>> 225 &property : propertiesList) 226 { 227 // Store DBus properties that are also 228 // Redfish properties with same name and a 229 // string value 230 const std::string &propertyName = 231 property.first; 232 nlohmann::json &object = 233 asyncResp->res 234 .jsonValue["StorageControllers"][index]; 235 if ((propertyName == "PartNumber") || 236 (propertyName == "SerialNumber") || 237 (propertyName == "Manufacturer") || 238 (propertyName == "Model")) 239 { 240 const std::string *value = 241 std::get_if<std::string>( 242 &property.second); 243 if (value == nullptr) 244 { 245 // illegal property 246 messages::internalError(asyncResp->res); 247 continue; 248 } 249 object[propertyName] = *value; 250 } 251 } 252 }, 253 connectionName, path, "org.freedesktop.DBus.Properties", 254 "GetAll", 255 "xyz.openbmc_project.Inventory.Decorator.Asset"); 256 } 257 258 // this is done after we know the json array will no longer be 259 // resized, as json::array uses vector underneath and we need 260 // references to its members that won't change 261 size_t count = 0; 262 for (const auto &[path, interfaceDict] : subtree) 263 { 264 auto subHealth = std::make_shared<HealthPopulate>( 265 asyncResp, root[count]["Status"]); 266 subHealth->inventory.emplace_back(path); 267 health->inventory.emplace_back(path); 268 health->children.emplace_back(subHealth); 269 count++; 270 } 271 }, 272 "xyz.openbmc_project.ObjectMapper", 273 "/xyz/openbmc_project/object_mapper", 274 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 275 "/xyz/openbmc_project/inventory", int32_t(0), 276 std::array<const char *, 1>{ 277 "xyz.openbmc_project.Inventory.Item.StorageController"}); 278 } 279 }; 280 281 class Drive : public Node 282 { 283 public: 284 Drive(CrowApp &app) : 285 Node(app, "/redfish/v1/Systems/system/Storage/1/Drives/<str>/", 286 std::string()) 287 { 288 entityPrivileges = { 289 {boost::beast::http::verb::get, {{"Login"}}}, 290 {boost::beast::http::verb::head, {{"Login"}}}, 291 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 292 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 293 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 294 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 295 } 296 297 private: 298 void doGet(crow::Response &res, const crow::Request &req, 299 const std::vector<std::string> ¶ms) override 300 { 301 auto asyncResp = std::make_shared<AsyncResp>(res); 302 if (params.size() != 1) 303 { 304 messages::internalError(asyncResp->res); 305 return; 306 } 307 const std::string &driveId = params[0]; 308 309 crow::connections::systemBus->async_method_call( 310 [asyncResp, 311 driveId](const boost::system::error_code ec, 312 const crow::openbmc_mapper::GetSubTreeType &subtree) { 313 if (ec) 314 { 315 BMCWEB_LOG_ERROR << "Drive mapper call error"; 316 messages::internalError(asyncResp->res); 317 return; 318 } 319 320 auto object = std::find_if( 321 subtree.begin(), subtree.end(), [&driveId](auto &object) { 322 const std::string &path = object.first; 323 return boost::ends_with(path, "/" + driveId); 324 }); 325 326 if (object == subtree.end()) 327 { 328 messages::resourceNotFound(asyncResp->res, "Drive", 329 driveId); 330 return; 331 } 332 333 const std::string &path = object->first; 334 const std::vector< 335 std::pair<std::string, std::vector<std::string>>> 336 &connectionNames = object->second; 337 338 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 339 asyncResp->res.jsonValue["@odata.context"] = 340 "/redfish/v1/$metadata#Drive.Drive"; 341 asyncResp->res.jsonValue["@odata.id"] = 342 "/redfish/v1/Systems/system/Storage/1/Drives/" + driveId; 343 asyncResp->res.jsonValue["Name"] = driveId; 344 asyncResp->res.jsonValue["Id"] = driveId; 345 346 if (connectionNames.size() != 1) 347 { 348 BMCWEB_LOG_ERROR << "Connection size " 349 << connectionNames.size() 350 << ", greater than 1"; 351 messages::internalError(asyncResp->res); 352 return; 353 } 354 355 getMainChassisId( 356 asyncResp, [](const std::string &chassisId, 357 std::shared_ptr<AsyncResp> aRsp) { 358 aRsp->res.jsonValue["Links"]["Chassis"] = { 359 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}}; 360 }); 361 362 const std::string &connectionName = connectionNames[0].first; 363 crow::connections::systemBus->async_method_call( 364 [asyncResp](const boost::system::error_code ec, 365 const std::vector<std::pair< 366 std::string, 367 std::variant<bool, std::string, uint64_t>>> 368 &propertiesList) { 369 if (ec) 370 { 371 // this interface isn't necessary 372 return; 373 } 374 for (const std::pair<std::string, 375 std::variant<bool, std::string, 376 uint64_t>> &property : 377 propertiesList) 378 { 379 // Store DBus properties that are also 380 // Redfish properties with same name and a 381 // string value 382 const std::string &propertyName = property.first; 383 if ((propertyName == "PartNumber") || 384 (propertyName == "SerialNumber") || 385 (propertyName == "Manufacturer") || 386 (propertyName == "Model")) 387 { 388 const std::string *value = 389 std::get_if<std::string>(&property.second); 390 if (value == nullptr) 391 { 392 // illegal property 393 messages::internalError(asyncResp->res); 394 continue; 395 } 396 asyncResp->res.jsonValue[propertyName] = *value; 397 } 398 } 399 }, 400 connectionName, path, "org.freedesktop.DBus.Properties", 401 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); 402 403 // default it to Enabled 404 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 405 406 auto health = std::make_shared<HealthPopulate>(asyncResp); 407 health->inventory.emplace_back(path); 408 health->populate(); 409 410 crow::connections::systemBus->async_method_call( 411 [asyncResp, path](const boost::system::error_code ec, 412 const std::variant<bool> present) { 413 // this interface isn't necessary, only check it if we 414 // get a good return 415 if (ec) 416 { 417 return; 418 } 419 const bool *enabled = std::get_if<bool>(&present); 420 if (enabled == nullptr) 421 { 422 BMCWEB_LOG_DEBUG << "Illegal property present"; 423 messages::internalError(asyncResp->res); 424 return; 425 } 426 if (!(*enabled)) 427 { 428 asyncResp->res.jsonValue["Status"]["State"] = 429 "Disabled"; 430 } 431 }, 432 connectionName, path, "org.freedesktop.DBus.Properties", 433 "Get", "xyz.openbmc_project.Inventory.Item", "Present"); 434 435 crow::connections::systemBus->async_method_call( 436 [asyncResp](const boost::system::error_code ec, 437 const std::variant<bool> rebuilding) { 438 // this interface isn't necessary, only check it if we 439 // get a good return 440 if (ec) 441 { 442 return; 443 } 444 const bool *updating = std::get_if<bool>(&rebuilding); 445 if (updating == nullptr) 446 { 447 BMCWEB_LOG_DEBUG << "Illegal property present"; 448 messages::internalError(asyncResp->res); 449 return; 450 } 451 452 // updating and disabled in the backend shouldn't be 453 // able to be set at the same time, so we don't need to 454 // check for the race condition of these two calls 455 if ((*updating)) 456 { 457 asyncResp->res.jsonValue["Status"]["State"] = 458 "Updating"; 459 } 460 }, 461 connectionName, path, "org.freedesktop.DBus.Properties", 462 "Get", "xyz.openbmc_project.State.Drive", "Rebuilding"); 463 }, 464 "xyz.openbmc_project.ObjectMapper", 465 "/xyz/openbmc_project/object_mapper", 466 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 467 "/xyz/openbmc_project/inventory", int32_t(0), 468 std::array<const char *, 1>{ 469 "xyz.openbmc_project.Inventory.Item.Drive"}); 470 } 471 }; 472 } // namespace redfish 473