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