xref: /openbmc/bmcweb/redfish-core/lib/storage_chassis.hpp (revision 7bf29ab37ad696aed3f0580ef0f6cb90864c89ee)
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