xref: /openbmc/bmcweb/features/redfish/lib/systems.hpp (revision c5d03ff4737b1f4e4eb7e59f533d6c5a1faeeab5)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 #pragma once
17 
18 #include "redfish_util.hpp"
19 
20 #include <boost/container/flat_map.hpp>
21 #include <node.hpp>
22 #include <utils/fw_utils.hpp>
23 #include <utils/json_utils.hpp>
24 #include <variant>
25 
26 namespace redfish
27 {
28 
29 /**
30  * @brief Retrieves computer system properties over dbus
31  *
32  * @param[in] aResp Shared pointer for completing asynchronous calls
33  * @param[in] name  Computer system name from request
34  *
35  * @return None.
36  */
37 void getComputerSystem(std::shared_ptr<AsyncResp> aResp)
38 {
39     BMCWEB_LOG_DEBUG << "Get available system components.";
40     crow::connections::systemBus->async_method_call(
41         [aResp](
42             const boost::system::error_code ec,
43             const std::vector<std::pair<
44                 std::string,
45                 std::vector<std::pair<std::string, std::vector<std::string>>>>>
46                 &subtree) {
47             if (ec)
48             {
49                 BMCWEB_LOG_DEBUG << "DBUS response error";
50                 messages::internalError(aResp->res);
51                 return;
52             }
53             // Iterate over all retrieved ObjectPaths.
54             for (const std::pair<std::string,
55                                  std::vector<std::pair<
56                                      std::string, std::vector<std::string>>>>
57                      &object : subtree)
58             {
59                 const std::string &path = object.first;
60                 BMCWEB_LOG_DEBUG << "Got path: " << path;
61                 const std::vector<
62                     std::pair<std::string, std::vector<std::string>>>
63                     &connectionNames = object.second;
64                 if (connectionNames.size() < 1)
65                 {
66                     continue;
67                 }
68 
69                 // This is not system, so check if it's cpu, dimm, UUID or
70                 // BiosVer
71                 for (const auto &connection : connectionNames)
72                 {
73                     for (const auto &interfaceName : connection.second)
74                     {
75                         if (interfaceName ==
76                             "xyz.openbmc_project.Inventory.Item.Dimm")
77                         {
78                             BMCWEB_LOG_DEBUG
79                                 << "Found Dimm, now get its properties.";
80                             crow::connections::systemBus->async_method_call(
81                                 [aResp](const boost::system::error_code ec,
82                                         const std::vector<
83                                             std::pair<std::string, VariantType>>
84                                             &properties) {
85                                     if (ec)
86                                     {
87                                         BMCWEB_LOG_ERROR
88                                             << "DBUS response error " << ec;
89                                         messages::internalError(aResp->res);
90                                         return;
91                                     }
92                                     BMCWEB_LOG_DEBUG << "Got "
93                                                      << properties.size()
94                                                      << "Dimm properties.";
95                                     for (const std::pair<std::string,
96                                                          VariantType>
97                                              &property : properties)
98                                     {
99                                         if (property.first == "MemorySizeInKb")
100                                         {
101                                             const uint64_t *value =
102                                                 sdbusplus::message::variant_ns::
103                                                     get_if<uint64_t>(
104                                                         &property.second);
105                                             if (value != nullptr)
106                                             {
107                                                 aResp->res.jsonValue
108                                                     ["TotalSystemMemoryGi"
109                                                      "B"] +=
110                                                     *value / (1024 * 1024);
111                                                 aResp->res
112                                                     .jsonValue["MemorySummary"]
113                                                               ["Status"]
114                                                               ["State"] =
115                                                     "Enabled";
116                                             }
117                                         }
118                                     }
119                                 },
120                                 connection.first, path,
121                                 "org.freedesktop.DBus.Properties", "GetAll",
122                                 "xyz.openbmc_project.Inventory.Item.Dimm");
123                         }
124                         else if (interfaceName ==
125                                  "xyz.openbmc_project.Inventory.Item.Cpu")
126                         {
127                             BMCWEB_LOG_DEBUG
128                                 << "Found Cpu, now get its properties.";
129                             crow::connections::systemBus->async_method_call(
130                                 [aResp](const boost::system::error_code ec,
131                                         const std::vector<
132                                             std::pair<std::string, VariantType>>
133                                             &properties) {
134                                     if (ec)
135                                     {
136                                         BMCWEB_LOG_ERROR
137                                             << "DBUS response error " << ec;
138                                         messages::internalError(aResp->res);
139                                         return;
140                                     }
141                                     BMCWEB_LOG_DEBUG << "Got "
142                                                      << properties.size()
143                                                      << "Cpu properties.";
144                                     for (const auto &property : properties)
145                                     {
146                                         if (property.first == "ProcessorFamily")
147                                         {
148                                             const std::string *value =
149                                                 sdbusplus::message::variant_ns::
150                                                     get_if<std::string>(
151                                                         &property.second);
152                                             if (value != nullptr)
153                                             {
154                                                 nlohmann::json &procSummary =
155                                                     aResp->res.jsonValue
156                                                         ["ProcessorSumm"
157                                                          "ary"];
158                                                 nlohmann::json &procCount =
159                                                     procSummary["Count"];
160 
161                                                 procCount =
162                                                     procCount.get<int>() + 1;
163                                                 procSummary["Status"]["State"] =
164                                                     "Enabled";
165                                                 procSummary["Model"] = *value;
166                                             }
167                                         }
168                                     }
169                                 },
170                                 connection.first, path,
171                                 "org.freedesktop.DBus.Properties", "GetAll",
172                                 "xyz.openbmc_project.Inventory.Item.Cpu");
173                         }
174                         else if (interfaceName ==
175                                  "xyz.openbmc_project.Common.UUID")
176                         {
177                             BMCWEB_LOG_DEBUG
178                                 << "Found UUID, now get its properties.";
179                             crow::connections::systemBus->async_method_call(
180                                 [aResp](const boost::system::error_code ec,
181                                         const std::vector<
182                                             std::pair<std::string, VariantType>>
183                                             &properties) {
184                                     if (ec)
185                                     {
186                                         BMCWEB_LOG_DEBUG
187                                             << "DBUS response error " << ec;
188                                         messages::internalError(aResp->res);
189                                         return;
190                                     }
191                                     BMCWEB_LOG_DEBUG << "Got "
192                                                      << properties.size()
193                                                      << "UUID properties.";
194                                     for (const std::pair<std::string,
195                                                          VariantType>
196                                              &property : properties)
197                                     {
198                                         if (property.first == "UUID")
199                                         {
200                                             const std::string *value =
201                                                 sdbusplus::message::variant_ns::
202                                                     get_if<std::string>(
203                                                         &property.second);
204 
205                                             if (value != nullptr)
206                                             {
207                                                 std::string valueStr = *value;
208                                                 if (valueStr.size() == 32)
209                                                 {
210                                                     valueStr.insert(8, 1, '-');
211                                                     valueStr.insert(13, 1, '-');
212                                                     valueStr.insert(18, 1, '-');
213                                                     valueStr.insert(23, 1, '-');
214                                                 }
215                                                 BMCWEB_LOG_DEBUG << "UUID = "
216                                                                  << valueStr;
217                                                 aResp->res.jsonValue["UUID"] =
218                                                     valueStr;
219                                             }
220                                         }
221                                     }
222                                 },
223                                 connection.first, path,
224                                 "org.freedesktop.DBus.Properties", "GetAll",
225                                 "xyz.openbmc_project.Common.UUID");
226                         }
227                         else if (interfaceName ==
228                                  "xyz.openbmc_project.Inventory.Item.System")
229                         {
230                             crow::connections::systemBus->async_method_call(
231                                 [aResp](const boost::system::error_code ec,
232                                         const std::vector<
233                                             std::pair<std::string, VariantType>>
234                                             &propertiesList) {
235                                     if (ec)
236                                     {
237                                         BMCWEB_LOG_ERROR
238                                             << "DBUS response error: " << ec;
239                                         messages::internalError(aResp->res);
240                                         return;
241                                     }
242                                     BMCWEB_LOG_DEBUG << "Got "
243                                                      << propertiesList.size()
244                                                      << "properties for system";
245                                     for (const std::pair<std::string,
246                                                          VariantType>
247                                              &property : propertiesList)
248                                     {
249                                         const std::string &propertyName =
250                                             property.first;
251                                         if ((propertyName == "PartNumber") ||
252                                             (propertyName == "SerialNumber") ||
253                                             (propertyName == "Manufacturer") ||
254                                             (propertyName == "Model"))
255                                         {
256                                             const std::string *value =
257                                                 std::get_if<std::string>(
258                                                     &property.second);
259                                             if (value != nullptr)
260                                             {
261                                                 aResp->res
262                                                     .jsonValue[propertyName] =
263                                                     *value;
264                                             }
265                                         }
266                                     }
267                                     aResp->res.jsonValue["Name"] = "system";
268                                     aResp->res.jsonValue["Id"] =
269                                         aResp->res.jsonValue["SerialNumber"];
270                                     // Grab the bios version
271                                     fw_util::getActiveFwVersion(
272                                         aResp, fw_util::biosPurpose,
273                                         "BiosVersion");
274                                 },
275                                 connection.first, path,
276                                 "org.freedesktop.DBus.Properties", "GetAll",
277                                 "xyz.openbmc_project.Inventory.Decorator."
278                                 "Asset");
279                         }
280                     }
281                 }
282             }
283         },
284         "xyz.openbmc_project.ObjectMapper",
285         "/xyz/openbmc_project/object_mapper",
286         "xyz.openbmc_project.ObjectMapper", "GetSubTree",
287         "/xyz/openbmc_project/inventory", int32_t(0),
288         std::array<const char *, 5>{
289             "xyz.openbmc_project.Inventory.Decorator.Asset",
290             "xyz.openbmc_project.Inventory.Item.Cpu",
291             "xyz.openbmc_project.Inventory.Item.Dimm",
292             "xyz.openbmc_project.Inventory.Item.System",
293             "xyz.openbmc_project.Common.UUID",
294         });
295 }
296 
297 /**
298  * @brief Retrieves identify led group properties over dbus
299  *
300  * @param[in] aResp     Shared pointer for generating response message.
301  * @param[in] callback  Callback for process retrieved data.
302  *
303  * @return None.
304  */
305 template <typename CallbackFunc>
306 void getLedGroupIdentify(std::shared_ptr<AsyncResp> aResp,
307                          CallbackFunc &&callback)
308 {
309     BMCWEB_LOG_DEBUG << "Get led groups";
310     crow::connections::systemBus->async_method_call(
311         [aResp,
312          callback{std::move(callback)}](const boost::system::error_code &ec,
313                                         const ManagedObjectsType &resp) {
314             if (ec)
315             {
316                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
317                 messages::internalError(aResp->res);
318                 return;
319             }
320             BMCWEB_LOG_DEBUG << "Got " << resp.size() << "led group objects.";
321             for (const auto &objPath : resp)
322             {
323                 const std::string &path = objPath.first;
324                 if (path.rfind("enclosure_identify") != std::string::npos)
325                 {
326                     for (const auto &interface : objPath.second)
327                     {
328                         if (interface.first == "xyz.openbmc_project.Led.Group")
329                         {
330                             for (const auto &property : interface.second)
331                             {
332                                 if (property.first == "Asserted")
333                                 {
334                                     const bool *asserted =
335                                         std::get_if<bool>(&property.second);
336                                     if (nullptr != asserted)
337                                     {
338                                         callback(*asserted, aResp);
339                                     }
340                                     else
341                                     {
342                                         callback(false, aResp);
343                                     }
344                                 }
345                             }
346                         }
347                     }
348                 }
349             }
350         },
351         "xyz.openbmc_project.LED.GroupManager",
352         "/xyz/openbmc_project/led/groups", "org.freedesktop.DBus.ObjectManager",
353         "GetManagedObjects");
354 }
355 
356 template <typename CallbackFunc>
357 void getLedIdentify(std::shared_ptr<AsyncResp> aResp, CallbackFunc &&callback)
358 {
359     BMCWEB_LOG_DEBUG << "Get identify led properties";
360     crow::connections::systemBus->async_method_call(
361         [aResp,
362          callback{std::move(callback)}](const boost::system::error_code ec,
363                                         const PropertiesType &properties) {
364             if (ec)
365             {
366                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
367                 messages::internalError(aResp->res);
368                 return;
369             }
370             BMCWEB_LOG_DEBUG << "Got " << properties.size()
371                              << "led properties.";
372             std::string output;
373             for (const auto &property : properties)
374             {
375                 if (property.first == "State")
376                 {
377                     const std::string *s =
378                         std::get_if<std::string>(&property.second);
379                     if (nullptr != s)
380                     {
381                         BMCWEB_LOG_DEBUG << "Identify Led State: " << *s;
382                         const auto pos = s->rfind('.');
383                         if (pos != std::string::npos)
384                         {
385                             auto led = s->substr(pos + 1);
386                             for (const std::pair<const char *, const char *>
387                                      &p :
388                                  std::array<
389                                      std::pair<const char *, const char *>, 3>{
390                                      {{"On", "Lit"},
391                                       {"Blink", "Blinking"},
392                                       {"Off", "Off"}}})
393                             {
394                                 if (led == p.first)
395                                 {
396                                     output = p.second;
397                                 }
398                             }
399                         }
400                     }
401                 }
402             }
403             callback(output, aResp);
404         },
405         "xyz.openbmc_project.LED.Controller.identify",
406         "/xyz/openbmc_project/led/physical/identify",
407         "org.freedesktop.DBus.Properties", "GetAll",
408         "xyz.openbmc_project.Led.Physical");
409 }
410 /**
411  * @brief Retrieves host state properties over dbus
412  *
413  * @param[in] aResp     Shared pointer for completing asynchronous calls.
414  *
415  * @return None.
416  */
417 void getHostState(std::shared_ptr<AsyncResp> aResp)
418 {
419     BMCWEB_LOG_DEBUG << "Get host information.";
420     crow::connections::systemBus->async_method_call(
421         [aResp](const boost::system::error_code ec,
422                 const std::variant<std::string> &hostState) {
423             if (ec)
424             {
425                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
426                 messages::internalError(aResp->res);
427                 return;
428             }
429 
430             const std::string *s = std::get_if<std::string>(&hostState);
431             BMCWEB_LOG_DEBUG << "Host state: " << *s;
432             if (s != nullptr)
433             {
434                 // Verify Host State
435                 if (*s == "xyz.openbmc_project.State.Host.HostState.Running")
436                 {
437                     aResp->res.jsonValue["PowerState"] = "On";
438                     aResp->res.jsonValue["Status"]["State"] = "Enabled";
439                 }
440                 else
441                 {
442                     aResp->res.jsonValue["PowerState"] = "Off";
443                     aResp->res.jsonValue["Status"]["State"] = "Disabled";
444                 }
445             }
446         },
447         "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0",
448         "org.freedesktop.DBus.Properties", "Get",
449         "xyz.openbmc_project.State.Host", "CurrentHostState");
450 }
451 
452 /**
453  * @brief Traslates boot source DBUS property value to redfish.
454  *
455  * @param[in] dbusSource    The boot source in DBUS speak.
456  *
457  * @return Returns as a string, the boot source in Redfish terms. If translation
458  * cannot be done, returns an empty string.
459  */
460 static std::string dbusToRfBootSource(const std::string &dbusSource)
461 {
462     if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default")
463     {
464         return "None";
465     }
466     else if (dbusSource ==
467              "xyz.openbmc_project.Control.Boot.Source.Sources.Disk")
468     {
469         return "Hdd";
470     }
471     else if (dbusSource ==
472              "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia")
473     {
474         return "Cd";
475     }
476     else if (dbusSource ==
477              "xyz.openbmc_project.Control.Boot.Source.Sources.Network")
478     {
479         return "Pxe";
480     }
481     else if (dbusSource ==
482              "xyz.openbmc_project.Control.Boot.Source.Sources.Removable")
483     {
484         return "Usb";
485     }
486     else
487     {
488         return "";
489     }
490 }
491 
492 /**
493  * @brief Traslates boot mode DBUS property value to redfish.
494  *
495  * @param[in] dbusMode    The boot mode in DBUS speak.
496  *
497  * @return Returns as a string, the boot mode in Redfish terms. If translation
498  * cannot be done, returns an empty string.
499  */
500 static std::string dbusToRfBootMode(const std::string &dbusMode)
501 {
502     if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular")
503     {
504         return "None";
505     }
506     else if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe")
507     {
508         return "Diags";
509     }
510     else if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup")
511     {
512         return "BiosSetup";
513     }
514     else
515     {
516         return "";
517     }
518 }
519 
520 /**
521  * @brief Traslates boot source from Redfish to DBUS property value.
522  *
523  * @param[in] rfSource    The boot source in Redfish.
524  *
525  * @return Returns as a string, the boot source as expected by DBUS.
526  * If translation cannot be done, returns an empty string.
527  */
528 static std::string rfToDbusBootSource(const std::string &rfSource)
529 {
530     if (rfSource == "None")
531     {
532         return "xyz.openbmc_project.Control.Boot.Source.Sources.Default";
533     }
534     else if (rfSource == "Hdd")
535     {
536         return "xyz.openbmc_project.Control.Boot.Source.Sources.Disk";
537     }
538     else if (rfSource == "Cd")
539     {
540         return "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia";
541     }
542     else if (rfSource == "Pxe")
543     {
544         return "xyz.openbmc_project.Control.Boot.Source.Sources.Network";
545     }
546     else if (rfSource == "Usb")
547     {
548         return "xyz.openbmc_project.Control.Boot.Source.Sources.Removable";
549     }
550     else
551     {
552         return "";
553     }
554 }
555 
556 /**
557  * @brief Traslates boot mode from Redfish to DBUS property value.
558  *
559  * @param[in] rfMode    The boot mode in Redfish.
560  *
561  * @return Returns as a string, the boot mode as expected by DBUS.
562  * If translation cannot be done, returns an empty string.
563  */
564 static std::string rfToDbusBootMode(const std::string &rfMode)
565 {
566     if (rfMode == "None")
567     {
568         return "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
569     }
570     else if (rfMode == "Diags")
571     {
572         return "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe";
573     }
574     else if (rfMode == "BiosSetup")
575     {
576         return "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup";
577     }
578     else
579     {
580         return "";
581     }
582 }
583 
584 /**
585  * @brief Retrieves boot mode over DBUS and fills out the response
586  *
587  * @param[in] aResp         Shared pointer for generating response message.
588  * @param[in] bootDbusObj   The dbus object to query for boot properties.
589  *
590  * @return None.
591  */
592 static void getBootMode(std::shared_ptr<AsyncResp> aResp,
593                         std::string bootDbusObj)
594 {
595     crow::connections::systemBus->async_method_call(
596         [aResp](const boost::system::error_code ec,
597                 const std::variant<std::string> &bootMode) {
598             if (ec)
599             {
600                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
601                 messages::internalError(aResp->res);
602                 return;
603             }
604 
605             const std::string *bootModeStr =
606                 std::get_if<std::string>(&bootMode);
607 
608             if (!bootModeStr)
609             {
610                 messages::internalError(aResp->res);
611                 return;
612             }
613 
614             BMCWEB_LOG_DEBUG << "Boot mode: " << *bootModeStr;
615 
616             // TODO (Santosh): Do we need to support override mode?
617             aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = "Legacy";
618             aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget@Redfish."
619                                          "AllowableValues"] = {
620                 "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup"};
621 
622             if (*bootModeStr !=
623                 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular")
624             {
625                 auto rfMode = dbusToRfBootMode(*bootModeStr);
626                 if (!rfMode.empty())
627                 {
628                     aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] =
629                         rfMode;
630                 }
631             }
632 
633             // If the BootSourceOverrideTarget is still "None" at the end,
634             // reset the BootSourceOverrideEnabled to indicate that
635             // overrides are disabled
636             if (aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] ==
637                 "None")
638             {
639                 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] =
640                     "Disabled";
641             }
642         },
643         "xyz.openbmc_project.Settings", bootDbusObj,
644         "org.freedesktop.DBus.Properties", "Get",
645         "xyz.openbmc_project.Control.Boot.Mode", "BootMode");
646 }
647 
648 /**
649  * @brief Retrieves boot source over DBUS
650  *
651  * @param[in] aResp         Shared pointer for generating response message.
652  * @param[in] oneTimeEnable Boolean to indicate boot properties are one-time.
653  *
654  * @return None.
655  */
656 static void getBootSource(std::shared_ptr<AsyncResp> aResp, bool oneTimeEnabled)
657 {
658     std::string bootDbusObj =
659         oneTimeEnabled ? "/xyz/openbmc_project/control/host0/boot/one_time"
660                        : "/xyz/openbmc_project/control/host0/boot";
661 
662     BMCWEB_LOG_DEBUG << "Is one time: " << oneTimeEnabled;
663     aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] =
664         (oneTimeEnabled) ? "Once" : "Continuous";
665 
666     crow::connections::systemBus->async_method_call(
667         [aResp, bootDbusObj](const boost::system::error_code ec,
668                              const std::variant<std::string> &bootSource) {
669             if (ec)
670             {
671                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
672                 messages::internalError(aResp->res);
673                 return;
674             }
675 
676             const std::string *bootSourceStr =
677                 std::get_if<std::string>(&bootSource);
678 
679             if (!bootSourceStr)
680             {
681                 messages::internalError(aResp->res);
682                 return;
683             }
684             BMCWEB_LOG_DEBUG << "Boot source: " << *bootSourceStr;
685 
686             auto rfSource = dbusToRfBootSource(*bootSourceStr);
687             if (!rfSource.empty())
688             {
689                 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] =
690                     rfSource;
691             }
692         },
693         "xyz.openbmc_project.Settings", bootDbusObj,
694         "org.freedesktop.DBus.Properties", "Get",
695         "xyz.openbmc_project.Control.Boot.Source", "BootSource");
696     getBootMode(std::move(aResp), std::move(bootDbusObj));
697 }
698 
699 /**
700  * @brief Retrieves "One time" enabled setting over DBUS and calls function to
701  * get boot source and boot mode.
702  *
703  * @param[in] aResp     Shared pointer for generating response message.
704  *
705  * @return None.
706  */
707 static void getBootProperties(std::shared_ptr<AsyncResp> aResp)
708 {
709     BMCWEB_LOG_DEBUG << "Get boot information.";
710 
711     crow::connections::systemBus->async_method_call(
712         [aResp](const boost::system::error_code ec,
713                 const sdbusplus::message::variant<bool> &oneTime) {
714             if (ec)
715             {
716                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
717                 messages::internalError(aResp->res);
718                 return;
719             }
720 
721             const bool *oneTimePtr = std::get_if<bool>(&oneTime);
722 
723             if (!oneTimePtr)
724             {
725                 messages::internalError(aResp->res);
726                 return;
727             }
728             getBootSource(aResp, *oneTimePtr);
729         },
730         "xyz.openbmc_project.Settings",
731         "/xyz/openbmc_project/control/host0/boot/one_time",
732         "org.freedesktop.DBus.Properties", "Get",
733         "xyz.openbmc_project.Object.Enable", "Enabled");
734 }
735 
736 /**
737  * @brief Sets boot properties into DBUS object(s).
738  *
739  * @param[in] aResp           Shared pointer for generating response message.
740  * @param[in] oneTimeEnabled  Is "one-time" setting already enabled.
741  * @param[in] bootSource      The boot source to set.
742  * @param[in] bootEnable      The source override "enable" to set.
743  *
744  * @return None.
745  */
746 static void setBootModeOrSource(std::shared_ptr<AsyncResp> aResp,
747                                 bool oneTimeEnabled,
748                                 std::optional<std::string> bootSource,
749                                 std::optional<std::string> bootEnable)
750 {
751     if (bootEnable && (bootEnable != "Once") && (bootEnable != "Continuous") &&
752         (bootEnable != "Disabled"))
753     {
754         BMCWEB_LOG_DEBUG << "Unsupported value for BootSourceOverrideEnabled: "
755                          << *bootEnable;
756         messages::propertyValueNotInList(aResp->res, *bootEnable,
757                                          "BootSourceOverrideEnabled");
758         return;
759     }
760 
761     bool oneTimeSetting = oneTimeEnabled;
762     // Validate incoming parameters
763     if (bootEnable)
764     {
765         if (*bootEnable == "Once")
766         {
767             oneTimeSetting = true;
768         }
769         else if (*bootEnable == "Continuous")
770         {
771             oneTimeSetting = false;
772         }
773         else if (*bootEnable == "Disabled")
774         {
775             oneTimeSetting = false;
776         }
777         else
778         {
779 
780             BMCWEB_LOG_DEBUG << "Unsupported value for "
781                                 "BootSourceOverrideEnabled: "
782                              << *bootEnable;
783             messages::propertyValueNotInList(aResp->res, *bootEnable,
784                                              "BootSourceOverrideEnabled");
785             return;
786         }
787     }
788     std::string bootSourceStr;
789     std::string bootModeStr;
790     if (bootSource)
791     {
792         bootSourceStr = rfToDbusBootSource(*bootSource);
793         bootModeStr = rfToDbusBootMode(*bootSource);
794 
795         BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr;
796         BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr;
797 
798         if (bootSourceStr.empty() && bootModeStr.empty())
799         {
800             BMCWEB_LOG_DEBUG << "Invalid property value for "
801                                 "BootSourceOverrideTarget: "
802                              << *bootSource;
803             messages::propertyValueNotInList(aResp->res, *bootSource,
804                                              "BootSourceTargetOverride");
805             return;
806         }
807     }
808     const char *bootObj =
809         oneTimeSetting ? "/xyz/openbmc_project/control/host0/boot/one_time"
810                        : "/xyz/openbmc_project/control/host0/boot";
811     // Figure out what properties to set
812     if (bootEnable && (*bootEnable == "Disabled"))
813     {
814         BMCWEB_LOG_DEBUG << "Boot source override will be disabled";
815         // Request to only turn OFF/ON enabled, if turning enabled OFF, need
816         // to reset the source and mode too. If turning it ON, we only need
817         // to set the enabled property
818         bootSourceStr =
819             "xyz.openbmc_project.Control.Boot.Source.Sources.Default";
820         bootModeStr = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
821     }
822     else if (bootSource)
823     {
824         // Source target specified
825         BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource;
826         // Figure out which DBUS interface and property to use
827         bootSourceStr = rfToDbusBootSource(*bootSource);
828         bootModeStr = rfToDbusBootMode(*bootSource);
829 
830         BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr;
831         BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr;
832 
833         if (bootSourceStr.empty() && bootModeStr.empty())
834         {
835             BMCWEB_LOG_DEBUG << "Invalid property value for "
836                                 "BootSourceOverrideTarget: "
837                              << *bootSource;
838             messages::propertyValueNotInList(aResp->res, *bootSource,
839                                              "BootSourceTargetOverride");
840             return;
841         }
842 
843         if (!bootSourceStr.empty())
844         {
845             // If setting to anything other than default, also reset boot
846             // mode property
847             if (bootSourceStr !=
848                 "xyz.openbmc_project.Control.Boot.Source.Sources.Default")
849             {
850                 bootModeStr =
851                     "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular";
852             }
853         }
854         else // if (!bootModeStr.empty())
855         {
856             // If setting to anything other than default, also reset boot
857             // source property
858             if (bootModeStr !=
859                 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular")
860             {
861                 bootSourceStr =
862                     "xyz.openbmc_project.Control.Boot.Source.Sources.Default";
863             }
864         }
865     }
866     if (!bootSourceStr.empty())
867     {
868         crow::connections::systemBus->async_method_call(
869             [aResp](const boost::system::error_code ec) {
870                 if (ec)
871                 {
872                     BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
873                     messages::internalError(aResp->res);
874                     return;
875                 }
876                 BMCWEB_LOG_DEBUG << "Boot source update done.";
877             },
878             "xyz.openbmc_project.Settings", bootObj,
879             "org.freedesktop.DBus.Properties", "Set",
880             "xyz.openbmc_project.Control.Boot.Source", "BootSource",
881             std::variant<std::string>(bootSourceStr));
882     }
883     if (!bootModeStr.empty())
884     {
885         crow::connections::systemBus->async_method_call(
886             [aResp](const boost::system::error_code ec) {
887                 if (ec)
888                 {
889                     BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
890                     messages::internalError(aResp->res);
891                     return;
892                 }
893                 BMCWEB_LOG_DEBUG << "Boot mode update done.";
894             },
895             "xyz.openbmc_project.Settings", bootObj,
896             "org.freedesktop.DBus.Properties", "Set",
897             "xyz.openbmc_project.Control.Boot.Mode", "BootMode",
898             std::variant<std::string>(bootModeStr));
899     }
900     crow::connections::systemBus->async_method_call(
901         [aResp{std::move(aResp)}](const boost::system::error_code ec) {
902             if (ec)
903             {
904                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
905                 messages::internalError(aResp->res);
906                 return;
907             }
908             BMCWEB_LOG_DEBUG << "Boot enable update done.";
909         },
910         "xyz.openbmc_project.Settings",
911         "/xyz/openbmc_project/control/host0/boot/one_time",
912         "org.freedesktop.DBus.Properties", "Set",
913         "xyz.openbmc_project.Object.Enable", "Enabled",
914         std::variant<bool>(oneTimeSetting));
915 }
916 
917 /**
918  * @brief Retrieves "One time" enabled setting over DBUS and calls function to
919  * set boot source/boot mode properties.
920  *
921  * @param[in] aResp      Shared pointer for generating response message.
922  * @param[in] bootSource The boot source from incoming RF request.
923  * @param[in] bootEnable The boot override enable from incoming RF request.
924  *
925  * @return None.
926  */
927 static void setBootProperties(std::shared_ptr<AsyncResp> aResp,
928                               std::optional<std::string> bootSource,
929                               std::optional<std::string> bootEnable)
930 {
931     BMCWEB_LOG_DEBUG << "Set boot information.";
932 
933     crow::connections::systemBus->async_method_call(
934         [aResp{std::move(aResp)}, bootSource{std::move(bootSource)},
935          bootEnable{std::move(bootEnable)}](
936             const boost::system::error_code ec,
937             const sdbusplus::message::variant<bool> &oneTime) {
938             if (ec)
939             {
940                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
941                 messages::internalError(aResp->res);
942                 return;
943             }
944 
945             const bool *oneTimePtr = std::get_if<bool>(&oneTime);
946 
947             if (!oneTimePtr)
948             {
949                 messages::internalError(aResp->res);
950                 return;
951             }
952 
953             BMCWEB_LOG_DEBUG << "Got one time: " << *oneTimePtr;
954 
955             setBootModeOrSource(aResp, *oneTimePtr, std::move(bootSource),
956                                 std::move(bootEnable));
957         },
958         "xyz.openbmc_project.Settings",
959         "/xyz/openbmc_project/control/host0/boot/one_time",
960         "org.freedesktop.DBus.Properties", "Get",
961         "xyz.openbmc_project.Object.Enable", "Enabled");
962 }
963 
964 /**
965  * SystemsCollection derived class for delivering ComputerSystems Collection
966  * Schema
967  */
968 class SystemsCollection : public Node
969 {
970   public:
971     SystemsCollection(CrowApp &app) : Node(app, "/redfish/v1/Systems/")
972     {
973         entityPrivileges = {
974             {boost::beast::http::verb::get, {{"Login"}}},
975             {boost::beast::http::verb::head, {{"Login"}}},
976             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
977             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
978             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
979             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
980     }
981 
982   private:
983     void doGet(crow::Response &res, const crow::Request &req,
984                const std::vector<std::string> &params) override
985     {
986         res.jsonValue["@odata.type"] =
987             "#ComputerSystemCollection.ComputerSystemCollection";
988         res.jsonValue["@odata.id"] = "/redfish/v1/Systems";
989         res.jsonValue["@odata.context"] =
990             "/redfish/v1/"
991             "$metadata#ComputerSystemCollection.ComputerSystemCollection";
992         res.jsonValue["Name"] = "Computer System Collection";
993         res.jsonValue["Members"] = {
994             {{"@odata.id", "/redfish/v1/Systems/system"}}};
995         res.jsonValue["Members@odata.count"] = 1;
996         res.end();
997     }
998 };
999 
1000 /**
1001  * SystemActionsReset class supports handle POST method for Reset action.
1002  * The class retrieves and sends data directly to D-Bus.
1003  */
1004 class SystemActionsReset : public Node
1005 {
1006   public:
1007     SystemActionsReset(CrowApp &app) :
1008         Node(app, "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/")
1009     {
1010         entityPrivileges = {
1011             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1012     }
1013 
1014   private:
1015     /**
1016      * Function handles POST method request.
1017      * Analyzes POST body message before sends Reset request data to D-Bus.
1018      */
1019     void doPost(crow::Response &res, const crow::Request &req,
1020                 const std::vector<std::string> &params) override
1021     {
1022         auto asyncResp = std::make_shared<AsyncResp>(res);
1023 
1024         std::string resetType;
1025         if (!json_util::readJson(req, res, "ResetType", resetType))
1026         {
1027             return;
1028         }
1029 
1030         // Get the command and host vs. chassis
1031         std::string command;
1032         bool hostCommand;
1033         if (resetType == "On")
1034         {
1035             command = "xyz.openbmc_project.State.Host.Transition.On";
1036             hostCommand = true;
1037         }
1038         else if (resetType == "ForceOff")
1039         {
1040             command = "xyz.openbmc_project.State.Chassis.Transition.Off";
1041             hostCommand = false;
1042         }
1043         else if (resetType == "ForceOn")
1044         {
1045             command = "xyz.openbmc_project.State.Host.Transition.On";
1046             hostCommand = true;
1047         }
1048         else if (resetType == "ForceRestart")
1049         {
1050             command = "xyz.openbmc_project.State.Chassis.Transition.Reset";
1051             hostCommand = false;
1052         }
1053         else if (resetType == "GracefulShutdown")
1054         {
1055             command = "xyz.openbmc_project.State.Host.Transition.Off";
1056             hostCommand = true;
1057         }
1058         else if (resetType == "GracefulRestart")
1059         {
1060             command = "xyz.openbmc_project.State.Host.Transition.Reboot";
1061             hostCommand = true;
1062         }
1063         else if (resetType == "PowerCycle")
1064         {
1065             command = "xyz.openbmc_project.State.Chassis.Transition.PowerCycle";
1066             hostCommand = false;
1067         }
1068         else
1069         {
1070             messages::actionParameterUnknown(res, "Reset", resetType);
1071             return;
1072         }
1073 
1074         if (hostCommand)
1075         {
1076             crow::connections::systemBus->async_method_call(
1077                 [asyncResp, resetType](const boost::system::error_code ec) {
1078                     if (ec)
1079                     {
1080                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1081                         if (ec.value() == boost::asio::error::invalid_argument)
1082                         {
1083                             messages::actionParameterNotSupported(
1084                                 asyncResp->res, resetType, "Reset");
1085                         }
1086                         else
1087                         {
1088                             messages::internalError(asyncResp->res);
1089                         }
1090                         return;
1091                     }
1092                     messages::success(asyncResp->res);
1093                 },
1094                 "xyz.openbmc_project.State.Host",
1095                 "/xyz/openbmc_project/state/host0",
1096                 "org.freedesktop.DBus.Properties", "Set",
1097                 "xyz.openbmc_project.State.Host", "RequestedHostTransition",
1098                 std::variant<std::string>{command});
1099         }
1100         else
1101         {
1102             crow::connections::systemBus->async_method_call(
1103                 [asyncResp, resetType](const boost::system::error_code ec) {
1104                     if (ec)
1105                     {
1106                         BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1107                         if (ec.value() == boost::asio::error::invalid_argument)
1108                         {
1109                             messages::actionParameterNotSupported(
1110                                 asyncResp->res, resetType, "Reset");
1111                         }
1112                         else
1113                         {
1114                             messages::internalError(asyncResp->res);
1115                         }
1116                         return;
1117                     }
1118                     messages::success(asyncResp->res);
1119                 },
1120                 "xyz.openbmc_project.State.Chassis",
1121                 "/xyz/openbmc_project/state/chassis0",
1122                 "org.freedesktop.DBus.Properties", "Set",
1123                 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition",
1124                 std::variant<std::string>{command});
1125         }
1126     }
1127 };
1128 
1129 /**
1130  * Systems derived class for delivering Computer Systems Schema.
1131  */
1132 class Systems : public Node
1133 {
1134   public:
1135     /*
1136      * Default Constructor
1137      */
1138     Systems(CrowApp &app) : Node(app, "/redfish/v1/Systems/system/")
1139     {
1140         entityPrivileges = {
1141             {boost::beast::http::verb::get, {{"Login"}}},
1142             {boost::beast::http::verb::head, {{"Login"}}},
1143             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1144             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1145             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1146             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1147     }
1148 
1149   private:
1150     /**
1151      * Functions triggers appropriate requests on DBus
1152      */
1153     void doGet(crow::Response &res, const crow::Request &req,
1154                const std::vector<std::string> &params) override
1155     {
1156         res.jsonValue["@odata.type"] = "#ComputerSystem.v1_6_0.ComputerSystem";
1157         res.jsonValue["@odata.context"] =
1158             "/redfish/v1/$metadata#ComputerSystem.ComputerSystem";
1159         res.jsonValue["Name"] = "Computer System";
1160         res.jsonValue["Id"] = "system";
1161         res.jsonValue["SystemType"] = "Physical";
1162         res.jsonValue["Description"] = "Computer System";
1163         res.jsonValue["ProcessorSummary"]["Count"] = 0;
1164         res.jsonValue["ProcessorSummary"]["Status"]["State"] = "Disabled";
1165         res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = int(0);
1166         res.jsonValue["MemorySummary"]["Status"]["State"] = "Disabled";
1167         res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system";
1168 
1169         res.jsonValue["Processors"] = {
1170             {"@odata.id", "/redfish/v1/Systems/system/Processors"}};
1171         res.jsonValue["Memory"] = {
1172             {"@odata.id", "/redfish/v1/Systems/system/Memory"}};
1173 
1174         // TODO Need to support ForceRestart.
1175         res.jsonValue["Actions"]["#ComputerSystem.Reset"] = {
1176             {"target",
1177              "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"},
1178             {"ResetType@Redfish.AllowableValues",
1179              {"On", "ForceOff", "ForceOn", "ForceRestart", "GracefulRestart",
1180               "GracefulShutdown", "PowerCycle"}}};
1181 
1182         res.jsonValue["LogServices"] = {
1183             {"@odata.id", "/redfish/v1/Systems/system/LogServices"}};
1184 
1185         res.jsonValue["Links"]["ManagedBy"] = {
1186             {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
1187 
1188         res.jsonValue["Status"] = {
1189             {"Health", "OK"},
1190             {"State", "Enabled"},
1191         };
1192         auto asyncResp = std::make_shared<AsyncResp>(res);
1193 
1194         crow::connections::systemBus->async_method_call(
1195             [asyncResp](const boost::system::error_code ec,
1196                         const VariantType &biosId) {
1197                 if (ec)
1198                 {
1199                     BMCWEB_LOG_ERROR << ec;
1200                     messages::internalError(asyncResp->res);
1201                     return;
1202                 }
1203                 const std::string *strBiosId =
1204                     std::get_if<std::string>(&biosId);
1205                 if (strBiosId != nullptr)
1206                 {
1207                     BMCWEB_LOG_DEBUG << "bios ver. = " << strBiosId;
1208                     asyncResp->res.jsonValue["BiosVersion"] = *strBiosId;
1209                 }
1210             },
1211             "xyz.openbmc_project.Settings", "/xyz/openbmc_project/bios",
1212             "org.freedesktop.DBus.Properties", "Get",
1213             "xyz.openbmc_project.Inventory.Item.Bios", "BiosId");
1214 
1215         getMainChassisId(asyncResp, [](const std::string &chassisId,
1216                                        std::shared_ptr<AsyncResp> aRsp) {
1217             aRsp->res.jsonValue["Links"]["Chassis"] = {
1218                 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}};
1219         });
1220         getLedGroupIdentify(
1221             asyncResp,
1222             [](const bool &asserted, const std::shared_ptr<AsyncResp> aRsp) {
1223                 if (asserted)
1224                 {
1225                     // If led group is asserted, then another call is needed to
1226                     // get led status
1227                     getLedIdentify(
1228                         aRsp, [](const std::string &ledStatus,
1229                                  const std::shared_ptr<AsyncResp> aRsp) {
1230                             if (!ledStatus.empty())
1231                             {
1232                                 aRsp->res.jsonValue["IndicatorLED"] = ledStatus;
1233                             }
1234                         });
1235                 }
1236                 else
1237                 {
1238                     aRsp->res.jsonValue["IndicatorLED"] = "Off";
1239                 }
1240             });
1241         getComputerSystem(asyncResp);
1242         getHostState(asyncResp);
1243         getBootProperties(asyncResp);
1244     }
1245 
1246     void doPatch(crow::Response &res, const crow::Request &req,
1247                  const std::vector<std::string> &params) override
1248     {
1249         std::optional<std::string> indicatorLed;
1250         std::optional<nlohmann::json> bootProps;
1251         if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed, "Boot",
1252                                  bootProps))
1253         {
1254             return;
1255         }
1256 
1257         auto asyncResp = std::make_shared<AsyncResp>(res);
1258         asyncResp->res.result(boost::beast::http::status::no_content);
1259 
1260         if (bootProps)
1261         {
1262             std::optional<std::string> bootSource;
1263             std::optional<std::string> bootEnable;
1264 
1265             if (!json_util::readJson(*bootProps, asyncResp->res,
1266                                      "BootSourceOverrideTarget", bootSource,
1267                                      "BootSourceOverrideEnabled", bootEnable))
1268             {
1269                 return;
1270             }
1271             setBootProperties(asyncResp, std::move(bootSource),
1272                               std::move(bootEnable));
1273         }
1274         if (indicatorLed)
1275         {
1276             std::string dbusLedState;
1277             if (*indicatorLed == "Lit")
1278             {
1279                 dbusLedState = "xyz.openbmc_project.Led.Physical.Action.On";
1280             }
1281             else if (*indicatorLed == "Blinking")
1282             {
1283                 dbusLedState = "xyz.openbmc_project.Led.Physical.Action.Blink";
1284             }
1285             else if (*indicatorLed == "Off")
1286             {
1287                 dbusLedState = "xyz.openbmc_project.Led.Physical.Action.Off";
1288             }
1289             else
1290             {
1291                 messages::propertyValueNotInList(res, *indicatorLed,
1292                                                  "IndicatorLED");
1293                 return;
1294             }
1295 
1296             // Update led group
1297             BMCWEB_LOG_DEBUG << "Update led group.";
1298             crow::connections::systemBus->async_method_call(
1299                 [asyncResp](const boost::system::error_code ec) {
1300                     if (ec)
1301                     {
1302                         BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1303                         messages::internalError(asyncResp->res);
1304                         return;
1305                     }
1306                     BMCWEB_LOG_DEBUG << "Led group update done.";
1307                 },
1308                 "xyz.openbmc_project.LED.GroupManager",
1309                 "/xyz/openbmc_project/led/groups/enclosure_identify",
1310                 "org.freedesktop.DBus.Properties", "Set",
1311                 "xyz.openbmc_project.Led.Group", "Asserted",
1312                 std::variant<bool>(
1313                     (dbusLedState ==
1314                              "xyz.openbmc_project.Led.Physical.Action.Off"
1315                          ? false
1316                          : true)));
1317             // Update identify led status
1318             BMCWEB_LOG_DEBUG << "Update led SoftwareInventoryCollection.";
1319             crow::connections::systemBus->async_method_call(
1320                 [asyncResp{std::move(asyncResp)},
1321                  indicatorLed{std::move(*indicatorLed)}](
1322                     const boost::system::error_code ec) {
1323                     if (ec)
1324                     {
1325                         BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1326                         messages::internalError(asyncResp->res);
1327                         return;
1328                     }
1329                     BMCWEB_LOG_DEBUG << "Led state update done.";
1330                 },
1331                 "xyz.openbmc_project.LED.Controller.identify",
1332                 "/xyz/openbmc_project/led/physical/identify",
1333                 "org.freedesktop.DBus.Properties", "Set",
1334                 "xyz.openbmc_project.Led.Physical", "State",
1335                 std::variant<std::string>(dbusLedState));
1336         }
1337     }
1338 };
1339 } // namespace redfish
1340