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