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