1 /* 2 // Copyright (c) 2018 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 #include <boost/container/flat_map.hpp> 21 22 namespace redfish 23 { 24 25 /** 26 * DBus types primitives for several generic DBus interfaces 27 * TODO(Pawel) consider move this to separate file into boost::dbus 28 */ 29 // Note, this is not a very useful Variant, but because it isn't used to get 30 // values, it should be as simple as possible 31 // TODO(ed) invent a nullvariant type 32 using VariantType = sdbusplus::message::variant<bool, std::string>; 33 using ManagedObjectsType = std::vector<std::pair< 34 sdbusplus::message::object_path, 35 std::vector<std::pair<std::string, 36 std::vector<std::pair<std::string, VariantType>>>>>>; 37 38 using PropertiesType = boost::container::flat_map<std::string, VariantType>; 39 40 /** 41 * OnDemandChassisProvider 42 * Chassis provider class that retrieves data directly from dbus, before setting 43 * it into JSON output. This does not cache any data. 44 * 45 * Class can be a good example on how to scale different data providing 46 * solutions to produce single schema output. 47 * 48 * TODO(Pawel) 49 * This perhaps shall be different file, which has to be chosen on compile time 50 * depending on OEM needs 51 */ 52 class OnDemandChassisProvider 53 { 54 public: 55 /** 56 * Function that retrieves all Chassis available through EntityManager. 57 * @param callback a function that shall be called to convert Dbus output 58 * into JSON. 59 */ 60 template <typename CallbackFunc> 61 void getChassisList(CallbackFunc &&callback) 62 { 63 const std::array<const char *, 4> interfaces = { 64 "xyz.openbmc_project.Inventory.Item.Board", 65 "xyz.openbmc_project.Inventory.Item.Chassis", 66 "xyz.openbmc_project.Inventory.Item.PowerSupply", 67 "xyz.openbmc_project.Inventory.Item.System", 68 }; 69 crow::connections::systemBus->async_method_call( 70 [callback{std::move(callback)}]( 71 const boost::system::error_code error_code, 72 const std::vector<std::string> &resp) { 73 // Callback requires vector<string> to retrieve all available 74 // chassis list. 75 std::vector<std::string> chassisList; 76 if (error_code) 77 { 78 // Something wrong on DBus, the error_code is not important 79 // at this moment, just return success=false, and empty 80 // output. Since size of vector may vary depending on 81 // information from Entity Manager, and empty output could 82 // not be treated same way as error. 83 callback(false, chassisList); 84 return; 85 } 86 // Iterate over all retrieved ObjectPaths. 87 for (const std::string &objpath : resp) 88 { 89 std::size_t lastPos = objpath.rfind("/"); 90 if (lastPos != std::string::npos) 91 { 92 // and put it into output vector. 93 chassisList.emplace_back(objpath.substr(lastPos + 1)); 94 } 95 } 96 // Finally make a callback with useful data 97 callback(true, chassisList); 98 }, 99 "xyz.openbmc_project.ObjectMapper", 100 "/xyz/openbmc_project/object_mapper", 101 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 102 "/xyz/openbmc_project/inventory", int32_t(3), interfaces); 103 }; 104 }; 105 106 /** 107 * ChassisCollection derived class for delivering Chassis Collection Schema 108 */ 109 class ChassisCollection : public Node 110 { 111 public: 112 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") 113 { 114 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; 115 Node::json["@odata.id"] = "/redfish/v1/Chassis"; 116 Node::json["@odata.context"] = 117 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 118 Node::json["Name"] = "Chassis Collection"; 119 120 entityPrivileges = { 121 {boost::beast::http::verb::get, {{"Login"}}}, 122 {boost::beast::http::verb::head, {{"Login"}}}, 123 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 124 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 125 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 126 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 127 } 128 129 private: 130 /** 131 * Functions triggers appropriate requests on DBus 132 */ 133 void doGet(crow::Response &res, const crow::Request &req, 134 const std::vector<std::string> ¶ms) override 135 { 136 // get chassis list, and call the below callback for JSON preparation 137 chassisProvider.getChassisList( 138 [&](const bool &success, const std::vector<std::string> &output) { 139 if (success) 140 { 141 // ... prepare json array with appropriate @odata.id links 142 nlohmann::json chassisArray = nlohmann::json::array(); 143 for (const std::string &chassisItem : output) 144 { 145 chassisArray.push_back( 146 {{"@odata.id", 147 "/redfish/v1/Chassis/" + chassisItem}}); 148 } 149 // Then attach members, count size and return, 150 Node::json["Members"] = chassisArray; 151 Node::json["Members@odata.count"] = chassisArray.size(); 152 res.jsonValue = Node::json; 153 } 154 else 155 { 156 // ... otherwise, return INTERNALL ERROR 157 res.result( 158 boost::beast::http::status::internal_server_error); 159 } 160 res.end(); 161 }); 162 } 163 164 // Chassis Provider object 165 // TODO(Pawel) consider move it to singleton 166 OnDemandChassisProvider chassisProvider; 167 }; 168 169 /** 170 * Chassis override class for delivering Chassis Schema 171 */ 172 class Chassis : public Node 173 { 174 public: 175 Chassis(CrowApp &app) : 176 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 177 { 178 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 179 Node::json["@odata.id"] = "/redfish/v1/Chassis"; 180 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; 181 Node::json["Name"] = "Chassis Collection"; 182 Node::json["ChassisType"] = "RackMount"; 183 184 entityPrivileges = { 185 {boost::beast::http::verb::get, {{"Login"}}}, 186 {boost::beast::http::verb::head, {{"Login"}}}, 187 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 188 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 189 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 190 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 191 } 192 193 private: 194 /** 195 * Functions triggers appropriate requests on DBus 196 */ 197 void doGet(crow::Response &res, const crow::Request &req, 198 const std::vector<std::string> ¶ms) override 199 { 200 // Check if there is required param, truly entering this shall be 201 // impossible. 202 if (params.size() != 1) 203 { 204 res.result(boost::beast::http::status::internal_server_error); 205 res.end(); 206 return; 207 } 208 209 res.jsonValue = Node::json; 210 const std::string &chassisId = params[0]; 211 crow::connections::systemBus->async_method_call( 212 [&res, chassisId(std::string(chassisId))]( 213 const boost::system::error_code error_code, 214 const std::vector<std::pair< 215 std::string, std::vector<std::pair< 216 std::string, std::vector<std::string>>>>> 217 &subtree) { 218 if (error_code) 219 { 220 res.jsonValue = {}; 221 res.result( 222 boost::beast::http::status::internal_server_error); 223 res.end(); 224 return; 225 } 226 // Iterate over all retrieved ObjectPaths. 227 for (const std::pair< 228 std::string, 229 std::vector< 230 std::pair<std::string, std::vector<std::string>>>> 231 &object : subtree) 232 { 233 const std::string &path = object.first; 234 const std::vector< 235 std::pair<std::string, std::vector<std::string>>> 236 &connectionNames = object.second; 237 238 if (!boost::ends_with(path, chassisId)) 239 { 240 continue; 241 } 242 if (connectionNames.size() < 1) 243 { 244 BMCWEB_LOG_ERROR << "Only got " 245 << connectionNames.size() 246 << " Connection names"; 247 continue; 248 } 249 250 const std::string connectionName = connectionNames[0].first; 251 crow::connections::systemBus->async_method_call( 252 [&res, chassisId(std::string(chassisId))]( 253 const boost::system::error_code error_code, 254 const std::vector<std::pair< 255 std::string, VariantType>> &propertiesList) { 256 for (const std::pair<std::string, VariantType> 257 &property : propertiesList) 258 { 259 const std::string *value = 260 mapbox::getPtr<const std::string>( 261 property.second); 262 if (value != nullptr) 263 { 264 res.jsonValue[property.first] = *value; 265 } 266 } 267 res.jsonValue["Name"] = chassisId; 268 res.jsonValue["Id"] = chassisId; 269 res.jsonValue["Thermal"] = { 270 {"@odata.id", "/redfish/v1/Chassis/" + 271 chassisId + "/Thermal"}}; 272 res.end(); 273 }, 274 connectionName, path, "org.freedesktop.DBus.Properties", 275 "GetAll", 276 "xyz.openbmc_project.Inventory.Decorator.Asset"); 277 // Found the Connection we were looking for, return 278 return; 279 } 280 281 // Couldn't find an object with that name. return an error 282 res.result(boost::beast::http::status::not_found); 283 284 res.end(); 285 }, 286 "xyz.openbmc_project.ObjectMapper", 287 "/xyz/openbmc_project/object_mapper", 288 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 289 "/xyz/openbmc_project/inventory", int32_t(0), 290 std::array<const char *, 1>{ 291 "xyz.openbmc_project.Inventory.Decorator.Asset"}); 292 } 293 294 // Chassis Provider object 295 // TODO(Pawel) consider move it to singleton 296 OnDemandChassisProvider chassisProvider; 297 }; // namespace redfish 298 299 } // namespace redfish 300