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