1*7bf29ab3SChristopher Meis // SPDX-License-Identifier: Apache-2.0
2*7bf29ab3SChristopher Meis // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*7bf29ab3SChristopher Meis // SPDX-FileCopyrightText: Copyright 2019 Intel Corporation
4*7bf29ab3SChristopher Meis #pragma once
5*7bf29ab3SChristopher Meis
6*7bf29ab3SChristopher Meis #include "app.hpp"
7*7bf29ab3SChristopher Meis #include "async_resp.hpp"
8*7bf29ab3SChristopher Meis #include "error_messages.hpp"
9*7bf29ab3SChristopher Meis #include "generated/enums/drive.hpp"
10*7bf29ab3SChristopher Meis #include "generated/enums/protocol.hpp"
11*7bf29ab3SChristopher Meis #include "generated/enums/resource.hpp"
12*7bf29ab3SChristopher Meis #include "http_request.hpp"
13*7bf29ab3SChristopher Meis #include "query.hpp"
14*7bf29ab3SChristopher Meis #include "redfish_util.hpp"
15*7bf29ab3SChristopher Meis #include "registries/privilege_registry.hpp"
16*7bf29ab3SChristopher Meis
17*7bf29ab3SChristopher Meis namespace redfish
18*7bf29ab3SChristopher Meis {
19*7bf29ab3SChristopher Meis
getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)20*7bf29ab3SChristopher Meis inline void getDrivePresent(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
21*7bf29ab3SChristopher Meis const std::string& connectionName,
22*7bf29ab3SChristopher Meis const std::string& path)
23*7bf29ab3SChristopher Meis {
24*7bf29ab3SChristopher Meis dbus::utility::getProperty<bool>(
25*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.Inventory.Item", "Present",
26*7bf29ab3SChristopher Meis [asyncResp,
27*7bf29ab3SChristopher Meis path](const boost::system::error_code& ec, const bool isPresent) {
28*7bf29ab3SChristopher Meis // this interface isn't necessary, only check it if
29*7bf29ab3SChristopher Meis // we get a good return
30*7bf29ab3SChristopher Meis if (ec)
31*7bf29ab3SChristopher Meis {
32*7bf29ab3SChristopher Meis return;
33*7bf29ab3SChristopher Meis }
34*7bf29ab3SChristopher Meis
35*7bf29ab3SChristopher Meis if (!isPresent)
36*7bf29ab3SChristopher Meis {
37*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] =
38*7bf29ab3SChristopher Meis resource::State::Absent;
39*7bf29ab3SChristopher Meis }
40*7bf29ab3SChristopher Meis });
41*7bf29ab3SChristopher Meis }
42*7bf29ab3SChristopher Meis
getDriveState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)43*7bf29ab3SChristopher Meis inline void getDriveState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
44*7bf29ab3SChristopher Meis const std::string& connectionName,
45*7bf29ab3SChristopher Meis const std::string& path)
46*7bf29ab3SChristopher Meis {
47*7bf29ab3SChristopher Meis dbus::utility::getProperty<bool>(
48*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.State.Drive", "Rebuilding",
49*7bf29ab3SChristopher Meis [asyncResp](const boost::system::error_code& ec, const bool updating) {
50*7bf29ab3SChristopher Meis // this interface isn't necessary, only check it
51*7bf29ab3SChristopher Meis // if we get a good return
52*7bf29ab3SChristopher Meis if (ec)
53*7bf29ab3SChristopher Meis {
54*7bf29ab3SChristopher Meis return;
55*7bf29ab3SChristopher Meis }
56*7bf29ab3SChristopher Meis
57*7bf29ab3SChristopher Meis // updating and disabled in the backend shouldn't be
58*7bf29ab3SChristopher Meis // able to be set at the same time, so we don't need
59*7bf29ab3SChristopher Meis // to check for the race condition of these two
60*7bf29ab3SChristopher Meis // calls
61*7bf29ab3SChristopher Meis if (updating)
62*7bf29ab3SChristopher Meis {
63*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] =
64*7bf29ab3SChristopher Meis resource::State::Updating;
65*7bf29ab3SChristopher Meis }
66*7bf29ab3SChristopher Meis });
67*7bf29ab3SChristopher Meis }
68*7bf29ab3SChristopher Meis
convertDriveType(std::string_view type)69*7bf29ab3SChristopher Meis inline std::optional<drive::MediaType> convertDriveType(std::string_view type)
70*7bf29ab3SChristopher Meis {
71*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.HDD")
72*7bf29ab3SChristopher Meis {
73*7bf29ab3SChristopher Meis return drive::MediaType::HDD;
74*7bf29ab3SChristopher Meis }
75*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.SSD")
76*7bf29ab3SChristopher Meis {
77*7bf29ab3SChristopher Meis return drive::MediaType::SSD;
78*7bf29ab3SChristopher Meis }
79*7bf29ab3SChristopher Meis if (type == "xyz.openbmc_project.Inventory.Item.Drive.DriveType.Unknown")
80*7bf29ab3SChristopher Meis {
81*7bf29ab3SChristopher Meis return std::nullopt;
82*7bf29ab3SChristopher Meis }
83*7bf29ab3SChristopher Meis
84*7bf29ab3SChristopher Meis return drive::MediaType::Invalid;
85*7bf29ab3SChristopher Meis }
86*7bf29ab3SChristopher Meis
convertDriveProtocol(std::string_view proto)87*7bf29ab3SChristopher Meis inline std::optional<protocol::Protocol> convertDriveProtocol(
88*7bf29ab3SChristopher Meis std::string_view proto)
89*7bf29ab3SChristopher Meis {
90*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SAS")
91*7bf29ab3SChristopher Meis {
92*7bf29ab3SChristopher Meis return protocol::Protocol::SAS;
93*7bf29ab3SChristopher Meis }
94*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.SATA")
95*7bf29ab3SChristopher Meis {
96*7bf29ab3SChristopher Meis return protocol::Protocol::SATA;
97*7bf29ab3SChristopher Meis }
98*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.NVMe")
99*7bf29ab3SChristopher Meis {
100*7bf29ab3SChristopher Meis return protocol::Protocol::NVMe;
101*7bf29ab3SChristopher Meis }
102*7bf29ab3SChristopher Meis if (proto == "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.FC")
103*7bf29ab3SChristopher Meis {
104*7bf29ab3SChristopher Meis return protocol::Protocol::FC;
105*7bf29ab3SChristopher Meis }
106*7bf29ab3SChristopher Meis if (proto ==
107*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveProtocol.Unknown")
108*7bf29ab3SChristopher Meis {
109*7bf29ab3SChristopher Meis return std::nullopt;
110*7bf29ab3SChristopher Meis }
111*7bf29ab3SChristopher Meis
112*7bf29ab3SChristopher Meis return protocol::Protocol::Invalid;
113*7bf29ab3SChristopher Meis }
114*7bf29ab3SChristopher Meis
getDriveItemProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)115*7bf29ab3SChristopher Meis inline void getDriveItemProperties(
116*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
117*7bf29ab3SChristopher Meis const std::string& connectionName, const std::string& path)
118*7bf29ab3SChristopher Meis {
119*7bf29ab3SChristopher Meis dbus::utility::getAllProperties(
120*7bf29ab3SChristopher Meis connectionName, path, "xyz.openbmc_project.Inventory.Item.Drive",
121*7bf29ab3SChristopher Meis [asyncResp](const boost::system::error_code& ec,
122*7bf29ab3SChristopher Meis const std::vector<
123*7bf29ab3SChristopher Meis std::pair<std::string, dbus::utility::DbusVariantType>>&
124*7bf29ab3SChristopher Meis propertiesList) {
125*7bf29ab3SChristopher Meis if (ec)
126*7bf29ab3SChristopher Meis {
127*7bf29ab3SChristopher Meis // this interface isn't required
128*7bf29ab3SChristopher Meis return;
129*7bf29ab3SChristopher Meis }
130*7bf29ab3SChristopher Meis const std::string* encryptionStatus = nullptr;
131*7bf29ab3SChristopher Meis const bool* isLocked = nullptr;
132*7bf29ab3SChristopher Meis for (const std::pair<std::string, dbus::utility::DbusVariantType>&
133*7bf29ab3SChristopher Meis property : propertiesList)
134*7bf29ab3SChristopher Meis {
135*7bf29ab3SChristopher Meis const std::string& propertyName = property.first;
136*7bf29ab3SChristopher Meis if (propertyName == "Type")
137*7bf29ab3SChristopher Meis {
138*7bf29ab3SChristopher Meis const std::string* value =
139*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second);
140*7bf29ab3SChristopher Meis if (value == nullptr)
141*7bf29ab3SChristopher Meis {
142*7bf29ab3SChristopher Meis // illegal property
143*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Type");
144*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
145*7bf29ab3SChristopher Meis return;
146*7bf29ab3SChristopher Meis }
147*7bf29ab3SChristopher Meis
148*7bf29ab3SChristopher Meis std::optional<drive::MediaType> mediaType =
149*7bf29ab3SChristopher Meis convertDriveType(*value);
150*7bf29ab3SChristopher Meis if (!mediaType)
151*7bf29ab3SChristopher Meis {
152*7bf29ab3SChristopher Meis BMCWEB_LOG_WARNING("UnknownDriveType Interface: {}",
153*7bf29ab3SChristopher Meis *value);
154*7bf29ab3SChristopher Meis continue;
155*7bf29ab3SChristopher Meis }
156*7bf29ab3SChristopher Meis if (*mediaType == drive::MediaType::Invalid)
157*7bf29ab3SChristopher Meis {
158*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
159*7bf29ab3SChristopher Meis return;
160*7bf29ab3SChristopher Meis }
161*7bf29ab3SChristopher Meis
162*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["MediaType"] = *mediaType;
163*7bf29ab3SChristopher Meis }
164*7bf29ab3SChristopher Meis else if (propertyName == "Capacity")
165*7bf29ab3SChristopher Meis {
166*7bf29ab3SChristopher Meis const uint64_t* capacity =
167*7bf29ab3SChristopher Meis std::get_if<uint64_t>(&property.second);
168*7bf29ab3SChristopher Meis if (capacity == nullptr)
169*7bf29ab3SChristopher Meis {
170*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Capacity");
171*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
172*7bf29ab3SChristopher Meis return;
173*7bf29ab3SChristopher Meis }
174*7bf29ab3SChristopher Meis if (*capacity == 0)
175*7bf29ab3SChristopher Meis {
176*7bf29ab3SChristopher Meis // drive capacity not known
177*7bf29ab3SChristopher Meis continue;
178*7bf29ab3SChristopher Meis }
179*7bf29ab3SChristopher Meis
180*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["CapacityBytes"] = *capacity;
181*7bf29ab3SChristopher Meis }
182*7bf29ab3SChristopher Meis else if (propertyName == "Protocol")
183*7bf29ab3SChristopher Meis {
184*7bf29ab3SChristopher Meis const std::string* value =
185*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second);
186*7bf29ab3SChristopher Meis if (value == nullptr)
187*7bf29ab3SChristopher Meis {
188*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Protocol");
189*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
190*7bf29ab3SChristopher Meis return;
191*7bf29ab3SChristopher Meis }
192*7bf29ab3SChristopher Meis
193*7bf29ab3SChristopher Meis std::optional<protocol::Protocol> proto =
194*7bf29ab3SChristopher Meis convertDriveProtocol(*value);
195*7bf29ab3SChristopher Meis if (!proto)
196*7bf29ab3SChristopher Meis {
197*7bf29ab3SChristopher Meis BMCWEB_LOG_WARNING(
198*7bf29ab3SChristopher Meis "Unknown DrivePrototype Interface: {}", *value);
199*7bf29ab3SChristopher Meis continue;
200*7bf29ab3SChristopher Meis }
201*7bf29ab3SChristopher Meis if (*proto == protocol::Protocol::Invalid)
202*7bf29ab3SChristopher Meis {
203*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
204*7bf29ab3SChristopher Meis return;
205*7bf29ab3SChristopher Meis }
206*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Protocol"] = *proto;
207*7bf29ab3SChristopher Meis }
208*7bf29ab3SChristopher Meis else if (propertyName == "PredictedMediaLifeLeftPercent")
209*7bf29ab3SChristopher Meis {
210*7bf29ab3SChristopher Meis const uint8_t* lifeLeft =
211*7bf29ab3SChristopher Meis std::get_if<uint8_t>(&property.second);
212*7bf29ab3SChristopher Meis if (lifeLeft == nullptr)
213*7bf29ab3SChristopher Meis {
214*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR(
215*7bf29ab3SChristopher Meis "Illegal property: PredictedMediaLifeLeftPercent");
216*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
217*7bf29ab3SChristopher Meis return;
218*7bf29ab3SChristopher Meis }
219*7bf29ab3SChristopher Meis // 255 means reading the value is not supported
220*7bf29ab3SChristopher Meis if (*lifeLeft != 255)
221*7bf29ab3SChristopher Meis {
222*7bf29ab3SChristopher Meis asyncResp->res
223*7bf29ab3SChristopher Meis .jsonValue["PredictedMediaLifeLeftPercent"] =
224*7bf29ab3SChristopher Meis *lifeLeft;
225*7bf29ab3SChristopher Meis }
226*7bf29ab3SChristopher Meis }
227*7bf29ab3SChristopher Meis else if (propertyName == "EncryptionStatus")
228*7bf29ab3SChristopher Meis {
229*7bf29ab3SChristopher Meis encryptionStatus =
230*7bf29ab3SChristopher Meis std::get_if<std::string>(&property.second);
231*7bf29ab3SChristopher Meis if (encryptionStatus == nullptr)
232*7bf29ab3SChristopher Meis {
233*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: EncryptionStatus");
234*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
235*7bf29ab3SChristopher Meis return;
236*7bf29ab3SChristopher Meis }
237*7bf29ab3SChristopher Meis }
238*7bf29ab3SChristopher Meis else if (propertyName == "Locked")
239*7bf29ab3SChristopher Meis {
240*7bf29ab3SChristopher Meis isLocked = std::get_if<bool>(&property.second);
241*7bf29ab3SChristopher Meis if (isLocked == nullptr)
242*7bf29ab3SChristopher Meis {
243*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Illegal property: Locked");
244*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
245*7bf29ab3SChristopher Meis return;
246*7bf29ab3SChristopher Meis }
247*7bf29ab3SChristopher Meis }
248*7bf29ab3SChristopher Meis }
249*7bf29ab3SChristopher Meis
250*7bf29ab3SChristopher Meis if (encryptionStatus == nullptr || isLocked == nullptr ||
251*7bf29ab3SChristopher Meis *encryptionStatus ==
252*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Unknown")
253*7bf29ab3SChristopher Meis {
254*7bf29ab3SChristopher Meis return;
255*7bf29ab3SChristopher Meis }
256*7bf29ab3SChristopher Meis if (*encryptionStatus !=
257*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive.DriveEncryptionState.Encrypted")
258*7bf29ab3SChristopher Meis {
259*7bf29ab3SChristopher Meis //"The drive is not currently encrypted."
260*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] =
261*7bf29ab3SChristopher Meis drive::EncryptionStatus::Unencrypted;
262*7bf29ab3SChristopher Meis return;
263*7bf29ab3SChristopher Meis }
264*7bf29ab3SChristopher Meis if (*isLocked)
265*7bf29ab3SChristopher Meis {
266*7bf29ab3SChristopher Meis //"The drive is currently encrypted and the data is not
267*7bf29ab3SChristopher Meis // accessible to the user."
268*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] =
269*7bf29ab3SChristopher Meis drive::EncryptionStatus::Locked;
270*7bf29ab3SChristopher Meis return;
271*7bf29ab3SChristopher Meis }
272*7bf29ab3SChristopher Meis // if not locked
273*7bf29ab3SChristopher Meis // "The drive is currently encrypted but the data is accessible
274*7bf29ab3SChristopher Meis // to the user in unencrypted form."
275*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["EncryptionStatus"] =
276*7bf29ab3SChristopher Meis drive::EncryptionStatus::Unlocked;
277*7bf29ab3SChristopher Meis });
278*7bf29ab3SChristopher Meis }
279*7bf29ab3SChristopher Meis
addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path,const std::vector<std::string> & interfaces)280*7bf29ab3SChristopher Meis inline void addAllDriveInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
281*7bf29ab3SChristopher Meis const std::string& connectionName,
282*7bf29ab3SChristopher Meis const std::string& path,
283*7bf29ab3SChristopher Meis const std::vector<std::string>& interfaces)
284*7bf29ab3SChristopher Meis {
285*7bf29ab3SChristopher Meis for (const std::string& interface : interfaces)
286*7bf29ab3SChristopher Meis {
287*7bf29ab3SChristopher Meis if (interface == "xyz.openbmc_project.Inventory.Decorator.Asset")
288*7bf29ab3SChristopher Meis {
289*7bf29ab3SChristopher Meis asset_utils::getAssetInfo(asyncResp, connectionName, path,
290*7bf29ab3SChristopher Meis ""_json_pointer, false);
291*7bf29ab3SChristopher Meis }
292*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.Inventory.Item")
293*7bf29ab3SChristopher Meis {
294*7bf29ab3SChristopher Meis getDrivePresent(asyncResp, connectionName, path);
295*7bf29ab3SChristopher Meis }
296*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.State.Drive")
297*7bf29ab3SChristopher Meis {
298*7bf29ab3SChristopher Meis getDriveState(asyncResp, connectionName, path);
299*7bf29ab3SChristopher Meis }
300*7bf29ab3SChristopher Meis else if (interface == "xyz.openbmc_project.Inventory.Item.Drive")
301*7bf29ab3SChristopher Meis {
302*7bf29ab3SChristopher Meis getDriveItemProperties(asyncResp, connectionName, path);
303*7bf29ab3SChristopher Meis }
304*7bf29ab3SChristopher Meis }
305*7bf29ab3SChristopher Meis }
306*7bf29ab3SChristopher Meis
afterGetSubtreeSystemsStorageDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & driveId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)307*7bf29ab3SChristopher Meis inline void afterGetSubtreeSystemsStorageDrive(
308*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
309*7bf29ab3SChristopher Meis const std::string& driveId, const boost::system::error_code& ec,
310*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree)
311*7bf29ab3SChristopher Meis {
312*7bf29ab3SChristopher Meis if (ec)
313*7bf29ab3SChristopher Meis {
314*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Drive mapper call error");
315*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
316*7bf29ab3SChristopher Meis return;
317*7bf29ab3SChristopher Meis }
318*7bf29ab3SChristopher Meis
319*7bf29ab3SChristopher Meis auto drive = std::ranges::find_if(
320*7bf29ab3SChristopher Meis subtree,
321*7bf29ab3SChristopher Meis [&driveId](const std::pair<std::string,
322*7bf29ab3SChristopher Meis dbus::utility::MapperServiceMap>& object) {
323*7bf29ab3SChristopher Meis return sdbusplus::message::object_path(object.first).filename() ==
324*7bf29ab3SChristopher Meis driveId;
325*7bf29ab3SChristopher Meis });
326*7bf29ab3SChristopher Meis
327*7bf29ab3SChristopher Meis if (drive == subtree.end())
328*7bf29ab3SChristopher Meis {
329*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Drive", driveId);
330*7bf29ab3SChristopher Meis return;
331*7bf29ab3SChristopher Meis }
332*7bf29ab3SChristopher Meis
333*7bf29ab3SChristopher Meis const std::string& path = drive->first;
334*7bf29ab3SChristopher Meis const dbus::utility::MapperServiceMap& connectionNames = drive->second;
335*7bf29ab3SChristopher Meis
336*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
337*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] =
338*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Systems/{}/Storage/1/Drives/{}",
339*7bf29ab3SChristopher Meis BMCWEB_REDFISH_SYSTEM_URI_NAME, driveId);
340*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = driveId;
341*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Id"] = driveId;
342*7bf29ab3SChristopher Meis
343*7bf29ab3SChristopher Meis if (connectionNames.size() != 1)
344*7bf29ab3SChristopher Meis {
345*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Connection size {}, not equal to 1",
346*7bf29ab3SChristopher Meis connectionNames.size());
347*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
348*7bf29ab3SChristopher Meis return;
349*7bf29ab3SChristopher Meis }
350*7bf29ab3SChristopher Meis
351*7bf29ab3SChristopher Meis getMainChassisId(
352*7bf29ab3SChristopher Meis asyncResp, [](const std::string& chassisId,
353*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
354*7bf29ab3SChristopher Meis aRsp->res.jsonValue["Links"]["Chassis"]["@odata.id"] =
355*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
356*7bf29ab3SChristopher Meis });
357*7bf29ab3SChristopher Meis
358*7bf29ab3SChristopher Meis // default it to Enabled
359*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
360*7bf29ab3SChristopher Meis
361*7bf29ab3SChristopher Meis addAllDriveInfo(asyncResp, connectionNames[0].first, path,
362*7bf29ab3SChristopher Meis connectionNames[0].second);
363*7bf29ab3SChristopher Meis }
364*7bf29ab3SChristopher Meis
afterChassisDriveCollectionSubtreeGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)365*7bf29ab3SChristopher Meis inline void afterChassisDriveCollectionSubtreeGet(
366*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
367*7bf29ab3SChristopher Meis const std::string& chassisId, const boost::system::error_code& ec,
368*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree)
369*7bf29ab3SChristopher Meis {
370*7bf29ab3SChristopher Meis if (ec)
371*7bf29ab3SChristopher Meis {
372*7bf29ab3SChristopher Meis if (ec == boost::system::errc::host_unreachable)
373*7bf29ab3SChristopher Meis {
374*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
375*7bf29ab3SChristopher Meis return;
376*7bf29ab3SChristopher Meis }
377*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
378*7bf29ab3SChristopher Meis return;
379*7bf29ab3SChristopher Meis }
380*7bf29ab3SChristopher Meis
381*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths.
382*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree)
383*7bf29ab3SChristopher Meis {
384*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path);
385*7bf29ab3SChristopher Meis if (objPath.filename() != chassisId)
386*7bf29ab3SChristopher Meis {
387*7bf29ab3SChristopher Meis continue;
388*7bf29ab3SChristopher Meis }
389*7bf29ab3SChristopher Meis
390*7bf29ab3SChristopher Meis if (connectionNames.empty())
391*7bf29ab3SChristopher Meis {
392*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names");
393*7bf29ab3SChristopher Meis continue;
394*7bf29ab3SChristopher Meis }
395*7bf29ab3SChristopher Meis
396*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] =
397*7bf29ab3SChristopher Meis "#DriveCollection.DriveCollection";
398*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] =
399*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}/Drives", chassisId);
400*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = "Drive Collection";
401*7bf29ab3SChristopher Meis
402*7bf29ab3SChristopher Meis // Association lambda
403*7bf29ab3SChristopher Meis dbus::utility::getAssociationEndPoints(
404*7bf29ab3SChristopher Meis path + "/drive",
405*7bf29ab3SChristopher Meis [asyncResp, chassisId](const boost::system::error_code& ec3,
406*7bf29ab3SChristopher Meis const dbus::utility::MapperEndPoints& resp) {
407*7bf29ab3SChristopher Meis if (ec3)
408*7bf29ab3SChristopher Meis {
409*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Error in chassis Drive association ");
410*7bf29ab3SChristopher Meis }
411*7bf29ab3SChristopher Meis nlohmann::json& members = asyncResp->res.jsonValue["Members"];
412*7bf29ab3SChristopher Meis // important if array is empty
413*7bf29ab3SChristopher Meis members = nlohmann::json::array();
414*7bf29ab3SChristopher Meis
415*7bf29ab3SChristopher Meis std::vector<std::string> leafNames;
416*7bf29ab3SChristopher Meis for (const auto& drive : resp)
417*7bf29ab3SChristopher Meis {
418*7bf29ab3SChristopher Meis sdbusplus::message::object_path drivePath(drive);
419*7bf29ab3SChristopher Meis leafNames.push_back(drivePath.filename());
420*7bf29ab3SChristopher Meis }
421*7bf29ab3SChristopher Meis
422*7bf29ab3SChristopher Meis std::ranges::sort(leafNames, AlphanumLess<std::string>());
423*7bf29ab3SChristopher Meis
424*7bf29ab3SChristopher Meis for (const auto& leafName : leafNames)
425*7bf29ab3SChristopher Meis {
426*7bf29ab3SChristopher Meis nlohmann::json::object_t member;
427*7bf29ab3SChristopher Meis member["@odata.id"] =
428*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}/Drives/{}",
429*7bf29ab3SChristopher Meis chassisId, leafName);
430*7bf29ab3SChristopher Meis members.emplace_back(std::move(member));
431*7bf29ab3SChristopher Meis // navigation links will be registered in next patch set
432*7bf29ab3SChristopher Meis }
433*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
434*7bf29ab3SChristopher Meis }); // end association lambda
435*7bf29ab3SChristopher Meis
436*7bf29ab3SChristopher Meis } // end Iterate over all retrieved ObjectPaths
437*7bf29ab3SChristopher Meis }
438*7bf29ab3SChristopher Meis
439*7bf29ab3SChristopher Meis /**
440*7bf29ab3SChristopher Meis * Chassis drives, this URL will show all the DriveCollection
441*7bf29ab3SChristopher Meis * information
442*7bf29ab3SChristopher Meis */
chassisDriveCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)443*7bf29ab3SChristopher Meis inline void chassisDriveCollectionGet(
444*7bf29ab3SChristopher Meis crow::App& app, const crow::Request& req,
445*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
446*7bf29ab3SChristopher Meis const std::string& chassisId)
447*7bf29ab3SChristopher Meis {
448*7bf29ab3SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp))
449*7bf29ab3SChristopher Meis {
450*7bf29ab3SChristopher Meis return;
451*7bf29ab3SChristopher Meis }
452*7bf29ab3SChristopher Meis
453*7bf29ab3SChristopher Meis // mapper call lambda
454*7bf29ab3SChristopher Meis dbus::utility::getSubTree(
455*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
456*7bf29ab3SChristopher Meis std::bind_front(afterChassisDriveCollectionSubtreeGet, asyncResp,
457*7bf29ab3SChristopher Meis chassisId));
458*7bf29ab3SChristopher Meis }
459*7bf29ab3SChristopher Meis
buildDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName,const boost::system::error_code & ec,const dbus::utility::MapperGetSubTreeResponse & subtree)460*7bf29ab3SChristopher Meis inline void buildDrive(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
461*7bf29ab3SChristopher Meis const std::string& chassisId,
462*7bf29ab3SChristopher Meis const std::string& driveName,
463*7bf29ab3SChristopher Meis const boost::system::error_code& ec,
464*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree)
465*7bf29ab3SChristopher Meis {
466*7bf29ab3SChristopher Meis if (ec)
467*7bf29ab3SChristopher Meis {
468*7bf29ab3SChristopher Meis BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
469*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
470*7bf29ab3SChristopher Meis return;
471*7bf29ab3SChristopher Meis }
472*7bf29ab3SChristopher Meis
473*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths.
474*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree)
475*7bf29ab3SChristopher Meis {
476*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path);
477*7bf29ab3SChristopher Meis if (objPath.filename() != driveName)
478*7bf29ab3SChristopher Meis {
479*7bf29ab3SChristopher Meis continue;
480*7bf29ab3SChristopher Meis }
481*7bf29ab3SChristopher Meis
482*7bf29ab3SChristopher Meis if (connectionNames.empty())
483*7bf29ab3SChristopher Meis {
484*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names");
485*7bf29ab3SChristopher Meis continue;
486*7bf29ab3SChristopher Meis }
487*7bf29ab3SChristopher Meis
488*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
489*7bf29ab3SChristopher Meis "/redfish/v1/Chassis/{}/Drives/{}", chassisId, driveName);
490*7bf29ab3SChristopher Meis
491*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["@odata.type"] = "#Drive.v1_7_0.Drive";
492*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Name"] = driveName;
493*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Id"] = driveName;
494*7bf29ab3SChristopher Meis // default it to Enabled
495*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
496*7bf29ab3SChristopher Meis
497*7bf29ab3SChristopher Meis nlohmann::json::object_t linkChassisNav;
498*7bf29ab3SChristopher Meis linkChassisNav["@odata.id"] =
499*7bf29ab3SChristopher Meis boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
500*7bf29ab3SChristopher Meis asyncResp->res.jsonValue["Links"]["Chassis"] = linkChassisNav;
501*7bf29ab3SChristopher Meis
502*7bf29ab3SChristopher Meis addAllDriveInfo(asyncResp, connectionNames[0].first, path,
503*7bf29ab3SChristopher Meis connectionNames[0].second);
504*7bf29ab3SChristopher Meis }
505*7bf29ab3SChristopher Meis }
506*7bf29ab3SChristopher Meis
matchAndFillDrive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName,const std::vector<std::string> & resp)507*7bf29ab3SChristopher Meis inline void matchAndFillDrive(
508*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
509*7bf29ab3SChristopher Meis const std::string& chassisId, const std::string& driveName,
510*7bf29ab3SChristopher Meis const std::vector<std::string>& resp)
511*7bf29ab3SChristopher Meis {
512*7bf29ab3SChristopher Meis for (const std::string& drivePath : resp)
513*7bf29ab3SChristopher Meis {
514*7bf29ab3SChristopher Meis sdbusplus::message::object_path path(drivePath);
515*7bf29ab3SChristopher Meis std::string leaf = path.filename();
516*7bf29ab3SChristopher Meis if (leaf != driveName)
517*7bf29ab3SChristopher Meis {
518*7bf29ab3SChristopher Meis continue;
519*7bf29ab3SChristopher Meis }
520*7bf29ab3SChristopher Meis // mapper call drive
521*7bf29ab3SChristopher Meis constexpr std::array<std::string_view, 1> driveInterface = {
522*7bf29ab3SChristopher Meis "xyz.openbmc_project.Inventory.Item.Drive"};
523*7bf29ab3SChristopher Meis dbus::utility::getSubTree(
524*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, driveInterface,
525*7bf29ab3SChristopher Meis [asyncResp, chassisId, driveName](
526*7bf29ab3SChristopher Meis const boost::system::error_code& ec,
527*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) {
528*7bf29ab3SChristopher Meis buildDrive(asyncResp, chassisId, driveName, ec, subtree);
529*7bf29ab3SChristopher Meis });
530*7bf29ab3SChristopher Meis }
531*7bf29ab3SChristopher Meis }
532*7bf29ab3SChristopher Meis
handleChassisDriveGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & driveName)533*7bf29ab3SChristopher Meis inline void handleChassisDriveGet(
534*7bf29ab3SChristopher Meis crow::App& app, const crow::Request& req,
535*7bf29ab3SChristopher Meis const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
536*7bf29ab3SChristopher Meis const std::string& chassisId, const std::string& driveName)
537*7bf29ab3SChristopher Meis {
538*7bf29ab3SChristopher Meis if (!redfish::setUpRedfishRoute(app, req, asyncResp))
539*7bf29ab3SChristopher Meis {
540*7bf29ab3SChristopher Meis return;
541*7bf29ab3SChristopher Meis }
542*7bf29ab3SChristopher Meis
543*7bf29ab3SChristopher Meis // mapper call chassis
544*7bf29ab3SChristopher Meis dbus::utility::getSubTree(
545*7bf29ab3SChristopher Meis "/xyz/openbmc_project/inventory", 0, chassisInterfaces,
546*7bf29ab3SChristopher Meis [asyncResp, chassisId,
547*7bf29ab3SChristopher Meis driveName](const boost::system::error_code& ec,
548*7bf29ab3SChristopher Meis const dbus::utility::MapperGetSubTreeResponse& subtree) {
549*7bf29ab3SChristopher Meis if (ec)
550*7bf29ab3SChristopher Meis {
551*7bf29ab3SChristopher Meis messages::internalError(asyncResp->res);
552*7bf29ab3SChristopher Meis return;
553*7bf29ab3SChristopher Meis }
554*7bf29ab3SChristopher Meis
555*7bf29ab3SChristopher Meis // Iterate over all retrieved ObjectPaths.
556*7bf29ab3SChristopher Meis for (const auto& [path, connectionNames] : subtree)
557*7bf29ab3SChristopher Meis {
558*7bf29ab3SChristopher Meis sdbusplus::message::object_path objPath(path);
559*7bf29ab3SChristopher Meis if (objPath.filename() != chassisId)
560*7bf29ab3SChristopher Meis {
561*7bf29ab3SChristopher Meis continue;
562*7bf29ab3SChristopher Meis }
563*7bf29ab3SChristopher Meis
564*7bf29ab3SChristopher Meis if (connectionNames.empty())
565*7bf29ab3SChristopher Meis {
566*7bf29ab3SChristopher Meis BMCWEB_LOG_ERROR("Got 0 Connection names");
567*7bf29ab3SChristopher Meis continue;
568*7bf29ab3SChristopher Meis }
569*7bf29ab3SChristopher Meis
570*7bf29ab3SChristopher Meis dbus::utility::getAssociationEndPoints(
571*7bf29ab3SChristopher Meis path + "/drive",
572*7bf29ab3SChristopher Meis [asyncResp, chassisId,
573*7bf29ab3SChristopher Meis driveName](const boost::system::error_code& ec3,
574*7bf29ab3SChristopher Meis const dbus::utility::MapperEndPoints& resp) {
575*7bf29ab3SChristopher Meis if (ec3)
576*7bf29ab3SChristopher Meis {
577*7bf29ab3SChristopher Meis return; // no drives = no failures
578*7bf29ab3SChristopher Meis }
579*7bf29ab3SChristopher Meis matchAndFillDrive(asyncResp, chassisId, driveName,
580*7bf29ab3SChristopher Meis resp);
581*7bf29ab3SChristopher Meis });
582*7bf29ab3SChristopher Meis return;
583*7bf29ab3SChristopher Meis }
584*7bf29ab3SChristopher Meis // Couldn't find an object with that name. return an error
585*7bf29ab3SChristopher Meis messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
586*7bf29ab3SChristopher Meis });
587*7bf29ab3SChristopher Meis }
588*7bf29ab3SChristopher Meis
589*7bf29ab3SChristopher Meis /**
590*7bf29ab3SChristopher Meis * This URL will show the drive interface for the specific drive in the chassis
591*7bf29ab3SChristopher Meis */
requestRoutesChassisDrive(App & app)592*7bf29ab3SChristopher Meis inline void requestRoutesChassisDrive(App& app)
593*7bf29ab3SChristopher Meis {
594*7bf29ab3SChristopher Meis BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/")
595*7bf29ab3SChristopher Meis .privileges(redfish::privileges::getDriveCollection)
596*7bf29ab3SChristopher Meis .methods(boost::beast::http::verb::get)(
597*7bf29ab3SChristopher Meis std::bind_front(chassisDriveCollectionGet, std::ref(app)));
598*7bf29ab3SChristopher Meis
599*7bf29ab3SChristopher Meis BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Drives/<str>/")
600*7bf29ab3SChristopher Meis .privileges(redfish::privileges::getChassis)
601*7bf29ab3SChristopher Meis .methods(boost::beast::http::verb::get)(
602*7bf29ab3SChristopher Meis std::bind_front(handleChassisDriveGet, std::ref(app)));
603*7bf29ab3SChristopher Meis }
604*7bf29ab3SChristopher Meis
605*7bf29ab3SChristopher Meis } // namespace redfish
606