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