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 20 #include <boost/container/flat_map.hpp> 21 #include <node.hpp> 22 #include <utils/collection.hpp> 23 #include <utils/json_utils.hpp> 24 25 namespace redfish 26 { 27 28 using InterfacesProperties = boost::container::flat_map< 29 std::string, 30 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>; 31 32 inline void 33 getCpuDataByInterface(const std::shared_ptr<AsyncResp>& aResp, 34 const InterfacesProperties& cpuInterfacesProperties) 35 { 36 BMCWEB_LOG_DEBUG << "Get CPU resources by interface."; 37 38 // Added for future purpose. Once present and functional attributes added 39 // in busctl call, need to add actual logic to fetch original values. 40 bool present = false; 41 const bool functional = true; 42 auto health = std::make_shared<HealthPopulate>(aResp); 43 health->populate(); 44 45 for (const auto& interface : cpuInterfacesProperties) 46 { 47 for (const auto& property : interface.second) 48 { 49 if (property.first == "CoreCount") 50 { 51 const uint16_t* coresCount = 52 std::get_if<uint16_t>(&property.second); 53 if (coresCount == nullptr) 54 { 55 // Important property not in desired type 56 messages::internalError(aResp->res); 57 return; 58 } 59 if (*coresCount == 0) 60 { 61 // Slot is not populated, set status end return 62 aResp->res.jsonValue["Status"]["State"] = "Absent"; 63 // HTTP Code will be set up automatically, just return 64 return; 65 } 66 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 67 present = true; 68 aResp->res.jsonValue["TotalCores"] = *coresCount; 69 } 70 else if (property.first == "Socket") 71 { 72 const std::string* value = 73 std::get_if<std::string>(&property.second); 74 if (value != nullptr) 75 { 76 aResp->res.jsonValue["Socket"] = *value; 77 } 78 } 79 else if (property.first == "ThreadCount") 80 { 81 const int64_t* value = std::get_if<int64_t>(&property.second); 82 if (value != nullptr) 83 { 84 aResp->res.jsonValue["TotalThreads"] = *value; 85 } 86 } 87 else if (property.first == "Family") 88 { 89 const std::string* value = 90 std::get_if<std::string>(&property.second); 91 if (value != nullptr) 92 { 93 aResp->res.jsonValue["ProcessorId"]["EffectiveFamily"] = 94 *value; 95 } 96 } 97 else if (property.first == "Id") 98 { 99 const uint64_t* value = std::get_if<uint64_t>(&property.second); 100 if (value != nullptr && *value != 0) 101 { 102 present = true; 103 aResp->res 104 .jsonValue["ProcessorId"]["IdentificationRegisters"] = 105 boost::lexical_cast<std::string>(*value); 106 } 107 } 108 } 109 } 110 111 if (present == false) 112 { 113 aResp->res.jsonValue["Status"]["State"] = "Absent"; 114 aResp->res.jsonValue["Status"]["Health"] = "OK"; 115 } 116 else 117 { 118 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 119 if (functional) 120 { 121 aResp->res.jsonValue["Status"]["Health"] = "OK"; 122 } 123 else 124 { 125 aResp->res.jsonValue["Status"]["Health"] = "Critical"; 126 } 127 } 128 129 return; 130 } 131 132 inline void getCpuDataByService(std::shared_ptr<AsyncResp> aResp, 133 const std::string& cpuId, 134 const std::string& service, 135 const std::string& objPath) 136 { 137 BMCWEB_LOG_DEBUG << "Get available system cpu resources by service."; 138 139 crow::connections::systemBus->async_method_call( 140 [cpuId, service, objPath, aResp{std::move(aResp)}]( 141 const boost::system::error_code ec, 142 const dbus::utility::ManagedObjectType& dbusData) { 143 if (ec) 144 { 145 BMCWEB_LOG_DEBUG << "DBUS response error"; 146 messages::internalError(aResp->res); 147 return; 148 } 149 aResp->res.jsonValue["Id"] = cpuId; 150 aResp->res.jsonValue["Name"] = "Processor"; 151 aResp->res.jsonValue["ProcessorType"] = "CPU"; 152 153 bool slotPresent = false; 154 std::string corePath = objPath + "/core"; 155 size_t totalCores = 0; 156 for (const auto& object : dbusData) 157 { 158 if (object.first.str == objPath) 159 { 160 getCpuDataByInterface(aResp, object.second); 161 } 162 else if (boost::starts_with(object.first.str, corePath)) 163 { 164 for (const auto& interface : object.second) 165 { 166 if (interface.first == 167 "xyz.openbmc_project.Inventory.Item") 168 { 169 for (const auto& property : interface.second) 170 { 171 if (property.first == "Present") 172 { 173 const bool* present = 174 std::get_if<bool>(&property.second); 175 if (present != nullptr) 176 { 177 if (*present == true) 178 { 179 slotPresent = true; 180 totalCores++; 181 } 182 } 183 } 184 } 185 } 186 } 187 } 188 } 189 // In getCpuDataByInterface(), state and health are set 190 // based on the present and functional status. If core 191 // count is zero, then it has a higher precedence. 192 if (slotPresent) 193 { 194 if (totalCores == 0) 195 { 196 // Slot is not populated, set status end return 197 aResp->res.jsonValue["Status"]["State"] = "Absent"; 198 aResp->res.jsonValue["Status"]["Health"] = "OK"; 199 } 200 aResp->res.jsonValue["TotalCores"] = totalCores; 201 } 202 return; 203 }, 204 service, "/xyz/openbmc_project/inventory", 205 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 206 } 207 208 inline void getCpuAssetData(std::shared_ptr<AsyncResp> aResp, 209 const std::string& service, 210 const std::string& objPath) 211 { 212 BMCWEB_LOG_DEBUG << "Get Cpu Asset Data"; 213 crow::connections::systemBus->async_method_call( 214 [objPath, aResp{std::move(aResp)}]( 215 const boost::system::error_code ec, 216 const boost::container::flat_map< 217 std::string, std::variant<std::string, uint32_t, uint16_t, 218 bool>>& properties) { 219 if (ec) 220 { 221 BMCWEB_LOG_DEBUG << "DBUS response error"; 222 messages::internalError(aResp->res); 223 return; 224 } 225 226 for (const auto& property : properties) 227 { 228 if (property.first == "SerialNumber") 229 { 230 const std::string* sn = 231 std::get_if<std::string>(&property.second); 232 if (sn != nullptr && !sn->empty()) 233 { 234 aResp->res.jsonValue["SerialNumber"] = *sn; 235 } 236 } 237 else if (property.first == "Model") 238 { 239 const std::string* model = 240 std::get_if<std::string>(&property.second); 241 if (model != nullptr && !model->empty()) 242 { 243 aResp->res.jsonValue["Model"] = *model; 244 } 245 } 246 else if (property.first == "Manufacturer") 247 { 248 249 const std::string* mfg = 250 std::get_if<std::string>(&property.second); 251 if (mfg != nullptr) 252 { 253 aResp->res.jsonValue["Manufacturer"] = *mfg; 254 255 // Otherwise would be unexpected. 256 if (mfg->find("Intel") != std::string::npos) 257 { 258 aResp->res.jsonValue["ProcessorArchitecture"] = 259 "x86"; 260 aResp->res.jsonValue["InstructionSet"] = "x86-64"; 261 } 262 else if (mfg->find("IBM") != std::string::npos) 263 { 264 aResp->res.jsonValue["ProcessorArchitecture"] = 265 "Power"; 266 aResp->res.jsonValue["InstructionSet"] = "PowerISA"; 267 } 268 } 269 } 270 } 271 }, 272 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 273 "xyz.openbmc_project.Inventory.Decorator.Asset"); 274 } 275 276 inline void getCpuRevisionData(std::shared_ptr<AsyncResp> aResp, 277 const std::string& service, 278 const std::string& objPath) 279 { 280 BMCWEB_LOG_DEBUG << "Get Cpu Revision Data"; 281 crow::connections::systemBus->async_method_call( 282 [objPath, aResp{std::move(aResp)}]( 283 const boost::system::error_code ec, 284 const boost::container::flat_map< 285 std::string, std::variant<std::string, uint32_t, uint16_t, 286 bool>>& properties) { 287 if (ec) 288 { 289 BMCWEB_LOG_DEBUG << "DBUS response error"; 290 messages::internalError(aResp->res); 291 return; 292 } 293 294 for (const auto& property : properties) 295 { 296 if (property.first == "Version") 297 { 298 const std::string* ver = 299 std::get_if<std::string>(&property.second); 300 if (ver != nullptr) 301 { 302 aResp->res.jsonValue["Version"] = *ver; 303 } 304 break; 305 } 306 } 307 }, 308 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", 309 "xyz.openbmc_project.Inventory.Decorator.Revision"); 310 } 311 312 inline void getAcceleratorDataByService(std::shared_ptr<AsyncResp> aResp, 313 const std::string& acclrtrId, 314 const std::string& service, 315 const std::string& objPath) 316 { 317 BMCWEB_LOG_DEBUG 318 << "Get available system Accelerator resources by service."; 319 crow::connections::systemBus->async_method_call( 320 [acclrtrId, aResp{std::move(aResp)}]( 321 const boost::system::error_code ec, 322 const boost::container::flat_map< 323 std::string, std::variant<std::string, uint32_t, uint16_t, 324 bool>>& properties) { 325 if (ec) 326 { 327 BMCWEB_LOG_DEBUG << "DBUS response error"; 328 messages::internalError(aResp->res); 329 return; 330 } 331 aResp->res.jsonValue["Id"] = acclrtrId; 332 aResp->res.jsonValue["Name"] = "Processor"; 333 const bool* accPresent = nullptr; 334 const bool* accFunctional = nullptr; 335 336 for (const auto& property : properties) 337 { 338 if (property.first == "Functional") 339 { 340 accFunctional = std::get_if<bool>(&property.second); 341 } 342 else if (property.first == "Present") 343 { 344 accPresent = std::get_if<bool>(&property.second); 345 } 346 } 347 348 std::string state = "Enabled"; 349 std::string health = "OK"; 350 351 if (accPresent != nullptr && *accPresent == false) 352 { 353 state = "Absent"; 354 } 355 356 if ((accFunctional != nullptr) && (*accFunctional == false)) 357 { 358 if (state == "Enabled") 359 { 360 health = "Critical"; 361 } 362 } 363 364 aResp->res.jsonValue["Status"]["State"] = state; 365 aResp->res.jsonValue["Status"]["Health"] = health; 366 aResp->res.jsonValue["ProcessorType"] = "Accelerator"; 367 }, 368 service, objPath, "org.freedesktop.DBus.Properties", "GetAll", ""); 369 } 370 371 inline void getProcessorData(std::shared_ptr<AsyncResp> aResp, 372 const std::string& processorId, 373 const std::vector<const char*>& inventoryItems) 374 { 375 BMCWEB_LOG_DEBUG << "Get available system processor resources."; 376 377 crow::connections::systemBus->async_method_call( 378 [processorId, aResp{std::move(aResp)}]( 379 const boost::system::error_code ec, 380 const boost::container::flat_map< 381 std::string, boost::container::flat_map< 382 std::string, std::vector<std::string>>>& 383 subtree) { 384 if (ec) 385 { 386 BMCWEB_LOG_DEBUG << "DBUS response error"; 387 messages::internalError(aResp->res); 388 return; 389 } 390 for (const auto& object : subtree) 391 { 392 if (boost::ends_with(object.first, processorId)) 393 { 394 for (const auto& service : object.second) 395 { 396 for (const auto& inventory : service.second) 397 { 398 if (inventory == "xyz.openbmc_project." 399 "Inventory.Decorator.Asset") 400 { 401 getCpuAssetData(aResp, service.first, 402 object.first); 403 } 404 else if (inventory == 405 "xyz.openbmc_project." 406 "Inventory.Decorator.Revision") 407 { 408 getCpuRevisionData(aResp, service.first, 409 object.first); 410 } 411 else if (inventory == "xyz.openbmc_project." 412 "Inventory.Item.Cpu") 413 { 414 getCpuDataByService(aResp, processorId, 415 service.first, 416 object.first); 417 } 418 else if (inventory == "xyz.openbmc_project." 419 "Inventory.Item.Accelerator") 420 { 421 getAcceleratorDataByService(aResp, processorId, 422 service.first, 423 object.first); 424 } 425 } 426 } 427 return; 428 } 429 } 430 // Object not found 431 messages::resourceNotFound(aResp->res, "Processor", processorId); 432 return; 433 }, 434 "xyz.openbmc_project.ObjectMapper", 435 "/xyz/openbmc_project/object_mapper", 436 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 437 "/xyz/openbmc_project/inventory", 0, inventoryItems); 438 } 439 440 class ProcessorCollection : public Node 441 { 442 public: 443 /* 444 * Default Constructor 445 */ 446 ProcessorCollection(App& app) : 447 Node(app, "/redfish/v1/Systems/system/Processors/") 448 { 449 entityPrivileges = { 450 {boost::beast::http::verb::get, {{"Login"}}}, 451 {boost::beast::http::verb::head, {{"Login"}}}, 452 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 453 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 454 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 455 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 456 } 457 458 private: 459 /** 460 * Functions triggers appropriate requests on DBus 461 */ 462 void doGet(crow::Response& res, const crow::Request&, 463 const std::vector<std::string>&) override 464 { 465 res.jsonValue["@odata.type"] = 466 "#ProcessorCollection.ProcessorCollection"; 467 res.jsonValue["Name"] = "Processor Collection"; 468 469 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors"; 470 auto asyncResp = std::make_shared<AsyncResp>(res); 471 472 collection_util::getCollectionMembers( 473 asyncResp, "/redfish/v1/Systems/system/Processors", 474 {"xyz.openbmc_project.Inventory.Item.Cpu", 475 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 476 } 477 }; 478 479 class Processor : public Node 480 { 481 public: 482 /* 483 * Default Constructor 484 */ 485 Processor(App& app) : 486 Node(app, "/redfish/v1/Systems/system/Processors/<str>/", std::string()) 487 { 488 entityPrivileges = { 489 {boost::beast::http::verb::get, {{"Login"}}}, 490 {boost::beast::http::verb::head, {{"Login"}}}, 491 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 492 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 493 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 494 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 495 } 496 497 private: 498 /** 499 * Functions triggers appropriate requests on DBus 500 */ 501 void doGet(crow::Response& res, const crow::Request&, 502 const std::vector<std::string>& params) override 503 { 504 // Check if there is required param, truly entering this shall be 505 // impossible 506 if (params.size() != 1) 507 { 508 messages::internalError(res); 509 510 res.end(); 511 return; 512 } 513 const std::string& processorId = params[0]; 514 res.jsonValue["@odata.type"] = "#Processor.v1_9_0.Processor"; 515 res.jsonValue["@odata.id"] = 516 "/redfish/v1/Systems/system/Processors/" + processorId; 517 518 auto asyncResp = std::make_shared<AsyncResp>(res); 519 520 getProcessorData(asyncResp, processorId, 521 {"xyz.openbmc_project.Inventory.Item.Cpu", 522 "xyz.openbmc_project.Inventory.Decorator.Asset", 523 "xyz.openbmc_project.Inventory.Item.Accelerator"}); 524 } 525 }; 526 527 } // namespace redfish 528