xref: /openbmc/bmcweb/features/redfish/lib/managers.hpp (revision 08fad5d9dc59323a8916ff97a035221621047d8c)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "bmcweb_config.h"
7 
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_singleton.hpp"
11 #include "dbus_utility.hpp"
12 #include "error_messages.hpp"
13 #include "generated/enums/action_info.hpp"
14 #include "generated/enums/manager.hpp"
15 #include "generated/enums/resource.hpp"
16 #include "http_request.hpp"
17 #include "led.hpp"
18 #include "logging.hpp"
19 #include "persistent_data.hpp"
20 #include "query.hpp"
21 #include "redfish.hpp"
22 #include "redfish_util.hpp"
23 #include "registries/privilege_registry.hpp"
24 #include "utils/dbus_utils.hpp"
25 #include "utils/etag_utils.hpp"
26 #include "utils/json_utils.hpp"
27 #include "utils/manager_utils.hpp"
28 #include "utils/sw_utils.hpp"
29 #include "utils/systemd_utils.hpp"
30 #include "utils/time_utils.hpp"
31 
32 #include <systemd/sd-bus.h>
33 
34 #include <boost/beast/http/status.hpp>
35 #include <boost/beast/http/verb.hpp>
36 #include <boost/system/error_code.hpp>
37 #include <boost/url/format.hpp>
38 #include <boost/url/url.hpp>
39 #include <nlohmann/json.hpp>
40 #include <sdbusplus/asio/property.hpp>
41 #include <sdbusplus/message.hpp>
42 #include <sdbusplus/message/native_types.hpp>
43 #include <sdbusplus/unpack_properties.hpp>
44 
45 #include <array>
46 #include <cstddef>
47 #include <cstdint>
48 #include <format>
49 #include <functional>
50 #include <map>
51 #include <memory>
52 #include <optional>
53 #include <ranges>
54 #include <string>
55 #include <string_view>
56 #include <utility>
57 
58 namespace redfish
59 {
60 
61 inline void handleSetLocationIndicatorActive(
62     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
63     bool locationIndicatorActive, const std::string& managerId,
64     const boost::system::error_code& ec,
65     const dbus::utility::MapperGetSubTreePathsResponse& subtreePaths)
66 {
67     if (ec)
68     {
69         if (ec == boost::system::errc::io_error)
70         {
71             // Not found
72             BMCWEB_LOG_WARNING("Manager {} not found", managerId);
73             messages::resourceNotFound(asyncResp->res, "Manager", managerId);
74             return;
75         }
76         BMCWEB_LOG_ERROR("D-Bus response error {}", ec.value());
77         messages::internalError(asyncResp->res);
78         return;
79     }
80     if (subtreePaths.empty())
81     {
82         BMCWEB_LOG_WARNING("Manager {} not found", managerId);
83         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
84         return;
85     }
86     // Assume only 1 bmc D-Bus object
87     // Throw an error if there is more than 1
88     if (subtreePaths.size() != 1)
89     {
90         BMCWEB_LOG_ERROR("Found {} Bmc D-Bus paths", subtreePaths.size());
91         messages::internalError(asyncResp->res);
92         return;
93     }
94 
95     setLocationIndicatorActive(asyncResp, subtreePaths[0],
96                                locationIndicatorActive);
97 }
98 
99 /**
100  * Set the locationIndicatorActive.
101  *
102  * @param[in,out]   asyncResp                   Async HTTP response.
103  * @param[in]       locationIndicatorActive     Value of the property
104  */
105 inline void setLocationIndicatorActiveState(
106     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
107     bool locationIndicatorActive, const std::string& managerId)
108 {
109     // GetSubTree on all interfaces which provide info about a Manager
110     constexpr std::array<std::string_view, 1> interfaces = {
111         "xyz.openbmc_project.Inventory.Item.Bmc"};
112     dbus::utility::getSubTreePaths(
113         "/xyz/openbmc_project/inventory", 0, interfaces,
114         std::bind_front(handleSetLocationIndicatorActive, asyncResp,
115                         locationIndicatorActive, managerId));
116 }
117 
118 inline std::string getBMCUpdateServiceName()
119 {
120     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
121     {
122         return "xyz.openbmc_project.Software.Manager";
123     }
124     return "xyz.openbmc_project.Software.BMC.Updater";
125 }
126 
127 inline std::string getBMCUpdateServicePath()
128 {
129     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
130     {
131         return "/xyz/openbmc_project/software/bmc";
132     }
133     return "/xyz/openbmc_project/software";
134 }
135 
136 /**
137  * Function reboots the BMC.
138  *
139  * @param[in] asyncResp - Shared pointer for completing asynchronous calls
140  */
141 inline void doBMCGracefulRestart(
142     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
143 {
144     const char* processName = "xyz.openbmc_project.State.BMC";
145     const char* objectPath = "/xyz/openbmc_project/state/bmc0";
146     const char* interfaceName = "xyz.openbmc_project.State.BMC";
147     const std::string& propertyValue =
148         "xyz.openbmc_project.State.BMC.Transition.Reboot";
149     const char* destProperty = "RequestedBMCTransition";
150 
151     // Create the D-Bus variant for D-Bus call.
152     sdbusplus::asio::setProperty(
153         *crow::connections::systemBus, processName, objectPath, interfaceName,
154         destProperty, propertyValue,
155         [asyncResp](const boost::system::error_code& ec) {
156             // Use "Set" method to set the property value.
157             if (ec)
158             {
159                 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec);
160                 messages::internalError(asyncResp->res);
161                 return;
162             }
163 
164             messages::success(asyncResp->res);
165         });
166 }
167 
168 inline void doBMCForceRestart(
169     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
170 {
171     const char* processName = "xyz.openbmc_project.State.BMC";
172     const char* objectPath = "/xyz/openbmc_project/state/bmc0";
173     const char* interfaceName = "xyz.openbmc_project.State.BMC";
174     const std::string& propertyValue =
175         "xyz.openbmc_project.State.BMC.Transition.HardReboot";
176     const char* destProperty = "RequestedBMCTransition";
177 
178     // Create the D-Bus variant for D-Bus call.
179     sdbusplus::asio::setProperty(
180         *crow::connections::systemBus, processName, objectPath, interfaceName,
181         destProperty, propertyValue,
182         [asyncResp](const boost::system::error_code& ec) {
183             // Use "Set" method to set the property value.
184             if (ec)
185             {
186                 BMCWEB_LOG_DEBUG("[Set] Bad D-Bus request error: {}", ec);
187                 messages::internalError(asyncResp->res);
188                 return;
189             }
190 
191             messages::success(asyncResp->res);
192         });
193 }
194 
195 /**
196  * ManagerResetAction handles POST method request.
197  * Analyzes POST body before sending Reset (Reboot) request data to D-Bus.
198  * OpenBMC supports ResetType "GracefulRestart" and "ForceRestart".
199  */
200 
201 inline void handleManagerResetAction(
202     crow::App& app, const crow::Request& req,
203     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204     const std::string& managerId)
205 {
206     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
207     {
208         return;
209     }
210     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
211     {
212         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
213         return;
214     }
215 
216     BMCWEB_LOG_DEBUG("Post Manager Reset.");
217 
218     std::string resetType;
219 
220     if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
221     {
222         return;
223     }
224 
225     if (resetType == "GracefulRestart")
226     {
227         BMCWEB_LOG_DEBUG("Proceeding with {}", resetType);
228         doBMCGracefulRestart(asyncResp);
229         return;
230     }
231     if (resetType == "ForceRestart")
232     {
233         BMCWEB_LOG_DEBUG("Proceeding with {}", resetType);
234         doBMCForceRestart(asyncResp);
235         return;
236     }
237     BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType);
238     messages::actionParameterNotSupported(asyncResp->res, resetType,
239                                           "ResetType");
240 }
241 
242 /**
243  * Function handles ResetToDefaults POST method request.
244  *
245  * Analyzes POST body message and factory resets BMC by calling
246  * BMC code updater factory reset followed by a BMC reboot.
247  *
248  * BMC code updater factory reset wipes the whole BMC read-write
249  * filesystem which includes things like the network settings.
250  *
251  * OpenBMC only supports ResetType "ResetAll".
252  */
253 
254 inline void handleManagerResetToDefaultsAction(
255     crow::App& app, const crow::Request& req,
256     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
257     const std::string& managerId)
258 {
259     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
260     {
261         return;
262     }
263 
264     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
265     {
266         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
267         return;
268     }
269 
270     BMCWEB_LOG_DEBUG("Post ResetToDefaults.");
271 
272     std::optional<std::string> resetType;
273 
274     if (!json_util::readJsonAction( //
275             req, asyncResp->res,    //
276             "ResetType", resetType  //
277             ))
278     {
279         BMCWEB_LOG_DEBUG("Missing property ResetType.");
280 
281         messages::actionParameterMissing(asyncResp->res, "ResetToDefaults",
282                                          "ResetType");
283         return;
284     }
285 
286     if (resetType.value_or("") != "ResetAll")
287     {
288         BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}",
289                          resetType.value_or(""));
290         messages::actionParameterNotSupported(
291             asyncResp->res, resetType.value_or(""), "ResetType");
292         return;
293     }
294 
295     crow::connections::systemBus->async_method_call(
296         [asyncResp](const boost::system::error_code& ec) {
297             if (ec)
298             {
299                 BMCWEB_LOG_DEBUG("Failed to ResetToDefaults: {}", ec);
300                 messages::internalError(asyncResp->res);
301                 return;
302             }
303             // Factory Reset doesn't actually happen until a reboot
304             // Can't erase what the BMC is running on
305             doBMCGracefulRestart(asyncResp);
306         },
307         getBMCUpdateServiceName(), getBMCUpdateServicePath(),
308         "xyz.openbmc_project.Common.FactoryReset", "Reset");
309 }
310 
311 /**
312  * ManagerResetActionInfo derived class for delivering Manager
313  * ResetType AllowableValues using ResetInfo schema.
314  */
315 inline void handleManagerResetActionInfo(
316     crow::App& app, const crow::Request& req,
317     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
318     const std::string& managerId)
319 {
320     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
321     {
322         return;
323     }
324 
325     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
326     {
327         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
328         return;
329     }
330 
331     asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo";
332     asyncResp->res.jsonValue["@odata.id"] =
333         boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo",
334                             BMCWEB_REDFISH_MANAGER_URI_NAME);
335     asyncResp->res.jsonValue["Name"] = "Reset Action Info";
336     asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
337     nlohmann::json::object_t parameter;
338     parameter["Name"] = "ResetType";
339     parameter["Required"] = true;
340     parameter["DataType"] = action_info::ParameterTypes::String;
341 
342     nlohmann::json::array_t allowableValues;
343     allowableValues.emplace_back("GracefulRestart");
344     allowableValues.emplace_back("ForceRestart");
345     parameter["AllowableValues"] = std::move(allowableValues);
346 
347     nlohmann::json::array_t parameters;
348     parameters.emplace_back(std::move(parameter));
349 
350     asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
351 }
352 
353 /**
354  * @brief Retrieves BMC manager location data over DBus
355  *
356  * @param[in] asyncResp Shared pointer for completing asynchronous calls
357  * @param[in] connectionName - service name
358  * @param[in] path - object path
359  * @return none
360  */
361 inline void getLocation(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
362                         const std::string& connectionName,
363                         const std::string& path)
364 {
365     BMCWEB_LOG_DEBUG("Get BMC manager Location data.");
366 
367     dbus::utility::getProperty<std::string>(
368         connectionName, path,
369         "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
370         [asyncResp](const boost::system::error_code& ec,
371                     const std::string& property) {
372             if (ec)
373             {
374                 BMCWEB_LOG_DEBUG("DBUS response error for "
375                                  "Location");
376                 messages::internalError(asyncResp->res);
377                 return;
378             }
379 
380             asyncResp->res
381                 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
382                 property;
383         });
384 }
385 // avoid name collision systems.hpp
386 inline void managerGetLastResetTime(
387     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
388 {
389     BMCWEB_LOG_DEBUG("Getting Manager Last Reset Time");
390 
391     dbus::utility::getProperty<uint64_t>(
392         "xyz.openbmc_project.State.BMC", "/xyz/openbmc_project/state/bmc0",
393         "xyz.openbmc_project.State.BMC", "LastRebootTime",
394         [asyncResp](const boost::system::error_code& ec,
395                     const uint64_t lastResetTime) {
396             if (ec)
397             {
398                 BMCWEB_LOG_DEBUG("D-BUS response error {}", ec);
399                 return;
400             }
401 
402             // LastRebootTime is epoch time, in milliseconds
403             // https://github.com/openbmc/phosphor-dbus-interfaces/blob/7f9a128eb9296e926422ddc312c148b625890bb6/xyz/openbmc_project/State/BMC.interface.yaml#L19
404             uint64_t lastResetTimeStamp = lastResetTime / 1000;
405 
406             // Convert to ISO 8601 standard
407             asyncResp->res.jsonValue["LastResetTime"] =
408                 redfish::time_utils::getDateTimeUint(lastResetTimeStamp);
409         });
410 }
411 
412 /**
413  * @brief Set the running firmware image
414  *
415  * @param[i,o] asyncResp - Async response object
416  * @param[i] runningFirmwareTarget - Image to make the running image
417  *
418  * @return void
419  */
420 inline void setActiveFirmwareImage(
421     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
422     const std::string& runningFirmwareTarget)
423 {
424     // Get the Id from /redfish/v1/UpdateService/FirmwareInventory/<Id>
425     std::string::size_type idPos = runningFirmwareTarget.rfind('/');
426     if (idPos == std::string::npos)
427     {
428         messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget,
429                                          "@odata.id");
430         BMCWEB_LOG_DEBUG("Can't parse firmware ID!");
431         return;
432     }
433     idPos++;
434     if (idPos >= runningFirmwareTarget.size())
435     {
436         messages::propertyValueNotInList(asyncResp->res, runningFirmwareTarget,
437                                          "@odata.id");
438         BMCWEB_LOG_DEBUG("Invalid firmware ID.");
439         return;
440     }
441     std::string firmwareId = runningFirmwareTarget.substr(idPos);
442 
443     // Make sure the image is valid before setting priority
444     sdbusplus::message::object_path objPath("/xyz/openbmc_project/software");
445     dbus::utility::getManagedObjects(
446         getBMCUpdateServiceName(), objPath,
447         [asyncResp, firmwareId, runningFirmwareTarget](
448             const boost::system::error_code& ec,
449             const dbus::utility::ManagedObjectType& subtree) {
450             if (ec)
451             {
452                 BMCWEB_LOG_DEBUG("D-Bus response error getting objects.");
453                 messages::internalError(asyncResp->res);
454                 return;
455             }
456 
457             if (subtree.empty())
458             {
459                 BMCWEB_LOG_DEBUG("Can't find image!");
460                 messages::internalError(asyncResp->res);
461                 return;
462             }
463 
464             bool foundImage = false;
465             for (const auto& object : subtree)
466             {
467                 const std::string& path =
468                     static_cast<const std::string&>(object.first);
469                 std::size_t idPos2 = path.rfind('/');
470 
471                 if (idPos2 == std::string::npos)
472                 {
473                     continue;
474                 }
475 
476                 idPos2++;
477                 if (idPos2 >= path.size())
478                 {
479                     continue;
480                 }
481 
482                 if (path.substr(idPos2) == firmwareId)
483                 {
484                     foundImage = true;
485                     break;
486                 }
487             }
488 
489             if (!foundImage)
490             {
491                 messages::propertyValueNotInList(
492                     asyncResp->res, runningFirmwareTarget, "@odata.id");
493                 BMCWEB_LOG_DEBUG("Invalid firmware ID.");
494                 return;
495             }
496 
497             BMCWEB_LOG_DEBUG("Setting firmware version {} to priority 0.",
498                              firmwareId);
499 
500             // Only support Immediate
501             // An addition could be a Redfish Setting like
502             // ActiveSoftwareImageApplyTime and support OnReset
503             sdbusplus::asio::setProperty(
504                 *crow::connections::systemBus, getBMCUpdateServiceName(),
505                 "/xyz/openbmc_project/software/" + firmwareId,
506                 "xyz.openbmc_project.Software.RedundancyPriority", "Priority",
507                 static_cast<uint8_t>(0),
508                 [asyncResp](const boost::system::error_code& ec2) {
509                     if (ec2)
510                     {
511                         BMCWEB_LOG_DEBUG("D-Bus response error setting.");
512                         messages::internalError(asyncResp->res);
513                         return;
514                     }
515                     doBMCGracefulRestart(asyncResp);
516                 });
517         });
518 }
519 
520 inline void afterSetDateTime(
521     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
522     const boost::system::error_code& ec, const sdbusplus::message_t& msg)
523 {
524     if (ec)
525     {
526         BMCWEB_LOG_DEBUG("Failed to set elapsed time. DBUS response error {}",
527                          ec);
528         const sd_bus_error* dbusError = msg.get_error();
529         if (dbusError != nullptr)
530         {
531             std::string_view errorName(dbusError->name);
532             if (errorName ==
533                 "org.freedesktop.timedate1.AutomaticTimeSyncEnabled")
534             {
535                 BMCWEB_LOG_DEBUG("Setting conflict");
536                 messages::propertyValueConflict(
537                     asyncResp->res, "DateTime",
538                     "Managers/NetworkProtocol/NTPProcotolEnabled");
539                 return;
540             }
541         }
542         messages::internalError(asyncResp->res);
543         return;
544     }
545     asyncResp->res.result(boost::beast::http::status::no_content);
546 }
547 
548 inline void setDateTime(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
549                         const std::string& datetime)
550 {
551     BMCWEB_LOG_DEBUG("Set date time: {}", datetime);
552 
553     std::optional<redfish::time_utils::usSinceEpoch> us =
554         redfish::time_utils::dateStringToEpoch(datetime);
555     if (!us)
556     {
557         messages::propertyValueFormatError(asyncResp->res, datetime,
558                                            "DateTime");
559         return;
560     }
561     // Set the absolute datetime
562     bool relative = false;
563     bool interactive = false;
564     crow::connections::systemBus->async_method_call(
565         [asyncResp](const boost::system::error_code& ec,
566                     const sdbusplus::message_t& msg) {
567             afterSetDateTime(asyncResp, ec, msg);
568         },
569         "org.freedesktop.timedate1", "/org/freedesktop/timedate1",
570         "org.freedesktop.timedate1", "SetTime", us->count(), relative,
571         interactive);
572 }
573 
574 inline void checkForQuiesced(
575     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
576 {
577     dbus::utility::getProperty<std::string>(
578         "org.freedesktop.systemd1",
579         "/org/freedesktop/systemd1/unit/obmc-bmc-service-quiesce@0.target",
580         "org.freedesktop.systemd1.Unit", "ActiveState",
581         [asyncResp](const boost::system::error_code& ec,
582                     const std::string& val) {
583             if (!ec)
584             {
585                 if (val == "active")
586                 {
587                     asyncResp->res.jsonValue["Status"]["Health"] =
588                         resource::Health::Critical;
589                     asyncResp->res.jsonValue["Status"]["State"] =
590                         resource::State::Quiesced;
591                     return;
592                 }
593             }
594             asyncResp->res.jsonValue["Status"]["Health"] = resource::Health::OK;
595             asyncResp->res.jsonValue["Status"]["State"] =
596                 resource::State::Enabled;
597         });
598 }
599 
600 inline void getPhysicalAssets(
601     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
602     const boost::system::error_code& ec,
603     const dbus::utility::DBusPropertiesMap& propertiesList)
604 {
605     if (ec)
606     {
607         BMCWEB_LOG_DEBUG("Can't get bmc asset!");
608         return;
609     }
610 
611     const std::string* partNumber = nullptr;
612     const std::string* serialNumber = nullptr;
613     const std::string* manufacturer = nullptr;
614     const std::string* model = nullptr;
615     const std::string* sparePartNumber = nullptr;
616 
617     const bool success = sdbusplus::unpackPropertiesNoThrow(
618         dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber",
619         partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer,
620         "Model", model, "SparePartNumber", sparePartNumber);
621 
622     if (!success)
623     {
624         messages::internalError(asyncResp->res);
625         return;
626     }
627 
628     if (partNumber != nullptr)
629     {
630         asyncResp->res.jsonValue["PartNumber"] = *partNumber;
631     }
632 
633     if (serialNumber != nullptr)
634     {
635         asyncResp->res.jsonValue["SerialNumber"] = *serialNumber;
636     }
637 
638     if (manufacturer != nullptr)
639     {
640         asyncResp->res.jsonValue["Manufacturer"] = *manufacturer;
641     }
642 
643     if (model != nullptr)
644     {
645         asyncResp->res.jsonValue["Model"] = *model;
646     }
647 
648     if (sparePartNumber != nullptr)
649     {
650         asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber;
651     }
652 }
653 
654 inline void getManagerData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
655                            const std::string& managerPath,
656                            const dbus::utility::MapperServiceMap& serviceMap)
657 {
658     if (managerPath.empty() || serviceMap.size() != 1)
659     {
660         BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!");
661         messages::internalError(asyncResp->res);
662         return;
663     }
664 
665     for (const auto& [connectionName, interfaces] : serviceMap)
666     {
667         for (const auto& interfaceName : interfaces)
668         {
669             if (interfaceName ==
670                 "xyz.openbmc_project.Inventory.Decorator.Asset")
671             {
672                 dbus::utility::getAllProperties(
673                     *crow::connections::systemBus, connectionName, managerPath,
674                     "xyz.openbmc_project.Inventory.Decorator.Asset",
675                     std::bind_front(getPhysicalAssets, asyncResp));
676             }
677             else if (interfaceName ==
678                      "xyz.openbmc_project.Inventory.Decorator.LocationCode")
679             {
680                 getLocation(asyncResp, connectionName, managerPath);
681             }
682             else if (interfaceName ==
683                      "xyz.openbmc_project.Association.Definitions")
684             {
685                 getLocationIndicatorActive(asyncResp, managerPath);
686             }
687         }
688     }
689 }
690 
691 inline void afterGetManagerObject(
692     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
693     const boost::system::error_code& ec,
694     const dbus::utility::MapperGetSubTreeResponse& subtree,
695     const std::function<
696         void(const std::string& managerPath,
697              const dbus::utility::MapperServiceMap& serviceMap)>& callback)
698 {
699     if (ec)
700     {
701         BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec);
702         return;
703     }
704     if (subtree.empty())
705     {
706         BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!");
707         return;
708     }
709     // Assume only 1 bmc D-Bus object
710     // Throw an error if there is more than 1
711     if (subtree.size() > 1)
712     {
713         BMCWEB_LOG_ERROR("Found more than 1 bmc D-Bus object!");
714         messages::internalError(asyncResp->res);
715         return;
716     }
717 
718     callback(subtree[0].first, subtree[0].second);
719 }
720 
721 inline void getManagerObject(
722     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723     const std::string& /* managerId */,
724     std::function<void(const std::string& managerPath,
725                        const dbus::utility::MapperServiceMap& serviceMap)>&&
726         callback)
727 {
728     constexpr std::array<std::string_view, 1> interfaces = {
729         "xyz.openbmc_project.Inventory.Item.Bmc"};
730     dbus::utility::getSubTree(
731         "/xyz/openbmc_project/inventory", 0, interfaces,
732         [asyncResp, callback{std::move(callback)}](
733             const boost::system::error_code& ec,
734             const dbus::utility::MapperGetSubTreeResponse& subtree) {
735             afterGetManagerObject(asyncResp, ec, subtree, callback);
736         });
737 }
738 
739 inline void handleManagerGet(
740     App& app, const crow::Request& req,
741     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
742     const std::string& managerId)
743 {
744     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
745     {
746         return;
747     }
748 
749     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
750     {
751         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
752         return;
753     }
754 
755     std::string uuid = persistent_data::getConfig().systemUuid;
756 
757     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
758         "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
759     asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_15_0.Manager";
760     asyncResp->res.jsonValue["Id"] = BMCWEB_REDFISH_MANAGER_URI_NAME;
761     asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
762     asyncResp->res.jsonValue["Description"] = "Baseboard Management Controller";
763     asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On;
764 
765     asyncResp->res.jsonValue["ManagerType"] = manager::ManagerType::BMC;
766     asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
767     asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
768     asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
769 
770     asyncResp->res.jsonValue["LogServices"]["@odata.id"] = boost::urls::format(
771         "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
772     asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] =
773         boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol",
774                             BMCWEB_REDFISH_MANAGER_URI_NAME);
775     asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] =
776         boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces",
777                             BMCWEB_REDFISH_MANAGER_URI_NAME);
778 
779     manager_utils::getServiceIdentification(asyncResp, false);
780 
781     if constexpr (BMCWEB_VM_NBDPROXY)
782     {
783         asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] =
784             boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia",
785                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
786     }
787 
788     // Manager.Reset (an action) can be many values, OpenBMC only
789     // supports BMC reboot.
790     nlohmann::json& managerReset =
791         asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
792     managerReset["target"] =
793         boost::urls::format("/redfish/v1/Managers/{}/Actions/Manager.Reset",
794                             BMCWEB_REDFISH_MANAGER_URI_NAME);
795     managerReset["@Redfish.ActionInfo"] =
796         boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo",
797                             BMCWEB_REDFISH_MANAGER_URI_NAME);
798 
799     // ResetToDefaults (Factory Reset) has values like
800     // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
801     // on OpenBMC
802     nlohmann::json& resetToDefaults =
803         asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
804     resetToDefaults["target"] = boost::urls::format(
805         "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults",
806         BMCWEB_REDFISH_MANAGER_URI_NAME);
807     resetToDefaults["ResetType@Redfish.AllowableValues"] =
808         nlohmann::json::array_t({"ResetAll"});
809 
810     std::pair<std::string, std::string> redfishDateTimeOffset =
811         redfish::time_utils::getDateTimeOffsetNow();
812 
813     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
814     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
815         redfishDateTimeOffset.second;
816 
817     if constexpr (BMCWEB_KVM)
818     {
819         // Fill in GraphicalConsole info
820         asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
821         asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] =
822             4;
823         asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
824             nlohmann::json::array_t({"KVMIP"});
825     }
826     if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
827     {
828         asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
829 
830         nlohmann::json::array_t managerForServers;
831         nlohmann::json::object_t manager;
832         manager["@odata.id"] = std::format("/redfish/v1/Systems/{}",
833                                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
834         managerForServers.emplace_back(std::move(manager));
835 
836         asyncResp->res.jsonValue["Links"]["ManagerForServers"] =
837             std::move(managerForServers);
838     }
839 
840     sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose,
841                                          "FirmwareVersion", true);
842 
843     managerGetLastResetTime(asyncResp);
844 
845     // ManagerDiagnosticData is added for all BMCs.
846     nlohmann::json& managerDiagnosticData =
847         asyncResp->res.jsonValue["ManagerDiagnosticData"];
848     managerDiagnosticData["@odata.id"] =
849         boost::urls::format("/redfish/v1/Managers/{}/ManagerDiagnosticData",
850                             BMCWEB_REDFISH_MANAGER_URI_NAME);
851 
852     getMainChassisId(
853         asyncResp, [](const std::string& chassisId,
854                       const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
855             aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
856             nlohmann::json::array_t managerForChassis;
857             nlohmann::json::object_t managerObj;
858             boost::urls::url chassiUrl =
859                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
860             managerObj["@odata.id"] = chassiUrl;
861             managerForChassis.emplace_back(std::move(managerObj));
862             aRsp->res.jsonValue["Links"]["ManagerForChassis"] =
863                 std::move(managerForChassis);
864             aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] =
865                 chassiUrl;
866         });
867 
868     dbus::utility::getProperty<double>(
869         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
870         "org.freedesktop.systemd1.Manager", "Progress",
871         [asyncResp](const boost::system::error_code& ec, double val) {
872             if (ec)
873             {
874                 BMCWEB_LOG_ERROR("Error while getting progress");
875                 messages::internalError(asyncResp->res);
876                 return;
877             }
878             if (val < 1.0)
879             {
880                 asyncResp->res.jsonValue["Status"]["Health"] =
881                     resource::Health::OK;
882                 asyncResp->res.jsonValue["Status"]["State"] =
883                     resource::State::Starting;
884                 return;
885             }
886             checkForQuiesced(asyncResp);
887         });
888 
889     getManagerObject(asyncResp, managerId,
890                      std::bind_front(getManagerData, asyncResp));
891     etag_utils::setEtagOmitDateTimeHandler(asyncResp);
892 
893     RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
894 }
895 
896 inline void handleManagerPatch(
897     App& app, const crow::Request& req,
898     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
899     const std::string& managerId)
900 {
901     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
902     {
903         return;
904     }
905 
906     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
907     {
908         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
909         return;
910     }
911 
912     std::optional<std::string> activeSoftwareImageOdataId;
913     std::optional<std::string> datetime;
914     std::optional<bool> locationIndicatorActive;
915     std::optional<nlohmann::json::object_t> pidControllers;
916     std::optional<nlohmann::json::object_t> fanControllers;
917     std::optional<nlohmann::json::object_t> fanZones;
918     std::optional<nlohmann::json::object_t> stepwiseControllers;
919     std::optional<std::string> profile;
920     std::optional<std::string> serviceIdentification;
921 
922     if (!json_util::readJsonPatch(                            //
923             req, asyncResp->res,                              //
924             "DateTime", datetime,                             //
925             "Links/ActiveSoftwareImage/@odata.id",
926             activeSoftwareImageOdataId,                       //
927             "LocationIndicatorActive",
928             locationIndicatorActive,                          //
929             "Oem/OpenBmc/Fan/FanControllers", fanControllers, //
930             "Oem/OpenBmc/Fan/FanZones", fanZones,             //
931             "Oem/OpenBmc/Fan/PidControllers", pidControllers, //
932             "Oem/OpenBmc/Fan/Profile", profile,               //
933             "Oem/OpenBmc/Fan/StepwiseControllers",
934             stepwiseControllers,                              //
935             "ServiceIdentification", serviceIdentification    //
936             ))
937     {
938         return;
939     }
940 
941     if (activeSoftwareImageOdataId)
942     {
943         setActiveFirmwareImage(asyncResp, *activeSoftwareImageOdataId);
944     }
945 
946     if (datetime)
947     {
948         setDateTime(asyncResp, *datetime);
949     }
950 
951     if (locationIndicatorActive)
952     {
953         setLocationIndicatorActiveState(asyncResp, *locationIndicatorActive,
954                                         managerId);
955     }
956 
957     if (serviceIdentification)
958     {
959         manager_utils::setServiceIdentification(asyncResp,
960                                                 serviceIdentification.value());
961     }
962 
963     RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
964 }
965 
966 inline void handleManagerCollectionGet(
967     crow::App& app, const crow::Request& req,
968     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
969 {
970     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
971     {
972         return;
973     }
974     // Collections don't include the static data added by SubRoute
975     // because it has a duplicate entry for members
976     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
977     asyncResp->res.jsonValue["@odata.type"] =
978         "#ManagerCollection.ManagerCollection";
979     asyncResp->res.jsonValue["Name"] = "Manager Collection";
980     asyncResp->res.jsonValue["Members@odata.count"] = 1;
981     nlohmann::json::array_t members;
982     nlohmann::json& bmc = members.emplace_back();
983     bmc["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}",
984                                            BMCWEB_REDFISH_MANAGER_URI_NAME);
985     asyncResp->res.jsonValue["Members"] = std::move(members);
986 }
987 
988 inline void requestRoutesManager(App& app)
989 {
990     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/")
991         .privileges(redfish::privileges::getManager)
992         .methods(boost::beast::http::verb::get)(
993             std::bind_front(handleManagerGet, std::ref(app)));
994 
995     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/")
996         .privileges(redfish::privileges::patchManager)
997         .methods(boost::beast::http::verb::patch)(
998             std::bind_front(handleManagerPatch, std::ref(app)));
999 
1000     BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
1001         .privileges(redfish::privileges::getManagerCollection)
1002         .methods(boost::beast::http::verb::get)(
1003             std::bind_front(handleManagerCollectionGet, std::ref(app)));
1004 }
1005 
1006 inline void requestRoutesManagerResetAction(App& app)
1007 {
1008     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Actions/Manager.Reset/")
1009         .privileges(redfish::privileges::postManager)
1010         .methods(boost::beast::http::verb::post)(
1011             std::bind_front(handleManagerResetAction, std::ref(app)));
1012 
1013     BMCWEB_ROUTE(app,
1014                  "/redfish/v1/Managers/<str>/Actions/Manager.ResetToDefaults/")
1015         .privileges(redfish::privileges::postManager)
1016         .methods(boost::beast::http::verb::post)(
1017             std::bind_front(handleManagerResetToDefaultsAction, std::ref(app)));
1018 
1019     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/ResetActionInfo/")
1020         .privileges(redfish::privileges::getActionInfo)
1021         .methods(boost::beast::http::verb::get)(
1022             std::bind_front(handleManagerResetActionInfo, std::ref(app)));
1023 }
1024 
1025 } // namespace redfish
1026