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