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