xref: /openbmc/bmcweb/redfish-core/lib/processor.hpp (revision 04e438cbad66838724d78ce12f28aff1fb892a63)
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