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