xref: /openbmc/bmcweb/redfish-core/lib/virtual_media.hpp (revision 2077899f6d4534d98ca2cca3bc9020dbc6a4aef9)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include <boost/container/flat_map.hpp>
19 #include <node.hpp>
20 #include <utils/json_utils.hpp>
21 // for GetObjectType and ManagedObjectType
22 #include <account_service.hpp>
23 
24 namespace redfish
25 
26 {
27 
28 /**
29  * @brief Read all known properties from VM object interfaces
30  */
31 static void vmParseInterfaceObject(const DbusInterfaceType &interface,
32                                    std::shared_ptr<AsyncResp> aResp)
33 {
34     const auto mountPointIface =
35         interface.find("xyz.openbmc_project.VirtualMedia.MountPoint");
36     if (mountPointIface == interface.cend())
37     {
38         BMCWEB_LOG_DEBUG << "Interface MountPoint not found";
39         return;
40     }
41 
42     const auto processIface =
43         interface.find("xyz.openbmc_project.VirtualMedia.Process");
44     if (processIface == interface.cend())
45     {
46         BMCWEB_LOG_DEBUG << "Interface Process not found";
47         return;
48     }
49 
50     const auto endpointIdProperty = mountPointIface->second.find("EndpointId");
51     if (endpointIdProperty == mountPointIface->second.cend())
52     {
53         BMCWEB_LOG_DEBUG << "Property EndpointId not found";
54         return;
55     }
56 
57     const auto activeProperty = processIface->second.find("Active");
58     if (activeProperty == processIface->second.cend())
59     {
60         BMCWEB_LOG_DEBUG << "Property Active not found";
61         return;
62     }
63 
64     const bool *activeValue = std::get_if<bool>(&activeProperty->second);
65     if (!activeValue)
66     {
67         BMCWEB_LOG_DEBUG << "Value Active not found";
68         return;
69     }
70 
71     const std::string *endpointIdValue =
72         std::get_if<std::string>(&endpointIdProperty->second);
73     if (endpointIdValue)
74     {
75         if (!endpointIdValue->empty())
76         {
77             // Proxy mode
78             aResp->res.jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] =
79                 *endpointIdValue;
80             aResp->res.jsonValue["TransferProtocolType"] = "OEM";
81             aResp->res.jsonValue["Inserted"] = *activeValue;
82             if (*activeValue == true)
83             {
84                 aResp->res.jsonValue["ConnectedVia"] = "Applet";
85             }
86         }
87         else
88         {
89             // Legacy mode
90             const auto imageUrlProperty =
91                 mountPointIface->second.find("ImageURL");
92             if (imageUrlProperty != processIface->second.cend())
93             {
94                 const std::string *imageUrlValue =
95                     std::get_if<std::string>(&imageUrlProperty->second);
96                 if (imageUrlValue && !imageUrlValue->empty())
97                 {
98                     aResp->res.jsonValue["ImageName"] = *imageUrlValue;
99                     aResp->res.jsonValue["Inserted"] = *activeValue;
100                     if (*activeValue == true)
101                     {
102                         aResp->res.jsonValue["ConnectedVia"] = "URI";
103                     }
104                 }
105             }
106         }
107     }
108 }
109 
110 /**
111  * @brief Fill template for Virtual Media Item.
112  */
113 static nlohmann::json vmItemTemplate(const std::string &name,
114                                      const std::string &resName)
115 {
116     nlohmann::json item;
117     item["@odata.id"] =
118         "/redfish/v1/Managers/" + name + "/VirtualMedia/" + resName;
119     item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia";
120     item["@odata.context"] = "/redfish/v1/$metadata#VirtualMedia.VirtualMedia";
121     item["Name"] = "Virtual Removable Media";
122     item["Id"] = resName;
123     item["Image"] = nullptr;
124     item["Inserted"] = nullptr;
125     item["ImageName"] = nullptr;
126     item["WriteProtected"] = true;
127     item["ConnectedVia"] = "NotConnected";
128     item["MediaTypes"] = {"CD", "USBStick"};
129     item["TransferMethod"] = "Stream";
130     item["TransferProtocolType"] = nullptr;
131     item["Oem"]["OpenBmc"]["WebSocketEndpoint"] = nullptr;
132     item["Oem"]["OpenBMC"]["@odata.type"] =
133         "#OemVirtualMedia.v1_0_0.VirtualMedia";
134 
135     return item;
136 }
137 
138 /**
139  *  @brief Fills collection data
140  */
141 static void getVmResourceList(std::shared_ptr<AsyncResp> aResp,
142                               const std::string &service,
143                               const std::string &name)
144 {
145     BMCWEB_LOG_DEBUG << "Get available Virtual Media resources.";
146     crow::connections::systemBus->async_method_call(
147         [name, aResp{std::move(aResp)}](const boost::system::error_code ec,
148                                         ManagedObjectType &subtree) {
149             if (ec)
150             {
151                 BMCWEB_LOG_DEBUG << "DBUS response error";
152                 return;
153             }
154             nlohmann::json &members = aResp->res.jsonValue["Members"];
155             members = nlohmann::json::array();
156 
157             for (const auto &object : subtree)
158             {
159                 nlohmann::json item;
160                 const std::string &path =
161                     static_cast<const std::string &>(object.first);
162                 std::size_t lastIndex = path.rfind("/");
163                 if (lastIndex == std::string::npos)
164                 {
165                     continue;
166                 }
167 
168                 lastIndex += 1;
169 
170                 item["@odata.id"] = "/redfish/v1/Managers/" + name +
171                                     "/VirtualMedia/" + path.substr(lastIndex);
172 
173                 members.emplace_back(std::move(item));
174             }
175             aResp->res.jsonValue["Members@odata.count"] = members.size();
176         },
177         service, "/xyz/openbmc_project/VirtualMedia",
178         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
179 }
180 
181 /**
182  *  @brief Fills data for specific resource
183  */
184 static void getVmData(std::shared_ptr<AsyncResp> aResp,
185                       const std::string &service, const std::string &name,
186                       const std::string &resName)
187 {
188     BMCWEB_LOG_DEBUG << "Get Virtual Media resource data.";
189 
190     crow::connections::systemBus->async_method_call(
191         [resName, name, aResp](const boost::system::error_code ec,
192                                ManagedObjectType &subtree) {
193             if (ec)
194             {
195                 BMCWEB_LOG_DEBUG << "DBUS response error";
196 
197                 return;
198             }
199 
200             for (auto &item : subtree)
201             {
202                 const std::string &path =
203                     static_cast<const std::string &>(item.first);
204 
205                 std::size_t lastItem = path.rfind("/");
206                 if (lastItem == std::string::npos)
207                 {
208                     continue;
209                 }
210 
211                 if (path.substr(lastItem + 1) != resName)
212                 {
213                     continue;
214                 }
215 
216                 aResp->res.jsonValue = vmItemTemplate(name, resName);
217 
218                 // Check if dbus path is Legacy type
219                 if (path.find("VirtualMedia/Legacy") != std::string::npos)
220                 {
221                     aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"]
222                                         ["target"] =
223                         "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
224                         resName + "/Actions/VirtualMedia.InsertMedia";
225                 }
226 
227                 vmParseInterfaceObject(item.second, aResp);
228 
229                 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"]
230                                     ["target"] =
231                     "/redfish/v1/Managers/" + name + "/VirtualMedia/" +
232                     resName + "/Actions/VirtualMedia.EjectMedia";
233 
234                 return;
235             }
236 
237             messages::resourceNotFound(
238                 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName);
239         },
240         service, "/xyz/openbmc_project/VirtualMedia",
241         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
242 }
243 
244 /**
245    @brief InsertMedia action class
246  */
247 class VirtualMediaActionInsertMedia : public Node
248 {
249   public:
250     VirtualMediaActionInsertMedia(CrowApp &app) :
251         Node(app,
252              "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
253              "VirtualMedia.InsertMedia",
254              std::string(), std::string())
255     {
256         entityPrivileges = {
257             {boost::beast::http::verb::get, {{"Login"}}},
258             {boost::beast::http::verb::head, {{"Login"}}},
259             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
260             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
261             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
262             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
263     }
264 
265   private:
266     /**
267      * @brief Function handles POST method request.
268      *
269      * Analyzes POST body message before sends Reset request data to dbus.
270      */
271     void doPost(crow::Response &res, const crow::Request &req,
272                 const std::vector<std::string> &params) override
273     {
274         auto aResp = std::make_shared<AsyncResp>(res);
275 
276         if (params.size() != 2)
277         {
278             messages::internalError(res);
279             return;
280         }
281 
282         // take resource name from URL
283         const std::string &resName = params[1];
284 
285         if (params[0] != "bmc")
286         {
287             messages::resourceNotFound(res, "VirtualMedia.Insert", resName);
288 
289             return;
290         }
291 
292         crow::connections::systemBus->async_method_call(
293             [this, aResp{std::move(aResp)}, req,
294              resName](const boost::system::error_code ec,
295                       const GetObjectType &getObjectType) {
296                 if (ec)
297                 {
298                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
299                                      << ec;
300                     messages::internalError(aResp->res);
301 
302                     return;
303                 }
304                 std::string service = getObjectType.begin()->first;
305                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
306 
307                 crow::connections::systemBus->async_method_call(
308                     [this, service, resName, req, aResp{std::move(aResp)}](
309                         const boost::system::error_code ec,
310                         ManagedObjectType &subtree) {
311                         if (ec)
312                         {
313                             BMCWEB_LOG_DEBUG << "DBUS response error";
314 
315                             return;
316                         }
317 
318                         for (const auto &object : subtree)
319                         {
320                             const std::string &path =
321                                 static_cast<const std::string &>(object.first);
322 
323                             std::size_t lastIndex = path.rfind("/");
324                             if (lastIndex == std::string::npos)
325                             {
326                                 continue;
327                             }
328 
329                             lastIndex += 1;
330 
331                             if (path.substr(lastIndex) == resName)
332                             {
333                                 lastIndex = path.rfind("Proxy");
334                                 if (lastIndex != std::string::npos)
335                                 {
336                                     // Not possible in proxy mode
337                                     BMCWEB_LOG_DEBUG << "InsertMedia not "
338                                                         "allowed in proxy mode";
339                                     messages::resourceNotFound(
340                                         aResp->res, "VirtualMedia.InsertMedia",
341                                         resName);
342 
343                                     return;
344                                 }
345 
346                                 lastIndex = path.rfind("Legacy");
347                                 if (lastIndex != std::string::npos)
348                                 {
349                                     // Legacy mode
350                                     std::string imageUrl;
351 
352                                     // Read obligatory paramters (url of image)
353                                     if (!json_util::readJson(req, aResp->res,
354                                                              "Image", imageUrl))
355                                     {
356                                         BMCWEB_LOG_DEBUG
357                                             << "Image is not provided";
358                                         return;
359                                     }
360 
361                                     // must not be empty
362                                     if (imageUrl.size() == 0)
363                                     {
364                                         BMCWEB_LOG_ERROR
365                                             << "Request action parameter "
366                                                "Image is empty.";
367                                         messages::propertyValueFormatError(
368                                             aResp->res, "<empty>", "Image");
369 
370                                         return;
371                                     }
372 
373                                     // manager is irrelevant for VirtualMedia
374                                     // dbus calls
375                                     doVmAction(std::move(aResp), service,
376                                                resName, true, imageUrl);
377 
378                                     return;
379                                 }
380                             }
381                         }
382                         BMCWEB_LOG_DEBUG << "Parent item not found";
383                         messages::resourceNotFound(aResp->res, "VirtualMedia",
384                                                    resName);
385                     },
386                     service, "/xyz/openbmc_project/VirtualMedia",
387                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
388             },
389             "xyz.openbmc_project.ObjectMapper",
390             "/xyz/openbmc_project/object_mapper",
391             "xyz.openbmc_project.ObjectMapper", "GetObject",
392             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
393     }
394 
395     /**
396      * @brief Function transceives data with dbus directly.
397      *
398      * All BMC state properties will be retrieved before sending reset request.
399      */
400     void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
401                     const std::string &service, const std::string &name,
402                     bool legacy, const std::string &imageUrl)
403     {
404 
405         // Legacy mount requires parameter with image
406         if (legacy)
407         {
408             crow::connections::systemBus->async_method_call(
409                 [asyncResp](const boost::system::error_code ec) {
410                     if (ec)
411                     {
412                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
413                         messages::internalError(asyncResp->res);
414 
415                         return;
416                     }
417                 },
418                 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
419                 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl);
420         }
421         else // proxy
422         {
423             crow::connections::systemBus->async_method_call(
424                 [asyncResp](const boost::system::error_code ec) {
425                     if (ec)
426                     {
427                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
428                         messages::internalError(asyncResp->res);
429 
430                         return;
431                     }
432                 },
433                 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
434                 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
435         }
436     }
437 };
438 
439 /**
440    @brief EjectMedia action class
441  */
442 class VirtualMediaActionEjectMedia : public Node
443 {
444   public:
445     VirtualMediaActionEjectMedia(CrowApp &app) :
446         Node(app,
447              "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/"
448              "VirtualMedia.EjectMedia",
449              std::string(), std::string())
450     {
451         entityPrivileges = {
452             {boost::beast::http::verb::get, {{"Login"}}},
453             {boost::beast::http::verb::head, {{"Login"}}},
454             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
455             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
456             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
457             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
458     }
459 
460   private:
461     /**
462      * @brief Function handles POST method request.
463      *
464      * Analyzes POST body message before sends Reset request data to dbus.
465      */
466     void doPost(crow::Response &res, const crow::Request &req,
467                 const std::vector<std::string> &params) override
468     {
469         auto aResp = std::make_shared<AsyncResp>(res);
470 
471         if (params.size() != 2)
472         {
473             messages::internalError(res);
474             return;
475         }
476 
477         // take resource name from URL
478         const std::string &resName = params[1];
479 
480         if (params[0] != "bmc")
481         {
482             messages::resourceNotFound(res, "VirtualMedia.Eject", resName);
483 
484             return;
485         }
486 
487         crow::connections::systemBus->async_method_call(
488             [this, aResp{std::move(aResp)}, req,
489              resName](const boost::system::error_code ec,
490                       const GetObjectType &getObjectType) {
491                 if (ec)
492                 {
493                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
494                                      << ec;
495                     messages::internalError(aResp->res);
496 
497                     return;
498                 }
499                 std::string service = getObjectType.begin()->first;
500                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
501 
502                 crow::connections::systemBus->async_method_call(
503                     [this, resName, service, req, aResp{std::move(aResp)}](
504                         const boost::system::error_code ec,
505                         ManagedObjectType &subtree) {
506                         if (ec)
507                         {
508                             BMCWEB_LOG_DEBUG << "DBUS response error";
509 
510                             return;
511                         }
512 
513                         for (const auto &object : subtree)
514                         {
515                             const std::string &path =
516                                 static_cast<const std::string &>(object.first);
517 
518                             std::size_t lastIndex = path.rfind("/");
519                             if (lastIndex == std::string::npos)
520                             {
521                                 continue;
522                             }
523 
524                             lastIndex += 1;
525 
526                             if (path.substr(lastIndex) == resName)
527                             {
528                                 lastIndex = path.rfind("Proxy");
529                                 if (lastIndex != std::string::npos)
530                                 {
531                                     // Proxy mode
532                                     doVmAction(std::move(aResp), service,
533                                                resName, false);
534                                 }
535 
536                                 lastIndex = path.rfind("Legacy");
537                                 if (lastIndex != std::string::npos)
538                                 {
539                                     // Legacy mode
540                                     doVmAction(std::move(aResp), service,
541                                                resName, true);
542                                 }
543 
544                                 return;
545                             }
546                         }
547                         BMCWEB_LOG_DEBUG << "Parent item not found";
548                         messages::resourceNotFound(aResp->res, "VirtualMedia",
549                                                    resName);
550                     },
551                     service, "/xyz/openbmc_project/VirtualMedia",
552                     "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
553             },
554             "xyz.openbmc_project.ObjectMapper",
555             "/xyz/openbmc_project/object_mapper",
556             "xyz.openbmc_project.ObjectMapper", "GetObject",
557             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
558     }
559 
560     /**
561      * @brief Function transceives data with dbus directly.
562      *
563      * All BMC state properties will be retrieved before sending reset request.
564      */
565     void doVmAction(std::shared_ptr<AsyncResp> asyncResp,
566                     const std::string &service, const std::string &name,
567                     bool legacy)
568     {
569 
570         // Legacy mount requires parameter with image
571         if (legacy)
572         {
573             crow::connections::systemBus->async_method_call(
574                 [asyncResp](const boost::system::error_code ec) {
575                     if (ec)
576                     {
577                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
578 
579                         messages::internalError(asyncResp->res);
580                         return;
581                     }
582                 },
583                 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name,
584                 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount");
585         }
586         else // proxy
587         {
588             crow::connections::systemBus->async_method_call(
589                 [asyncResp](const boost::system::error_code ec) {
590                     if (ec)
591                     {
592                         BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec;
593 
594                         messages::internalError(asyncResp->res);
595                         return;
596                     }
597                 },
598                 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name,
599                 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
600         }
601     }
602 };
603 
604 class VirtualMediaCollection : public Node
605 {
606   public:
607     /*
608      * Default Constructor
609      */
610     VirtualMediaCollection(CrowApp &app) :
611         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/", std::string())
612     {
613         entityPrivileges = {
614             {boost::beast::http::verb::get, {{"Login"}}},
615             {boost::beast::http::verb::head, {{"Login"}}},
616             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
617             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
618             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
619             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
620     }
621 
622   private:
623     /**
624      * Functions triggers appropriate requests on DBus
625      */
626     void doGet(crow::Response &res, const crow::Request &req,
627                const std::vector<std::string> &params) override
628     {
629         auto asyncResp = std::make_shared<AsyncResp>(res);
630 
631         // Check if there is required param, truly entering this shall be
632         // impossible
633         if (params.size() != 1)
634         {
635             messages::internalError(res);
636 
637             return;
638         }
639 
640         const std::string &name = params[0];
641 
642         if (name != "bmc")
643         {
644             messages::resourceNotFound(asyncResp->res, "VirtualMedia", name);
645 
646             return;
647         }
648 
649         res.jsonValue["@odata.type"] =
650             "#VirtualMediaCollection.VirtualMediaCollection";
651         res.jsonValue["Name"] = "Virtual Media Services";
652         res.jsonValue["@odata.context"] =
653             "/redfish/v1/"
654             "$metadata#VirtualMediaCollection.VirtualMediaCollection";
655         res.jsonValue["@odata.id"] =
656             "/redfish/v1/Managers/" + name + "/VirtualMedia/";
657 
658         crow::connections::systemBus->async_method_call(
659             [asyncResp, name](const boost::system::error_code ec,
660                               const GetObjectType &getObjectType) {
661                 if (ec)
662                 {
663                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
664                                      << ec;
665                     messages::internalError(asyncResp->res);
666 
667                     return;
668                 }
669                 std::string service = getObjectType.begin()->first;
670                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
671 
672                 getVmResourceList(asyncResp, service, name);
673             },
674             "xyz.openbmc_project.ObjectMapper",
675             "/xyz/openbmc_project/object_mapper",
676             "xyz.openbmc_project.ObjectMapper", "GetObject",
677             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
678     }
679 };
680 
681 class VirtualMedia : public Node
682 {
683   public:
684     /*
685      * Default Constructor
686      */
687     VirtualMedia(CrowApp &app) :
688         Node(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/",
689              std::string(), std::string())
690     {
691         entityPrivileges = {
692             {boost::beast::http::verb::get, {{"Login"}}},
693             {boost::beast::http::verb::head, {{"Login"}}},
694             {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
695             {boost::beast::http::verb::put, {{"ConfigureManager"}}},
696             {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
697             {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
698     }
699 
700   private:
701     /**
702      * Functions triggers appropriate requests on DBus
703      */
704     void doGet(crow::Response &res, const crow::Request &req,
705                const std::vector<std::string> &params) override
706     {
707         // Check if there is required param, truly entering this shall be
708         // impossible
709         if (params.size() != 2)
710         {
711             messages::internalError(res);
712 
713             res.end();
714             return;
715         }
716         const std::string &name = params[0];
717         const std::string &resName = params[1];
718 
719         auto asyncResp = std::make_shared<AsyncResp>(res);
720 
721         if (name != "bmc")
722         {
723             messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName);
724 
725             return;
726         }
727 
728         crow::connections::systemBus->async_method_call(
729             [asyncResp, name, resName](const boost::system::error_code ec,
730                                        const GetObjectType &getObjectType) {
731                 if (ec)
732                 {
733                     BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: "
734                                      << ec;
735                     messages::internalError(asyncResp->res);
736 
737                     return;
738                 }
739                 std::string service = getObjectType.begin()->first;
740                 BMCWEB_LOG_DEBUG << "GetObjectType: " << service;
741 
742                 getVmData(asyncResp, service, name, resName);
743             },
744             "xyz.openbmc_project.ObjectMapper",
745             "/xyz/openbmc_project/object_mapper",
746             "xyz.openbmc_project.ObjectMapper", "GetObject",
747             "/xyz/openbmc_project/VirtualMedia", std::array<const char *, 0>());
748     }
749 };
750 
751 } // namespace redfish
752