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<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 = {{crow::HTTPMethod::GET, {{"Login"}}}, 112 {crow::HTTPMethod::HEAD, {{"Login"}}}, 113 {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 114 {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 115 {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 116 {crow::HTTPMethod::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 chassis_provider.get_chassis_list( 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 chassis_array = nlohmann::json::array(); 131 for (const std::string &chassis_item : output) { 132 chassis_array.push_back( 133 {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}}); 134 } 135 // Then attach members, count size and return, 136 Node::json["Members"] = chassis_array; 137 Node::json["Members@odata.count"] = chassis_array.size(); 138 res.json_value = Node::json; 139 } else { 140 // ... otherwise, return INTERNALL ERROR 141 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 142 } 143 res.end(); 144 }); 145 } 146 147 // Chassis Provider object 148 // TODO(Pawel) consider move it to singleton 149 OnDemandChassisProvider chassis_provider; 150 }; 151 152 /** 153 * Chassis override class for delivering Chassis Schema 154 */ 155 class Chassis : public Node { 156 public: 157 /* 158 * Default Constructor 159 */ 160 template <typename CrowApp> 161 Chassis(CrowApp &app) 162 : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { 163 Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 164 Node::json["@odata.id"] = "/redfish/v1/Chassis"; 165 Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; 166 Node::json["Name"] = "Chassis Collection"; 167 Node::json["ChassisType"] = "RackMount"; 168 169 entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, 170 {crow::HTTPMethod::HEAD, {{"Login"}}}, 171 {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 172 {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 173 {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 174 {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; 175 } 176 177 private: 178 /** 179 * Functions triggers appropriate requests on DBus 180 */ 181 void doGet(crow::response &res, const crow::request &req, 182 const std::vector<std::string> ¶ms) override { 183 // Check if there is required param, truly entering this shall be 184 // impossible. 185 if (params.size() != 1) { 186 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 187 res.end(); 188 return; 189 } 190 191 res.json_value = Node::json; 192 const std::string &chassis_id = params[0]; 193 crow::connections::system_bus->async_method_call( 194 [&res, chassis_id(std::string(chassis_id)) ]( 195 const boost::system::error_code error_code, 196 const std::vector<std::pair< 197 std::string, 198 std::vector<std::pair<std::string, std::vector<std::string>>>>> 199 &subtree) { 200 if (error_code) { 201 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 202 res.json_value = {}; 203 res.end(); 204 return; 205 } 206 // Iterate over all retrieved ObjectPaths. 207 for (const std::pair<std::string, 208 std::vector<std::pair<std::string, 209 std::vector<std::string>>>> 210 &object : subtree) { 211 const std::string &path = object.first; 212 const std::vector<std::pair<std::string, std::vector<std::string>>> 213 &connectionNames = object.second; 214 if (!boost::ends_with(path, chassis_id)) { 215 continue; 216 } 217 if (connectionNames.size() < 1) { 218 res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 219 res.end(); 220 return; 221 } 222 const std::string connectionName = connectionNames[0].first; 223 crow::connections::system_bus->async_method_call( 224 [&res, chassis_id(std::string(chassis_id)) ]( 225 const boost::system::error_code error_code, 226 const std::vector<std::pair<std::string, VariantType>> 227 &propertiesList) { 228 for (const std::pair<std::string, VariantType> &property : 229 propertiesList) { 230 const std::string *value = 231 mapbox::get_ptr<const std::string>(property.second); 232 if (value != nullptr) { 233 res.json_value[property.first] = *value; 234 } 235 } 236 res.json_value["Name"] = chassis_id; 237 res.json_value["Thermal"] = { 238 {"@odata.id", 239 "/redfish/v1/Chassis/" + chassis_id + "/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 // Couldn't find an object with that name. return an error 248 res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 249 res.end(); 250 }, 251 "xyz.openbmc_project.ObjectMapper", 252 "/xyz/openbmc_project/object_mapper", 253 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 254 "/xyz/openbmc_project/inventory", int32_t(0), 255 std::array<const char *, 1>{ 256 "xyz.openbmc_project.Inventory.Decorator.Asset"}); 257 } 258 259 // Chassis Provider object 260 // TODO(Pawel) consider move it to singleton 261 OnDemandChassisProvider chassis_provider; 262 }; 263 264 } // namespace redfish 265