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