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