1*e37f8451SRapkiewicz, Pawel /* 2*e37f8451SRapkiewicz, Pawel // Copyright (c) 2018 Intel Corporation 3*e37f8451SRapkiewicz, Pawel // 4*e37f8451SRapkiewicz, Pawel // Licensed under the Apache License, Version 2.0 (the "License"); 5*e37f8451SRapkiewicz, Pawel // you may not use this file except in compliance with the License. 6*e37f8451SRapkiewicz, Pawel // You may obtain a copy of the License at 7*e37f8451SRapkiewicz, Pawel // 8*e37f8451SRapkiewicz, Pawel // http://www.apache.org/licenses/LICENSE-2.0 9*e37f8451SRapkiewicz, Pawel // 10*e37f8451SRapkiewicz, Pawel // Unless required by applicable law or agreed to in writing, software 11*e37f8451SRapkiewicz, Pawel // distributed under the License is distributed on an "AS IS" BASIS, 12*e37f8451SRapkiewicz, Pawel // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*e37f8451SRapkiewicz, Pawel // See the License for the specific language governing permissions and 14*e37f8451SRapkiewicz, Pawel // limitations under the License. 15*e37f8451SRapkiewicz, Pawel */ 16*e37f8451SRapkiewicz, Pawel #pragma once 17*e37f8451SRapkiewicz, Pawel 18*e37f8451SRapkiewicz, Pawel #include "node.hpp" 19*e37f8451SRapkiewicz, Pawel #include <boost/container/flat_map.hpp> 20*e37f8451SRapkiewicz, Pawel 21*e37f8451SRapkiewicz, Pawel namespace redfish { 22*e37f8451SRapkiewicz, Pawel 23*e37f8451SRapkiewicz, Pawel /** 24*e37f8451SRapkiewicz, Pawel * DBus types primitives for several generic DBus interfaces 25*e37f8451SRapkiewicz, Pawel * TODO(Pawel) consider move this to separate file into boost::dbus 26*e37f8451SRapkiewicz, Pawel */ 27*e37f8451SRapkiewicz, Pawel using ManagedObjectsType = std::vector< 28*e37f8451SRapkiewicz, Pawel std::pair<dbus::object_path, 29*e37f8451SRapkiewicz, Pawel std::vector<std::pair< 30*e37f8451SRapkiewicz, Pawel std::string, 31*e37f8451SRapkiewicz, Pawel std::vector<std::pair<std::string, dbus::dbus_variant>>>>>>; 32*e37f8451SRapkiewicz, Pawel 33*e37f8451SRapkiewicz, Pawel using PropertiesType = 34*e37f8451SRapkiewicz, Pawel boost::container::flat_map<std::string, dbus::dbus_variant>; 35*e37f8451SRapkiewicz, Pawel 36*e37f8451SRapkiewicz, Pawel /** 37*e37f8451SRapkiewicz, Pawel * OnDemandChassisProvider 38*e37f8451SRapkiewicz, Pawel * Chassis provider class that retrieves data directly from dbus, before seting 39*e37f8451SRapkiewicz, Pawel * it into JSON output. This does not cache any data. 40*e37f8451SRapkiewicz, Pawel * 41*e37f8451SRapkiewicz, Pawel * Class can be a good example on how to scale different data providing 42*e37f8451SRapkiewicz, Pawel * solutions to produce single schema output. 43*e37f8451SRapkiewicz, Pawel * 44*e37f8451SRapkiewicz, Pawel * TODO(Pawel) 45*e37f8451SRapkiewicz, Pawel * This perhaps shall be different file, which has to be chosen on compile time 46*e37f8451SRapkiewicz, Pawel * depending on OEM needs 47*e37f8451SRapkiewicz, Pawel */ 48*e37f8451SRapkiewicz, Pawel class OnDemandChassisProvider { 49*e37f8451SRapkiewicz, Pawel public: 50*e37f8451SRapkiewicz, Pawel /** 51*e37f8451SRapkiewicz, Pawel * Function that retrieves all properties for given Chassis Object from 52*e37f8451SRapkiewicz, Pawel * EntityManager 53*e37f8451SRapkiewicz, Pawel * @param res_name a chassis resource name to query on DBus 54*e37f8451SRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 55*e37f8451SRapkiewicz, Pawel * JSON 56*e37f8451SRapkiewicz, Pawel */ 57*e37f8451SRapkiewicz, Pawel template <typename CallbackFunc> 58*e37f8451SRapkiewicz, Pawel void get_chassis_data(const std::string &res_name, CallbackFunc &&callback) { 59*e37f8451SRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 60*e37f8451SRapkiewicz, Pawel [callback{std::move(callback)}]( 61*e37f8451SRapkiewicz, Pawel const boost::system::error_code error_code, 62*e37f8451SRapkiewicz, Pawel const PropertiesType &properties) { 63*e37f8451SRapkiewicz, Pawel // Callback requires flat_map<string, string> so prepare one. 64*e37f8451SRapkiewicz, Pawel boost::container::flat_map<std::string, std::string> output; 65*e37f8451SRapkiewicz, Pawel if (error_code) { 66*e37f8451SRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 67*e37f8451SRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 68*e37f8451SRapkiewicz, Pawel // of map may vary depending on Chassis type, an empty output could 69*e37f8451SRapkiewicz, Pawel // not be treated same way as error. 70*e37f8451SRapkiewicz, Pawel callback(false, output); 71*e37f8451SRapkiewicz, Pawel return; 72*e37f8451SRapkiewicz, Pawel } 73*e37f8451SRapkiewicz, Pawel for (const std::pair<const char *, const char *> &p : 74*e37f8451SRapkiewicz, Pawel std::array<std::pair<const char *, const char *>, 5>{ 75*e37f8451SRapkiewicz, Pawel {{"name", "Name"}, 76*e37f8451SRapkiewicz, Pawel {"manufacturer", "Manufacturer"}, 77*e37f8451SRapkiewicz, Pawel {"model", "Model"}, 78*e37f8451SRapkiewicz, Pawel {"part_number", "PartNumber"}, 79*e37f8451SRapkiewicz, Pawel {"serial_number", "SerialNumber"}}}) { 80*e37f8451SRapkiewicz, Pawel PropertiesType::const_iterator it = properties.find(p.first); 81*e37f8451SRapkiewicz, Pawel if (it != properties.end()) { 82*e37f8451SRapkiewicz, Pawel const std::string *s = boost::get<std::string>(&it->second); 83*e37f8451SRapkiewicz, Pawel if (s != nullptr) { 84*e37f8451SRapkiewicz, Pawel output[p.second] = *s; 85*e37f8451SRapkiewicz, Pawel } 86*e37f8451SRapkiewicz, Pawel } 87*e37f8451SRapkiewicz, Pawel } 88*e37f8451SRapkiewicz, Pawel // Callback with success, and hopefully data. 89*e37f8451SRapkiewicz, Pawel callback(true, output); 90*e37f8451SRapkiewicz, Pawel }, 91*e37f8451SRapkiewicz, Pawel {"xyz.openbmc_project.EntityManager", 92*e37f8451SRapkiewicz, Pawel "/xyz/openbmc_project/Inventory/Item/Chassis/" + res_name, 93*e37f8451SRapkiewicz, Pawel "org.freedesktop.DBus.Properties", "GetAll"}, 94*e37f8451SRapkiewicz, Pawel "xyz.openbmc_project.Configuration.Chassis"); 95*e37f8451SRapkiewicz, Pawel } 96*e37f8451SRapkiewicz, Pawel 97*e37f8451SRapkiewicz, Pawel /** 98*e37f8451SRapkiewicz, Pawel * Function that retrieves all Chassis available through EntityManager. 99*e37f8451SRapkiewicz, Pawel * @param callback a function that shall be called to convert Dbus output into 100*e37f8451SRapkiewicz, Pawel * JSON. 101*e37f8451SRapkiewicz, Pawel */ 102*e37f8451SRapkiewicz, Pawel template <typename CallbackFunc> 103*e37f8451SRapkiewicz, Pawel void get_chassis_list(CallbackFunc &&callback) { 104*e37f8451SRapkiewicz, Pawel crow::connections::system_bus->async_method_call( 105*e37f8451SRapkiewicz, Pawel [callback{std::move(callback)}]( 106*e37f8451SRapkiewicz, Pawel const boost::system::error_code error_code, 107*e37f8451SRapkiewicz, Pawel const ManagedObjectsType &resp) { 108*e37f8451SRapkiewicz, Pawel // Callback requires vector<string> to retrieve all available chassis 109*e37f8451SRapkiewicz, Pawel // list. 110*e37f8451SRapkiewicz, Pawel std::vector<std::string> chassis_list; 111*e37f8451SRapkiewicz, Pawel if (error_code) { 112*e37f8451SRapkiewicz, Pawel // Something wrong on DBus, the error_code is not important at this 113*e37f8451SRapkiewicz, Pawel // moment, just return success=false, and empty output. Since size 114*e37f8451SRapkiewicz, Pawel // of vector may vary depending on information from Entity Manager, 115*e37f8451SRapkiewicz, Pawel // and empty output could not be treated same way as error. 116*e37f8451SRapkiewicz, Pawel callback(false, chassis_list); 117*e37f8451SRapkiewicz, Pawel return; 118*e37f8451SRapkiewicz, Pawel } 119*e37f8451SRapkiewicz, Pawel 120*e37f8451SRapkiewicz, Pawel // Iterate over all retrieved ObjectPaths. 121*e37f8451SRapkiewicz, Pawel for (auto &objpath : resp) { 122*e37f8451SRapkiewicz, Pawel // And all interfaces available for certain ObjectPath. 123*e37f8451SRapkiewicz, Pawel for (auto &interface : objpath.second) { 124*e37f8451SRapkiewicz, Pawel // If interface is xyz.openbmc_project.Configuration.Chassis, this 125*e37f8451SRapkiewicz, Pawel // is Chassis. 126*e37f8451SRapkiewicz, Pawel if (interface.first == 127*e37f8451SRapkiewicz, Pawel "xyz.openbmc_project.Configuration.Chassis") { 128*e37f8451SRapkiewicz, Pawel // Cut out everyting until last "/", ... 129*e37f8451SRapkiewicz, Pawel const std::string &chassis_id = objpath.first.value; 130*e37f8451SRapkiewicz, Pawel std::size_t last_pos = chassis_id.rfind("/"); 131*e37f8451SRapkiewicz, Pawel if (last_pos != std::string::npos) { 132*e37f8451SRapkiewicz, Pawel // and put it into output vector. 133*e37f8451SRapkiewicz, Pawel chassis_list.emplace_back(chassis_id.substr(last_pos + 1)); 134*e37f8451SRapkiewicz, Pawel } 135*e37f8451SRapkiewicz, Pawel } 136*e37f8451SRapkiewicz, Pawel } 137*e37f8451SRapkiewicz, Pawel } 138*e37f8451SRapkiewicz, Pawel // Finally make a callback with usefull data 139*e37f8451SRapkiewicz, Pawel callback(true, chassis_list); 140*e37f8451SRapkiewicz, Pawel }, 141*e37f8451SRapkiewicz, Pawel {"xyz.openbmc_project.EntityManager", 142*e37f8451SRapkiewicz, Pawel "/xyz/openbmc_project/Inventory/Item/Chassis", 143*e37f8451SRapkiewicz, Pawel "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"}); 144*e37f8451SRapkiewicz, Pawel }; 145*e37f8451SRapkiewicz, Pawel }; 146*e37f8451SRapkiewicz, Pawel 147*e37f8451SRapkiewicz, Pawel /** 148*e37f8451SRapkiewicz, Pawel * ChassisCollection derived class for delivering Chassis Collection Schema 149*e37f8451SRapkiewicz, Pawel */ 150*e37f8451SRapkiewicz, Pawel class ChassisCollection : public Node { 151*e37f8451SRapkiewicz, Pawel public: 152*e37f8451SRapkiewicz, Pawel template <typename CrowApp> 153*e37f8451SRapkiewicz, Pawel ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") { 154*e37f8451SRapkiewicz, Pawel Node::json["@odata.type"] = "#ChassisCollection.ChassisCollection"; 155*e37f8451SRapkiewicz, Pawel Node::json["@odata.id"] = "/redfish/v1/Chassis"; 156*e37f8451SRapkiewicz, Pawel Node::json["@odata.context"] = 157*e37f8451SRapkiewicz, Pawel "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 158*e37f8451SRapkiewicz, Pawel Node::json["Name"] = "Chassis Collection"; 159*e37f8451SRapkiewicz, Pawel 160*e37f8451SRapkiewicz, Pawel entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, 161*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::HEAD, {{"Login"}}}, 162*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 163*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 164*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 165*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; 166*e37f8451SRapkiewicz, Pawel } 167*e37f8451SRapkiewicz, Pawel 168*e37f8451SRapkiewicz, Pawel private: 169*e37f8451SRapkiewicz, Pawel /** 170*e37f8451SRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 171*e37f8451SRapkiewicz, Pawel */ 172*e37f8451SRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 173*e37f8451SRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 174*e37f8451SRapkiewicz, Pawel // Get chassis list, and call the below callback for JSON preparation 175*e37f8451SRapkiewicz, Pawel chassis_provider.get_chassis_list( 176*e37f8451SRapkiewicz, Pawel [&](const bool &success, const std::vector<std::string> &output) { 177*e37f8451SRapkiewicz, Pawel if (success) { 178*e37f8451SRapkiewicz, Pawel // ... prepare json array with appropriate @odata.id links 179*e37f8451SRapkiewicz, Pawel nlohmann::json chassis_array = nlohmann::json::array(); 180*e37f8451SRapkiewicz, Pawel for (const std::string &chassis_item : output) { 181*e37f8451SRapkiewicz, Pawel chassis_array.push_back( 182*e37f8451SRapkiewicz, Pawel {{"@odata.id", "/redfish/v1/Chassis/" + chassis_item}}); 183*e37f8451SRapkiewicz, Pawel } 184*e37f8451SRapkiewicz, Pawel // Then attach members, count size and return, 185*e37f8451SRapkiewicz, Pawel Node::json["Members"] = chassis_array; 186*e37f8451SRapkiewicz, Pawel Node::json["Members@odata.count"] = chassis_array.size(); 187*e37f8451SRapkiewicz, Pawel res.json_value = Node::json; 188*e37f8451SRapkiewicz, Pawel } else { 189*e37f8451SRapkiewicz, Pawel // ... otherwise, return INTERNALL ERROR 190*e37f8451SRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 191*e37f8451SRapkiewicz, Pawel } 192*e37f8451SRapkiewicz, Pawel res.end(); 193*e37f8451SRapkiewicz, Pawel }); 194*e37f8451SRapkiewicz, Pawel } 195*e37f8451SRapkiewicz, Pawel 196*e37f8451SRapkiewicz, Pawel // Chassis Provider object 197*e37f8451SRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 198*e37f8451SRapkiewicz, Pawel OnDemandChassisProvider chassis_provider; 199*e37f8451SRapkiewicz, Pawel }; 200*e37f8451SRapkiewicz, Pawel 201*e37f8451SRapkiewicz, Pawel /** 202*e37f8451SRapkiewicz, Pawel * Chassis override class for delivering Chassis Schema 203*e37f8451SRapkiewicz, Pawel */ 204*e37f8451SRapkiewicz, Pawel class Chassis : public Node { 205*e37f8451SRapkiewicz, Pawel public: 206*e37f8451SRapkiewicz, Pawel /* 207*e37f8451SRapkiewicz, Pawel * Default Constructor 208*e37f8451SRapkiewicz, Pawel */ 209*e37f8451SRapkiewicz, Pawel template <typename CrowApp> 210*e37f8451SRapkiewicz, Pawel Chassis(CrowApp &app) 211*e37f8451SRapkiewicz, Pawel : Node(app, "/redfish/v1/Chassis/<str>/", std::string()) { 212*e37f8451SRapkiewicz, Pawel Node::json["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 213*e37f8451SRapkiewicz, Pawel Node::json["@odata.id"] = "/redfish/v1/Chassis"; 214*e37f8451SRapkiewicz, Pawel Node::json["@odata.context"] = "/redfish/v1/$metadata#Chassis.Chassis"; 215*e37f8451SRapkiewicz, Pawel Node::json["Name"] = "Chassis Collection"; 216*e37f8451SRapkiewicz, Pawel 217*e37f8451SRapkiewicz, Pawel entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, 218*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::HEAD, {{"Login"}}}, 219*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, 220*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, 221*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, 222*e37f8451SRapkiewicz, Pawel {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; 223*e37f8451SRapkiewicz, Pawel } 224*e37f8451SRapkiewicz, Pawel 225*e37f8451SRapkiewicz, Pawel private: 226*e37f8451SRapkiewicz, Pawel /** 227*e37f8451SRapkiewicz, Pawel * Functions triggers appropriate requests on DBus 228*e37f8451SRapkiewicz, Pawel */ 229*e37f8451SRapkiewicz, Pawel void doGet(crow::response &res, const crow::request &req, 230*e37f8451SRapkiewicz, Pawel const std::vector<std::string> ¶ms) override { 231*e37f8451SRapkiewicz, Pawel // Check if there is required param, truly entering this shall be 232*e37f8451SRapkiewicz, Pawel // impossible. 233*e37f8451SRapkiewicz, Pawel if (params.size() != 1) { 234*e37f8451SRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); 235*e37f8451SRapkiewicz, Pawel res.end(); 236*e37f8451SRapkiewicz, Pawel return; 237*e37f8451SRapkiewicz, Pawel } 238*e37f8451SRapkiewicz, Pawel const std::string &chassis_id = params[0]; 239*e37f8451SRapkiewicz, Pawel // Get certain Chassis Data, and call the below callback for JSON 240*e37f8451SRapkiewicz, Pawel // preparation lambda requires everything as reference, and chassis_id, 241*e37f8451SRapkiewicz, Pawel // which is local by copy 242*e37f8451SRapkiewicz, Pawel chassis_provider.get_chassis_data( 243*e37f8451SRapkiewicz, Pawel chassis_id, 244*e37f8451SRapkiewicz, Pawel [&, chassis_id](const bool &success, 245*e37f8451SRapkiewicz, Pawel const boost::container::flat_map<std::string, 246*e37f8451SRapkiewicz, Pawel std::string> &output) { 247*e37f8451SRapkiewicz, Pawel // Create JSON copy based on Node::json, this is to avoid possible 248*e37f8451SRapkiewicz, Pawel // race condition 249*e37f8451SRapkiewicz, Pawel nlohmann::json json_response(Node::json); 250*e37f8451SRapkiewicz, Pawel // If success... 251*e37f8451SRapkiewicz, Pawel if (success) { 252*e37f8451SRapkiewicz, Pawel // prepare all the schema required fields. 253*e37f8451SRapkiewicz, Pawel json_response["@odata.id"] = "/redfish/v1/Chassis/" + chassis_id; 254*e37f8451SRapkiewicz, Pawel // also the one from dbus 255*e37f8451SRapkiewicz, Pawel for (const std::pair<std::string, std::string> &chassis_item : 256*e37f8451SRapkiewicz, Pawel output) { 257*e37f8451SRapkiewicz, Pawel json_response[chassis_item.first] = chassis_item.second; 258*e37f8451SRapkiewicz, Pawel } 259*e37f8451SRapkiewicz, Pawel json_response["Id"] = chassis_id; 260*e37f8451SRapkiewicz, Pawel 261*e37f8451SRapkiewicz, Pawel // prepare respond, and send 262*e37f8451SRapkiewicz, Pawel res.json_value = json_response; 263*e37f8451SRapkiewicz, Pawel } else { 264*e37f8451SRapkiewicz, Pawel // ... otherwise return error 265*e37f8451SRapkiewicz, Pawel // TODO(Pawel)consider distinguish between non existing object, and 266*e37f8451SRapkiewicz, Pawel // other errors 267*e37f8451SRapkiewicz, Pawel res.code = static_cast<int>(HttpRespCode::NOT_FOUND); 268*e37f8451SRapkiewicz, Pawel } 269*e37f8451SRapkiewicz, Pawel res.end(); 270*e37f8451SRapkiewicz, Pawel }); 271*e37f8451SRapkiewicz, Pawel } 272*e37f8451SRapkiewicz, Pawel 273*e37f8451SRapkiewicz, Pawel // Chassis Provider object 274*e37f8451SRapkiewicz, Pawel // TODO(Pawel) consider move it to singleton 275*e37f8451SRapkiewicz, Pawel OnDemandChassisProvider chassis_provider; 276*e37f8451SRapkiewicz, Pawel }; 277*e37f8451SRapkiewicz, Pawel 278*e37f8451SRapkiewicz, Pawel } // namespace redfish 279