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 get_chassis_list(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::system_bus->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> chassis_list; 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, chassis_list); 78 return; 79 } 80 // Iterate over all retrieved ObjectPaths. 81 for (const std::string &objpath : resp) { 82 std::size_t last_pos = objpath.rfind("/"); 83 if (last_pos != std::string::npos) { 84 // and put it into output vector. 85 chassis_list.emplace_back(objpath.substr(last_pos + 1)); 86 } 87 } 88 // Finally make a callback with useful data 89 callback(true, chassis_list); 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 template <typename CrowApp> 104 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { 105 Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; 106 Node::json["@odata.id"] = "/redfish/v1/Chassis"; 107 Node::json["@odata.context"] = 108 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 109 Node::json["Name"] = "Chassis Collection"; 110 111 entityPrivileges = { 112 {boost::beast::http::verb::get, {{"Login"}}}, 113 {boost::beast::http::verb::head, {{"Login"}}}, 114 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 115 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 116 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 117 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 118 } 119 120 private: 121 /** 122 * Functions triggers appropriate requests on DBus 123 */ 124 void doGet(crow::response &res, const crow::request &req, 125 const std::vector<std::string> ¶ms) override { 126 // Get chassis list, and call the below callback for JSON preparation 127 chassis_provider.get_chassis_list( 128 [&](const bool &success, const std::vector<std::string> &output) { 129 if (success) { 130 // ... prepare json array with appropriate @odata.id links 131 nlohmann::json chassis_array = nlohmann::json::array(); 132 for (const std::string &chassis_item : output) { 133 chassis_array.push_back( 134 {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}}); 135 } 136 // Then attach members, count size and return, 137 Node::json["Members"] = chassis_array; 138 Node::json["Members@odata.count"] = chassis_array.size(); 139 res.json_value = Node::json; 140 } else { 141 // ... otherwise, return INTERNALL ERROR 142 res.result(boost::beast::http::status::internal_server_error); 143 } 144 res.end(); 145 }); 146 } 147 148 // Chassis Provider object 149 // TODO(Pawel) consider move it to singleton 150 OnDemandChassisProvider chassis_provider; 151 }; 152 153 /** 154 * Chassis override class for delivering Chassis Schema 155 */ 156 class Chassis : public Node { 157 public: 158 /* 159 * Default Constructor 160 */ 161 template <typename CrowApp> 162 Chassis(CrowApp &app) 163 : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { 164 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 165 Node::json["@odata.id"] = "/redfish/v1/Chassis"; 166 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; 167 Node::json["Name"] = "Chassis Collection"; 168 Node::json["ChassisType"] = "RackMount"; 169 170 entityPrivileges = { 171 {boost::beast::http::verb::get, {{"Login"}}}, 172 {boost::beast::http::verb::head, {{"Login"}}}, 173 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 174 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 175 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 176 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 177 } 178 179 private: 180 /** 181 * Functions triggers appropriate requests on DBus 182 */ 183 void doGet(crow::response &res, const crow::request &req, 184 const std::vector<std::string> ¶ms) override { 185 // Check if there is required param, truly entering this shall be 186 // impossible. 187 if (params.size() != 1) { 188 res.result(boost::beast::http::status::internal_server_error); 189 res.end(); 190 return; 191 } 192 193 res.json_value = Node::json; 194 const std::string &chassis_id = params[0]; 195 crow::connections::system_bus->async_method_call( 196 [&res, chassis_id(std::string(chassis_id)) ]( 197 const boost::system::error_code error_code, 198 const std::vector<std::pair< 199 std::string, 200 std::vector<std::pair<std::string, std::vector<std::string>>>>> 201 &subtree) { 202 if (error_code) { 203 res.json_value = {}; 204 res.result(boost::beast::http::status::internal_server_error); 205 res.end(); 206 return; 207 } 208 // Iterate over all retrieved ObjectPaths. 209 for (const std::pair<std::string, 210 std::vector<std::pair<std::string, 211 std::vector<std::string>>>> 212 &object : subtree) { 213 const std::string &path = object.first; 214 const std::vector<std::pair<std::string, std::vector<std::string>>> 215 &connectionNames = object.second; 216 217 if (!boost::ends_with(path, chassis_id)) { 218 continue; 219 } 220 if (connectionNames.size() < 1) { 221 CROW_LOG_ERROR << "Only got " << connectionNames.size() 222 << " connection names"; 223 continue; 224 } 225 226 const std::string connectionName = connectionNames[0].first; 227 crow::connections::system_bus->async_method_call( 228 [&res, chassis_id(std::string(chassis_id)) ]( 229 const boost::system::error_code error_code, 230 const std::vector<std::pair<std::string, VariantType>> 231 &propertiesList) { 232 for (const std::pair<std::string, VariantType> &property : 233 propertiesList) { 234 const std::string *value = 235 mapbox::get_ptr<const std::string>(property.second); 236 if (value != nullptr) { 237 res.json_value[property.first] = *value; 238 } 239 } 240 res.json_value["Name"] = chassis_id; 241 res.json_value["Thermal"] = { 242 {"@odata.id", 243 "/redfish/v1/Chassis/" + chassis_id + "/Thermal"}}; 244 res.end(); 245 }, 246 connectionName, path, "org.freedesktop.DBus.Properties", 247 "GetAll", "xyz.openbmc_project.Inventory.Decorator.Asset"); 248 // Found the connection we were looking for, return 249 return; 250 } 251 252 // Couldn't find an object with that name. return an error 253 res.result(boost::beast::http::status::not_found); 254 255 res.end(); 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.Decorator.Asset"}); 263 } 264 265 // Chassis Provider object 266 // TODO(Pawel) consider move it to singleton 267 OnDemandChassisProvider chassis_provider; 268 }; // namespace redfish 269 270 } // namespace redfish 271