blob: 293e8020c041d0f1a52fff143ba3fdd85d863d3e [file] [log] [blame]
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001/*
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
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020018#include <dbus_singleton.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020019#include <error_messages.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020020#include <node.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020021#include <utils/json_utils.hpp>
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010022#include <boost/container/flat_map.hpp>
23
24namespace redfish {
25
26/**
27 * DBus types primitives for several generic DBus interfaces
28 * TODO(Pawel) consider move this to separate file into boost::dbus
29 */
Ed Tanousaa2e59c2018-04-12 12:17:20 -070030using PropertiesMapType = boost::container::flat_map<
31 std::string,
32 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
33 int32_t, uint32_t, int64_t, uint64_t, double>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010034
35using GetManagedObjectsType = boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070036 sdbusplus::message::object_path,
37 boost::container::flat_map<
38 std::string,
39 boost::container::flat_map<
40 std::string, sdbusplus::message::variant<
41 std::string, bool, uint8_t, int16_t, uint16_t,
42 int32_t, uint32_t, int64_t, uint64_t, double>>>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010043
44/**
45 * Structure for keeping IPv4 data required by Redfish
46 * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
47 */
48struct IPv4AddressData {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020049 std::string id;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010050 const std::string *address;
51 const std::string *domain;
52 const std::string *gateway;
53 std::string netmask;
54 std::string origin;
55 bool global;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020056 /**
57 * @brief Operator< to enable sorting
58 *
59 * @param[in] obj Object to compare with
60 *
61 * @return This object id < supplied object id
62 */
63 bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010064};
65
66/**
67 * Structure for keeping basic single Ethernet Interface information
68 * available from DBus
69 */
70struct EthernetInterfaceData {
71 const unsigned int *speed;
Ed Tanous55c7b7a2018-05-22 15:27:24 -070072 const bool *autoNeg;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010073 const std::string *hostname;
Ed Tanous55c7b7a2018-05-22 15:27:24 -070074 const std::string *defaultGateway;
75 const std::string *macAddress;
76 const unsigned int *vlanId;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010077};
78
79/**
80 * OnDemandEthernetProvider
Gunnar Mills274fad52018-06-13 15:45:36 -050081 * Ethernet provider class that retrieves data directly from dbus, before
82 * setting it into JSON output. This does not cache any data.
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010083 *
84 * TODO(Pawel)
85 * This perhaps shall be different file, which has to be chosen on compile time
86 * depending on OEM needs
87 */
88class OnDemandEthernetProvider {
89 private:
90 // Consts that may have influence on EthernetProvider performance/memory usage
Ed Tanous55c7b7a2018-05-22 15:27:24 -070091 const size_t maxIpV4AddressesPerInterface = 10;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010092
93 // Helper function that allows to extract GetAllPropertiesType from
94 // GetManagedObjectsType, based on object path, and interface name
95 const PropertiesMapType *extractInterfaceProperties(
Ed Tanousaa2e59c2018-04-12 12:17:20 -070096 const sdbusplus::message::object_path &objpath,
97 const std::string &interface, const GetManagedObjectsType &dbus_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070098 const auto &dbusObj = dbus_data.find(objpath);
99 if (dbusObj != dbus_data.end()) {
100 const auto &iface = dbusObj->second.find(interface);
101 if (iface != dbusObj->second.end()) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100102 return &iface->second;
103 }
104 }
105 return nullptr;
106 }
107
108 // Helper Wrapper that does inline object_path conversion from string
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700109 // into sdbusplus::message::object_path type
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100110 inline const PropertiesMapType *extractInterfaceProperties(
111 const std::string &objpath, const std::string &interface,
112 const GetManagedObjectsType &dbus_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700113 const auto &dbusObj = sdbusplus::message::object_path{objpath};
114 return extractInterfaceProperties(dbusObj, interface, dbus_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100115 }
116
117 // Helper function that allows to get pointer to the property from
118 // GetAllPropertiesType native, or extracted by GetAllPropertiesType
119 template <typename T>
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700120 inline T const *const extractProperty(const PropertiesMapType &properties,
121 const std::string &name) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100122 const auto &property = properties.find(name);
123 if (property != properties.end()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700124 return mapbox::getPtr<const T>(property->second);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100125 }
126 return nullptr;
127 }
128 // TODO(Pawel) Consider to move the above functions to dbus
129 // generic_interfaces.hpp
130
131 // Helper function that extracts data from several dbus objects and several
132 // interfaces required by single ethernet interface instance
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700133 void extractEthernetInterfaceData(const std::string &ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100134 const GetManagedObjectsType &dbus_data,
135 EthernetInterfaceData &eth_data) {
136 // Extract data that contains MAC Address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700137 const PropertiesMapType *macProperties = extractInterfaceProperties(
138 "/xyz/openbmc_project/network/" + ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100139 "xyz.openbmc_project.Network.MACAddress", dbus_data);
140
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700141 if (macProperties != nullptr) {
142 eth_data.macAddress =
143 extractProperty<std::string>(*macProperties, "MACAddress");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100144 }
145
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700146 const PropertiesMapType *vlanProperties = extractInterfaceProperties(
147 "/xyz/openbmc_project/network/" + ethifaceId,
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +0200148 "xyz.openbmc_project.Network.VLAN", dbus_data);
149
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700150 if (vlanProperties != nullptr) {
151 eth_data.vlanId = extractProperty<unsigned int>(*vlanProperties, "Id");
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +0200152 }
153
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100154 // Extract data that contains link information (auto negotiation and speed)
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700155 const PropertiesMapType *ethProperties = extractInterfaceProperties(
156 "/xyz/openbmc_project/network/" + ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100157 "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
158
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700159 if (ethProperties != nullptr) {
160 eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
161 eth_data.speed = extractProperty<unsigned int>(*ethProperties, "Speed");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100162 }
163
164 // Extract data that contains network config (HostName and DefaultGW)
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700165 const PropertiesMapType *configProperties = extractInterfaceProperties(
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100166 "/xyz/openbmc_project/network/config",
167 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
168
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700169 if (configProperties != nullptr) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100170 eth_data.hostname =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700171 extractProperty<std::string>(*configProperties, "HostName");
172 eth_data.defaultGateway =
173 extractProperty<std::string>(*configProperties, "DefaultGateway");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100174 }
175 }
176
177 // Helper function that changes bits netmask notation (i.e. /24)
178 // into full dot notation
179 inline std::string getNetmask(unsigned int bits) {
180 uint32_t value = 0xffffffff << (32 - bits);
181 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
182 std::to_string((value >> 16) & 0xff) + "." +
183 std::to_string((value >> 8) & 0xff) + "." +
184 std::to_string(value & 0xff);
185 return netmask;
186 }
187
188 // Helper function that extracts data for single ethernet ipv4 address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700189 void extractIPv4Data(const std::string &ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100190 const GetManagedObjectsType &dbus_data,
191 std::vector<IPv4AddressData> &ipv4_config) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200192 const std::string pathStart =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700193 "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200194
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100195 // Since there might be several IPv4 configurations aligned with
196 // single ethernet interface, loop over all of them
197 for (auto &objpath : dbus_data) {
Gunnar Mills274fad52018-06-13 15:45:36 -0500198 // Check if proper patter for object path appears
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100199 if (boost::starts_with(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200200 static_cast<const std::string &>(objpath.first),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700201 "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/")) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100202 // and get approrpiate interface
203 const auto &interface =
204 objpath.second.find("xyz.openbmc_project.Network.IP");
205 if (interface != objpath.second.end()) {
206 // Make a properties 'shortcut', to make everything more readable
207 const PropertiesMapType &properties = interface->second;
208 // Instance IPv4AddressData structure, and set as appropriate
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700209 IPv4AddressData ipv4Address;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200210
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700211 ipv4Address.id = static_cast<const std::string &>(objpath.first)
212 .substr(pathStart.size());
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200213
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100214 // IPv4 address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700215 ipv4Address.address =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100216 extractProperty<std::string>(properties, "Address");
217 // IPv4 gateway
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700218 ipv4Address.gateway =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100219 extractProperty<std::string>(properties, "Gateway");
220
221 // Origin is kind of DBus object so fetch pointer...
222 const std::string *origin =
223 extractProperty<std::string>(properties, "Origin");
224 if (origin != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700225 ipv4Address.origin =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200226 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100227 }
228
229 // Netmask is presented as PrefixLength
230 const auto *mask =
231 extractProperty<uint8_t>(properties, "PrefixLength");
232 if (mask != nullptr) {
233 // convert it to the string
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700234 ipv4Address.netmask = getNetmask(*mask);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100235 }
236
237 // Attach IPv4 only if address is present
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700238 if (ipv4Address.address != nullptr) {
239 // Check if given addres is local, or global
240 if (boost::starts_with(*ipv4Address.address, "169.254")) {
241 ipv4Address.global = false;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100242 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700243 ipv4Address.global = true;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100244 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700245 ipv4_config.emplace_back(std::move(ipv4Address));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100246 }
247 }
248 }
249 }
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200250
251 /**
252 * We have to sort this vector and ensure that order of IPv4 addresses
253 * is consistent between GETs to allow modification and deletion in PATCHes
254 */
255 std::sort(ipv4_config.begin(), ipv4_config.end());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100256 }
257
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200258 static const constexpr int ipV4AddressSectionsCount = 4;
259
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100260 public:
261 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200262 * @brief Creates VLAN for given interface with given Id through D-Bus
263 *
264 * @param[in] ifaceId Id of interface for which VLAN will be created
265 * @param[in] inputVlanId ID of the new VLAN
266 * @param[in] callback Function that will be called after the operation
267 *
268 * @return None.
269 */
270 template <typename CallbackFunc>
271 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
272 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700273 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200274 callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
275 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
276 static_cast<uint32_t>(inputVlanId));
277 };
278
279 /**
280 * @brief Sets given Id on the given VLAN interface through D-Bus
281 *
282 * @param[in] ifaceId Id of VLAN interface that should be modified
283 * @param[in] inputVlanId New ID of the VLAN
284 * @param[in] callback Function that will be called after the operation
285 *
286 * @return None.
287 */
288 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200289 static void changeVlanId(const std::string &ifaceId,
290 const uint32_t &inputVlanId,
291 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700292 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200293 callback, "xyz.openbmc_project.Network",
294 std::string("/xyz/openbmc_project/network/") + ifaceId,
295 "org.freedesktop.DBus.Properties", "Set",
296 "xyz.openbmc_project.Network.VLAN", "Id",
297 sdbusplus::message::variant<uint32_t>(inputVlanId));
298 };
299
300 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200301 * @brief Helper function that verifies IP address to check if it is in
302 * proper format. If bits pointer is provided, also calculates active
303 * bit count for Subnet Mask.
304 *
305 * @param[in] ip IP that will be verified
306 * @param[out] bits Calculated mask in bits notation
307 *
308 * @return true in case of success, false otherwise
309 */
310 bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
311 uint8_t *bits = nullptr) {
312 std::vector<std::string> bytesInMask;
313
314 boost::split(bytesInMask, ip, boost::is_any_of("."));
315
316 if (bytesInMask.size() != ipV4AddressSectionsCount) {
317 return false;
318 }
319
320 if (bits != nullptr) {
321 *bits = 0;
322 }
323
324 char *endPtr;
325 long previousValue = 255;
326 bool firstZeroInByteHit;
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200327 for (const std::string &byte : bytesInMask) {
328 if (byte.empty()) {
329 return false;
330 }
331
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200332 // Use strtol instead of stroi to avoid exceptions
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200333 long value = std::strtol(byte.c_str(), &endPtr, 10);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200334
335 // endPtr should point to the end of the string, otherwise given string
336 // is not 100% number
337 if (*endPtr != '\0') {
338 return false;
339 }
340
341 // Value should be contained in byte
342 if (value < 0 || value > 255) {
343 return false;
344 }
345
346 if (bits != nullptr) {
347 // Mask has to be continuous between bytes
348 if (previousValue != 255 && value != 0) {
349 return false;
350 }
351
352 // Mask has to be continuous inside bytes
353 firstZeroInByteHit = false;
354
355 // Count bits
356 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
357 if (value & (1 << bitIdx)) {
358 if (firstZeroInByteHit) {
359 // Continuity not preserved
360 return false;
361 } else {
362 (*bits)++;
363 }
364 } else {
365 firstZeroInByteHit = true;
366 }
367 }
368 }
369
370 previousValue = value;
371 }
372
373 return true;
374 }
375
376 /**
377 * @brief Changes IPv4 address type property (Address, Gateway)
378 *
379 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700380 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200381 * @param[in] ipHash DBus Hash id of modified IP
382 * @param[in] name Name of field in JSON representation
383 * @param[in] newValue New value that should be written
384 * @param[io] asyncResp Response object that will be returned to client
385 *
386 * @return true if give IP is valid and has been sent do D-Bus, false
387 * otherwise
388 */
389 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
390 const std::string &ipHash,
391 const std::string &name,
392 const std::string &newValue,
393 const std::shared_ptr<AsyncResp> &asyncResp) {
394 auto callback = [
395 asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
396 newValue{std::move(newValue)}
397 ](const boost::system::error_code ec) {
398 if (ec) {
399 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700400 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200401 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
402 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700403 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200404 }
405 };
406
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700407 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200408 std::move(callback), "xyz.openbmc_project.Network",
409 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
410 "org.freedesktop.DBus.Properties", "Set",
411 "xyz.openbmc_project.Network.IP", name,
412 sdbusplus::message::variant<std::string>(newValue));
413 };
414
415 /**
416 * @brief Changes IPv4 address origin property
417 *
418 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700419 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200420 * @param[in] ipHash DBus Hash id of modified IP
421 * @param[in] newValue New value in Redfish format
422 * @param[in] newValueDbus New value in D-Bus format
423 * @param[io] asyncResp Response object that will be returned to client
424 *
425 * @return true if give IP is valid and has been sent do D-Bus, false
426 * otherwise
427 */
428 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
429 const std::string &ipHash, const std::string &newValue,
430 const std::string &newValueDbus,
431 const std::shared_ptr<AsyncResp> &asyncResp) {
432 auto callback =
433 [ asyncResp, ipIdx{std::move(ipIdx)},
434 newValue{std::move(newValue)} ](const boost::system::error_code ec) {
435 if (ec) {
436 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700437 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200438 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
439 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700440 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200441 newValue;
442 }
443 };
444
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700445 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200446 std::move(callback), "xyz.openbmc_project.Network",
447 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
448 "org.freedesktop.DBus.Properties", "Set",
449 "xyz.openbmc_project.Network.IP", "Origin",
450 sdbusplus::message::variant<std::string>(newValueDbus));
451 };
452
453 /**
454 * @brief Modifies SubnetMask for given IP
455 *
456 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700457 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200458 * @param[in] ipHash DBus Hash id of modified IP
459 * @param[in] newValueStr Mask in dot notation as string
460 * @param[in] newValue Mask as PrefixLength in bitcount
461 * @param[io] asyncResp Response object that will be returned to client
462 *
463 * @return None
464 */
465 void changeIPv4SubnetMaskProperty(
466 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
467 const std::string &newValueStr, uint8_t &newValue,
468 const std::shared_ptr<AsyncResp> &asyncResp) {
469 auto callback = [
470 asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
471 ](const boost::system::error_code ec) {
472 if (ec) {
473 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700474 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200475 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
476 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700477 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200478 newValueStr;
479 }
480 };
481
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700482 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200483 std::move(callback), "xyz.openbmc_project.Network",
484 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
485 "org.freedesktop.DBus.Properties", "Set",
486 "xyz.openbmc_project.Network.IP", "PrefixLength",
487 sdbusplus::message::variant<uint8_t>(newValue));
488 };
489
490 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200491 * @brief Disables VLAN with given ifaceId
492 *
493 * @param[in] ifaceId Id of VLAN interface that should be disabled
494 * @param[in] callback Function that will be called after the operation
495 *
496 * @return None.
497 */
498 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200499 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700500 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200501 callback, "xyz.openbmc_project.Network",
502 std::string("/xyz/openbmc_project/network/") + ifaceId,
503 "xyz.openbmc_project.Object.Delete", "Delete");
504 };
505
506 /**
507 * @brief Sets given HostName of the machine through D-Bus
508 *
509 * @param[in] newHostname New name that HostName will be changed to
510 * @param[in] callback Function that will be called after the operation
511 *
512 * @return None.
513 */
514 template <typename CallbackFunc>
515 void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700516 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200517 callback, "xyz.openbmc_project.Network",
518 "/xyz/openbmc_project/network/config",
519 "org.freedesktop.DBus.Properties", "Set",
520 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
521 sdbusplus::message::variant<std::string>(newHostname));
522 };
523
524 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200525 * @brief Deletes given IPv4
526 *
527 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700528 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200529 * @param[in] ipHash DBus Hash id of IP that should be deleted
530 * @param[io] asyncResp Response object that will be returned to client
531 *
532 * @return None
533 */
534 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
535 unsigned int ipIdx,
536 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700537 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200538 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
539 const boost::system::error_code ec) {
540 if (ec) {
541 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700542 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200543 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
544 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700545 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200546 }
547 },
548 "xyz.openbmc_project.Network",
549 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
550 "xyz.openbmc_project.Object.Delete", "Delete");
551 }
552
553 /**
554 * @brief Creates IPv4 with given data
555 *
556 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700557 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200558 * @param[in] ipHash DBus Hash id of IP that should be deleted
559 * @param[io] asyncResp Response object that will be returned to client
560 *
561 * @return None
562 */
563 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
564 uint8_t subnetMask, const std::string &gateway,
565 const std::string &address,
566 const std::shared_ptr<AsyncResp> &asyncResp) {
567 auto createIpHandler = [
568 ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
569 ](const boost::system::error_code ec) {
570 if (ec) {
571 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700572 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200573 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
574 }
575 };
576
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700577 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200578 std::move(createIpHandler), "xyz.openbmc_project.Network",
579 "/xyz/openbmc_project/network/" + ifaceId,
580 "xyz.openbmc_project.Network.IP.Create", "IP",
581 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
582 gateway);
583 }
584
585 /**
586 * @brief Translates Address Origin value from D-Bus to Redfish format and
587 * vice-versa
588 *
589 * @param[in] inputOrigin Input value that should be translated
590 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
591 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse
592 *
593 * @return Empty string in case of failure, translated value otherwise
594 */
595 std::string translateAddressOriginBetweenDBusAndRedfish(
596 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
597 // Invalid pointer
598 if (inputOrigin == nullptr) {
599 return "";
600 }
601
602 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
603 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
604
605 std::array<std::pair<const char *, const char *>, 6> translationTable{
606 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
607 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
608 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
609 "IPv4LinkLocal"},
610 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
611 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
612 "LinkLocal"},
613 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
614
615 for (unsigned int i = 0; i < translationTable.size(); i++) {
616 // Skip unrelated
617 if (isIPv4 && i >= firstIPv6OnlyIdx) break;
618 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
619
620 // When translating D-Bus to Redfish compare input to first element
621 if (isFromDBus && translationTable[i].first == *inputOrigin)
622 return translationTable[i].second;
623
624 // When translating Redfish to D-Bus compare input to second element
625 if (!isFromDBus && translationTable[i].second == *inputOrigin)
626 return translationTable[i].first;
627 }
628
629 // If we are still here, that means that value has not been found
630 return "";
631 }
632
633 /**
634 * Function that retrieves all properties for given Ethernet Interface
635 * Object
636 * from EntityManager Network Manager
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700637 * @param ethifaceId a eth interface id to query on DBus
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200638 * @param callback a function that shall be called to convert Dbus output
639 * into JSON
640 */
641 template <typename CallbackFunc>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700642 void getEthernetIfaceData(const std::string &ethifaceId,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200643 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700644 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200645 [
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700646 this, ethifaceId{std::move(ethifaceId)},
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200647 callback{std::move(callback)}
648 ](const boost::system::error_code error_code,
649 const GetManagedObjectsType &resp) {
650
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700651 EthernetInterfaceData ethData{};
652 std::vector<IPv4AddressData> ipv4Data;
653 ipv4Data.reserve(maxIpV4AddressesPerInterface);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200654
655 if (error_code) {
656 // Something wrong on DBus, the error_code is not important at
657 // this moment, just return success=false, and empty output. Since
658 // size of vector may vary depending on information from Network
659 // Manager, and empty output could not be treated same way as
660 // error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700661 callback(false, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200662 return;
663 }
664
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200665 // Find interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700666 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200667 resp.end()) {
668 // Interface has not been found
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700669 callback(false, ethData, ipv4Data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200670 return;
671 }
672
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700673 extractEthernetInterfaceData(ethifaceId, resp, ethData);
674 extractIPv4Data(ethifaceId, resp, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200675
676 // Fix global GW
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700677 for (IPv4AddressData &ipv4 : ipv4Data) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200678 if ((ipv4.global) &&
679 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700680 ipv4.gateway = ethData.defaultGateway;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200681 }
682 }
683
684 // Finally make a callback with usefull data
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700685 callback(true, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200686 },
687 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
688 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
689 };
690
691 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100692 * Function that retrieves all Ethernet Interfaces available through Network
693 * Manager
694 * @param callback a function that shall be called to convert Dbus output into
695 * JSON.
696 */
697 template <typename CallbackFunc>
698 void getEthernetIfaceList(CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700699 crow::connections::systemBus->async_method_call(
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100700 [ this, callback{std::move(callback)} ](
701 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700702 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100703 // Callback requires vector<string> to retrieve all available ethernet
704 // interfaces
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700705 std::vector<std::string> ifaceList;
706 ifaceList.reserve(resp.size());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100707 if (error_code) {
708 // Something wrong on DBus, the error_code is not important at this
709 // moment, just return success=false, and empty output. Since size
710 // of vector may vary depending on information from Network Manager,
711 // and empty output could not be treated same way as error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700712 callback(false, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100713 return;
714 }
715
716 // Iterate over all retrieved ObjectPaths.
717 for (auto &objpath : resp) {
718 // And all interfaces available for certain ObjectPath.
719 for (auto &interface : objpath.second) {
720 // If interface is xyz.openbmc_project.Network.EthernetInterface,
721 // this is what we're looking for.
722 if (interface.first ==
723 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700724 // Cut out everyting until last "/", ...
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700725 const std::string &ifaceId =
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200726 static_cast<const std::string &>(objpath.first);
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700727 std::size_t lastPos = ifaceId.rfind("/");
728 if (lastPos != std::string::npos) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100729 // and put it into output vector.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700730 ifaceList.emplace_back(ifaceId.substr(lastPos + 1));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100731 }
732 }
733 }
734 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700735 // Finally make a callback with usefull data
736 callback(true, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100737 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700738 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
739 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100740 };
741};
742
743/**
744 * EthernetCollection derived class for delivering Ethernet Collection Schema
745 */
746class EthernetCollection : public Node {
747 public:
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100748 // TODO(Pawel) Remove line from below, where we assume that there is only one
749 // manager called openbmc This shall be generic, but requires to update
750 // GetSubroutes method
751 EthernetCollection(CrowApp &app)
752 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
753 Node::json["@odata.type"] =
754 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
755 Node::json["@odata.context"] =
756 "/redfish/v1/"
757 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
758 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
759 Node::json["Name"] = "Ethernet Network Interface Collection";
760 Node::json["Description"] =
761 "Collection of EthernetInterfaces for this Manager";
762
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200763 entityPrivileges = {
764 {boost::beast::http::verb::get, {{"Login"}}},
765 {boost::beast::http::verb::head, {{"Login"}}},
766 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
767 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
768 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
769 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100770 }
771
772 private:
773 /**
774 * Functions triggers appropriate requests on DBus
775 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700776 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100777 const std::vector<std::string> &params) override {
778 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
779 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700780 std::string managerId = "openbmc";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100781
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700782 // get eth interface list, and call the below callback for JSON preparation
783 ethernetProvider.getEthernetIfaceList([&, managerId{std::move(managerId)} ](
784 const bool &success, const std::vector<std::string> &iface_list) {
785 if (success) {
786 nlohmann::json ifaceArray = nlohmann::json::array();
787 for (const std::string &ifaceItem : iface_list) {
788 ifaceArray.push_back(
789 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
790 "/EthernetInterfaces/" + ifaceItem}});
791 }
792 Node::json["Members"] = ifaceArray;
793 Node::json["Members@odata.count"] = ifaceArray.size();
794 Node::json["@odata.id"] =
795 "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces";
796 res.jsonValue = Node::json;
797 } else {
798 // No success, best what we can do is return INTERNALL ERROR
799 res.result(boost::beast::http::status::internal_server_error);
800 }
801 res.end();
802 });
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100803 }
804
805 // Ethernet Provider object
806 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700807 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100808};
809
810/**
811 * EthernetInterface derived class for delivering Ethernet Schema
812 */
813class EthernetInterface : public Node {
814 public:
815 /*
816 * Default Constructor
817 */
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100818 // TODO(Pawel) Remove line from below, where we assume that there is only one
819 // manager called openbmc This shall be generic, but requires to update
820 // GetSubroutes method
821 EthernetInterface(CrowApp &app)
822 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
823 std::string()) {
824 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
825 Node::json["@odata.context"] =
826 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
827 Node::json["Name"] = "Manager Ethernet Interface";
828 Node::json["Description"] = "Management Network Interface";
829
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200830 entityPrivileges = {
831 {boost::beast::http::verb::get, {{"Login"}}},
832 {boost::beast::http::verb::head, {{"Login"}}},
833 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
834 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
835 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
836 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100837 }
838
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200839 // TODO(kkowalsk) Find a suitable class/namespace for this
840 static void handleVlanPatch(const std::string &ifaceId,
841 const nlohmann::json &input,
842 const EthernetInterfaceData &eth_data,
843 const std::string &pathPrefix,
844 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200845 if (!input.is_object()) {
846 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700847 asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200848 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200849 return;
850 }
851
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200852 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
853 nlohmann::json &paramsJson =
854 (pathPrefix == "/")
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700855 ? asyncResp->res.jsonValue
856 : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>(
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200857 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200858 bool inputVlanEnabled;
859 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200860
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200861 json_util::Result inputVlanEnabledState = json_util::getBool(
862 "VLANEnable", input, inputVlanEnabled,
863 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700864 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200865 json_util::Result inputVlanIdState = json_util::getUnsigned(
866 "VLANId", input, inputVlanId,
867 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700868 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200869 bool inputInvalid = false;
870
871 // Do not proceed if fields in VLAN object were of wrong type
872 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
873 inputVlanIdState == json_util::Result::WRONG_TYPE) {
874 return;
875 }
876
877 // Verify input
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700878 if (eth_data.vlanId == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200879 // This interface is not a VLAN. Cannot do anything with it
880 // TODO(kkowalsk) Change this message
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700881 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200882 messages::propertyMissing("VLANEnable"),
883 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200884
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200885 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200886 } else {
887 // Load actual data into field values if they were not provided
888 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
889 inputVlanEnabled = true;
890 }
891
892 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700893 inputVlanId = *eth_data.vlanId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200894 }
895 }
896
897 // Do not proceed if input has not been valid
898 if (inputInvalid) {
899 return;
900 }
901
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200902 // VLAN is configured on the interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700903 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200904 // Change VLAN Id
905 paramsJson["VLANId"] = inputVlanId;
906 OnDemandEthernetProvider::changeVlanId(
907 ifaceId, static_cast<uint32_t>(inputVlanId),
908 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
909 const boost::system::error_code ec) {
910 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700911 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200912 messages::internalError(), pathPrefix);
913 } else {
914 paramsJson["VLANEnable"] = true;
915 }
916 });
917 } else if (inputVlanEnabled == false) {
918 // Disable VLAN
919 OnDemandEthernetProvider::disableVlan(
920 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
921 const boost::system::error_code ec) {
922 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700923 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200924 messages::internalError(), pathPrefix);
925 } else {
926 paramsJson["VLANEnable"] = false;
927 }
928 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200929 }
930 }
931
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200932 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200933 void handleHostnamePatch(const nlohmann::json &input,
934 const EthernetInterfaceData &eth_data,
935 const std::shared_ptr<AsyncResp> &asyncResp) {
936 if (input.is_string()) {
937 std::string newHostname = input.get<std::string>();
938
939 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
940 // Change hostname
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700941 ethernetProvider.setHostName(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200942 newHostname,
943 [asyncResp, newHostname](const boost::system::error_code ec) {
944 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700945 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200946 messages::internalError(),
947 "/HostName");
948 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700949 asyncResp->res.jsonValue["HostName"] = newHostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200950 }
951 });
952 }
953 } else {
954 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700955 asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200956 messages::propertyValueTypeError(input.dump(), "HostName"),
957 "/HostName");
958 }
959 }
960
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200961 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
962 const std::vector<IPv4AddressData> &ipv4_data,
963 const std::shared_ptr<AsyncResp> &asyncResp) {
964 if (!input.is_array()) {
965 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700966 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200967 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
968 "/IPv4Addresses");
969 return;
970 }
971
972 // According to Redfish PATCH definition, size must be at least equal
973 if (input.size() < ipv4_data.size()) {
974 // TODO(kkowalsk) This should be a message indicating that not enough
975 // data has been provided
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700976 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200977 messages::internalError(), "/IPv4Addresses");
978 return;
979 }
980
981 json_util::Result addressFieldState;
982 json_util::Result subnetMaskFieldState;
983 json_util::Result addressOriginFieldState;
984 json_util::Result gatewayFieldState;
985 const std::string *addressFieldValue;
986 const std::string *subnetMaskFieldValue;
987 const std::string *addressOriginFieldValue = nullptr;
988 const std::string *gatewayFieldValue;
989 uint8_t subnetMaskAsPrefixLength;
990 std::string addressOriginInDBusFormat;
991
992 bool errorDetected = false;
993 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
994 // Check that entry is not of some unexpected type
995 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
996 // Invalid object type
997 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700998 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200999 messages::propertyValueTypeError(input[entryIdx].dump(),
1000 "IPv4Address"),
1001 "/IPv4Addresses/" + std::to_string(entryIdx));
1002
1003 continue;
1004 }
1005
1006 // Try to load fields
1007 addressFieldState = json_util::getString(
1008 "Address", input[entryIdx], addressFieldValue,
1009 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001010 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001011 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1012 subnetMaskFieldState = json_util::getString(
1013 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1014 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001015 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001016 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1017 addressOriginFieldState = json_util::getString(
1018 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1019 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001020 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001021 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1022 gatewayFieldState = json_util::getString(
1023 "Gateway", input[entryIdx], gatewayFieldValue,
1024 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001025 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001026 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1027
1028 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1029 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1030 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1031 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1032 return;
1033 }
1034
1035 if (addressFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001036 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001037 errorDetected = true;
1038 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001039 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001040 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1041 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1042 }
1043
1044 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001045 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001046 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1047 errorDetected = true;
1048 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001049 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001050 messages::propertyValueFormatError(*subnetMaskFieldValue,
1051 "SubnetMask"),
1052 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1053 }
1054
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001055 // get Address origin in proper format
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001056 addressOriginInDBusFormat =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001057 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001058 addressOriginFieldValue, true, false);
1059
1060 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1061 addressOriginInDBusFormat.empty()) {
1062 errorDetected = true;
1063 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001064 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001065 messages::propertyValueNotInList(*addressOriginFieldValue,
1066 "AddressOrigin"),
1067 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1068 }
1069
1070 if (gatewayFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001071 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001072 errorDetected = true;
1073 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001074 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001075 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1076 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1077 }
1078
1079 // If any error occured do not proceed with current entry, but do not
1080 // end loop
1081 if (errorDetected) {
1082 errorDetected = false;
1083 continue;
1084 }
1085
1086 if (entryIdx >= ipv4_data.size()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001087 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx];
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001088
1089 // Verify that all field were provided
1090 if (addressFieldState == json_util::Result::NOT_EXIST) {
1091 errorDetected = true;
1092 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001093 asyncResp->res.jsonValue, messages::propertyMissing("Address"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001094 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1095 }
1096
1097 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1098 errorDetected = true;
1099 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001100 asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001101 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1102 }
1103
1104 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1105 errorDetected = true;
1106 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001107 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001108 messages::propertyMissing("AddressOrigin"),
1109 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1110 }
1111
1112 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1113 errorDetected = true;
1114 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001115 asyncResp->res.jsonValue, messages::propertyMissing("Gateway"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001116 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1117 }
1118
1119 // If any error occured do not proceed with current entry, but do not
1120 // end loop
1121 if (errorDetected) {
1122 errorDetected = false;
1123 continue;
1124 }
1125
1126 // Create IPv4 with provided data
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001127 ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength,
1128 *gatewayFieldValue, *addressFieldValue,
1129 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001130 } else {
1131 // Existing object that should be modified/deleted/remain unchanged
1132 if (input[entryIdx].is_null()) {
1133 // Object should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001134 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx,
1135 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001136 } else if (input[entryIdx].is_object()) {
1137 if (input[entryIdx].size() == 0) {
1138 // Object shall remain unchanged
1139 continue;
1140 }
1141
1142 // Apply changes
1143 if (addressFieldState == json_util::Result::SUCCESS &&
1144 ipv4_data[entryIdx].address != nullptr &&
1145 *ipv4_data[entryIdx].address != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001146 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001147 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1148 *addressFieldValue, asyncResp);
1149 }
1150
1151 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1152 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001153 ethernetProvider.changeIPv4SubnetMaskProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001154 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1155 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1156 }
1157
1158 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1159 ipv4_data[entryIdx].origin != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001160 ethernetProvider.changeIPv4Origin(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001161 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1162 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1163 }
1164
1165 if (gatewayFieldState == json_util::Result::SUCCESS &&
1166 ipv4_data[entryIdx].gateway != nullptr &&
1167 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001168 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001169 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1170 *gatewayFieldValue, asyncResp);
1171 }
1172 }
1173 }
1174 }
1175 }
1176
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001177 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001178 const std::string &ifaceId, const EthernetInterfaceData &eth_data,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001179 const std::vector<IPv4AddressData> &ipv4_data) {
1180 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001181 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001182
1183 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001184 jsonResponse["Id"] = ifaceId;
1185 jsonResponse["@odata.id"] =
1186 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001187
1188 // ... then the one from DBus, regarding eth iface...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001189 if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001190
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001191 if (eth_data.macAddress != nullptr)
1192 jsonResponse["MACAddress"] = *eth_data.macAddress;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001193
1194 if (eth_data.hostname != nullptr)
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001195 jsonResponse["HostName"] = *eth_data.hostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001196
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001197 if (eth_data.vlanId != nullptr) {
1198 nlohmann::json &vlanObj = jsonResponse["VLAN"];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001199 vlanObj["VLANEnable"] = true;
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001200 vlanObj["VLANId"] = *eth_data.vlanId;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001201 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001202 nlohmann::json &vlanObj = jsonResponse["VLANs"];
Kowalski, Kamileb547732018-05-16 10:25:07 +02001203 vlanObj["@odata.id"] =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001204 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
Kowalski, Kamileb547732018-05-16 10:25:07 +02001205 "/VLANs";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001206 }
1207
1208 // ... at last, check if there are IPv4 data and prepare appropriate
1209 // collection
1210 if (ipv4_data.size() > 0) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001211 nlohmann::json ipv4Array = nlohmann::json::array();
1212 for (auto &ipv4Config : ipv4_data) {
1213 nlohmann::json jsonIpv4;
1214 if (ipv4Config.address != nullptr) {
1215 jsonIpv4["Address"] = *ipv4Config.address;
1216 if (ipv4Config.gateway != nullptr)
1217 jsonIpv4["Gateway"] = *ipv4Config.gateway;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001218
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001219 jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1220 jsonIpv4["SubnetMask"] = ipv4Config.netmask;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001221
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001222 ipv4Array.push_back(std::move(jsonIpv4));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001223 }
1224 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001225 jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001226 }
1227
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001228 return jsonResponse;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001229 }
1230
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001231 /**
1232 * Functions triggers appropriate requests on DBus
1233 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001234 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001235 const std::vector<std::string> &params) override {
1236 // TODO(Pawel) this shall be parametrized call (two params) to get
1237 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1238 // Check if there is required param, truly entering this shall be
1239 // impossible.
1240 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001241 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001242 res.end();
1243 return;
1244 }
1245
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001246 const std::string &ifaceId = params[0];
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001247
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001248 // get single eth interface data, and call the below callback for JSON
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001249 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001250 ethernetProvider.getEthernetIfaceData(
1251 ifaceId,
1252 [&, ifaceId](const bool &success, const EthernetInterfaceData &eth_data,
1253 const std::vector<IPv4AddressData> &ipv4_data) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001254 if (success) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001255 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001256 } else {
1257 // ... otherwise return error
1258 // TODO(Pawel)consider distinguish between non existing object, and
1259 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001260 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001261 res.jsonValue,
1262 messages::resourceNotFound("EthernetInterface", ifaceId));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001263 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001264 }
1265 res.end();
1266 });
1267 }
1268
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001269 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001270 const std::vector<std::string> &params) override {
1271 // TODO(Pawel) this shall be parametrized call (two params) to get
1272 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1273 // Check if there is required param, truly entering this shall be
1274 // impossible.
1275 if (params.size() != 1) {
1276 res.result(boost::beast::http::status::internal_server_error);
1277 res.end();
1278 return;
1279 }
1280
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001281 const std::string &ifaceId = params[0];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001282
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001283 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001284
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001285 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001286 return;
1287 }
1288
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001289 // get single eth interface data, and call the below callback for JSON
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001290 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001291 ethernetProvider.getEthernetIfaceData(
1292 ifaceId, [&, ifaceId, patchReq = std::move(patchReq) ](
1293 const bool &success, const EthernetInterfaceData &eth_data,
1294 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001295 if (!success) {
1296 // ... otherwise return error
1297 // TODO(Pawel)consider distinguish between non existing object, and
1298 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001299 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001300 res.jsonValue,
1301 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001302 res.result(boost::beast::http::status::not_found);
1303 res.end();
1304
1305 return;
1306 }
1307
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001308 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001309
1310 std::shared_ptr<AsyncResp> asyncResp =
1311 std::make_shared<AsyncResp>(res);
1312
1313 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1314 ++propertyIt) {
1315 if (propertyIt.key() == "VLAN") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001316 handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001317 asyncResp);
1318 } else if (propertyIt.key() == "HostName") {
1319 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001320 } else if (propertyIt.key() == "IPv4Addresses") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001321 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001322 asyncResp);
1323 } else if (propertyIt.key() == "IPv6Addresses") {
1324 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1325 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001326 res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001327 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001328 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001329 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001330
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001331 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001332 // Field not in scope of defined fields
1333 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001334 res.jsonValue, messages::propertyUnknown(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001335 } else if (*fieldInJsonIt != *propertyIt) {
1336 // User attempted to modify non-writable field
1337 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001338 res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001339 messages::propertyNotWritable(propertyIt.key()));
1340 }
1341 }
1342 }
1343 });
1344 }
1345
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001346 // Ethernet Provider object
1347 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001348 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001349};
1350
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001351class VlanNetworkInterfaceCollection;
1352
1353/**
1354 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1355 */
1356class VlanNetworkInterface : public Node {
1357 public:
1358 /*
1359 * Default Constructor
1360 */
1361 template <typename CrowApp>
1362 // TODO(Pawel) Remove line from below, where we assume that there is only one
1363 // manager called openbmc This shall be generic, but requires to update
1364 // GetSubroutes method
1365 VlanNetworkInterface(CrowApp &app)
Kowalski, Kamileb547732018-05-16 10:25:07 +02001366 : Node(
1367 app,
1368 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1369 std::string(), std::string()) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001370 Node::json["@odata.type"] =
1371 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1372 Node::json["@odata.context"] =
1373 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1374 Node::json["Name"] = "VLAN Network Interface";
1375
1376 entityPrivileges = {
1377 {boost::beast::http::verb::get, {{"Login"}}},
1378 {boost::beast::http::verb::head, {{"Login"}}},
1379 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1380 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1381 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1382 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1383 }
1384
1385 private:
1386 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001387 const std::string &parent_ifaceId, const std::string &ifaceId,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001388 const EthernetInterfaceData &eth_data,
1389 const std::vector<IPv4AddressData> &ipv4_data) {
1390 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001391 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001392
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001393 if (eth_data.vlanId == nullptr) {
Kowalski, Kamileb547732018-05-16 10:25:07 +02001394 // Interface not a VLAN - abort
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001395 messages::addMessageToErrorJson(jsonResponse, messages::internalError());
1396 return jsonResponse;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001397 }
1398
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001399 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001400 jsonResponse["Id"] = ifaceId;
1401 jsonResponse["@odata.id"] =
1402 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId +
1403 "/VLANs/" + ifaceId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001404
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001405 jsonResponse["VLANEnable"] = true;
1406 jsonResponse["VLANId"] = *eth_data.vlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001407
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001408 return jsonResponse;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001409 }
1410
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001411 bool verifyNames(crow::Response &res, const std::string &parent,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001412 const std::string &iface) {
1413 if (!boost::starts_with(iface, parent + "_")) {
1414 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001415 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001416 messages::resourceNotFound("VLAN Network Interface", iface));
1417 res.result(boost::beast::http::status::not_found);
1418 res.end();
1419
1420 return false;
1421 } else {
1422 return true;
1423 }
1424 }
1425
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001426 /**
1427 * Functions triggers appropriate requests on DBus
1428 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001429 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001430 const std::vector<std::string> &params) override {
1431 // TODO(Pawel) this shall be parametrized call (two params) to get
1432 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1433 // Check if there is required param, truly entering this shall be
1434 // impossible.
1435 if (params.size() != 2) {
1436 res.result(boost::beast::http::status::internal_server_error);
1437 res.end();
1438 return;
1439 }
1440
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001441 const std::string &parentIfaceId = params[0];
1442 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001443
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001444 // Get single eth interface data, and call the below callback for JSON
1445 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001446 ethernetProvider.getEthernetIfaceData(
1447 ifaceId,
1448 [&, parentIfaceId, ifaceId](
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001449 const bool &success, const EthernetInterfaceData &eth_data,
1450 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001451 if (success && eth_data.vlanId != nullptr) {
1452 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001453 eth_data, ipv4_data);
1454 } else {
1455 // ... otherwise return error
Kowalski, Kamileb547732018-05-16 10:25:07 +02001456 // TODO(Pawel)consider distinguish between non existing object, and
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001457 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001458 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001459 res.jsonValue,
1460 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001461 res.result(boost::beast::http::status::not_found);
1462 }
1463 res.end();
1464 });
1465 }
1466
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001467 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001468 const std::vector<std::string> &params) override {
1469 if (params.size() != 2) {
1470 res.result(boost::beast::http::status::internal_server_error);
1471 res.end();
1472 return;
1473 }
1474
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001475 const std::string &parent_ifaceId = params[0];
1476 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001477
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001478 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001479 return;
1480 }
1481
1482 nlohmann::json patchReq;
1483
1484 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1485 return;
1486 }
1487
1488 // Get single eth interface data, and call the below callback for JSON
1489 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001490 ethernetProvider.getEthernetIfaceData(
1491 ifaceId,
1492 [&, parent_ifaceId, ifaceId, patchReq = std::move(patchReq) ](
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001493 const bool &success, const EthernetInterfaceData &eth_data,
1494 const std::vector<IPv4AddressData> &ipv4_data) {
1495 if (!success) {
1496 // ... otherwise return error
1497 // TODO(Pawel)consider distinguish between non existing object,
1498 // and
1499 // other errors
1500 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001501 res.jsonValue,
1502 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001503 res.result(boost::beast::http::status::not_found);
1504 res.end();
1505
1506 return;
1507 }
1508
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001509 res.jsonValue = parseInterfaceData(parent_ifaceId, ifaceId,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001510 eth_data, ipv4_data);
1511
1512 std::shared_ptr<AsyncResp> asyncResp =
1513 std::make_shared<AsyncResp>(res);
1514
1515 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1516 ++propertyIt) {
1517 if (propertyIt.key() != "VLANEnable" &&
1518 propertyIt.key() != "VLANId") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001519 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001520
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001521 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001522 // Field not in scope of defined fields
1523 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001524 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001525 messages::propertyUnknown(propertyIt.key()));
1526 } else if (*fieldInJsonIt != *propertyIt) {
1527 // User attempted to modify non-writable field
1528 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001529 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001530 messages::propertyNotWritable(propertyIt.key()));
1531 }
1532 }
1533 }
1534
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001535 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/",
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001536 asyncResp);
1537 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001538 }
1539
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001540 void doDelete(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001541 const std::vector<std::string> &params) override {
1542 if (params.size() != 2) {
1543 res.result(boost::beast::http::status::internal_server_error);
1544 res.end();
1545 return;
1546 }
1547
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001548 const std::string &parent_ifaceId = params[0];
1549 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001550
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001551 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001552 return;
1553 }
1554
1555 // Get single eth interface data, and call the below callback for JSON
1556 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001557 ethernetProvider.getEthernetIfaceData(
1558 ifaceId,
1559 [&, parent_ifaceId, ifaceId](
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001560 const bool &success, const EthernetInterfaceData &eth_data,
1561 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001562 if (success && eth_data.vlanId != nullptr) {
1563 res.jsonValue = parseInterfaceData(parent_ifaceId, ifaceId,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001564 eth_data, ipv4_data);
1565
1566 // Disable VLAN
1567 OnDemandEthernetProvider::disableVlan(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001568 ifaceId, [&](const boost::system::error_code ec) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001569 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001570 res.jsonValue = nlohmann::json::object();
1571 messages::addMessageToErrorJson(res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001572 messages::internalError());
1573 res.result(
1574 boost::beast::http::status::internal_server_error);
1575 }
1576 res.end();
1577 });
1578 } else {
1579 // ... otherwise return error
1580 // TODO(Pawel)consider distinguish between non existing object,
1581 // and
1582 // other errors
1583 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001584 res.jsonValue,
1585 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001586 res.result(boost::beast::http::status::not_found);
1587 res.end();
1588 }
1589 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001590 }
1591
1592 /**
1593 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1594 * method, to maintain consistency of returned data, as Collection's doPost
1595 * should return data for created member which should match member's doGet
1596 * result in 100%.
1597 */
1598 friend VlanNetworkInterfaceCollection;
1599
1600 // Ethernet Provider object
1601 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001602 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001603};
1604
1605/**
1606 * VlanNetworkInterfaceCollection derived class for delivering
1607 * VLANNetworkInterface Collection Schema
1608 */
1609class VlanNetworkInterfaceCollection : public Node {
1610 public:
1611 template <typename CrowApp>
1612 // TODO(Pawel) Remove line from below, where we assume that there is only one
1613 // manager called openbmc This shall be generic, but requires to update
1614 // GetSubroutes method
1615 VlanNetworkInterfaceCollection(CrowApp &app)
1616 : Node(app,
1617 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1618 std::string()),
1619 memberVlan(app) {
1620 Node::json["@odata.type"] =
1621 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1622 Node::json["@odata.context"] =
1623 "/redfish/v1/$metadata"
1624 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1625 Node::json["Name"] = "VLAN Network Interface Collection";
1626
1627 entityPrivileges = {
1628 {boost::beast::http::verb::get, {{"Login"}}},
1629 {boost::beast::http::verb::head, {{"Login"}}},
1630 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1631 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1632 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1633 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1634 }
1635
1636 private:
1637 /**
1638 * Functions triggers appropriate requests on DBus
1639 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001640 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001641 const std::vector<std::string> &params) override {
1642 if (params.size() != 1) {
1643 // This means there is a problem with the router
1644 res.result(boost::beast::http::status::internal_server_error);
1645 res.end();
1646
1647 return;
1648 }
1649
1650 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1651 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001652 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001653 std::string rootInterfaceName = params[0];
1654
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001655 // get eth interface list, and call the below callback for JSON preparation
1656 ethernetProvider.getEthernetIfaceList([
1657 &, managerId{std::move(managerId)},
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001658 rootInterfaceName{std::move(rootInterfaceName)}
1659 ](const bool &success, const std::vector<std::string> &iface_list) {
1660 if (success) {
1661 bool rootInterfaceFound = false;
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001662 nlohmann::json ifaceArray = nlohmann::json::array();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001663
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001664 for (const std::string &ifaceItem : iface_list) {
1665 if (ifaceItem == rootInterfaceName) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001666 rootInterfaceFound = true;
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001667 } else if (boost::starts_with(ifaceItem, rootInterfaceName + "_")) {
1668 ifaceArray.push_back(
1669 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001670 "/EthernetInterfaces/" + rootInterfaceName +
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001671 "/VLANs/" + ifaceItem}});
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001672 }
1673 }
1674
1675 if (rootInterfaceFound) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001676 Node::json["Members"] = ifaceArray;
1677 Node::json["Members@odata.count"] = ifaceArray.size();
1678 Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId +
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001679 "/EthernetInterfaces/" + rootInterfaceName +
1680 "/VLANs";
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001681 res.jsonValue = Node::json;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001682 } else {
1683 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001684 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1685 rootInterfaceName));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001686 res.result(boost::beast::http::status::not_found);
1687 res.end();
1688 }
1689 } else {
1690 // No success, best what we can do is return INTERNALL ERROR
1691 res.result(boost::beast::http::status::internal_server_error);
1692 }
1693 res.end();
1694 });
1695 }
1696
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001697 void doPost(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001698 const std::vector<std::string> &params) override {
1699 if (params.size() != 1) {
1700 // This means there is a problem with the router
1701 res.result(boost::beast::http::status::internal_server_error);
1702 res.end();
1703 return;
1704 }
1705
1706 nlohmann::json postReq;
1707
1708 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1709 return;
1710 }
1711
1712 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1713 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001714 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001715 std::string rootInterfaceName = params[0];
1716 uint64_t vlanId;
1717 bool errorDetected;
1718
1719 if (json_util::getUnsigned(
1720 "VLANId", postReq, vlanId,
1721 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1722 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001723 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001724 res.end();
1725 return;
1726 }
1727
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001728 // get eth interface list, and call the below callback for JSON preparation
1729 ethernetProvider.getEthernetIfaceList([
1730 &, managerId{std::move(managerId)},
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001731 rootInterfaceName{std::move(rootInterfaceName)}
1732 ](const bool &success, const std::vector<std::string> &iface_list) {
1733 if (success) {
1734 bool rootInterfaceFound = false;
1735
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001736 for (const std::string &ifaceItem : iface_list) {
1737 if (ifaceItem == rootInterfaceName) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001738 rootInterfaceFound = true;
1739 break;
1740 }
1741 }
1742
1743 if (rootInterfaceFound) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001744 ethernetProvider.createVlan(
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001745 rootInterfaceName, vlanId,
1746 [&, vlanId, rootInterfaceName,
1747 req{std::move(req)} ](const boost::system::error_code ec) {
1748 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001749 messages::addMessageToErrorJson(res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001750 messages::internalError());
1751 res.end();
1752 } else {
1753 memberVlan.doGet(
1754 res, req,
1755 {rootInterfaceName,
1756 rootInterfaceName + "_" + std::to_string(vlanId)});
1757 }
1758 });
1759 } else {
1760 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001761 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1762 rootInterfaceName));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001763 res.result(boost::beast::http::status::not_found);
1764 res.end();
1765 }
1766 } else {
1767 // No success, best what we can do is return INTERNALL ERROR
1768 res.result(boost::beast::http::status::internal_server_error);
1769 res.end();
1770 }
1771 });
1772 }
1773
1774 // Ethernet Provider object
1775 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001776 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001777 VlanNetworkInterface memberVlan;
1778};
1779
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001780} // namespace redfish