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