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 <node.hpp> 19 20 namespace redfish 21 { 22 class StorageCollection : public Node 23 { 24 public: 25 StorageCollection(CrowApp &app) : 26 Node(app, "/redfish/v1/Systems/system/Storage/") 27 { 28 entityPrivileges = { 29 {boost::beast::http::verb::get, {{"Login"}}}, 30 {boost::beast::http::verb::head, {{"Login"}}}, 31 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 32 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 33 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 34 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 35 } 36 37 private: 38 void doGet(crow::Response &res, const crow::Request &req, 39 const std::vector<std::string> ¶ms) override 40 { 41 res.jsonValue["@odata.type"] = "#StorageCollection.StorageCollection"; 42 res.jsonValue["@odata.context"] = 43 "/redfish/v1/$metadata#StorageCollection.StorageCollection"; 44 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage"; 45 res.jsonValue["Name"] = "Storage Collection"; 46 res.jsonValue["Members"] = { 47 {{"@odata.id", "/redfish/v1/Systems/system/Storage/1"}}}; 48 res.jsonValue["Members@odata.count"] = 1; 49 res.end(); 50 } 51 }; 52 53 class Storage : public Node 54 { 55 public: 56 Storage(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/Storage/1") 57 { 58 entityPrivileges = { 59 {boost::beast::http::verb::get, {{"Login"}}}, 60 {boost::beast::http::verb::head, {{"Login"}}}, 61 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 62 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 63 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 64 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 65 } 66 67 private: 68 void doGet(crow::Response &res, const crow::Request &req, 69 const std::vector<std::string> ¶ms) override 70 { 71 res.jsonValue["@odata.type"] = "#Storage.v1_7_1.Storage"; 72 res.jsonValue["@odata.context"] = 73 "/redfish/v1/$metadata#Storage.Storage"; 74 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Storage/1"; 75 res.jsonValue["Name"] = "Storage Controller"; 76 res.jsonValue["Id"] = "1"; 77 78 auto asyncResp = std::make_shared<AsyncResp>(res); 79 crow::connections::systemBus->async_method_call( 80 [asyncResp](const boost::system::error_code ec, 81 const std::vector<std::string> &storageList) { 82 nlohmann::json &storageArray = 83 asyncResp->res.jsonValue["Drives"]; 84 storageArray = nlohmann::json::array(); 85 asyncResp->res.jsonValue["Drives@odata.count"] = 0; 86 if (ec) 87 { 88 BMCWEB_LOG_ERROR << "Drive mapper call error"; 89 messages::internalError(asyncResp->res); 90 return; 91 } 92 for (const std::string &objpath : storageList) 93 { 94 std::size_t lastPos = objpath.rfind("/"); 95 if (lastPos == std::string::npos || 96 (objpath.size() <= lastPos + 1)) 97 { 98 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 99 continue; 100 } 101 102 storageArray.push_back( 103 {{"@odata.id", 104 "/redfish/v1/Systems/system/Storage/1/Drive/" + 105 objpath.substr(lastPos + 1)}}); 106 } 107 108 asyncResp->res.jsonValue["Drives@odata.count"] = 109 storageArray.size(); 110 }, 111 "xyz.openbmc_project.ObjectMapper", 112 "/xyz/openbmc_project/object_mapper", 113 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 114 "/xyz/openbmc_project/inventory", int32_t(0), 115 std::array<const char *, 1>{ 116 "xyz.openbmc_project.Inventory.Item.Drive"}); 117 } 118 }; 119 120 class Drive : public Node 121 { 122 public: 123 Drive(CrowApp &app) : 124 Node(app, "/redfish/v1/Systems/system/Storage/1/Drive/<str>/", 125 std::string()) 126 { 127 entityPrivileges = { 128 {boost::beast::http::verb::get, {{"Login"}}}, 129 {boost::beast::http::verb::head, {{"Login"}}}, 130 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 131 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 132 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 133 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 134 } 135 136 private: 137 void doGet(crow::Response &res, const crow::Request &req, 138 const std::vector<std::string> ¶ms) override 139 { 140 const std::string &driveId = params[0]; 141 142 auto asyncResp = std::make_shared<AsyncResp>(res); 143 144 crow::connections::systemBus->async_method_call( 145 [asyncResp, driveId]( 146 const boost::system::error_code ec, 147 const std::vector<std::pair< 148 std::string, std::vector<std::pair< 149 std::string, std::vector<std::string>>>>> 150 &subtree) { 151 if (ec) 152 { 153 BMCWEB_LOG_ERROR << "Drive mapper call error"; 154 messages::internalError(asyncResp->res); 155 return; 156 } 157 158 auto object = std::find_if( 159 subtree.begin(), subtree.end(), [&driveId](auto &object) { 160 const std::string &path = object.first; 161 return boost::ends_with(path, "/" + driveId); 162 }); 163 164 if (object == subtree.end()) 165 { 166 messages::resourceNotFound(asyncResp->res, "Drive", 167 driveId); 168 return; 169 } 170 171 const std::string &path = object->first; 172 const std::vector< 173 std::pair<std::string, std::vector<std::string>>> 174 &connectionNames = object->second; 175 176 asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive"; 177 asyncResp->res.jsonValue["@odata.context"] = 178 "/redfish/v1/$metadata#Drive.Drive"; 179 asyncResp->res.jsonValue["@odata.id"] = 180 "/redfish/v1/Systems/system/Storage/1/Drive/" + driveId; 181 182 if (connectionNames.size() != 1) 183 { 184 BMCWEB_LOG_ERROR << "Connection size " 185 << connectionNames.size() 186 << ", greater than 1"; 187 messages::internalError(asyncResp->res); 188 return; 189 } 190 191 getMainChassisId( 192 asyncResp, [](const std::string &chassisId, 193 std::shared_ptr<AsyncResp> aRsp) { 194 aRsp->res.jsonValue["Links"]["Chassis"] = { 195 {"@odata.id", "/redfish/v1/Chassis/" + chassisId}}; 196 }); 197 198 const std::string &connectionName = connectionNames[0].first; 199 crow::connections::systemBus->async_method_call( 200 [asyncResp, 201 driveId](const boost::system::error_code ec, 202 const std::vector<std::pair< 203 std::string, 204 std::variant<bool, std::string, uint64_t>>> 205 &propertiesList) { 206 if (ec) 207 { 208 // this interface isn't necessary 209 return; 210 } 211 for (const std::pair<std::string, 212 std::variant<bool, std::string, 213 uint64_t>> &property : 214 propertiesList) 215 { 216 // Store DBus properties that are also 217 // Redfish properties with same name and a 218 // string value 219 const std::string &propertyName = property.first; 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>(&property.second); 227 if (value == nullptr) 228 { 229 // illegal property 230 messages::internalError(asyncResp->res); 231 continue; 232 } 233 asyncResp->res.jsonValue[propertyName] = *value; 234 } 235 } 236 asyncResp->res.jsonValue["Name"] = driveId; 237 asyncResp->res.jsonValue["Id"] = driveId; 238 }, 239 connectionName, path, "org.freedesktop.DBus.Properties", 240 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); 241 242 // default it to Enabled 243 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 244 245 crow::connections::systemBus->async_method_call( 246 [asyncResp, path](const boost::system::error_code ec, 247 const std::variant<bool> present) { 248 // this interface isn't necessary, only check it if we 249 // get a good return 250 if (!ec) 251 { 252 const bool *enabled = std::get_if<bool>(&present); 253 if (enabled == nullptr) 254 { 255 BMCWEB_LOG_DEBUG << "Illegal property present"; 256 messages::internalError(asyncResp->res); 257 return; 258 } 259 if (!(*enabled)) 260 { 261 asyncResp->res.jsonValue["Status"]["State"] = 262 "Disabled"; 263 return; 264 } 265 } 266 267 // only populate if Enabled, assume enabled unless item 268 // interface says otherwise 269 auto health = 270 std::make_shared<HealthPopulate>(asyncResp); 271 health->inventory = std::vector<std::string>{path}; 272 273 health->populate(); 274 }, 275 connectionName, path, "org.freedesktop.DBus.Properties", 276 "Get", "xyz.openbmc_project.Inventory.Item", "Present"); 277 }, 278 "xyz.openbmc_project.ObjectMapper", 279 "/xyz/openbmc_project/object_mapper", 280 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 281 "/xyz/openbmc_project/inventory", int32_t(0), 282 std::array<const char *, 1>{ 283 "xyz.openbmc_project.Inventory.Item.Drive"}); 284 } 285 }; 286 } // namespace redfish 287