xref: /openbmc/bmcweb/redfish-core/lib/managers.hpp (revision f58e571b2516c0bf374587dd261f912d914ac5a2)
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/etag_utils.hpp"
26 #include "utils/json_utils.hpp"
27 #include "utils/manager_utils.hpp"
28 #include "utils/sw_utils.hpp"
29 #include "utils/systemd_utils.hpp"
30 #include "utils/time_utils.hpp"
31 
32 #include <systemd/sd-bus.h>
33 
34 #include <boost/beast/http/status.hpp>
35 #include <boost/beast/http/verb.hpp>
36 #include <boost/system/error_code.hpp>
37 #include <boost/url/format.hpp>
38 #include <boost/url/url.hpp>
39 #include <nlohmann/json.hpp>
40 #include <sdbusplus/asio/property.hpp>
41 #include <sdbusplus/message.hpp>
42 #include <sdbusplus/message/native_types.hpp>
43 #include <sdbusplus/unpack_properties.hpp>
44 
45 #include <array>
46 #include <cstddef>
47 #include <cstdint>
48 #include <format>
49 #include <functional>
50 #include <map>
51 #include <memory>
52 #include <optional>
53 #include <ranges>
54 #include <string>
55 #include <string_view>
56 #include <utility>
57 
58 namespace redfish
59 {
60 
handleSetLocationIndicatorActive(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,bool locationIndicatorActive,const std::string & managerId,const boost::system::error_code & ec,const std::string & managerPath,const dbus::utility::MapperServiceMap &)61 inline void handleSetLocationIndicatorActive(
62     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
63     bool locationIndicatorActive, const std::string& managerId,
64     const boost::system::error_code& ec, const std::string& managerPath,
65     const dbus::utility::MapperServiceMap& /*serviceMap*/)
66 {
67     if (ec)
68     {
69         if (ec == boost::system::errc::io_error)
70         {
71             // Not found
72             BMCWEB_LOG_WARNING("Manager {} not found", managerId);
73             messages::resourceNotFound(asyncResp->res, "Manager", managerId);
74             return;
75         }
76         BMCWEB_LOG_ERROR("D-Bus response error {}", ec.value());
77         messages::internalError(asyncResp->res);
78         return;
79     }
80     if (managerPath.empty())
81     {
82         BMCWEB_LOG_WARNING("Manager {} not found", managerId);
83         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
84         return;
85     }
86 
87     setLocationIndicatorActive(asyncResp, managerPath, locationIndicatorActive);
88 }
89 
90 /**
91  * Set the locationIndicatorActive.
92  *
93  * @param[in,out]   asyncResp                   Async HTTP response.
94  * @param[in]       locationIndicatorActive     Value of the property
95  */
setLocationIndicatorActiveState(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,bool locationIndicatorActive,const std::string & managerId)96 inline void setLocationIndicatorActiveState(
97     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
98     bool locationIndicatorActive, const std::string& managerId)
99 {
100     manager_utils::getValidManagerPath(
101         asyncResp, managerId,
102         std::bind_front(handleSetLocationIndicatorActive, asyncResp,
103                         locationIndicatorActive, managerId));
104 }
105 
getBMCUpdateServiceName()106 inline std::string getBMCUpdateServiceName()
107 {
108     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
109     {
110         return "xyz.openbmc_project.Software.Manager";
111     }
112     return "xyz.openbmc_project.Software.BMC.Updater";
113 }
114 
getBMCUpdateServicePath()115 inline std::string getBMCUpdateServicePath()
116 {
117     if constexpr (BMCWEB_REDFISH_UPDATESERVICE_USE_DBUS)
118     {
119         return "/xyz/openbmc_project/software/bmc";
120     }
121     return "/xyz/openbmc_project/software";
122 }
123 
setBMCTransition(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & transitionValue)124 inline void setBMCTransition(
125     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
126     const std::string& transitionValue)
127 {
128     sdbusplus::asio::setProperty(
129         *crow::connections::systemBus, "xyz.openbmc_project.State.BMC",
130         "/xyz/openbmc_project/state/bmc0", "xyz.openbmc_project.State.BMC",
131         "RequestedBMCTransition", transitionValue,
132         [asyncResp](const boost::system::error_code& ec) {
133             if (ec)
134             {
135                 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec);
136                 messages::internalError(asyncResp->res);
137                 return;
138             }
139             messages::success(asyncResp->res);
140         });
141 }
142 
143 /**
144  * Function reboots the BMC.
145  *
146  * @param[in] asyncResp - Shared pointer for completing asynchronous calls
147  */
doBMCGracefulRestart(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)148 inline void doBMCGracefulRestart(
149     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
150 {
151     setBMCTransition(asyncResp,
152                      "xyz.openbmc_project.State.BMC.Transition.Reboot");
153 }
154 
doBMCForceRestart(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)155 inline void doBMCForceRestart(
156     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
157 {
158     setBMCTransition(asyncResp,
159                      "xyz.openbmc_project.State.BMC.Transition.HardReboot");
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 
handleManagerResetAction(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)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 
handleManagerResetToDefaultsAction(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)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  */
handleManagerResetActionInfo(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)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  */
getLocation(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & connectionName,const std::string & path)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
managerGetLastResetTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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  */
setActiveFirmwareImage(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & runningFirmwareTarget)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 
afterSetDateTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const sdbusplus::message_t & msg)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 
setDateTime(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & datetime)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 
checkForQuiesced(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)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 
getPhysicalAssets(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & propertiesList)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 
getManagerData(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const std::string & managerPath,const dbus::utility::MapperServiceMap & serviceMap)621 inline void getManagerData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
622                            const boost::system::error_code& ec,
623                            const std::string& managerPath,
624                            const dbus::utility::MapperServiceMap& serviceMap)
625 
626 {
627     if (ec)
628     {
629         BMCWEB_LOG_DEBUG("D-Bus response error on GetSubTree {}", ec);
630         return;
631     }
632     if (managerPath.empty() || serviceMap.empty())
633     {
634         BMCWEB_LOG_DEBUG("Can't find bmc D-Bus object!");
635         return;
636     }
637 
638     for (const auto& [connectionName, interfaces] : serviceMap)
639     {
640         for (const auto& interfaceName : interfaces)
641         {
642             if (interfaceName ==
643                 "xyz.openbmc_project.Inventory.Decorator.Asset")
644             {
645                 dbus::utility::getAllProperties(
646                     *crow::connections::systemBus, connectionName, managerPath,
647                     "xyz.openbmc_project.Inventory.Decorator.Asset",
648                     std::bind_front(getPhysicalAssets, asyncResp));
649             }
650             else if (interfaceName ==
651                      "xyz.openbmc_project.Inventory.Decorator.LocationCode")
652             {
653                 getLocation(asyncResp, connectionName, managerPath);
654             }
655             else if (interfaceName ==
656                      "xyz.openbmc_project.Association.Definitions")
657             {
658                 getLocationIndicatorActive(asyncResp, managerPath);
659             }
660         }
661     }
662 }
663 
handleManagerGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)664 inline void handleManagerGet(
665     App& app, const crow::Request& req,
666     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
667     const std::string& managerId)
668 {
669     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
670     {
671         return;
672     }
673 
674     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
675     {
676         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
677         return;
678     }
679 
680     std::string uuid = persistent_data::getConfig().systemUuid;
681 
682     asyncResp->res.jsonValue["@odata.id"] =
683         boost::urls::format("/redfish/v1/Managers/{}", managerId);
684     asyncResp->res.jsonValue["@odata.type"] = "#Manager.v1_15_0.Manager";
685     asyncResp->res.jsonValue["Id"] = managerId;
686     asyncResp->res.jsonValue["Name"] = "OpenBmc Manager";
687     asyncResp->res.jsonValue["Description"] = "Baseboard Management Controller";
688     asyncResp->res.jsonValue["PowerState"] = resource::PowerState::On;
689 
690     asyncResp->res.jsonValue["ManagerType"] = manager::ManagerType::BMC;
691     asyncResp->res.jsonValue["UUID"] = systemd_utils::getUuid();
692     asyncResp->res.jsonValue["ServiceEntryPointUUID"] = uuid;
693     asyncResp->res.jsonValue["Model"] = "OpenBmc"; // TODO(ed), get model
694 
695     asyncResp->res.jsonValue["LogServices"]["@odata.id"] =
696         boost::urls::format("/redfish/v1/Managers/{}/LogServices", managerId);
697     asyncResp->res.jsonValue["NetworkProtocol"]["@odata.id"] =
698         boost::urls::format("/redfish/v1/Managers/{}/NetworkProtocol",
699                             managerId);
700     asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] =
701         boost::urls::format("/redfish/v1/Managers/{}/EthernetInterfaces",
702                             managerId);
703 
704     manager_utils::getServiceIdentification(asyncResp, false);
705 
706     if constexpr (BMCWEB_VM_NBDPROXY)
707     {
708         asyncResp->res.jsonValue["VirtualMedia"]["@odata.id"] =
709             boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia",
710                                 managerId);
711     }
712 
713     // Manager.Reset (an action) can be many values, OpenBMC only
714     // supports BMC reboot.
715     nlohmann::json& managerReset =
716         asyncResp->res.jsonValue["Actions"]["#Manager.Reset"];
717     managerReset["target"] = boost::urls::format(
718         "/redfish/v1/Managers/{}/Actions/Manager.Reset", managerId);
719     managerReset["@Redfish.ActionInfo"] = boost::urls::format(
720         "/redfish/v1/Managers/{}/ResetActionInfo", managerId);
721 
722     // ResetToDefaults (Factory Reset) has values like
723     // PreserveNetworkAndUsers and PreserveNetwork that aren't supported
724     // on OpenBMC
725     nlohmann::json& resetToDefaults =
726         asyncResp->res.jsonValue["Actions"]["#Manager.ResetToDefaults"];
727     resetToDefaults["target"] = boost::urls::format(
728         "/redfish/v1/Managers/{}/Actions/Manager.ResetToDefaults", managerId);
729     resetToDefaults["ResetType@Redfish.AllowableValues"] =
730         nlohmann::json::array_t({"ResetAll"});
731 
732     std::pair<std::string, std::string> redfishDateTimeOffset =
733         redfish::time_utils::getDateTimeOffsetNow();
734 
735     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
736     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
737         redfishDateTimeOffset.second;
738 
739     if constexpr (BMCWEB_KVM)
740     {
741         // Fill in GraphicalConsole info
742         asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true;
743         asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] =
744             4;
745         asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] =
746             nlohmann::json::array_t({"KVMIP"});
747     }
748     if constexpr (!BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
749     {
750         asyncResp->res.jsonValue["Links"]["ManagerForServers@odata.count"] = 1;
751 
752         nlohmann::json::array_t managerForServers;
753         nlohmann::json::object_t manager;
754         manager["@odata.id"] = boost::urls::format(
755             "/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME);
756         managerForServers.emplace_back(std::move(manager));
757 
758         asyncResp->res.jsonValue["Links"]["ManagerForServers"] =
759             std::move(managerForServers);
760     }
761 
762     sw_util::populateSoftwareInformation(asyncResp, sw_util::bmcPurpose,
763                                          "FirmwareVersion", true);
764 
765     managerGetLastResetTime(asyncResp);
766 
767     // ManagerDiagnosticData is added for all BMCs.
768     nlohmann::json& managerDiagnosticData =
769         asyncResp->res.jsonValue["ManagerDiagnosticData"];
770     managerDiagnosticData["@odata.id"] = boost::urls::format(
771         "/redfish/v1/Managers/{}/ManagerDiagnosticData", managerId);
772 
773     getMainChassisId(
774         asyncResp, [](const std::string& chassisId,
775                       const std::shared_ptr<bmcweb::AsyncResp>& aRsp) {
776             aRsp->res.jsonValue["Links"]["ManagerForChassis@odata.count"] = 1;
777             nlohmann::json::array_t managerForChassis;
778             nlohmann::json::object_t managerObj;
779             boost::urls::url chassiUrl =
780                 boost::urls::format("/redfish/v1/Chassis/{}", chassisId);
781             managerObj["@odata.id"] = chassiUrl;
782             managerForChassis.emplace_back(std::move(managerObj));
783             aRsp->res.jsonValue["Links"]["ManagerForChassis"] =
784                 std::move(managerForChassis);
785             aRsp->res.jsonValue["Links"]["ManagerInChassis"]["@odata.id"] =
786                 chassiUrl;
787         });
788 
789     dbus::utility::getProperty<double>(
790         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
791         "org.freedesktop.systemd1.Manager", "Progress",
792         [asyncResp](const boost::system::error_code& ec, double val) {
793             if (ec)
794             {
795                 BMCWEB_LOG_ERROR("Error while getting progress");
796                 messages::internalError(asyncResp->res);
797                 return;
798             }
799             if (val < 1.0)
800             {
801                 asyncResp->res.jsonValue["Status"]["Health"] =
802                     resource::Health::OK;
803                 asyncResp->res.jsonValue["Status"]["State"] =
804                     resource::State::Starting;
805                 return;
806             }
807             checkForQuiesced(asyncResp);
808         });
809 
810     manager_utils::getValidManagerPath(
811         asyncResp, managerId, std::bind_front(getManagerData, asyncResp));
812 
813     etag_utils::setEtagOmitDateTimeHandler(asyncResp);
814 
815     RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
816 }
817 
handleManagerPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)818 inline void handleManagerPatch(
819     App& app, const crow::Request& req,
820     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
821     const std::string& managerId)
822 {
823     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
824     {
825         return;
826     }
827 
828     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
829     {
830         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
831         return;
832     }
833 
834     std::optional<std::string> activeSoftwareImageOdataId;
835     std::optional<std::string> datetime;
836     std::optional<bool> locationIndicatorActive;
837     std::optional<nlohmann::json::object_t> pidControllers;
838     std::optional<nlohmann::json::object_t> fanControllers;
839     std::optional<nlohmann::json::object_t> fanZones;
840     std::optional<nlohmann::json::object_t> stepwiseControllers;
841     std::optional<std::string> profile;
842     std::optional<std::string> serviceIdentification;
843 
844     if (!json_util::readJsonPatch(                            //
845             req, asyncResp->res,                              //
846             "DateTime", datetime,                             //
847             "Links/ActiveSoftwareImage/@odata.id",
848             activeSoftwareImageOdataId,                       //
849             "LocationIndicatorActive",
850             locationIndicatorActive,                          //
851             "Oem/OpenBmc/Fan/FanControllers", fanControllers, //
852             "Oem/OpenBmc/Fan/FanZones", fanZones,             //
853             "Oem/OpenBmc/Fan/PidControllers", pidControllers, //
854             "Oem/OpenBmc/Fan/Profile", profile,               //
855             "Oem/OpenBmc/Fan/StepwiseControllers",
856             stepwiseControllers,                              //
857             "ServiceIdentification", serviceIdentification    //
858             ))
859     {
860         return;
861     }
862 
863     if (activeSoftwareImageOdataId)
864     {
865         setActiveFirmwareImage(asyncResp, *activeSoftwareImageOdataId);
866     }
867 
868     if (datetime)
869     {
870         setDateTime(asyncResp, *datetime);
871     }
872 
873     if (locationIndicatorActive)
874     {
875         setLocationIndicatorActiveState(asyncResp, *locationIndicatorActive,
876                                         managerId);
877     }
878 
879     if (serviceIdentification)
880     {
881         manager_utils::setServiceIdentification(asyncResp,
882                                                 serviceIdentification.value());
883     }
884 
885     RedfishService::getInstance(app).handleSubRoute(req, asyncResp);
886 }
887 
handleManagerCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)888 inline void handleManagerCollectionGet(
889     crow::App& app, const crow::Request& req,
890     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
891 {
892     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
893     {
894         return;
895     }
896     // Collections don't include the static data added by SubRoute
897     // because it has a duplicate entry for members
898     asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Managers";
899     asyncResp->res.jsonValue["@odata.type"] =
900         "#ManagerCollection.ManagerCollection";
901     asyncResp->res.jsonValue["Name"] = "Manager Collection";
902     asyncResp->res.jsonValue["Members@odata.count"] = 1;
903     nlohmann::json::array_t members;
904     nlohmann::json& bmc = members.emplace_back();
905     bmc["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}",
906                                            BMCWEB_REDFISH_MANAGER_URI_NAME);
907     asyncResp->res.jsonValue["Members"] = std::move(members);
908 }
909 
requestRoutesManager(App & app)910 inline void requestRoutesManager(App& app)
911 {
912     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/")
913         .privileges(redfish::privileges::getManager)
914         .methods(boost::beast::http::verb::get)(
915             std::bind_front(handleManagerGet, std::ref(app)));
916 
917     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/")
918         .privileges(redfish::privileges::patchManager)
919         .methods(boost::beast::http::verb::patch)(
920             std::bind_front(handleManagerPatch, std::ref(app)));
921 
922     BMCWEB_ROUTE(app, "/redfish/v1/Managers/")
923         .privileges(redfish::privileges::getManagerCollection)
924         .methods(boost::beast::http::verb::get)(
925             std::bind_front(handleManagerCollectionGet, std::ref(app)));
926 }
927 
requestRoutesManagerResetAction(App & app)928 inline void requestRoutesManagerResetAction(App& app)
929 {
930     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/Actions/Manager.Reset/")
931         .privileges(redfish::privileges::postManager)
932         .methods(boost::beast::http::verb::post)(
933             std::bind_front(handleManagerResetAction, std::ref(app)));
934 
935     BMCWEB_ROUTE(app,
936                  "/redfish/v1/Managers/<str>/Actions/Manager.ResetToDefaults/")
937         .privileges(redfish::privileges::postManager)
938         .methods(boost::beast::http::verb::post)(
939             std::bind_front(handleManagerResetToDefaultsAction, std::ref(app)));
940 
941     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/ResetActionInfo/")
942         .privileges(redfish::privileges::getActionInfo)
943         .methods(boost::beast::http::verb::get)(
944             std::bind_front(handleManagerResetActionInfo, std::ref(app)));
945 }
946 
947 } // namespace redfish
948