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", int32_t(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 *, 3> interfaces = { 186 "xyz.openbmc_project.Inventory.Item.Board", 187 "xyz.openbmc_project.Inventory.Item.Chassis", 188 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 189 190 auto asyncResp = std::make_shared<AsyncResp>(res); 191 crow::connections::systemBus->async_method_call( 192 [asyncResp](const boost::system::error_code ec, 193 const std::vector<std::string> &chassisList) { 194 if (ec) 195 { 196 messages::internalError(asyncResp->res); 197 return; 198 } 199 nlohmann::json &chassisArray = 200 asyncResp->res.jsonValue["Members"]; 201 chassisArray = nlohmann::json::array(); 202 for (const std::string &objpath : chassisList) 203 { 204 std::size_t lastPos = objpath.rfind("/"); 205 if (lastPos == std::string::npos) 206 { 207 BMCWEB_LOG_ERROR << "Failed to find '/' in " << objpath; 208 continue; 209 } 210 chassisArray.push_back( 211 {{"@odata.id", "/redfish/v1/Chassis/" + 212 objpath.substr(lastPos + 1)}}); 213 } 214 215 asyncResp->res.jsonValue["Members@odata.count"] = 216 chassisArray.size(); 217 }, 218 "xyz.openbmc_project.ObjectMapper", 219 "/xyz/openbmc_project/object_mapper", 220 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 221 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 222 } 223 }; 224 225 /** 226 * Chassis override class for delivering Chassis Schema 227 */ 228 class Chassis : public Node 229 { 230 public: 231 Chassis(CrowApp &app) : 232 Node(app, "/redfish/v1/Chassis/<str>/", std::string()) 233 { 234 entityPrivileges = { 235 {boost::beast::http::verb::get, {{"Login"}}}, 236 {boost::beast::http::verb::head, {{"Login"}}}, 237 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 238 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 239 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 240 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 241 } 242 243 private: 244 /** 245 * Functions triggers appropriate requests on DBus 246 */ 247 void doGet(crow::Response &res, const crow::Request &req, 248 const std::vector<std::string> ¶ms) override 249 { 250 const std::array<const char *, 3> interfaces = { 251 "xyz.openbmc_project.Inventory.Item.Board", 252 "xyz.openbmc_project.Inventory.Item.Chassis", 253 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 254 255 // Check if there is required param, truly entering this shall be 256 // impossible. 257 if (params.size() != 1) 258 { 259 messages::internalError(res); 260 res.end(); 261 return; 262 } 263 const std::string &chassisId = params[0]; 264 265 auto asyncResp = std::make_shared<AsyncResp>(res); 266 crow::connections::systemBus->async_method_call( 267 [asyncResp, chassisId(std::string(chassisId))]( 268 const boost::system::error_code ec, 269 const std::vector<std::pair< 270 std::string, std::vector<std::pair< 271 std::string, std::vector<std::string>>>>> 272 &subtree) { 273 if (ec) 274 { 275 messages::internalError(asyncResp->res); 276 return; 277 } 278 // Iterate over all retrieved ObjectPaths. 279 for (const std::pair< 280 std::string, 281 std::vector< 282 std::pair<std::string, std::vector<std::string>>>> 283 &object : subtree) 284 { 285 const std::string &path = object.first; 286 const std::vector< 287 std::pair<std::string, std::vector<std::string>>> 288 &connectionNames = object.second; 289 290 if (!boost::ends_with(path, chassisId)) 291 { 292 continue; 293 } 294 295 auto health = std::make_shared<HealthPopulate>(asyncResp); 296 297 crow::connections::systemBus->async_method_call( 298 [health](const boost::system::error_code ec, 299 std::variant<std::vector<std::string>> &resp) { 300 if (ec) 301 { 302 return; // no sensors = no failures 303 } 304 std::vector<std::string> *data = 305 std::get_if<std::vector<std::string>>(&resp); 306 if (data == nullptr) 307 { 308 return; 309 } 310 health->inventory = std::move(*data); 311 }, 312 "xyz.openbmc_project.ObjectMapper", 313 path + "/all_sensors", 314 "org.freedesktop.DBus.Properties", "Get", 315 "xyz.openbmc_project.Association", "endpoints"); 316 317 health->populate(); 318 319 if (connectionNames.size() < 1) 320 { 321 BMCWEB_LOG_ERROR << "Only got " 322 << connectionNames.size() 323 << " Connection names"; 324 continue; 325 } 326 327 asyncResp->res.jsonValue["@odata.type"] = 328 "#Chassis.v1_4_0.Chassis"; 329 asyncResp->res.jsonValue["@odata.id"] = 330 "/redfish/v1/Chassis/" + chassisId; 331 asyncResp->res.jsonValue["@odata.context"] = 332 "/redfish/v1/$metadata#Chassis.Chassis"; 333 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 334 asyncResp->res.jsonValue["ChassisType"] = "RackMount"; 335 336 const std::string &connectionName = 337 connectionNames[0].first; 338 crow::connections::systemBus->async_method_call( 339 [asyncResp, chassisId(std::string(chassisId))]( 340 const boost::system::error_code ec, 341 const std::vector<std::pair< 342 std::string, VariantType>> &propertiesList) { 343 for (const std::pair<std::string, VariantType> 344 &property : propertiesList) 345 { 346 // Store DBus properties that are also Redfish 347 // properties with same name and a string value 348 const std::string &propertyName = 349 property.first; 350 if ((propertyName == "PartNumber") || 351 (propertyName == "SerialNumber") || 352 (propertyName == "Manufacturer") || 353 (propertyName == "Model")) 354 { 355 const std::string *value = 356 std::get_if<std::string>( 357 &property.second); 358 if (value != nullptr) 359 { 360 asyncResp->res.jsonValue[propertyName] = 361 *value; 362 } 363 } 364 } 365 asyncResp->res.jsonValue["Name"] = chassisId; 366 asyncResp->res.jsonValue["Id"] = chassisId; 367 asyncResp->res.jsonValue["Thermal"] = { 368 {"@odata.id", "/redfish/v1/Chassis/" + 369 chassisId + "/Thermal"}}; 370 // Power object 371 asyncResp->res.jsonValue["Power"] = { 372 {"@odata.id", "/redfish/v1/Chassis/" + 373 chassisId + "/Power"}}; 374 asyncResp->res.jsonValue["Status"] = { 375 {"State", "Enabled"}, 376 }; 377 378 asyncResp->res 379 .jsonValue["Links"]["ComputerSystems"] = { 380 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 381 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 382 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 383 getChassisState(asyncResp); 384 }, 385 connectionName, path, "org.freedesktop.DBus.Properties", 386 "GetAll", 387 "xyz.openbmc_project.Inventory.Decorator.Asset"); 388 return; 389 } 390 391 // Couldn't find an object with that name. return an error 392 messages::resourceNotFound( 393 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); 394 }, 395 "xyz.openbmc_project.ObjectMapper", 396 "/xyz/openbmc_project/object_mapper", 397 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 398 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 399 400 getPhysicalSecurityData(asyncResp); 401 } 402 }; 403 } // namespace redfish 404