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