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 const std::array<const char *, 3> interfaces = { 185 "xyz.openbmc_project.Inventory.Item.Board", 186 "xyz.openbmc_project.Inventory.Item.Chassis", 187 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 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", int32_t(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 *, 3> interfaces = { 250 "xyz.openbmc_project.Inventory.Item.Board", 251 "xyz.openbmc_project.Inventory.Item.Chassis", 252 "xyz.openbmc_project.Inventory.Item.PowerSupply"}; 253 254 // Check if there is required param, truly entering this shall be 255 // impossible. 256 if (params.size() != 1) 257 { 258 messages::internalError(res); 259 res.end(); 260 return; 261 } 262 const std::string &chassisId = params[0]; 263 264 auto asyncResp = std::make_shared<AsyncResp>(res); 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, std::vector<std::pair< 270 std::string, std::vector<std::string>>>>> 271 &subtree) { 272 if (ec) 273 { 274 messages::internalError(asyncResp->res); 275 return; 276 } 277 // Iterate over all retrieved ObjectPaths. 278 for (const std::pair< 279 std::string, 280 std::vector< 281 std::pair<std::string, std::vector<std::string>>>> 282 &object : subtree) 283 { 284 const std::string &path = object.first; 285 const std::vector< 286 std::pair<std::string, std::vector<std::string>>> 287 &connectionNames = object.second; 288 289 if (!boost::ends_with(path, chassisId)) 290 { 291 continue; 292 } 293 294 if (connectionNames.size() < 1) 295 { 296 BMCWEB_LOG_ERROR << "Only got " 297 << connectionNames.size() 298 << " Connection names"; 299 continue; 300 } 301 302 asyncResp->res.jsonValue["@odata.type"] = 303 "#Chassis.v1_4_0.Chassis"; 304 asyncResp->res.jsonValue["@odata.id"] = 305 "/redfish/v1/Chassis/" + chassisId; 306 asyncResp->res.jsonValue["@odata.context"] = 307 "/redfish/v1/$metadata#Chassis.Chassis"; 308 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 309 asyncResp->res.jsonValue["ChassisType"] = "RackMount"; 310 311 const std::string &connectionName = 312 connectionNames[0].first; 313 crow::connections::systemBus->async_method_call( 314 [asyncResp, chassisId(std::string(chassisId))]( 315 const boost::system::error_code ec, 316 const std::vector<std::pair< 317 std::string, VariantType>> &propertiesList) { 318 for (const std::pair<std::string, VariantType> 319 &property : propertiesList) 320 { 321 // Store DBus properties that are also Redfish 322 // properties with same name and a string value 323 const std::string &propertyName = 324 property.first; 325 if ((propertyName == "PartNumber") || 326 (propertyName == "SerialNumber") || 327 (propertyName == "Manufacturer") || 328 (propertyName == "Model")) 329 { 330 const std::string *value = 331 std::get_if<std::string>( 332 &property.second); 333 if (value != nullptr) 334 { 335 asyncResp->res.jsonValue[propertyName] = 336 *value; 337 } 338 } 339 } 340 asyncResp->res.jsonValue["Name"] = chassisId; 341 asyncResp->res.jsonValue["Id"] = chassisId; 342 asyncResp->res.jsonValue["Thermal"] = { 343 {"@odata.id", "/redfish/v1/Chassis/" + 344 chassisId + "/Thermal"}}; 345 // Power object 346 asyncResp->res.jsonValue["Power"] = { 347 {"@odata.id", "/redfish/v1/Chassis/" + 348 chassisId + "/Power"}}; 349 asyncResp->res.jsonValue["Status"] = { 350 {"Health", "OK"}, 351 {"State", "Enabled"}, 352 }; 353 354 asyncResp->res 355 .jsonValue["Links"]["ComputerSystems"] = { 356 {{"@odata.id", "/redfish/v1/Systems/system"}}}; 357 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 358 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 359 getChassisState(asyncResp); 360 }, 361 connectionName, path, "org.freedesktop.DBus.Properties", 362 "GetAll", 363 "xyz.openbmc_project.Inventory.Decorator.Asset"); 364 return; 365 } 366 367 // Couldn't find an object with that name. return an error 368 messages::resourceNotFound( 369 asyncResp->res, "#Chassis.v1_4_0.Chassis", chassisId); 370 }, 371 "xyz.openbmc_project.ObjectMapper", 372 "/xyz/openbmc_project/object_mapper", 373 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 374 "/xyz/openbmc_project/inventory", int32_t(0), interfaces); 375 376 getPhysicalSecurityData(asyncResp); 377 } 378 }; 379 } // namespace redfish 380