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