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