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