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