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 res.jsonValue["@odata.type"] = "#Chassis.v1_4_0.Chassis"; 283 res.jsonValue["@odata.id"] = "/redfish/v1/Chassis/" + chassisId; 284 res.jsonValue["@odata.context"] = 285 "/redfish/v1/$metadata#Chassis.Chassis"; 286 res.jsonValue["Name"] = "Chassis Collection"; 287 res.jsonValue["ChassisType"] = "RackMount"; 288 289 auto asyncResp = std::make_shared<AsyncResp>(res); 290 crow::connections::systemBus->async_method_call( 291 [asyncResp, chassisId(std::string(chassisId))]( 292 const boost::system::error_code ec, 293 const std::vector<std::pair< 294 std::string, std::vector<std::pair< 295 std::string, std::vector<std::string>>>>> 296 &subtree) { 297 if (ec) 298 { 299 messages::internalError(asyncResp->res); 300 return; 301 } 302 // Iterate over all retrieved ObjectPaths. 303 for (const std::pair< 304 std::string, 305 std::vector< 306 std::pair<std::string, std::vector<std::string>>>> 307 &object : subtree) 308 { 309 const std::string &path = object.first; 310 const std::vector< 311 std::pair<std::string, std::vector<std::string>>> 312 &connectionNames = object.second; 313 314 // If only one chassis, just select the first one 315 #ifndef BMCWEB_ENABLE_REDFISH_ONE_CHASSIS 316 if (!boost::ends_with(path, chassisId)) 317 { 318 continue; 319 } 320 #endif 321 if (connectionNames.size() < 1) 322 { 323 BMCWEB_LOG_ERROR << "Only got " 324 << connectionNames.size() 325 << " Connection names"; 326 continue; 327 } 328 329 const std::string connectionName = connectionNames[0].first; 330 crow::connections::systemBus->async_method_call( 331 [asyncResp, chassisId(std::string(chassisId))]( 332 const boost::system::error_code ec, 333 const std::vector<std::pair< 334 std::string, VariantType>> &propertiesList) { 335 for (const std::pair<std::string, VariantType> 336 &property : propertiesList) 337 { 338 // Store DBus properties that are also Redfish 339 // properties with same name and a string value 340 const std::string &propertyName = 341 property.first; 342 if ((propertyName == "PartNumber") || 343 (propertyName == "SerialNumber") || 344 (propertyName == "Manufacturer") || 345 (propertyName == "Model")) 346 { 347 const std::string *value = 348 std::get_if<std::string>( 349 &property.second); 350 if (value != nullptr) 351 { 352 asyncResp->res.jsonValue[propertyName] = 353 *value; 354 } 355 } 356 } 357 asyncResp->res.jsonValue["Name"] = chassisId; 358 asyncResp->res.jsonValue["Id"] = chassisId; 359 asyncResp->res.jsonValue["Thermal"] = { 360 {"@odata.id", "/redfish/v1/Chassis/" + 361 chassisId + "/Thermal"}}; 362 // Power object 363 asyncResp->res.jsonValue["Power"] = { 364 {"@odata.id", "/redfish/v1/Chassis/" + 365 chassisId + "/Power"}}; 366 asyncResp->res.jsonValue["Status"] = { 367 {"Health", "OK"}, 368 {"State", "Enabled"}, 369 }; 370 371 asyncResp->res 372 .jsonValue["Links"]["ComputerSystems"] = { 373 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 374 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 375 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 376 getChassisState(asyncResp); 377 }, 378 connectionName, path, "org.freedesktop.DBus.Properties", 379 "GetAll", 380 "xyz.openbmc_project.Inventory.Decorator.Asset"); 381 return; 382 } 383 384 // Couldn't find an object with that name. return an error 385 messages::resourceNotFound( 386 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); 387 }, 388 "xyz.openbmc_project.ObjectMapper", 389 "/xyz/openbmc_project/object_mapper", 390 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 391 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 392 393 getPhysicalSecurityData(asyncResp); 394 } 395 }; 396 } // namespace redfish 397