blob: 46b35d5d6df745a301728b75e967e5a1781ae82c [file] [log] [blame]
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +02001/*
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
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080018#include "app.hpp"
19#include "dbus_utility.hpp"
James Feist35e257a2020-06-05 13:30:51 -070020#include "health.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080021#include "query.hpp"
22#include "registries/privilege_registry.hpp"
23#include "utils/collection.hpp"
24#include "utils/dbus_utils.hpp"
25#include "utils/hex_utils.hpp"
26#include "utils/json_utils.hpp"
James Feist35e257a2020-06-05 13:30:51 -070027
George Liue99073f2022-12-09 11:06:16 +080028#include <boost/system/error_code.hpp>
Ed Tanousef4c65b2023-04-24 15:28:50 -070029#include <boost/url/format.hpp>
Nan Zhoud7f04fd2022-05-01 01:11:07 +000030#include <nlohmann/json.hpp>
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +020031#include <sdbusplus/asio/property.hpp>
32#include <sdbusplus/unpack_properties.hpp>
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +020033
George Liu7a1dbc42022-12-07 16:03:22 +080034#include <array>
35#include <string_view>
36
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +020037namespace redfish
38{
39
Gunnar Mills313efb12020-10-26 16:05:08 -050040inline std::string translateMemoryTypeToRedfish(const std::string& memoryType)
41{
42 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR")
43 {
44 return "DDR";
45 }
46 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2")
47 {
48 return "DDR2";
49 }
50 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR3")
51 {
52 return "DDR3";
53 }
54 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4")
55 {
56 return "DDR4";
57 }
58 if (memoryType ==
59 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR4E_SDRAM")
60 {
61 return "DDR4E_SDRAM";
62 }
Mansi Joshi11a2f0f2021-07-19 14:33:27 +053063 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR5")
64 {
65 return "DDR5";
66 }
Gunnar Mills313efb12020-10-26 16:05:08 -050067 if (memoryType ==
68 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR4_SDRAM")
69 {
70 return "LPDDR4_SDRAM";
71 }
72 if (memoryType ==
73 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.LPDDR3_SDRAM")
74 {
75 return "LPDDR3_SDRAM";
76 }
77 if (memoryType ==
78 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM")
79 {
80 return "DDR2_SDRAM_FB_DIMM";
81 }
George Liu0fda0f12021-11-16 10:06:17 +080082 if (memoryType ==
83 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR2_SDRAM_FB_DIMM_PROB")
Gunnar Mills313efb12020-10-26 16:05:08 -050084 {
85 return "DDR2_SDRAM_FB_DIMM_PROBE";
86 }
87 if (memoryType ==
88 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.DDR_SGRAM")
89 {
90 return "DDR_SGRAM";
91 }
92 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.ROM")
93 {
94 return "ROM";
95 }
96 if (memoryType ==
97 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.SDRAM")
98 {
99 return "SDRAM";
100 }
101 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.EDO")
102 {
103 return "EDO";
104 }
105 if (memoryType ==
106 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.FastPageMode")
107 {
108 return "FastPageMode";
109 }
110 if (memoryType ==
111 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.PipelinedNibble")
112 {
113 return "PipelinedNibble";
114 }
115 if (memoryType ==
116 "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.Logical")
117 {
118 return "Logical";
119 }
120 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM")
121 {
122 return "HBM";
123 }
124 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM2")
125 {
126 return "HBM2";
127 }
Tyson Tuckerbearce34d512022-09-13 16:26:15 -0700128 if (memoryType == "xyz.openbmc_project.Inventory.Item.Dimm.DeviceType.HBM3")
129 {
130 return "HBM3";
131 }
Gunnar Mills313efb12020-10-26 16:05:08 -0500132 // This is values like Other or Unknown
133 // Also D-Bus values:
134 // DRAM
135 // EDRAM
136 // VRAM
137 // SRAM
138 // RAM
139 // FLASH
140 // EEPROM
141 // FEPROM
142 // EPROM
143 // CDRAM
144 // ThreeDRAM
145 // RDRAM
146 // FBD2
147 // LPDDR_SDRAM
148 // LPDDR2_SDRAM
Mansi Joshi11a2f0f2021-07-19 14:33:27 +0530149 // LPDDR5_SDRAM
Gunnar Mills313efb12020-10-26 16:05:08 -0500150 return "";
151}
152
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200153inline void dimmPropToHex(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
154 const char* key, const uint16_t* value,
155 const nlohmann::json::json_pointer& jsonPtr)
James Feistc50e7c62020-07-27 15:39:36 -0700156{
James Feistc50e7c62020-07-27 15:39:36 -0700157 if (value == nullptr)
158 {
James Feistc50e7c62020-07-27 15:39:36 -0700159 return;
160 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000161 aResp->res.jsonValue[jsonPtr][key] = "0x" + intToHexString(*value, 4);
James Feistc50e7c62020-07-27 15:39:36 -0700162}
163
zhanghch058d1b46d2021-04-01 11:18:24 +0800164inline void getPersistentMemoryProperties(
165 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200166 const dbus::utility::DBusPropertiesMap& properties,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000167 const nlohmann::json::json_pointer& jsonPtr)
James Feistc50e7c62020-07-27 15:39:36 -0700168{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200169 const uint16_t* moduleManufacturerID = nullptr;
170 const uint16_t* moduleProductID = nullptr;
171 const uint16_t* subsystemVendorID = nullptr;
172 const uint16_t* subsystemDeviceID = nullptr;
173 const uint64_t* volatileRegionSizeLimitInKiB = nullptr;
174 const uint64_t* pmRegionSizeLimitInKiB = nullptr;
175 const uint64_t* volatileSizeInKiB = nullptr;
176 const uint64_t* pmSizeInKiB = nullptr;
177 const uint64_t* cacheSizeInKB = nullptr;
178 const uint64_t* voltaileRegionMaxSizeInKib = nullptr;
179 const uint64_t* pmRegionMaxSizeInKiB = nullptr;
180 const uint64_t* allocationIncrementInKiB = nullptr;
181 const uint64_t* allocationAlignmentInKiB = nullptr;
182 const uint64_t* volatileRegionNumberLimit = nullptr;
183 const uint64_t* pmRegionNumberLimit = nullptr;
184 const uint64_t* spareDeviceCount = nullptr;
185 const bool* isSpareDeviceInUse = nullptr;
186 const bool* isRankSpareEnabled = nullptr;
187 const std::vector<uint32_t>* maxAveragePowerLimitmW = nullptr;
188 const bool* configurationLocked = nullptr;
189 const std::string* allowedMemoryModes = nullptr;
190 const std::string* memoryMedia = nullptr;
191 const bool* configurationLockCapable = nullptr;
192 const bool* dataLockCapable = nullptr;
193 const bool* passphraseCapable = nullptr;
194 const uint64_t* maxPassphraseCount = nullptr;
195 const uint64_t* passphraseLockLimit = nullptr;
James Feistc50e7c62020-07-27 15:39:36 -0700196
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200197 const bool success = sdbusplus::unpackPropertiesNoThrow(
198 dbus_utils::UnpackErrorPrinter(), properties, "ModuleManufacturerID",
199 moduleManufacturerID, "ModuleProductID", moduleProductID,
200 "SubsystemVendorID", subsystemVendorID, "SubsystemDeviceID",
201 subsystemDeviceID, "VolatileRegionSizeLimitInKiB",
202 volatileRegionSizeLimitInKiB, "PmRegionSizeLimitInKiB",
203 pmRegionSizeLimitInKiB, "VolatileSizeInKiB", volatileSizeInKiB,
204 "PmSizeInKiB", pmSizeInKiB, "CacheSizeInKB", cacheSizeInKB,
205 "VoltaileRegionMaxSizeInKib", voltaileRegionMaxSizeInKib,
206 "PmRegionMaxSizeInKiB", pmRegionMaxSizeInKiB,
207 "AllocationIncrementInKiB", allocationIncrementInKiB,
208 "AllocationAlignmentInKiB", allocationAlignmentInKiB,
209 "VolatileRegionNumberLimit", volatileRegionNumberLimit,
210 "PmRegionNumberLimit", pmRegionNumberLimit, "SpareDeviceCount",
211 spareDeviceCount, "IsSpareDeviceInUse", isSpareDeviceInUse,
212 "IsRankSpareEnabled", isRankSpareEnabled, "MaxAveragePowerLimitmW",
213 maxAveragePowerLimitmW, "ConfigurationLocked", configurationLocked,
214 "AllowedMemoryModes", allowedMemoryModes, "MemoryMedia", memoryMedia,
215 "ConfigurationLockCapable", configurationLockCapable, "DataLockCapable",
216 dataLockCapable, "PassphraseCapable", passphraseCapable,
217 "MaxPassphraseCount", maxPassphraseCount, "PassphraseLockLimit",
218 passphraseLockLimit);
219
220 if (!success)
221 {
222 messages::internalError(aResp->res);
223 return;
224 }
225
226 dimmPropToHex(aResp, "ModuleManufacturerID", moduleManufacturerID, jsonPtr);
227 dimmPropToHex(aResp, "ModuleProductID", moduleProductID, jsonPtr);
228 dimmPropToHex(aResp, "MemorySubsystemControllerManufacturerID",
229 subsystemVendorID, jsonPtr);
230 dimmPropToHex(aResp, "MemorySubsystemControllerProductID",
231 subsystemDeviceID, jsonPtr);
232
233 if (volatileRegionSizeLimitInKiB != nullptr)
234 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000235 aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeLimitMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200236 (*volatileRegionSizeLimitInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800237 }
James Feistc50e7c62020-07-27 15:39:36 -0700238
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200239 if (pmRegionSizeLimitInKiB != nullptr)
240 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000241 aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeLimitMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200242 (*pmRegionSizeLimitInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800243 }
James Feistc50e7c62020-07-27 15:39:36 -0700244
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200245 if (volatileSizeInKiB != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800246 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200247 aResp->res.jsonValue[jsonPtr]["VolatileSizeMiB"] =
248 (*volatileSizeInKiB) >> 10;
249 }
250
251 if (pmSizeInKiB != nullptr)
252 {
Patrick Williams89492a12023-05-10 07:51:34 -0500253 aResp->res.jsonValue[jsonPtr]["NonVolatileSizeMiB"] = (*pmSizeInKiB) >>
254 10;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200255 }
256
257 if (cacheSizeInKB != nullptr)
258 {
259 aResp->res.jsonValue[jsonPtr]["CacheSizeMiB"] = (*cacheSizeInKB >> 10);
260 }
261
262 if (voltaileRegionMaxSizeInKib != nullptr)
263 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000264 aResp->res.jsonValue[jsonPtr]["VolatileRegionSizeMaxMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200265 (*voltaileRegionMaxSizeInKib) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800266 }
James Feistc50e7c62020-07-27 15:39:36 -0700267
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200268 if (pmRegionMaxSizeInKiB != nullptr)
269 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000270 aResp->res.jsonValue[jsonPtr]["PersistentRegionSizeMaxMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200271 (*pmRegionMaxSizeInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800272 }
James Feistc50e7c62020-07-27 15:39:36 -0700273
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200274 if (allocationIncrementInKiB != nullptr)
275 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000276 aResp->res.jsonValue[jsonPtr]["AllocationIncrementMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200277 (*allocationIncrementInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800278 }
James Feistc50e7c62020-07-27 15:39:36 -0700279
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200280 if (allocationAlignmentInKiB != nullptr)
281 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000282 aResp->res.jsonValue[jsonPtr]["AllocationAlignmentMiB"] =
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200283 (*allocationAlignmentInKiB) >> 10;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800284 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200285
286 if (volatileRegionNumberLimit != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800287 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200288 aResp->res.jsonValue[jsonPtr]["VolatileRegionNumberLimit"] =
289 *volatileRegionNumberLimit;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800290 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200291
292 if (pmRegionNumberLimit != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800293 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200294 aResp->res.jsonValue[jsonPtr]["PersistentRegionNumberLimit"] =
295 *pmRegionNumberLimit;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800296 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200297
298 if (spareDeviceCount != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800299 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200300 aResp->res.jsonValue[jsonPtr]["SpareDeviceCount"] = *spareDeviceCount;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800301 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200302
303 if (isSpareDeviceInUse != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800304 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200305 aResp->res.jsonValue[jsonPtr]["IsSpareDeviceEnabled"] =
306 *isSpareDeviceInUse;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800307 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200308
309 if (isRankSpareEnabled != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800310 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200311 aResp->res.jsonValue[jsonPtr]["IsRankSpareEnabled"] =
312 *isRankSpareEnabled;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800313 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200314
315 if (maxAveragePowerLimitmW != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800316 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200317 aResp->res.jsonValue[jsonPtr]["MaxTDPMilliWatts"] =
318 *maxAveragePowerLimitmW;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800319 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200320
321 if (configurationLocked != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800322 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200323 aResp->res.jsonValue[jsonPtr]["ConfigurationLocked"] =
324 *configurationLocked;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800325 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200326
327 if (allowedMemoryModes != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800328 {
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800329 constexpr const std::array<const char*, 3> values{"Volatile", "PMEM",
330 "Block"};
James Feistc50e7c62020-07-27 15:39:36 -0700331
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800332 for (const char* v : values)
333 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200334 if (allowedMemoryModes->ends_with(v))
James Feistc50e7c62020-07-27 15:39:36 -0700335 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000336 aResp->res.jsonValue[jsonPtr]["OperatingMemoryModes"].push_back(
337 v);
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800338 break;
James Feistc50e7c62020-07-27 15:39:36 -0700339 }
340 }
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800341 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200342
343 if (memoryMedia != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800344 {
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800345 constexpr const std::array<const char*, 3> values{"DRAM", "NAND",
346 "Intel3DXPoint"};
James Feistc50e7c62020-07-27 15:39:36 -0700347
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800348 for (const char* v : values)
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700349 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200350 if (memoryMedia->ends_with(v))
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700351 {
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000352 aResp->res.jsonValue[jsonPtr]["MemoryMedia"].push_back(v);
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800353 break;
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700354 }
Ed Tanous5fb91ba2020-09-28 15:41:28 -0700355 }
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800356 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200357
358 if (configurationLockCapable != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800359 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200360 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
361 ["ConfigurationLockCapable"] =
362 *configurationLockCapable;
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800363 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200364
365 if (dataLockCapable != nullptr)
Jiaqing Zhao80badf72022-03-18 17:48:53 +0800366 {
Patrick Williams89492a12023-05-10 07:51:34 -0500367 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
368 ["DataLockCapable"] = *dataLockCapable;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200369 }
370
371 if (passphraseCapable != nullptr)
372 {
Patrick Williams89492a12023-05-10 07:51:34 -0500373 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
374 ["PassphraseCapable"] = *passphraseCapable;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200375 }
376
377 if (maxPassphraseCount != nullptr)
378 {
Patrick Williams89492a12023-05-10 07:51:34 -0500379 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
380 ["MaxPassphraseCount"] = *maxPassphraseCount;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200381 }
382
383 if (passphraseLockLimit != nullptr)
384 {
Patrick Williams89492a12023-05-10 07:51:34 -0500385 aResp->res.jsonValue[jsonPtr]["SecurityCapabilities"]
386 ["PassphraseLockLimit"] = *passphraseLockLimit;
James Feistc50e7c62020-07-27 15:39:36 -0700387 }
388}
389
Nan Zhou9a5acea2022-05-17 21:12:43 +0000390inline void
391 assembleDimmProperties(std::string_view dimmId,
392 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000393 const dbus::utility::DBusPropertiesMap& properties,
394 const nlohmann::json::json_pointer& jsonPtr)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000395{
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000396 aResp->res.jsonValue[jsonPtr]["Id"] = dimmId;
397 aResp->res.jsonValue[jsonPtr]["Name"] = "DIMM Slot";
398 aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Enabled";
399 aResp->res.jsonValue[jsonPtr]["Status"]["Health"] = "OK";
Nan Zhou9a5acea2022-05-17 21:12:43 +0000400
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200401 const uint16_t* memoryDataWidth = nullptr;
402 const size_t* memorySizeInKB = nullptr;
403 const std::string* partNumber = nullptr;
404 const std::string* serialNumber = nullptr;
405 const std::string* manufacturer = nullptr;
406 const uint16_t* revisionCode = nullptr;
407 const bool* present = nullptr;
408 const uint16_t* memoryTotalWidth = nullptr;
409 const std::string* ecc = nullptr;
410 const std::string* formFactor = nullptr;
411 const std::vector<uint16_t>* allowedSpeedsMT = nullptr;
412 const uint8_t* memoryAttributes = nullptr;
413 const uint16_t* memoryConfiguredSpeedInMhz = nullptr;
414 const std::string* memoryType = nullptr;
415 const std::string* channel = nullptr;
416 const std::string* memoryController = nullptr;
417 const std::string* slot = nullptr;
418 const std::string* socket = nullptr;
419 const std::string* sparePartNumber = nullptr;
420 const std::string* model = nullptr;
421 const std::string* locationCode = nullptr;
422
423 const bool success = sdbusplus::unpackPropertiesNoThrow(
424 dbus_utils::UnpackErrorPrinter(), properties, "MemoryDataWidth",
425 memoryDataWidth, "MemorySizeInKB", memorySizeInKB, "PartNumber",
Nikhil Namjoshi656472d2022-09-13 20:54:44 +0000426 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
427 "RevisionCode", revisionCode, "Present", present, "MemoryTotalWidth",
428 memoryTotalWidth, "ECC", ecc, "FormFactor", formFactor,
429 "AllowedSpeedsMT", allowedSpeedsMT, "MemoryAttributes",
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200430 memoryAttributes, "MemoryConfiguredSpeedInMhz",
431 memoryConfiguredSpeedInMhz, "MemoryType", memoryType, "Channel",
432 channel, "MemoryController", memoryController, "Slot", slot, "Socket",
433 socket, "SparePartNumber", sparePartNumber, "Model", model,
434 "LocationCode", locationCode);
435
436 if (!success)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000437 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200438 messages::internalError(aResp->res);
439 return;
440 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000441
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200442 if (memoryDataWidth != nullptr)
443 {
444 aResp->res.jsonValue[jsonPtr]["DataWidthBits"] = *memoryDataWidth;
445 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000446
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200447 if (memorySizeInKB != nullptr)
448 {
449 aResp->res.jsonValue[jsonPtr]["CapacityMiB"] = (*memorySizeInKB >> 10);
450 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000451
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200452 if (partNumber != nullptr)
453 {
454 aResp->res.jsonValue[jsonPtr]["PartNumber"] = *partNumber;
455 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000456
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200457 if (serialNumber != nullptr)
458 {
459 aResp->res.jsonValue[jsonPtr]["SerialNumber"] = *serialNumber;
460 }
461
462 if (manufacturer != nullptr)
463 {
464 aResp->res.jsonValue[jsonPtr]["Manufacturer"] = *manufacturer;
465 }
466
467 if (revisionCode != nullptr)
468 {
469 aResp->res.jsonValue[jsonPtr]["FirmwareRevision"] =
470 std::to_string(*revisionCode);
471 }
472
473 if (present != nullptr && !*present)
474 {
475 aResp->res.jsonValue[jsonPtr]["Status"]["State"] = "Absent";
476 }
477
478 if (memoryTotalWidth != nullptr)
479 {
480 aResp->res.jsonValue[jsonPtr]["BusWidthBits"] = *memoryTotalWidth;
481 }
482
483 if (ecc != nullptr)
484 {
485 constexpr const std::array<const char*, 4> values{
486 "NoECC", "SingleBitECC", "MultiBitECC", "AddressParity"};
487
488 for (const char* v : values)
Nan Zhou9a5acea2022-05-17 21:12:43 +0000489 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200490 if (ecc->ends_with(v))
Nan Zhou9a5acea2022-05-17 21:12:43 +0000491 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200492 aResp->res.jsonValue[jsonPtr]["ErrorCorrection"] = v;
493 break;
Nan Zhou9a5acea2022-05-17 21:12:43 +0000494 }
Nan Zhou9a5acea2022-05-17 21:12:43 +0000495 }
496 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200497
498 if (formFactor != nullptr)
499 {
500 constexpr const std::array<const char*, 11> values{
501 "RDIMM", "UDIMM", "SO_DIMM", "LRDIMM",
502 "Mini_RDIMM", "Mini_UDIMM", "SO_RDIMM_72b", "SO_UDIMM_72b",
503 "SO_DIMM_16b", "SO_DIMM_32b", "Die"};
504
505 for (const char* v : values)
506 {
507 if (formFactor->ends_with(v))
508 {
509 aResp->res.jsonValue[jsonPtr]["BaseModuleType"] = v;
510 break;
511 }
512 }
513 }
514
515 if (allowedSpeedsMT != nullptr)
516 {
517 nlohmann::json& jValue =
518 aResp->res.jsonValue[jsonPtr]["AllowedSpeedsMHz"];
519 jValue = nlohmann::json::array();
520 for (uint16_t subVal : *allowedSpeedsMT)
521 {
522 jValue.push_back(subVal);
523 }
524 }
525
526 if (memoryAttributes != nullptr)
527 {
528 aResp->res.jsonValue[jsonPtr]["RankCount"] =
529 static_cast<uint64_t>(*memoryAttributes);
530 }
531
532 if (memoryConfiguredSpeedInMhz != nullptr)
533 {
534 aResp->res.jsonValue[jsonPtr]["OperatingSpeedMhz"] =
535 *memoryConfiguredSpeedInMhz;
536 }
537
538 if (memoryType != nullptr)
539 {
540 std::string memoryDeviceType =
541 translateMemoryTypeToRedfish(*memoryType);
542 // Values like "Unknown" or "Other" will return empty
543 // so just leave off
544 if (!memoryDeviceType.empty())
545 {
546 aResp->res.jsonValue[jsonPtr]["MemoryDeviceType"] =
547 memoryDeviceType;
548 }
549 if (memoryType->find("DDR") != std::string::npos)
550 {
551 aResp->res.jsonValue[jsonPtr]["MemoryType"] = "DRAM";
552 }
553 else if (memoryType->ends_with("Logical"))
554 {
555 aResp->res.jsonValue[jsonPtr]["MemoryType"] = "IntelOptane";
556 }
557 }
558
559 if (channel != nullptr)
560 {
561 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Channel"] = *channel;
562 }
563
564 if (memoryController != nullptr)
565 {
566 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["MemoryController"] =
567 *memoryController;
568 }
569
570 if (slot != nullptr)
571 {
572 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Slot"] = *slot;
573 }
574
575 if (socket != nullptr)
576 {
577 aResp->res.jsonValue[jsonPtr]["MemoryLocation"]["Socket"] = *socket;
578 }
579
580 if (sparePartNumber != nullptr)
581 {
582 aResp->res.jsonValue[jsonPtr]["SparePartNumber"] = *sparePartNumber;
583 }
584
585 if (model != nullptr)
586 {
587 aResp->res.jsonValue[jsonPtr]["Model"] = *model;
588 }
589
590 if (locationCode != nullptr)
591 {
Patrick Williams89492a12023-05-10 07:51:34 -0500592 aResp->res.jsonValue[jsonPtr]["Location"]["PartLocation"]
593 ["ServiceLabel"] = *locationCode;
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200594 }
595
596 getPersistentMemoryProperties(aResp, properties, jsonPtr);
Nan Zhou9a5acea2022-05-17 21:12:43 +0000597}
598
zhanghch058d1b46d2021-04-01 11:18:24 +0800599inline void getDimmDataByService(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous80789c82020-08-19 09:19:09 -0700600 const std::string& dimmId,
601 const std::string& service,
602 const std::string& objPath)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200603{
James Feist35e257a2020-06-05 13:30:51 -0700604 auto health = std::make_shared<HealthPopulate>(aResp);
605 health->selfPath = objPath;
606 health->populate();
607
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200608 BMCWEB_LOG_DEBUG << "Get available system components.";
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200609 sdbusplus::asio::getAllProperties(
610 *crow::connections::systemBus, service, objPath, "",
Ed Tanousb9d36b42022-02-26 21:42:46 -0800611 [dimmId, aResp{std::move(aResp)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800612 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800613 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700614 if (ec)
615 {
616 BMCWEB_LOG_DEBUG << "DBUS response error";
617 messages::internalError(aResp->res);
618 return;
619 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000620 assembleDimmProperties(dimmId, aResp, properties, ""_json_pointer);
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200621 });
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200622}
623
Nan Zhouef00d7d2022-05-20 21:22:32 +0000624inline void assembleDimmPartitionData(
625 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000626 const dbus::utility::DBusPropertiesMap& properties,
627 const nlohmann::json::json_pointer& regionPtr)
Nan Zhouef00d7d2022-05-20 21:22:32 +0000628{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200629 const std::string* memoryClassification = nullptr;
630 const uint64_t* offsetInKiB = nullptr;
631 const std::string* partitionId = nullptr;
632 const bool* passphraseState = nullptr;
633 const uint64_t* sizeInKiB = nullptr;
634
635 const bool success = sdbusplus::unpackPropertiesNoThrow(
636 dbus_utils::UnpackErrorPrinter(), properties, "MemoryClassification",
637 memoryClassification, "OffsetInKiB", offsetInKiB, "PartitionId",
638 partitionId, "PassphraseState", passphraseState, "SizeInKiB",
639 sizeInKiB);
640
641 if (!success)
Nan Zhouef00d7d2022-05-20 21:22:32 +0000642 {
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200643 messages::internalError(aResp->res);
644 return;
Nan Zhouef00d7d2022-05-20 21:22:32 +0000645 }
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200646
647 nlohmann::json::object_t partition;
648
649 if (memoryClassification != nullptr)
650 {
651 partition["MemoryClassification"] = *memoryClassification;
652 }
653
654 if (offsetInKiB != nullptr)
655 {
656 partition["OffsetMiB"] = (*offsetInKiB >> 10);
657 }
658
659 if (partitionId != nullptr)
660 {
661 partition["RegionId"] = *partitionId;
662 }
663
664 if (passphraseState != nullptr)
665 {
666 partition["PassphraseEnabled"] = *passphraseState;
667 }
668
669 if (sizeInKiB != nullptr)
670 {
671 partition["SizeMiB"] = (*sizeInKiB >> 10);
672 }
673
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000674 aResp->res.jsonValue[regionPtr].emplace_back(std::move(partition));
Nan Zhouef00d7d2022-05-20 21:22:32 +0000675}
676
zhanghch058d1b46d2021-04-01 11:18:24 +0800677inline void getDimmPartitionData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous23a21a12020-07-25 04:45:05 +0000678 const std::string& service,
679 const std::string& path)
James Feist45094ad2020-04-29 14:02:30 -0700680{
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200681 sdbusplus::asio::getAllProperties(
682 *crow::connections::systemBus, service, path,
683 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition",
James Feist45094ad2020-04-29 14:02:30 -0700684 [aResp{std::move(aResp)}](
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800685 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800686 const dbus::utility::DBusPropertiesMap& properties) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700687 if (ec)
688 {
689 BMCWEB_LOG_DEBUG << "DBUS response error";
690 messages::internalError(aResp->res);
James Feist45094ad2020-04-29 14:02:30 -0700691
Ed Tanous002d39b2022-05-31 08:59:27 -0700692 return;
693 }
Nan Zhoud7f04fd2022-05-01 01:11:07 +0000694 nlohmann::json::json_pointer regionPtr = "/Regions"_json_pointer;
695 assembleDimmPartitionData(aResp, properties, regionPtr);
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200696 }
James Feist45094ad2020-04-29 14:02:30 -0700697
Krzysztof Grobelnyc1343bf2022-08-31 13:15:26 +0200698 );
James Feist45094ad2020-04-29 14:02:30 -0700699}
700
zhanghch058d1b46d2021-04-01 11:18:24 +0800701inline void getDimmData(std::shared_ptr<bmcweb::AsyncResp> aResp,
Ed Tanous23a21a12020-07-25 04:45:05 +0000702 const std::string& dimmId)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200703{
704 BMCWEB_LOG_DEBUG << "Get available system dimm resources.";
George Liue99073f2022-12-09 11:06:16 +0800705 constexpr std::array<std::string_view, 2> dimmInterfaces = {
706 "xyz.openbmc_project.Inventory.Item.Dimm",
707 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition"};
708 dbus::utility::getSubTree(
709 "/xyz/openbmc_project/inventory", 0, dimmInterfaces,
Ed Tanous029573d2019-02-01 10:57:49 -0800710 [dimmId, aResp{std::move(aResp)}](
George Liue99073f2022-12-09 11:06:16 +0800711 const boost::system::error_code& ec,
Ed Tanousb9d36b42022-02-26 21:42:46 -0800712 const dbus::utility::MapperGetSubTreeResponse& subtree) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700713 if (ec)
714 {
715 BMCWEB_LOG_DEBUG << "DBUS response error";
716 messages::internalError(aResp->res);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200717
Ed Tanous002d39b2022-05-31 08:59:27 -0700718 return;
719 }
720 bool found = false;
Nan Zhou76686dc2022-06-17 23:01:51 +0000721 for (const auto& [rawPath, object] : subtree)
Ed Tanous002d39b2022-05-31 08:59:27 -0700722 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000723 sdbusplus::message::object_path path(rawPath);
724 for (const auto& [service, interfaces] : object)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200725 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000726 for (const auto& interface : interfaces)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200727 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000728 if (interface ==
729 "xyz.openbmc_project.Inventory.Item.Dimm" &&
730 path.filename() == dimmId)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200731 {
Nan Zhou76686dc2022-06-17 23:01:51 +0000732 getDimmDataByService(aResp, dimmId, service, rawPath);
733 found = true;
734 }
James Feist45094ad2020-04-29 14:02:30 -0700735
Nan Zhou76686dc2022-06-17 23:01:51 +0000736 // partitions are separate as there can be multiple
737 // per
738 // device, i.e.
739 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition1
740 // /xyz/openbmc_project/Inventory/Item/Dimm1/Partition2
741 if (interface ==
742 "xyz.openbmc_project.Inventory.Item.PersistentMemory.Partition" &&
743 path.parent_path().filename() == dimmId)
744 {
745 getDimmPartitionData(aResp, service, rawPath);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200746 }
747 }
748 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700749 }
750 // Object not found
751 if (!found)
752 {
753 messages::resourceNotFound(aResp->res, "Memory", dimmId);
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200754 return;
Ed Tanous002d39b2022-05-31 08:59:27 -0700755 }
756 // Set @odata only if object is found
757 aResp->res.jsonValue["@odata.type"] = "#Memory.v1_11_0.Memory";
Ed Tanousef4c65b2023-04-24 15:28:50 -0700758 aResp->res.jsonValue["@odata.id"] =
759 boost::urls::format("/redfish/v1/Systems/system/Memory/{}", dimmId);
Ed Tanous002d39b2022-05-31 08:59:27 -0700760 return;
George Liue99073f2022-12-09 11:06:16 +0800761 });
Ed Tanous271584a2019-07-09 16:24:22 -0700762}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200763
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700764inline void requestRoutesMemoryCollection(App& app)
Ed Tanous029573d2019-02-01 10:57:49 -0800765{
Ed Tanous029573d2019-02-01 10:57:49 -0800766 /**
767 * Functions triggers appropriate requests on DBus
768 */
Ed Tanous22d268c2022-05-19 09:39:07 -0700769 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/")
Ed Tanoused398212021-06-09 17:05:54 -0700770 .privileges(redfish::privileges::getMemoryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700771 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700772 [&app](const crow::Request& req,
Ed Tanous22d268c2022-05-19 09:39:07 -0700773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
774 const std::string& systemName) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000775 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700776 {
777 return;
778 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700779 if (systemName != "system")
780 {
781 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
782 systemName);
783 return;
784 }
785
Ed Tanous002d39b2022-05-31 08:59:27 -0700786 asyncResp->res.jsonValue["@odata.type"] =
787 "#MemoryCollection.MemoryCollection";
788 asyncResp->res.jsonValue["Name"] = "Memory Module Collection";
789 asyncResp->res.jsonValue["@odata.id"] =
790 "/redfish/v1/Systems/system/Memory";
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200791
George Liu7a1dbc42022-12-07 16:03:22 +0800792 constexpr std::array<std::string_view, 1> interfaces{
793 "xyz.openbmc_project.Inventory.Item.Dimm"};
Ed Tanous002d39b2022-05-31 08:59:27 -0700794 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000795 asyncResp, boost::urls::url("/redfish/v1/Systems/system/Memory"),
George Liu7a1dbc42022-12-07 16:03:22 +0800796 interfaces);
Ed Tanous002d39b2022-05-31 08:59:27 -0700797 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700798}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200799
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700800inline void requestRoutesMemory(App& app)
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200801{
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200802 /**
803 * Functions triggers appropriate requests on DBus
804 */
Ed Tanous22d268c2022-05-19 09:39:07 -0700805 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/Memory/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -0700806 .privileges(redfish::privileges::getMemory)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700807 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700808 [&app](const crow::Request& req,
809 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Ed Tanous22d268c2022-05-19 09:39:07 -0700810 const std::string& systemName, const std::string& dimmId) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000811 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700812 {
813 return;
814 }
Ed Tanous22d268c2022-05-19 09:39:07 -0700815 if (systemName != "system")
816 {
817 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
818 systemName);
819 return;
820 }
821
Ed Tanous002d39b2022-05-31 08:59:27 -0700822 getDimmData(asyncResp, dimmId);
823 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700824}
Rapkiewicz, Pawel443c2932018-10-22 15:08:49 +0200825
826} // namespace redfish