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