blob: 79b5305132f611338927d6a51f773b58aed83071 [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
32inline 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
132inline 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
208inline 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
276inline 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
312inline 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
371inline 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
440class 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
Gunnar Mills9dedf572020-10-14 16:36:35 -0500469 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system/Processors";
Gunnar Millsac6a4442020-10-14 14:55:29 -0500470 auto asyncResp = std::make_shared<AsyncResp>(res);
471
Gunnar Mills05030b82020-10-14 15:51:31 -0500472 collection_util::getCollectionMembers(
473 asyncResp, "/redfish/v1/Systems/system/Processors",
Gunnar Millsac6a4442020-10-14 14:55:29 -0500474 {"xyz.openbmc_project.Inventory.Item.Cpu",
475 "xyz.openbmc_project.Inventory.Item.Accelerator"});
476 }
477};
478
479class 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