xref: /openbmc/bmcweb/redfish-core/lib/managers.hpp (revision cc67d0a0fed101c930b334a583d9ca9b222ceb77)
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_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 
getBMCUpdateServiceName()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 
getBMCUpdateServicePath()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  */
doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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 
doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  */
requestRoutesManagerResetAction(App & app)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  */
requestRoutesManagerResetToDefaultsAction(App & app)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  */
requestRoutesManagerResetActionInfo(App & app)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  */
getLocation(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)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
managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  */
setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & runningFirmwareTarget)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 
afterSetDateTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const sdbusplus::message_t & msg)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 
setDateTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & datetime)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 
checkForQuiesced(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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 
requestRoutesManager(App & app)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             getHandleOemOpenBmc(req, asyncResp, managerId);
645 
646             // Manager.Reset (an action) can be many values, OpenBMC only
647             // supports BMC reboot.
648             nlohmann::json& managerReset =
649                 asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
650             managerReset["target"] = boost::urls::format(
651                 "/redfish/v1/Managers/{}/Actions/Manager.Reset",
652                 BMCWEB_REDFISH_MANAGER_URI_NAME);
653             managerReset["@Redfish.ActionInfo"] =
654                 boost::urls::format("/redfish/v1/Managers/{}/ResetActionInfo",
655                                     BMCWEB_REDFISH_MANAGER_URI_NAME);
656 
657             // ResetToDefaults (Factory Reset) has values like
658             // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
659             // on OpenBMC
660             nlohmann::json& resetToDefaults =
661                 asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
662             resetToDefaults["target"] = boost::urls::format(
663                 "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults",
664                 BMCWEB_REDFISH_MANAGER_URI_NAME);
665             resetToDefaults["ResetType@Redfish.AllowableValues"] =
666                 nlohmann::json::array_t({"ResetAll"});
667 
668             std::pair<std::string, std::string> redfishDateTimeOffset =
669                 redfish::time_utils::getDateTimeOffsetNow();
670 
671             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
672             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
673                 redfishDateTimeOffset.second;
674 
675             if constexpr (BMCWEB_KVM)
676             {
677                 // Fill in GraphicalConsole info
678                 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] =
679                     true;
680                 asyncResp->res
681                     .jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 4;
682                 asyncResp->res
683                     .jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
684                     nlohmann::json::array_t({"KVMIP"});
685             }
686             if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
687             {
688                 asyncResp->res
689                     .jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
690 
691                 nlohmann::json::array_t managerForServers;
692                 nlohmann::json::object_t manager;
693                 manager["@odata.id"] = std::format(
694                     "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
695                 managerForServers.emplace_back(std::move(manager));
696 
697                 asyncResp->res.jsonValue["Links"]["ManagerForServers"] =
698                     std::move(managerForServers);
699             }
700 
701             sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose,
702                                                  "FirmwareVersion", true);
703 
704             managerGetLastResetTime(asyncResp);
705 
706             // ManagerDiagnosticData is added for all BMCs.
707             nlohmann::json& managerDiagnosticData =
708                 asyncResp->res.jsonValue["ManagerDiagnosticData"];
709             managerDiagnosticData["@odata.id"] = boost::urls::format(
710                 "/redfish/v1/Managers/{}/ManagerDiagnosticData",
711                 BMCWEB_REDFISH_MANAGER_URI_NAME);
712 
713             if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA)
714             {
715                 auto pids = std::make_shared<GetPIDValues>(asyncResp);
716                 pids->run();
717             }
718 
719             getMainChassisId(asyncResp, [](const std::string& chassisId,
720                                            const std::shared_ptr<
721                                                bmcweb::AsyncResp>& aRsp) {
722                 aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] =
723                     1;
724                 nlohmann::json::array_t managerForChassis;
725                 nlohmann::json::object_t managerObj;
726                 boost::urls::url chassiUrl =
727                     boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
728                 managerObj["@odata.id"] = chassiUrl;
729                 managerForChassis.emplace_back(std::move(managerObj));
730                 aRsp->res.jsonValue["Links"]["ManagerForChassis"] =
731                     std::move(managerForChassis);
732                 aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] =
733                     chassiUrl;
734             });
735 
736             dbus::utility::getProperty<double>(
737                 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
738                 "org.freedesktop.systemd1.Manager", "Progress",
739                 [asyncResp](const boost::system::error_code& ec, double val) {
740                     if (ec)
741                     {
742                         BMCWEB_LOG_ERROR("Error while getting progress");
743                         messages::internalError(asyncResp->res);
744                         return;
745                     }
746                     if (val < 1.0)
747                     {
748                         asyncResp->res.jsonValue["Status"]["Health"] =
749                             resource::Health::OK;
750                         asyncResp->res.jsonValue["Status"]["State"] =
751                             resource::State::Starting;
752                         return;
753                     }
754                     checkForQuiesced(asyncResp);
755                 });
756 
757             constexpr std::array<std::string_view, 1> interfaces = {
758                 "xyz.openbmc_project.Inventory.Item.Bmc"};
759             dbus::utility::getSubTree(
760                 "/xyz/openbmc_project/inventory", 0, interfaces,
761                 [asyncResp](
762                     const boost::system::error_code& ec,
763                     const dbus::utility::MapperGetSubTreeResponse& subtree) {
764                     if (ec)
765                     {
766                         BMCWEB_LOG_DEBUG(
767                             "D-Bus response error on GetSubTree {}", ec);
768                         return;
769                     }
770                     if (subtree.empty())
771                     {
772                         BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!");
773                         return;
774                     }
775                     // Assume only 1 bmc D-Bus object
776                     // Throw an error if there is more than 1
777                     if (subtree.size() > 1)
778                     {
779                         BMCWEB_LOG_DEBUG("Found more than 1 bmc D-Bus object!");
780                         messages::internalError(asyncResp->res);
781                         return;
782                     }
783 
784                     if (subtree[0].first.empty() ||
785                         subtree[0].second.size() != 1)
786                     {
787                         BMCWEB_LOG_DEBUG("Error getting bmc D-Bus object!");
788                         messages::internalError(asyncResp->res);
789                         return;
790                     }
791 
792                     const std::string& path = subtree[0].first;
793                     const std::string& connectionName =
794                         subtree[0].second[0].first;
795 
796                     for (const auto& interfaceName :
797                          subtree[0].second[0].second)
798                     {
799                         if (interfaceName ==
800                             "xyz.openbmc_project.Inventory.Decorator.Asset")
801                         {
802                             dbus::utility::getAllProperties(
803                                 *crow::connections::systemBus, connectionName,
804                                 path,
805                                 "xyz.openbmc_project.Inventory.Decorator.Asset",
806                                 [asyncResp](
807                                     const boost::system::error_code& ec2,
808                                     const dbus::utility::DBusPropertiesMap&
809                                         propertiesList) {
810                                     if (ec2)
811                                     {
812                                         BMCWEB_LOG_DEBUG(
813                                             "Can't get bmc asset!");
814                                         return;
815                                     }
816 
817                                     const std::string* partNumber = nullptr;
818                                     const std::string* serialNumber = nullptr;
819                                     const std::string* manufacturer = nullptr;
820                                     const std::string* model = nullptr;
821                                     const std::string* sparePartNumber =
822                                         nullptr;
823 
824                                     const bool success =
825                                         sdbusplus::unpackPropertiesNoThrow(
826                                             dbus_utils::UnpackErrorPrinter(),
827                                             propertiesList, "PartNumber",
828                                             partNumber, "SerialNumber",
829                                             serialNumber, "Manufacturer",
830                                             manufacturer, "Model", model,
831                                             "SparePartNumber", sparePartNumber);
832 
833                                     if (!success)
834                                     {
835                                         messages::internalError(asyncResp->res);
836                                         return;
837                                     }
838 
839                                     if (partNumber != nullptr)
840                                     {
841                                         asyncResp->res.jsonValue["PartNumber"] =
842                                             *partNumber;
843                                     }
844 
845                                     if (serialNumber != nullptr)
846                                     {
847                                         asyncResp->res
848                                             .jsonValue["SerialNumber"] =
849                                             *serialNumber;
850                                     }
851 
852                                     if (manufacturer != nullptr)
853                                     {
854                                         asyncResp->res
855                                             .jsonValue["Manufacturer"] =
856                                             *manufacturer;
857                                     }
858 
859                                     if (model != nullptr)
860                                     {
861                                         asyncResp->res.jsonValue["Model"] =
862                                             *model;
863                                     }
864 
865                                     if (sparePartNumber != nullptr)
866                                     {
867                                         asyncResp->res
868                                             .jsonValue["SparePartNumber"] =
869                                             *sparePartNumber;
870                                     }
871                                 });
872                         }
873                         else if (
874                             interfaceName ==
875                             "xyz.openbmc_project.Inventory.Decorator.LocationCode")
876                         {
877                             getLocation(asyncResp, connectionName, path);
878                         }
879                     }
880                 });
881         });
882 
883     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/")
884         .privileges(redfish::privileges::patchManager)
885         .methods(boost::beast::http::verb::patch)(
886             [&app](const crow::Request& req,
887                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
888                    const std::string& managerId) {
889                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
890                 {
891                     return;
892                 }
893 
894                 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
895                 {
896                     messages::resourceNotFound(asyncResp->res, "Manager",
897                                                managerId);
898                     return;
899                 }
900 
901                 std::optional<std::string> activeSoftwareImageOdataId;
902                 std::optional<std::string> datetime;
903                 std::optional<nlohmann::json::object_t> pidControllers;
904                 std::optional<nlohmann::json::object_t> fanControllers;
905                 std::optional<nlohmann::json::object_t> fanZones;
906                 std::optional<nlohmann::json::object_t> stepwiseControllers;
907                 std::optional<std::string> profile;
908 
909                 if (!json_util::readJsonPatch(                            //
910                         req, asyncResp->res,                              //
911                         "DateTime", datetime,                             //
912                         "Links/ActiveSoftwareImage/@odata.id",
913                         activeSoftwareImageOdataId,                       //
914                         "Oem/OpenBmc/Fan/FanControllers", fanControllers, //
915                         "Oem/OpenBmc/Fan/FanZones", fanZones,             //
916                         "Oem/OpenBmc/Fan/PidControllers", pidControllers, //
917                         "Oem/OpenBmc/Fan/Profile", profile,               //
918                         "Oem/OpenBmc/Fan/StepwiseControllers",
919                         stepwiseControllers                               //
920                         ))
921                 {
922                     return;
923                 }
924 
925                 if (pidControllers || fanControllers || fanZones ||
926                     stepwiseControllers || profile)
927                 {
928                     if constexpr (BMCWEB_REDFISH_OEM_MANAGER_FAN_DATA)
929                     {
930                         std::vector<
931                             std::pair<std::string,
932                                       std::optional<nlohmann::json::object_t>>>
933                             configuration;
934                         if (pidControllers)
935                         {
936                             configuration.emplace_back(
937                                 "PidControllers", std::move(pidControllers));
938                         }
939                         if (fanControllers)
940                         {
941                             configuration.emplace_back(
942                                 "FanControllers", std::move(fanControllers));
943                         }
944                         if (fanZones)
945                         {
946                             configuration.emplace_back("FanZones",
947                                                        std::move(fanZones));
948                         }
949                         if (stepwiseControllers)
950                         {
951                             configuration.emplace_back(
952                                 "StepwiseControllers",
953                                 std::move(stepwiseControllers));
954                         }
955                         auto pid = std::make_shared<SetPIDValues>(
956                             asyncResp, std::move(configuration), profile);
957                         pid->run();
958                     }
959                     else
960                     {
961                         messages::propertyUnknown(asyncResp->res, "Oem");
962                         return;
963                     }
964                 }
965 
966                 if (activeSoftwareImageOdataId)
967                 {
968                     setActiveFirmwareImage(asyncResp,
969                                            *activeSoftwareImageOdataId);
970                 }
971 
972                 if (datetime)
973                 {
974                     setDateTime(asyncResp, *datetime);
975                 }
976             });
977 }
978 
requestRoutesManagerCollection(App & app)979 inline void requestRoutesManagerCollection(App& app)
980 {
981     BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
982         .privileges(redfish::privileges::getManagerCollection)
983         .methods(boost::beast::http::verb::get)(
984             [&app](const crow::Request& req,
985                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
986                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
987                 {
988                     return;
989                 }
990                 // Collections don't include the static data added by SubRoute
991                 // because it has a duplicate entry for members
992                 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
993                 asyncResp->res.jsonValue["@odata.type"] =
994                     "#ManagerCollection.ManagerCollection";
995                 asyncResp->res.jsonValue["Name"] = "Manager Collection";
996                 asyncResp->res.jsonValue["Members@odata.count"] = 1;
997                 nlohmann::json::array_t members;
998                 nlohmann::json& bmc = members.emplace_back();
999                 bmc["@odata.id"] = boost::urls::format(
1000                     "/redfish/v1/Managers/{}", BMCWEB_REDFISH_MANAGER_URI_NAME);
1001                 asyncResp->res.jsonValue["Members"] = std::move(members);
1002             });
1003 }
1004 } // namespace redfish
1005