Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 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 "node.hpp" |
| 19 | #include <boost/container/flat_map.hpp> |
| 20 | |
| 21 | namespace redfish { |
| 22 | |
| 23 | /** |
| 24 | * DBus types primitives for several generic DBus interfaces |
| 25 | * TODO(Pawel) consider move this to separate file into boost::dbus |
| 26 | */ |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 27 | using PropertiesMapType = boost::container::flat_map< |
| 28 | std::string, |
| 29 | sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t, |
| 30 | int32_t, uint32_t, int64_t, uint64_t, double>>; |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 31 | |
| 32 | using GetManagedObjectsType = boost::container::flat_map< |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 33 | sdbusplus::message::object_path, |
| 34 | boost::container::flat_map< |
| 35 | std::string, |
| 36 | boost::container::flat_map< |
| 37 | std::string, sdbusplus::message::variant< |
| 38 | std::string, bool, uint8_t, int16_t, uint16_t, |
| 39 | int32_t, uint32_t, int64_t, uint64_t, double>>>>; |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 40 | |
| 41 | /** |
| 42 | * Structure for keeping IPv4 data required by Redfish |
| 43 | * TODO(Pawel) consider change everything to ptr, or to non-ptr values. |
| 44 | */ |
| 45 | struct IPv4AddressData { |
| 46 | const std::string *address; |
| 47 | const std::string *domain; |
| 48 | const std::string *gateway; |
| 49 | std::string netmask; |
| 50 | std::string origin; |
| 51 | bool global; |
| 52 | }; |
| 53 | |
| 54 | /** |
| 55 | * Structure for keeping basic single Ethernet Interface information |
| 56 | * available from DBus |
| 57 | */ |
| 58 | struct EthernetInterfaceData { |
| 59 | const unsigned int *speed; |
| 60 | const bool *auto_neg; |
| 61 | const std::string *hostname; |
| 62 | const std::string *default_gateway; |
| 63 | const std::string *mac_address; |
Kowalski, Kamil | c7070ac | 2018-03-27 15:01:02 +0200 | [diff] [blame] | 64 | const unsigned int *vlan_id; |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 65 | }; |
| 66 | |
| 67 | /** |
| 68 | * OnDemandEthernetProvider |
Gunnar Mills | 274fad5 | 2018-06-13 15:45:36 -0500 | [diff] [blame] | 69 | * Ethernet provider class that retrieves data directly from dbus, before |
| 70 | * setting it into JSON output. This does not cache any data. |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 71 | * |
| 72 | * TODO(Pawel) |
| 73 | * This perhaps shall be different file, which has to be chosen on compile time |
| 74 | * depending on OEM needs |
| 75 | */ |
| 76 | class OnDemandEthernetProvider { |
| 77 | private: |
| 78 | // Consts that may have influence on EthernetProvider performance/memory usage |
| 79 | const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10; |
| 80 | |
| 81 | // Helper function that allows to extract GetAllPropertiesType from |
| 82 | // GetManagedObjectsType, based on object path, and interface name |
| 83 | const PropertiesMapType *extractInterfaceProperties( |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 84 | const sdbusplus::message::object_path &objpath, |
| 85 | const std::string &interface, const GetManagedObjectsType &dbus_data) { |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 86 | const auto &dbus_obj = dbus_data.find(objpath); |
| 87 | if (dbus_obj != dbus_data.end()) { |
| 88 | const auto &iface = dbus_obj->second.find(interface); |
| 89 | if (iface != dbus_obj->second.end()) { |
| 90 | return &iface->second; |
| 91 | } |
| 92 | } |
| 93 | return nullptr; |
| 94 | } |
| 95 | |
| 96 | // Helper Wrapper that does inline object_path conversion from string |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 97 | // into sdbusplus::message::object_path type |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 98 | inline const PropertiesMapType *extractInterfaceProperties( |
| 99 | const std::string &objpath, const std::string &interface, |
| 100 | const GetManagedObjectsType &dbus_data) { |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 101 | const auto &dbus_obj = sdbusplus::message::object_path{objpath}; |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 102 | return extractInterfaceProperties(dbus_obj, interface, dbus_data); |
| 103 | } |
| 104 | |
| 105 | // Helper function that allows to get pointer to the property from |
| 106 | // GetAllPropertiesType native, or extracted by GetAllPropertiesType |
| 107 | template <typename T> |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 108 | inline T const *const extractProperty(const PropertiesMapType &properties, |
| 109 | const std::string &name) { |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 110 | const auto &property = properties.find(name); |
| 111 | if (property != properties.end()) { |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 112 | return mapbox::get_ptr<const T>(property->second); |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 113 | } |
| 114 | return nullptr; |
| 115 | } |
| 116 | // TODO(Pawel) Consider to move the above functions to dbus |
| 117 | // generic_interfaces.hpp |
| 118 | |
| 119 | // Helper function that extracts data from several dbus objects and several |
| 120 | // interfaces required by single ethernet interface instance |
| 121 | void extractEthernetInterfaceData(const std::string ðiface_id, |
| 122 | const GetManagedObjectsType &dbus_data, |
| 123 | EthernetInterfaceData ð_data) { |
| 124 | // Extract data that contains MAC Address |
| 125 | const PropertiesMapType *mac_properties = extractInterfaceProperties( |
| 126 | "/xyz/openbmc_project/network/" + ethiface_id, |
| 127 | "xyz.openbmc_project.Network.MACAddress", dbus_data); |
| 128 | |
| 129 | if (mac_properties != nullptr) { |
| 130 | eth_data.mac_address = |
| 131 | extractProperty<std::string>(*mac_properties, "MACAddress"); |
| 132 | } |
| 133 | |
Kowalski, Kamil | c7070ac | 2018-03-27 15:01:02 +0200 | [diff] [blame] | 134 | const PropertiesMapType *vlan_properties = extractInterfaceProperties( |
| 135 | "/xyz/openbmc_project/network/" + ethiface_id, |
| 136 | "xyz.openbmc_project.Network.VLAN", dbus_data); |
| 137 | |
| 138 | if (vlan_properties != nullptr) { |
| 139 | eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id"); |
| 140 | } |
| 141 | |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 142 | // Extract data that contains link information (auto negotiation and speed) |
| 143 | const PropertiesMapType *eth_properties = extractInterfaceProperties( |
| 144 | "/xyz/openbmc_project/network/" + ethiface_id, |
| 145 | "xyz.openbmc_project.Network.EthernetInterface", dbus_data); |
| 146 | |
| 147 | if (eth_properties != nullptr) { |
| 148 | eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg"); |
| 149 | eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed"); |
| 150 | } |
| 151 | |
| 152 | // Extract data that contains network config (HostName and DefaultGW) |
| 153 | const PropertiesMapType *config_properties = extractInterfaceProperties( |
| 154 | "/xyz/openbmc_project/network/config", |
| 155 | "xyz.openbmc_project.Network.SystemConfiguration", dbus_data); |
| 156 | |
| 157 | if (config_properties != nullptr) { |
| 158 | eth_data.hostname = |
| 159 | extractProperty<std::string>(*config_properties, "HostName"); |
| 160 | eth_data.default_gateway = |
| 161 | extractProperty<std::string>(*config_properties, "DefaultGateway"); |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // Helper function that changes bits netmask notation (i.e. /24) |
| 166 | // into full dot notation |
| 167 | inline std::string getNetmask(unsigned int bits) { |
| 168 | uint32_t value = 0xffffffff << (32 - bits); |
| 169 | std::string netmask = std::to_string((value >> 24) & 0xff) + "." + |
| 170 | std::to_string((value >> 16) & 0xff) + "." + |
| 171 | std::to_string((value >> 8) & 0xff) + "." + |
| 172 | std::to_string(value & 0xff); |
| 173 | return netmask; |
| 174 | } |
| 175 | |
| 176 | // Helper function that extracts data for single ethernet ipv4 address |
| 177 | void extractIPv4Data(const std::string ðiface_id, |
| 178 | const GetManagedObjectsType &dbus_data, |
| 179 | std::vector<IPv4AddressData> &ipv4_config) { |
| 180 | // Since there might be several IPv4 configurations aligned with |
| 181 | // single ethernet interface, loop over all of them |
| 182 | for (auto &objpath : dbus_data) { |
Gunnar Mills | 274fad5 | 2018-06-13 15:45:36 -0500 | [diff] [blame] | 183 | // Check if proper patter for object path appears |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 184 | if (boost::starts_with( |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 185 | static_cast<std::string>(objpath.first), |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 186 | "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) { |
| 187 | // and get approrpiate interface |
| 188 | const auto &interface = |
| 189 | objpath.second.find("xyz.openbmc_project.Network.IP"); |
| 190 | if (interface != objpath.second.end()) { |
| 191 | // Make a properties 'shortcut', to make everything more readable |
| 192 | const PropertiesMapType &properties = interface->second; |
| 193 | // Instance IPv4AddressData structure, and set as appropriate |
| 194 | IPv4AddressData ipv4_address; |
| 195 | // IPv4 address |
| 196 | ipv4_address.address = |
| 197 | extractProperty<std::string>(properties, "Address"); |
| 198 | // IPv4 gateway |
| 199 | ipv4_address.gateway = |
| 200 | extractProperty<std::string>(properties, "Gateway"); |
| 201 | |
| 202 | // Origin is kind of DBus object so fetch pointer... |
| 203 | const std::string *origin = |
| 204 | extractProperty<std::string>(properties, "Origin"); |
| 205 | if (origin != nullptr) { |
| 206 | // ... and get everything after last dot |
| 207 | int last = origin->rfind("."); |
| 208 | if (last != std::string::npos) { |
| 209 | ipv4_address.origin = origin->substr(last + 1); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | // Netmask is presented as PrefixLength |
| 214 | const auto *mask = |
| 215 | extractProperty<uint8_t>(properties, "PrefixLength"); |
| 216 | if (mask != nullptr) { |
| 217 | // convert it to the string |
| 218 | ipv4_address.netmask = getNetmask(*mask); |
| 219 | } |
| 220 | |
| 221 | // Attach IPv4 only if address is present |
| 222 | if (ipv4_address.address != nullptr) { |
Gunnar Mills | 274fad5 | 2018-06-13 15:45:36 -0500 | [diff] [blame] | 223 | // Check if given address is local, or global |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 224 | if (boost::starts_with(*ipv4_address.address, "169.254")) { |
| 225 | ipv4_address.global = false; |
| 226 | } else { |
| 227 | ipv4_address.global = true; |
| 228 | } |
| 229 | ipv4_config.emplace_back(std::move(ipv4_address)); |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | public: |
| 237 | /** |
| 238 | * Function that retrieves all properties for given Ethernet Interface Object |
| 239 | * from EntityManager Network Manager |
| 240 | * @param ethiface_id a eth interface id to query on DBus |
| 241 | * @param callback a function that shall be called to convert Dbus output into |
| 242 | * JSON |
| 243 | */ |
| 244 | template <typename CallbackFunc> |
| 245 | void getEthernetIfaceData(const std::string ðiface_id, |
| 246 | CallbackFunc &&callback) { |
| 247 | crow::connections::system_bus->async_method_call( |
| 248 | [ |
| 249 | this, ethiface_id{std::move(ethiface_id)}, |
| 250 | callback{std::move(callback)} |
| 251 | ](const boost::system::error_code error_code, |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 252 | GetManagedObjectsType &resp) { |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 253 | |
Kowalski, Kamil | c7070ac | 2018-03-27 15:01:02 +0200 | [diff] [blame] | 254 | EthernetInterfaceData eth_data{}; |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 255 | std::vector<IPv4AddressData> ipv4_data; |
| 256 | ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE); |
| 257 | |
| 258 | if (error_code) { |
| 259 | // Something wrong on DBus, the error_code is not important at this |
| 260 | // moment, just return success=false, and empty output. Since size |
| 261 | // of vector may vary depending on information from Network Manager, |
| 262 | // and empty output could not be treated same way as error. |
| 263 | callback(false, eth_data, ipv4_data); |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | extractEthernetInterfaceData(ethiface_id, resp, eth_data); |
| 268 | extractIPv4Data(ethiface_id, resp, ipv4_data); |
| 269 | |
| 270 | // Fix global GW |
| 271 | for (IPv4AddressData &ipv4 : ipv4_data) { |
| 272 | if ((ipv4.global) && |
| 273 | ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) { |
| 274 | ipv4.gateway = eth_data.default_gateway; |
| 275 | } |
| 276 | } |
| 277 | |
Gunnar Mills | 274fad5 | 2018-06-13 15:45:36 -0500 | [diff] [blame] | 278 | // Finally make a callback with useful data |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 279 | callback(true, eth_data, ipv4_data); |
| 280 | }, |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 281 | "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", |
| 282 | "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 283 | }; |
| 284 | |
| 285 | /** |
| 286 | * Function that retrieves all Ethernet Interfaces available through Network |
| 287 | * Manager |
| 288 | * @param callback a function that shall be called to convert Dbus output into |
| 289 | * JSON. |
| 290 | */ |
| 291 | template <typename CallbackFunc> |
| 292 | void getEthernetIfaceList(CallbackFunc &&callback) { |
| 293 | crow::connections::system_bus->async_method_call( |
| 294 | [ this, callback{std::move(callback)} ]( |
| 295 | const boost::system::error_code error_code, |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 296 | GetManagedObjectsType &resp) { |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 297 | // Callback requires vector<string> to retrieve all available ethernet |
| 298 | // interfaces |
| 299 | std::vector<std::string> iface_list; |
| 300 | iface_list.reserve(resp.size()); |
| 301 | if (error_code) { |
| 302 | // Something wrong on DBus, the error_code is not important at this |
| 303 | // moment, just return success=false, and empty output. Since size |
| 304 | // of vector may vary depending on information from Network Manager, |
| 305 | // and empty output could not be treated same way as error. |
| 306 | callback(false, iface_list); |
| 307 | return; |
| 308 | } |
| 309 | |
| 310 | // Iterate over all retrieved ObjectPaths. |
| 311 | for (auto &objpath : resp) { |
| 312 | // And all interfaces available for certain ObjectPath. |
| 313 | for (auto &interface : objpath.second) { |
| 314 | // If interface is xyz.openbmc_project.Network.EthernetInterface, |
| 315 | // this is what we're looking for. |
| 316 | if (interface.first == |
| 317 | "xyz.openbmc_project.Network.EthernetInterface") { |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 318 | // Cut out everyting until last "/", ... |
| 319 | const std::string iface_id = |
| 320 | static_cast<std::string>(objpath.first); |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 321 | std::size_t last_pos = iface_id.rfind("/"); |
| 322 | if (last_pos != std::string::npos) { |
| 323 | // and put it into output vector. |
| 324 | iface_list.emplace_back(iface_id.substr(last_pos + 1)); |
| 325 | } |
| 326 | } |
| 327 | } |
| 328 | } |
Gunnar Mills | 274fad5 | 2018-06-13 15:45:36 -0500 | [diff] [blame] | 329 | // Finally make a callback with useful data |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 330 | callback(true, iface_list); |
| 331 | }, |
Ed Tanous | aa2e59c | 2018-04-12 12:17:20 -0700 | [diff] [blame^] | 332 | "xyz.openbmc_project.Network", "/xyz/openbmc_project/network", |
| 333 | "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 334 | }; |
| 335 | }; |
| 336 | |
| 337 | /** |
| 338 | * EthernetCollection derived class for delivering Ethernet Collection Schema |
| 339 | */ |
| 340 | class EthernetCollection : public Node { |
| 341 | public: |
| 342 | template <typename CrowApp> |
| 343 | // TODO(Pawel) Remove line from below, where we assume that there is only one |
| 344 | // manager called openbmc This shall be generic, but requires to update |
| 345 | // GetSubroutes method |
| 346 | EthernetCollection(CrowApp &app) |
| 347 | : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") { |
| 348 | Node::json["@odata.type"] = |
| 349 | "#EthernetInterfaceCollection.EthernetInterfaceCollection"; |
| 350 | Node::json["@odata.context"] = |
| 351 | "/redfish/v1/" |
| 352 | "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection"; |
| 353 | Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces"; |
| 354 | Node::json["Name"] = "Ethernet Network Interface Collection"; |
| 355 | Node::json["Description"] = |
| 356 | "Collection of EthernetInterfaces for this Manager"; |
| 357 | |
| 358 | entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, |
| 359 | {crow::HTTPMethod::HEAD, {{"Login"}}}, |
| 360 | {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, |
| 361 | {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, |
| 362 | {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, |
| 363 | {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; |
| 364 | } |
| 365 | |
| 366 | private: |
| 367 | /** |
| 368 | * Functions triggers appropriate requests on DBus |
| 369 | */ |
| 370 | void doGet(crow::response &res, const crow::request &req, |
| 371 | const std::vector<std::string> ¶ms) override { |
| 372 | // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for |
| 373 | // any Manager, not only hardcoded 'openbmc'. |
| 374 | std::string manager_id = "openbmc"; |
| 375 | |
| 376 | // Get eth interface list, and call the below callback for JSON preparation |
| 377 | ethernet_provider.getEthernetIfaceList( |
| 378 | [&, manager_id{std::move(manager_id)} ]( |
| 379 | const bool &success, const std::vector<std::string> &iface_list) { |
| 380 | if (success) { |
| 381 | nlohmann::json iface_array = nlohmann::json::array(); |
| 382 | for (const std::string &iface_item : iface_list) { |
| 383 | iface_array.push_back( |
| 384 | {{"@odata.id", "/redfish/v1/Managers/" + manager_id + |
| 385 | "/EthernetInterfaces/" + iface_item}}); |
| 386 | } |
| 387 | Node::json["Members"] = iface_array; |
| 388 | Node::json["Members@odata.count"] = iface_array.size(); |
| 389 | Node::json["@odata.id"] = |
| 390 | "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces"; |
| 391 | res.json_value = Node::json; |
| 392 | } else { |
| 393 | // No success, best what we can do is return INTERNALL ERROR |
| 394 | res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); |
| 395 | } |
| 396 | res.end(); |
| 397 | }); |
| 398 | } |
| 399 | |
| 400 | // Ethernet Provider object |
| 401 | // TODO(Pawel) consider move it to singleton |
| 402 | OnDemandEthernetProvider ethernet_provider; |
| 403 | }; |
| 404 | |
| 405 | /** |
| 406 | * EthernetInterface derived class for delivering Ethernet Schema |
| 407 | */ |
| 408 | class EthernetInterface : public Node { |
| 409 | public: |
| 410 | /* |
| 411 | * Default Constructor |
| 412 | */ |
| 413 | template <typename CrowApp> |
| 414 | // TODO(Pawel) Remove line from below, where we assume that there is only one |
| 415 | // manager called openbmc This shall be generic, but requires to update |
| 416 | // GetSubroutes method |
| 417 | EthernetInterface(CrowApp &app) |
| 418 | : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/", |
| 419 | std::string()) { |
| 420 | Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface"; |
| 421 | Node::json["@odata.context"] = |
| 422 | "/redfish/v1/$metadata#EthernetInterface.EthernetInterface"; |
| 423 | Node::json["Name"] = "Manager Ethernet Interface"; |
| 424 | Node::json["Description"] = "Management Network Interface"; |
| 425 | |
| 426 | entityPrivileges = {{crow::HTTPMethod::GET, {{"Login"}}}, |
| 427 | {crow::HTTPMethod::HEAD, {{"Login"}}}, |
| 428 | {crow::HTTPMethod::PATCH, {{"ConfigureComponents"}}}, |
| 429 | {crow::HTTPMethod::PUT, {{"ConfigureComponents"}}}, |
| 430 | {crow::HTTPMethod::DELETE, {{"ConfigureComponents"}}}, |
| 431 | {crow::HTTPMethod::POST, {{"ConfigureComponents"}}}}; |
| 432 | } |
| 433 | |
| 434 | private: |
| 435 | /** |
| 436 | * Functions triggers appropriate requests on DBus |
| 437 | */ |
| 438 | void doGet(crow::response &res, const crow::request &req, |
| 439 | const std::vector<std::string> ¶ms) override { |
| 440 | // TODO(Pawel) this shall be parametrized call (two params) to get |
| 441 | // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'. |
| 442 | // Check if there is required param, truly entering this shall be |
| 443 | // impossible. |
| 444 | if (params.size() != 1) { |
| 445 | res.code = static_cast<int>(HttpRespCode::INTERNAL_ERROR); |
| 446 | res.end(); |
| 447 | return; |
| 448 | } |
| 449 | |
| 450 | const std::string &iface_id = params[0]; |
| 451 | |
| 452 | // Get single eth interface data, and call the below callback for JSON |
| 453 | // preparation |
| 454 | ethernet_provider.getEthernetIfaceData( |
| 455 | iface_id, [&, iface_id](const bool &success, |
| 456 | const EthernetInterfaceData ð_data, |
| 457 | const std::vector<IPv4AddressData> &ipv4_data) { |
| 458 | if (success) { |
| 459 | // Copy JSON object to avoid race condition |
| 460 | nlohmann::json json_response(Node::json); |
| 461 | |
| 462 | // Fill out obvious data... |
| 463 | json_response["Id"] = iface_id; |
| 464 | json_response["@odata.id"] = |
| 465 | "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id; |
| 466 | |
| 467 | // ... then the one from DBus, regarding eth iface... |
| 468 | if (eth_data.speed != nullptr) |
| 469 | json_response["SpeedMbps"] = *eth_data.speed; |
| 470 | |
| 471 | if (eth_data.mac_address != nullptr) |
| 472 | json_response["MACAddress"] = *eth_data.mac_address; |
| 473 | |
| 474 | if (eth_data.hostname != nullptr) |
| 475 | json_response["HostName"] = *eth_data.hostname; |
| 476 | |
Kowalski, Kamil | c7070ac | 2018-03-27 15:01:02 +0200 | [diff] [blame] | 477 | if (eth_data.vlan_id != nullptr) { |
| 478 | json_response["VLAN"]["VLANEnable"] = true; |
| 479 | json_response["VLAN"]["VLANId"] = *eth_data.vlan_id; |
| 480 | } |
| 481 | |
Rapkiewicz, Pawel | 9391bb9 | 2018-03-20 03:12:18 +0100 | [diff] [blame] | 482 | // ... at last, check if there are IPv4 data and prepare appropriate |
| 483 | // collection |
| 484 | if (ipv4_data.size() > 0) { |
| 485 | nlohmann::json ipv4_array = nlohmann::json::array(); |
| 486 | for (auto &ipv4_config : ipv4_data) { |
| 487 | nlohmann::json json_ipv4; |
| 488 | if (ipv4_config.address != nullptr) { |
| 489 | json_ipv4["Address"] = *ipv4_config.address; |
| 490 | if (ipv4_config.gateway != nullptr) |
| 491 | json_ipv4["Gateway"] = *ipv4_config.gateway; |
| 492 | |
| 493 | json_ipv4["AddressOrigin"] = ipv4_config.origin; |
| 494 | json_ipv4["SubnetMask"] = ipv4_config.netmask; |
| 495 | |
| 496 | ipv4_array.push_back(json_ipv4); |
| 497 | } |
| 498 | } |
| 499 | json_response["IPv4Addresses"] = ipv4_array; |
| 500 | } |
| 501 | res.json_value = std::move(json_response); |
| 502 | } else { |
| 503 | // ... otherwise return error |
| 504 | // TODO(Pawel)consider distinguish between non existing object, and |
| 505 | // other errors |
| 506 | res.code = static_cast<int>(HttpRespCode::NOT_FOUND); |
| 507 | } |
| 508 | res.end(); |
| 509 | }); |
| 510 | } |
| 511 | |
| 512 | // Ethernet Provider object |
| 513 | // TODO(Pawel) consider move it to singleton |
| 514 | OnDemandEthernetProvider ethernet_provider; |
| 515 | }; |
| 516 | |
| 517 | } // namespace redfish |