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 20 #include <boost/container/flat_map.hpp> 21 #include <variant> 22 23 namespace redfish 24 { 25 26 /** 27 * DBus types primitives for several generic DBus interfaces 28 * TODO(Pawel) consider move this to separate file into boost::dbus 29 */ 30 // Note, this is not a very useful Variant, but because it isn't used to get 31 // values, it should be as simple as possible 32 // TODO(ed) invent a nullvariant type 33 using VariantType = std::variant<bool, std::string, uint64_t>; 34 using ManagedObjectsType = std::vector<std::pair< 35 sdbusplus::message::object_path, 36 std::vector<std::pair<std::string, 37 std::vector<std::pair<std::string, VariantType>>>>>>; 38 39 using PropertiesType = boost::container::flat_map<std::string, VariantType>; 40 41 void getIntrusionByService(std::shared_ptr<AsyncResp> aResp, 42 const std::string &service, 43 const std::string &objPath) 44 { 45 BMCWEB_LOG_DEBUG << "Get intrusion status by service \n"; 46 47 crow::connections::systemBus->async_method_call( 48 [aResp{std::move(aResp)}](const boost::system::error_code ec, 49 const std::variant<std::string> &value) { 50 if (ec) 51 { 52 // do not add err msg in redfish response, becaues this is not 53 // mandatory property 54 BMCWEB_LOG_ERROR << "DBUS response error " << ec << "\n"; 55 return; 56 } 57 58 const std::string *status = std::get_if<std::string>(&value); 59 60 if (status == nullptr) 61 { 62 BMCWEB_LOG_ERROR << "intrusion status read error \n"; 63 return; 64 } 65 66 aResp->res.jsonValue["PhysicalSecurity"] = { 67 {"IntrusionSensorNumber", 1}, {"IntrusionSensor", *status}}; 68 }, 69 service, objPath, "org.freedesktop.DBus.Properties", "Get", 70 "xyz.openbmc_project.Chassis.Intrusion", "Status"); 71 } 72 73 /** 74 * Retrieves physical security properties over dbus 75 */ 76 void getPhysicalSecurityData(std::shared_ptr<AsyncResp> aResp) 77 { 78 crow::connections::systemBus->async_method_call( 79 [aResp{std::move(aResp)}]( 80 const boost::system::error_code ec, 81 const std::vector<std::pair< 82 std::string, 83 std::vector<std::pair<std::string, std::vector<std::string>>>>> 84 &subtree) { 85 if (ec) 86 { 87 // do not add err msg in redfish response, becaues this is not 88 // mandatory property 89 BMCWEB_LOG_ERROR << "DBUS error: no matched iface " << ec 90 << "\n"; 91 return; 92 } 93 // Iterate over all retrieved ObjectPaths. 94 for (const auto &object : subtree) 95 { 96 for (const auto &service : object.second) 97 { 98 getIntrusionByService(aResp, service.first, object.first); 99 return; 100 } 101 } 102 }, 103 "xyz.openbmc_project.ObjectMapper", 104 "/xyz/openbmc_project/object_mapper", 105 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 106 "/xyz/openbmc_project/Intrusion", int32_t(1), 107 std::array<const char *, 1>{"xyz.openbmc_project.Chassis.Intrusion"}); 108 } 109 110 /** 111 * ChassisCollection derived class for delivering Chassis Collection Schema 112 */ 113 class ChassisCollection : public Node 114 { 115 public: 116 ChassisCollection(CrowApp &app) : Node(app, "/redfish/v1/Chassis/") 117 { 118 entityPrivileges = { 119 {boost::beast::http::verb::get, {{"Login"}}}, 120 {boost::beast::http::verb::head, {{"Login"}}}, 121 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 122 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 123 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 124 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 125 } 126 127 private: 128 /** 129 * Functions triggers appropriate requests on DBus 130 */ 131 void doGet(crow::Response &res, const crow::Request &req, 132 const std::vector<std::string> ¶ms) override 133 { 134 const std::array<const char *, 3> interfaces = { 135 "xyz.openbmc_project.Inventory.Item.Board", 136 "xyz.openbmc_project.Inventory.Item.Chassis", 137 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 138 res.jsonValue["@odata.type"] = "#ChassisCollection.ChassisCollection"; 139 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; 140 res.jsonValue["@odata.context"] = 141 "/redfish/v1/$metadata#ChassisCollection.ChassisCollection"; 142 res.jsonValue["Name"] = "Chassis Collection"; 143 144 auto asyncResp = std::make_shared<AsyncResp>(res); 145 crow::connections::systemBus->async_method_call( 146 [asyncResp](const boost::system::error_code ec, 147 const std::vector<std::string> &chassisList) { 148 if (ec) 149 { 150 messages::internalError(asyncResp->res); 151 return; 152 } 153 nlohmann::json &chassisArray = 154 asyncResp->res.jsonValue["Members"]; 155 chassisArray = nlohmann::json::array(); 156 for (const std::string &objpath : chassisList) 157 { 158 std::size_t lastPos = objpath.rfind("/"); 159 if (lastPos == std::string::npos) 160 { 161 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 162 continue; 163 } 164 chassisArray.push_back( 165 {{"@odata.id", "/redfish/v1/Chassis/" + 166 objpath.substr(lastPos + 1)}}); 167 } 168 169 asyncResp->res.jsonValue["Members@odata.count"] = 170 chassisArray.size(); 171 }, 172 "xyz.openbmc_project.ObjectMapper", 173 "/xyz/openbmc_project/object_mapper", 174 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 175 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 176 } 177 }; 178 179 /** 180 * Chassis override class for delivering Chassis Schema 181 */ 182 class Chassis : public Node 183 { 184 public: 185 Chassis(CrowApp &app) : 186 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 187 { 188 entityPrivileges = { 189 {boost::beast::http::verb::get, {{"Login"}}}, 190 {boost::beast::http::verb::head, {{"Login"}}}, 191 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 192 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 193 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 194 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 195 } 196 197 private: 198 /** 199 * Functions triggers appropriate requests on DBus 200 */ 201 void doGet(crow::Response &res, const crow::Request &req, 202 const std::vector<std::string> ¶ms) override 203 { 204 const std::array<const char *, 3> interfaces = { 205 "xyz.openbmc_project.Inventory.Item.Board", 206 "xyz.openbmc_project.Inventory.Item.Chassis", 207 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 208 209 // Check if there is required param, truly entering this shall be 210 // impossible. 211 if (params.size() != 1) 212 { 213 messages::internalError(res); 214 res.end(); 215 return; 216 } 217 const std::string &chassisId = params[0]; 218 219 res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 220 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 221 res.jsonValue["@odata.context"] = 222 "/redfish/v1/$metadata#Chassis.Chassis"; 223 res.jsonValue["Name"] = "Chassis Collection"; 224 res.jsonValue["ChassisType"] = "RackMount"; 225 res.jsonValue["PowerState"] = "On"; 226 227 auto asyncResp = std::make_shared<AsyncResp>(res); 228 crow::connections::systemBus->async_method_call( 229 [asyncResp, chassisId(std::string(chassisId))]( 230 const boost::system::error_code ec, 231 const std::vector<std::pair< 232 std::string, std::vector<std::pair< 233 std::string, std::vector<std::string>>>>> 234 &subtree) { 235 if (ec) 236 { 237 messages::internalError(asyncResp->res); 238 return; 239 } 240 // Iterate over all retrieved ObjectPaths. 241 for (const std::pair< 242 std::string, 243 std::vector< 244 std::pair<std::string, std::vector<std::string>>>> 245 &object : subtree) 246 { 247 const std::string &path = object.first; 248 const std::vector< 249 std::pair<std::string, std::vector<std::string>>> 250 &connectionNames = object.second; 251 252 if (!boost::ends_with(path, chassisId)) 253 { 254 continue; 255 } 256 if (connectionNames.size() < 1) 257 { 258 BMCWEB_LOG_ERROR << "Only got " 259 << connectionNames.size() 260 << " Connection names"; 261 continue; 262 } 263 264 const std::string connectionName = connectionNames[0].first; 265 crow::connections::systemBus->async_method_call( 266 [asyncResp, chassisId(std::string(chassisId))]( 267 const boost::system::error_code ec, 268 const std::vector<std::pair< 269 std::string, VariantType>> &propertiesList) { 270 for (const std::pair<std::string, VariantType> 271 &property : propertiesList) 272 { 273 // Store DBus properties that are also Redfish 274 // properties with same name and a string value 275 const std::string &propertyName = 276 property.first; 277 if ((propertyName == "PartNumber") || 278 (propertyName == "SerialNumber") || 279 (propertyName == "Manufacturer") || 280 (propertyName == "Model")) 281 { 282 const std::string *value = 283 std::get_if<std::string>( 284 &property.second); 285 if (value != nullptr) 286 { 287 asyncResp->res.jsonValue[propertyName] = 288 *value; 289 } 290 } 291 } 292 asyncResp->res.jsonValue["Name"] = chassisId; 293 asyncResp->res.jsonValue["Id"] = chassisId; 294 asyncResp->res.jsonValue["Thermal"] = { 295 {"@odata.id", "/redfish/v1/Chassis/" + 296 chassisId + "/Thermal"}}; 297 // Power object 298 asyncResp->res.jsonValue["Power"] = { 299 {"@odata.id", "/redfish/v1/Chassis/" + 300 chassisId + "/Power"}}; 301 asyncResp->res.jsonValue["Status"] = { 302 {"Health", "OK"}, 303 {"State", "Enabled"}, 304 }; 305 306 asyncResp->res 307 .jsonValue["Links"]["ComputerSystems"] = { 308 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 309 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 310 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 311 }, 312 connectionName, path, "org.freedesktop.DBus.Properties", 313 "GetAll", 314 "xyz.openbmc_project.Inventory.Decorator.Asset"); 315 return; 316 } 317 318 // Couldn't find an object with that name. return an error 319 messages::resourceNotFound( 320 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); 321 }, 322 "xyz.openbmc_project.ObjectMapper", 323 "/xyz/openbmc_project/object_mapper", 324 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 325 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 326 327 getPhysicalSecurityData(asyncResp); 328 } 329 }; 330 } // namespace redfish 331