blob: 82c01f949eb101b37e5fe7b656775b6338695ebc [file] [log] [blame]
Gunnar Millsac6a4442020-10-14 14:55:29 -05001/*
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
25namespace redfish
26{
27
28using InterfacesProperties = boost::container::flat_map<
29 std::string,
30 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>>;
31
Jonathan Doman2bab9832020-12-02 15:27:40 -080032using MapperGetSubTreeResponse = std::vector<
33 std::pair<std::string,
34 std::vector<std::pair<std::string, std::vector<std::string>>>>>;
35
Gunnar Millsac6a4442020-10-14 14:55:29 -050036inline 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 }
Jonathan Domandc3fa662020-10-26 23:10:24 -070074 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 }
Gunnar Millsac6a4442020-10-14 14:55:29 -050082 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 {
Jonathan Domandc3fa662020-10-26 23:10:24 -070093 const uint16_t* value = std::get_if<uint16_t>(&property.second);
Gunnar Millsac6a4442020-10-14 14:55:29 -050094 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
144inline 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
220inline 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
288inline 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
324inline 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
383inline void getProcessorData(std::shared_ptr<AsyncResp> aResp,
Jonathan Doman2bab9832020-12-02 15:27:40 -0800384 const std::string& processorId)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500385{
386 BMCWEB_LOG_DEBUG << "Get available system processor resources.";
387
388 crow::connections::systemBus->async_method_call(
Jonathan Doman2bab9832020-12-02 15:27:40 -0800389 [processorId,
390 aResp{std::move(aResp)}](const boost::system::error_code ec,
391 const MapperGetSubTreeResponse& subtree) {
Gunnar Millsac6a4442020-10-14 14:55:29 -0500392 if (ec)
393 {
394 BMCWEB_LOG_DEBUG << "DBUS response error";
395 messages::internalError(aResp->res);
396 return;
397 }
Jonathan Doman2bab9832020-12-02 15:27:40 -0800398 for (const auto& [objectPath, serviceMap] : subtree)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500399 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800400 // Ignore any objects which don't end with our desired cpu name
401 if (!boost::ends_with(objectPath, processorId))
Gunnar Millsac6a4442020-10-14 14:55:29 -0500402 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800403 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)
Gunnar Millsac6a4442020-10-14 14:55:29 -0500414 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800415 if (interface ==
416 "xyz.openbmc_project.Inventory.Decorator.Asset")
Gunnar Millsac6a4442020-10-14 14:55:29 -0500417 {
Jonathan Doman2bab9832020-12-02 15:27:40 -0800418 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);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500436 }
437 }
Gunnar Millsac6a4442020-10-14 14:55:29 -0500438 }
Jonathan Doman2bab9832020-12-02 15:27:40 -0800439 return;
Gunnar Millsac6a4442020-10-14 14:55:29 -0500440 }
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",
Jonathan Doman2bab9832020-12-02 15:27:40 -0800448 "/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"});
Gunnar Millsac6a4442020-10-14 14:55:29 -0500454}
455
456class 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
Gunnar Mills9dedf572020-10-14 16:36:35 -0500485 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors";
Gunnar Millsac6a4442020-10-14 14:55:29 -0500486 auto asyncResp = std::make_shared<AsyncResp>(res);
487
Gunnar Mills05030b82020-10-14 15:51:31 -0500488 collection_util::getCollectionMembers(
489 asyncResp, "/redfish/v1/Systems/system/Processors",
Gunnar Millsac6a4442020-10-14 14:55:29 -0500490 {"xyz.openbmc_project.Inventory.Item.Cpu",
491 "xyz.openbmc_project.Inventory.Item.Accelerator"});
492 }
493};
494
495class 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
Jonathan Doman2bab9832020-12-02 15:27:40 -0800536 getProcessorData(asyncResp, processorId);
Gunnar Millsac6a4442020-10-14 14:55:29 -0500537 }
538};
539
540} // namespace redfish