xref: /openbmc/bmcweb/features/redfish/include/redfish_aggregator.hpp (revision 7fb33566cfccc26694e95b2c3601eb477359c54a)
1 #pragma once
2 
3 #include <http_client.hpp>
4 
5 namespace redfish
6 {
7 
8 class RedfishAggregator
9 {
10   private:
11     RedfishAggregator()
12     {
13         getSatelliteConfigs(constructorCallback);
14     }
15 
16     // Dummy callback used by the Constructor so that it can report the number
17     // of satellite configs when the class is first created
18     static void constructorCallback(
19         const std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
20     {
21         BMCWEB_LOG_DEBUG << "There were "
22                          << std::to_string(satelliteInfo.size())
23                          << " satellite configs found at startup";
24     }
25 
26     // Polls D-Bus to get all available satellite config information
27     // Expects a handler which interacts with the returned configs
28     static void getSatelliteConfigs(
29         const std::function<void(
30             const std::unordered_map<std::string, boost::urls::url>&)>& handler)
31     {
32         BMCWEB_LOG_DEBUG << "Gathering satellite configs";
33         crow::connections::systemBus->async_method_call(
34             [handler](const boost::system::error_code ec,
35                       const dbus::utility::ManagedObjectType& objects) {
36                 if (ec)
37                 {
38                     BMCWEB_LOG_ERROR << "DBUS response error " << ec.value()
39                                      << ", " << ec.message();
40                     return;
41                 }
42 
43                 // Maps a chosen alias representing a satellite BMC to a url
44                 // containing the information required to create a http
45                 // connection to the satellite
46                 std::unordered_map<std::string, boost::urls::url> satelliteInfo;
47 
48                 findSatelliteConfigs(objects, satelliteInfo);
49 
50                 if (!satelliteInfo.empty())
51                 {
52                     BMCWEB_LOG_DEBUG << "Redfish Aggregation enabled with "
53                                      << std::to_string(satelliteInfo.size())
54                                      << " satellite BMCs";
55                 }
56                 else
57                 {
58                     BMCWEB_LOG_DEBUG
59                         << "No satellite BMCs detected.  Redfish Aggregation not enabled";
60                 }
61                 handler(satelliteInfo);
62             },
63             "xyz.openbmc_project.EntityManager", "/",
64             "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
65     }
66 
67     // Search D-Bus objects for satellite config objects and add their
68     // information if valid
69     static void findSatelliteConfigs(
70         const dbus::utility::ManagedObjectType& objects,
71         std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
72     {
73         for (const auto& objectPath : objects)
74         {
75             for (const auto& interface : objectPath.second)
76             {
77                 if (interface.first ==
78                     "xyz.openbmc_project.Configuration.SatelliteController")
79                 {
80                     BMCWEB_LOG_DEBUG << "Found Satellite Controller at "
81                                      << objectPath.first.str;
82 
83                     addSatelliteConfig(interface.second, satelliteInfo);
84                 }
85             }
86         }
87     }
88 
89     // Parse the properties of a satellite config object and add the
90     // configuration if the properties are valid
91     static void addSatelliteConfig(
92         const dbus::utility::DBusPropertiesMap& properties,
93         std::unordered_map<std::string, boost::urls::url>& satelliteInfo)
94     {
95         boost::urls::url url;
96         std::string name;
97 
98         for (const auto& prop : properties)
99         {
100             if (prop.first == "Name")
101             {
102                 const std::string* propVal =
103                     std::get_if<std::string>(&prop.second);
104                 if (propVal == nullptr)
105                 {
106                     BMCWEB_LOG_ERROR << "Invalid Name value";
107                     return;
108                 }
109 
110                 // The IDs will become <Name>_<ID> so the name should not
111                 // contain a '_'
112                 if (propVal->find('_') != std::string::npos)
113                 {
114                     BMCWEB_LOG_ERROR << "Name cannot contain a \"_\"";
115                     return;
116                 }
117                 name = *propVal;
118             }
119 
120             else if (prop.first == "Hostname")
121             {
122                 const std::string* propVal =
123                     std::get_if<std::string>(&prop.second);
124                 if (propVal == nullptr)
125                 {
126                     BMCWEB_LOG_ERROR << "Invalid Hostname value";
127                     return;
128                 }
129                 url.set_host(*propVal);
130             }
131 
132             else if (prop.first == "Port")
133             {
134                 const uint64_t* propVal = std::get_if<uint64_t>(&prop.second);
135                 if (propVal == nullptr)
136                 {
137                     BMCWEB_LOG_ERROR << "Invalid Port value";
138                     return;
139                 }
140 
141                 if (*propVal > std::numeric_limits<uint16_t>::max())
142                 {
143                     BMCWEB_LOG_ERROR << "Port value out of range";
144                     return;
145                 }
146                 url.set_port(static_cast<uint16_t>(*propVal));
147             }
148 
149             else if (prop.first == "AuthType")
150             {
151                 const std::string* propVal =
152                     std::get_if<std::string>(&prop.second);
153                 if (propVal == nullptr)
154                 {
155                     BMCWEB_LOG_ERROR << "Invalid AuthType value";
156                     return;
157                 }
158 
159                 // For now assume authentication not required to communicate
160                 // with the satellite BMC
161                 if (*propVal != "None")
162                 {
163                     BMCWEB_LOG_ERROR
164                         << "Unsupported AuthType value: " << *propVal
165                         << ", only \"none\" is supported";
166                     return;
167                 }
168                 url.set_scheme("http");
169             }
170         } // Finished reading properties
171 
172         // Make sure all required config information was made available
173         if (name.empty())
174         {
175             BMCWEB_LOG_ERROR << "Satellite config missing Name";
176             return;
177         }
178 
179         if (url.host().empty())
180         {
181             BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Host";
182             return;
183         }
184 
185         if (!url.has_port())
186         {
187             BMCWEB_LOG_ERROR << "Satellite config " << name << " missing Port";
188             return;
189         }
190 
191         if (!url.has_scheme())
192         {
193             BMCWEB_LOG_ERROR << "Satellite config " << name
194                              << " missing AuthType";
195             return;
196         }
197 
198         std::string resultString;
199         auto result = satelliteInfo.insert_or_assign(name, std::move(url));
200         if (result.second)
201         {
202             resultString = "Added new satellite config ";
203         }
204         else
205         {
206             resultString = "Updated existing satellite config ";
207         }
208 
209         BMCWEB_LOG_DEBUG << resultString << name << " at "
210                          << result.first->second.scheme() << "://"
211                          << result.first->second.encoded_host_and_port();
212     }
213 
214   public:
215     RedfishAggregator(const RedfishAggregator&) = delete;
216     RedfishAggregator& operator=(const RedfishAggregator&) = delete;
217     RedfishAggregator(RedfishAggregator&&) = delete;
218     RedfishAggregator& operator=(RedfishAggregator&&) = delete;
219     ~RedfishAggregator() = default;
220 
221     static RedfishAggregator& getInstance()
222     {
223         static RedfishAggregator handler;
224         return handler;
225     }
226 };
227 
228 } // namespace redfish
229