blob: 4c44527e93365e12a857456fe2d0360a63b5cdd2 [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
Ed Tanousa434f2b2018-07-27 13:04:22 -0700199 if (boost::starts_with(static_cast<const std::string &>(objpath.first),
200 pathStart)) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100201 // and get approrpiate interface
202 const auto &interface =
203 objpath.second.find("xyz.openbmc_project.Network.IP");
204 if (interface != objpath.second.end()) {
205 // Make a properties 'shortcut', to make everything more readable
206 const PropertiesMapType &properties = interface->second;
207 // Instance IPv4AddressData structure, and set as appropriate
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700208 IPv4AddressData ipv4Address;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200209
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700210 ipv4Address.id = static_cast<const std::string &>(objpath.first)
211 .substr(pathStart.size());
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200212
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100213 // IPv4 address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700214 ipv4Address.address =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100215 extractProperty<std::string>(properties, "Address");
216 // IPv4 gateway
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700217 ipv4Address.gateway =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100218 extractProperty<std::string>(properties, "Gateway");
219
220 // Origin is kind of DBus object so fetch pointer...
221 const std::string *origin =
222 extractProperty<std::string>(properties, "Origin");
223 if (origin != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700224 ipv4Address.origin =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200225 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100226 }
227
228 // Netmask is presented as PrefixLength
229 const auto *mask =
230 extractProperty<uint8_t>(properties, "PrefixLength");
231 if (mask != nullptr) {
232 // convert it to the string
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700233 ipv4Address.netmask = getNetmask(*mask);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100234 }
235
236 // Attach IPv4 only if address is present
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700237 if (ipv4Address.address != nullptr) {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700238 // Check if given address is local, or global
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700239 if (boost::starts_with(*ipv4Address.address, "169.254")) {
240 ipv4Address.global = false;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100241 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700242 ipv4Address.global = true;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100243 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700244 ipv4_config.emplace_back(std::move(ipv4Address));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100245 }
246 }
247 }
248 }
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200249
250 /**
251 * We have to sort this vector and ensure that order of IPv4 addresses
252 * is consistent between GETs to allow modification and deletion in PATCHes
253 */
254 std::sort(ipv4_config.begin(), ipv4_config.end());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100255 }
256
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200257 static const constexpr int ipV4AddressSectionsCount = 4;
258
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100259 public:
260 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200261 * @brief Creates VLAN for given interface with given Id through D-Bus
262 *
263 * @param[in] ifaceId Id of interface for which VLAN will be created
264 * @param[in] inputVlanId ID of the new VLAN
265 * @param[in] callback Function that will be called after the operation
266 *
267 * @return None.
268 */
269 template <typename CallbackFunc>
270 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
271 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700272 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200273 callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
274 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
275 static_cast<uint32_t>(inputVlanId));
276 };
277
278 /**
279 * @brief Sets given Id on the given VLAN interface through D-Bus
280 *
281 * @param[in] ifaceId Id of VLAN interface that should be modified
282 * @param[in] inputVlanId New ID of the VLAN
283 * @param[in] callback Function that will be called after the operation
284 *
285 * @return None.
286 */
287 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200288 static void changeVlanId(const std::string &ifaceId,
289 const uint32_t &inputVlanId,
290 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700291 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200292 callback, "xyz.openbmc_project.Network",
293 std::string("/xyz/openbmc_project/network/") + ifaceId,
294 "org.freedesktop.DBus.Properties", "Set",
295 "xyz.openbmc_project.Network.VLAN", "Id",
296 sdbusplus::message::variant<uint32_t>(inputVlanId));
297 };
298
299 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200300 * @brief Helper function that verifies IP address to check if it is in
301 * proper format. If bits pointer is provided, also calculates active
302 * bit count for Subnet Mask.
303 *
304 * @param[in] ip IP that will be verified
305 * @param[out] bits Calculated mask in bits notation
306 *
307 * @return true in case of success, false otherwise
308 */
309 bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
310 uint8_t *bits = nullptr) {
311 std::vector<std::string> bytesInMask;
312
313 boost::split(bytesInMask, ip, boost::is_any_of("."));
314
315 if (bytesInMask.size() != ipV4AddressSectionsCount) {
316 return false;
317 }
318
319 if (bits != nullptr) {
320 *bits = 0;
321 }
322
323 char *endPtr;
324 long previousValue = 255;
325 bool firstZeroInByteHit;
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200326 for (const std::string &byte : bytesInMask) {
327 if (byte.empty()) {
328 return false;
329 }
330
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200331 // Use strtol instead of stroi to avoid exceptions
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200332 long value = std::strtol(byte.c_str(), &endPtr, 10);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200333
334 // endPtr should point to the end of the string, otherwise given string
335 // is not 100% number
336 if (*endPtr != '\0') {
337 return false;
338 }
339
340 // Value should be contained in byte
341 if (value < 0 || value > 255) {
342 return false;
343 }
344
345 if (bits != nullptr) {
346 // Mask has to be continuous between bytes
347 if (previousValue != 255 && value != 0) {
348 return false;
349 }
350
351 // Mask has to be continuous inside bytes
352 firstZeroInByteHit = false;
353
354 // Count bits
355 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
356 if (value & (1 << bitIdx)) {
357 if (firstZeroInByteHit) {
358 // Continuity not preserved
359 return false;
360 } else {
361 (*bits)++;
362 }
363 } else {
364 firstZeroInByteHit = true;
365 }
366 }
367 }
368
369 previousValue = value;
370 }
371
372 return true;
373 }
374
375 /**
376 * @brief Changes IPv4 address type property (Address, Gateway)
377 *
378 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700379 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200380 * @param[in] ipHash DBus Hash id of modified IP
381 * @param[in] name Name of field in JSON representation
382 * @param[in] newValue New value that should be written
383 * @param[io] asyncResp Response object that will be returned to client
384 *
385 * @return true if give IP is valid and has been sent do D-Bus, false
386 * otherwise
387 */
388 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
389 const std::string &ipHash,
390 const std::string &name,
391 const std::string &newValue,
392 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700393 auto callback =
394 [asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
395 newValue{std::move(newValue)}](const boost::system::error_code ec) {
396 if (ec) {
397 messages::addMessageToJson(
398 asyncResp->res.jsonValue, messages::internalError(),
399 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
400 } else {
401 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
402 }
403 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200404
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700405 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200406 std::move(callback), "xyz.openbmc_project.Network",
407 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
408 "org.freedesktop.DBus.Properties", "Set",
409 "xyz.openbmc_project.Network.IP", name,
410 sdbusplus::message::variant<std::string>(newValue));
411 };
412
413 /**
414 * @brief Changes IPv4 address origin property
415 *
416 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700417 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200418 * @param[in] ipHash DBus Hash id of modified IP
419 * @param[in] newValue New value in Redfish format
420 * @param[in] newValueDbus New value in D-Bus format
421 * @param[io] asyncResp Response object that will be returned to client
422 *
423 * @return true if give IP is valid and has been sent do D-Bus, false
424 * otherwise
425 */
426 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
427 const std::string &ipHash, const std::string &newValue,
428 const std::string &newValueDbus,
429 const std::shared_ptr<AsyncResp> &asyncResp) {
430 auto callback =
Ed Tanousa434f2b2018-07-27 13:04:22 -0700431 [asyncResp, ipIdx{std::move(ipIdx)},
432 newValue{std::move(newValue)}](const boost::system::error_code ec) {
433 if (ec) {
434 messages::addMessageToJson(
435 asyncResp->res.jsonValue, messages::internalError(),
436 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
437 } else {
438 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
439 newValue;
440 }
441 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200442
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700443 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200444 std::move(callback), "xyz.openbmc_project.Network",
445 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
446 "org.freedesktop.DBus.Properties", "Set",
447 "xyz.openbmc_project.Network.IP", "Origin",
448 sdbusplus::message::variant<std::string>(newValueDbus));
449 };
450
451 /**
452 * @brief Modifies SubnetMask for given IP
453 *
454 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700455 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200456 * @param[in] ipHash DBus Hash id of modified IP
457 * @param[in] newValueStr Mask in dot notation as string
458 * @param[in] newValue Mask as PrefixLength in bitcount
459 * @param[io] asyncResp Response object that will be returned to client
460 *
461 * @return None
462 */
463 void changeIPv4SubnetMaskProperty(
464 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
465 const std::string &newValueStr, uint8_t &newValue,
466 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700467 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
468 newValueStr{std::move(newValueStr)}](
469 const boost::system::error_code ec) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200470 if (ec) {
471 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700472 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200473 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
474 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700475 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200476 newValueStr;
477 }
478 };
479
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700480 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200481 std::move(callback), "xyz.openbmc_project.Network",
482 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
483 "org.freedesktop.DBus.Properties", "Set",
484 "xyz.openbmc_project.Network.IP", "PrefixLength",
485 sdbusplus::message::variant<uint8_t>(newValue));
486 };
487
488 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200489 * @brief Disables VLAN with given ifaceId
490 *
491 * @param[in] ifaceId Id of VLAN interface that should be disabled
492 * @param[in] callback Function that will be called after the operation
493 *
494 * @return None.
495 */
496 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200497 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700498 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200499 callback, "xyz.openbmc_project.Network",
500 std::string("/xyz/openbmc_project/network/") + ifaceId,
501 "xyz.openbmc_project.Object.Delete", "Delete");
502 };
503
504 /**
505 * @brief Sets given HostName of the machine through D-Bus
506 *
507 * @param[in] newHostname New name that HostName will be changed to
508 * @param[in] callback Function that will be called after the operation
509 *
510 * @return None.
511 */
512 template <typename CallbackFunc>
513 void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700514 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200515 callback, "xyz.openbmc_project.Network",
516 "/xyz/openbmc_project/network/config",
517 "org.freedesktop.DBus.Properties", "Set",
518 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
519 sdbusplus::message::variant<std::string>(newHostname));
520 };
521
522 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200523 * @brief Deletes given IPv4
524 *
525 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700526 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200527 * @param[in] ipHash DBus Hash id of IP that should be deleted
528 * @param[io] asyncResp Response object that will be returned to client
529 *
530 * @return None
531 */
532 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
533 unsigned int ipIdx,
534 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700535 crow::connections::systemBus->async_method_call(
Ed Tanousa434f2b2018-07-27 13:04:22 -0700536 [ipIdx{std::move(ipIdx)},
537 asyncResp{std::move(asyncResp)}](const boost::system::error_code ec) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200538 if (ec) {
539 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700540 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200541 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
542 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700543 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200544 }
545 },
546 "xyz.openbmc_project.Network",
547 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
548 "xyz.openbmc_project.Object.Delete", "Delete");
549 }
550
551 /**
552 * @brief Creates IPv4 with given data
553 *
554 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700555 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200556 * @param[in] ipHash DBus Hash id of IP that should be deleted
557 * @param[io] asyncResp Response object that will be returned to client
558 *
559 * @return None
560 */
561 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
562 uint8_t subnetMask, const std::string &gateway,
563 const std::string &address,
564 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700565 auto createIpHandler =
566 [ipIdx{std::move(ipIdx)},
567 asyncResp{std::move(asyncResp)}](const boost::system::error_code ec) {
568 if (ec) {
569 messages::addMessageToJson(
570 asyncResp->res.jsonValue, messages::internalError(),
571 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
572 }
573 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200574
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700575 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200576 std::move(createIpHandler), "xyz.openbmc_project.Network",
577 "/xyz/openbmc_project/network/" + ifaceId,
578 "xyz.openbmc_project.Network.IP.Create", "IP",
579 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
580 gateway);
581 }
582
583 /**
584 * @brief Translates Address Origin value from D-Bus to Redfish format and
585 * vice-versa
586 *
587 * @param[in] inputOrigin Input value that should be translated
588 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
589 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse
590 *
591 * @return Empty string in case of failure, translated value otherwise
592 */
593 std::string translateAddressOriginBetweenDBusAndRedfish(
594 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
595 // Invalid pointer
596 if (inputOrigin == nullptr) {
597 return "";
598 }
599
600 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
601 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
602
603 std::array<std::pair<const char *, const char *>, 6> translationTable{
604 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
605 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
606 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
607 "IPv4LinkLocal"},
608 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
609 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
610 "LinkLocal"},
611 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
612
613 for (unsigned int i = 0; i < translationTable.size(); i++) {
614 // Skip unrelated
615 if (isIPv4 && i >= firstIPv6OnlyIdx) break;
616 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
617
618 // When translating D-Bus to Redfish compare input to first element
619 if (isFromDBus && translationTable[i].first == *inputOrigin)
620 return translationTable[i].second;
621
622 // When translating Redfish to D-Bus compare input to second element
623 if (!isFromDBus && translationTable[i].second == *inputOrigin)
624 return translationTable[i].first;
625 }
626
627 // If we are still here, that means that value has not been found
628 return "";
629 }
630
631 /**
632 * Function that retrieves all properties for given Ethernet Interface
633 * Object
634 * from EntityManager Network Manager
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700635 * @param ethifaceId a eth interface id to query on DBus
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200636 * @param callback a function that shall be called to convert Dbus output
637 * into JSON
638 */
639 template <typename CallbackFunc>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700640 void getEthernetIfaceData(const std::string &ethifaceId,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200641 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700642 crow::connections::systemBus->async_method_call(
Ed Tanousa434f2b2018-07-27 13:04:22 -0700643 [this, ethifaceId{std::move(ethifaceId)},
644 callback{std::move(callback)}](
645 const boost::system::error_code error_code,
646 const GetManagedObjectsType &resp) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700647 EthernetInterfaceData ethData{};
648 std::vector<IPv4AddressData> ipv4Data;
649 ipv4Data.reserve(maxIpV4AddressesPerInterface);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200650
651 if (error_code) {
652 // Something wrong on DBus, the error_code is not important at
653 // this moment, just return success=false, and empty output. Since
654 // size of vector may vary depending on information from Network
655 // Manager, and empty output could not be treated same way as
656 // error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700657 callback(false, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200658 return;
659 }
660
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200661 // Find interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700662 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200663 resp.end()) {
664 // Interface has not been found
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700665 callback(false, ethData, ipv4Data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200666 return;
667 }
668
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700669 extractEthernetInterfaceData(ethifaceId, resp, ethData);
670 extractIPv4Data(ethifaceId, resp, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200671
672 // Fix global GW
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700673 for (IPv4AddressData &ipv4 : ipv4Data) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200674 if ((ipv4.global) &&
675 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700676 ipv4.gateway = ethData.defaultGateway;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200677 }
678 }
679
Ed Tanousa434f2b2018-07-27 13:04:22 -0700680 // Finally make a callback with useful data
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700681 callback(true, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200682 },
683 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
684 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
685 };
686
687 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100688 * Function that retrieves all Ethernet Interfaces available through Network
689 * Manager
690 * @param callback a function that shall be called to convert Dbus output into
691 * JSON.
692 */
693 template <typename CallbackFunc>
694 void getEthernetIfaceList(CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700695 crow::connections::systemBus->async_method_call(
Ed Tanousa434f2b2018-07-27 13:04:22 -0700696 [this, callback{std::move(callback)}](
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100697 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700698 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100699 // Callback requires vector<string> to retrieve all available ethernet
700 // interfaces
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700701 std::vector<std::string> ifaceList;
702 ifaceList.reserve(resp.size());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100703 if (error_code) {
704 // Something wrong on DBus, the error_code is not important at this
705 // moment, just return success=false, and empty output. Since size
706 // of vector may vary depending on information from Network Manager,
707 // and empty output could not be treated same way as error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700708 callback(false, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100709 return;
710 }
711
712 // Iterate over all retrieved ObjectPaths.
713 for (auto &objpath : resp) {
714 // And all interfaces available for certain ObjectPath.
715 for (auto &interface : objpath.second) {
716 // If interface is xyz.openbmc_project.Network.EthernetInterface,
717 // this is what we're looking for.
718 if (interface.first ==
719 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700720 // Cut out everything until last "/", ...
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700721 const std::string &ifaceId =
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200722 static_cast<const std::string &>(objpath.first);
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700723 std::size_t lastPos = ifaceId.rfind("/");
724 if (lastPos != std::string::npos) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100725 // and put it into output vector.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700726 ifaceList.emplace_back(ifaceId.substr(lastPos + 1));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100727 }
728 }
729 }
730 }
Ed Tanousa434f2b2018-07-27 13:04:22 -0700731 // Finally make a callback with useful data
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700732 callback(true, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100733 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700734 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
735 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100736 };
737};
738
739/**
740 * EthernetCollection derived class for delivering Ethernet Collection Schema
741 */
742class EthernetCollection : public Node {
743 public:
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100744 // TODO(Pawel) Remove line from below, where we assume that there is only one
745 // manager called openbmc This shall be generic, but requires to update
746 // GetSubroutes method
747 EthernetCollection(CrowApp &app)
748 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
749 Node::json["@odata.type"] =
750 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
751 Node::json["@odata.context"] =
752 "/redfish/v1/"
753 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
754 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
755 Node::json["Name"] = "Ethernet Network Interface Collection";
756 Node::json["Description"] =
757 "Collection of EthernetInterfaces for this Manager";
758
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200759 entityPrivileges = {
760 {boost::beast::http::verb::get, {{"Login"}}},
761 {boost::beast::http::verb::head, {{"Login"}}},
762 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
763 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
764 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
765 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100766 }
767
768 private:
769 /**
770 * Functions triggers appropriate requests on DBus
771 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700772 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100773 const std::vector<std::string> &params) override {
774 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
775 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700776 std::string managerId = "openbmc";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100777
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700778 // get eth interface list, and call the below callback for JSON preparation
Ed Tanousa434f2b2018-07-27 13:04:22 -0700779 ethernetProvider.getEthernetIfaceList(
780 [&, managerId{std::move(managerId)}](
781 const bool &success, const std::vector<std::string> &iface_list) {
782 if (success) {
783 nlohmann::json ifaceArray = nlohmann::json::array();
784 for (const std::string &ifaceItem : iface_list) {
785 ifaceArray.push_back(
786 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
787 "/EthernetInterfaces/" + ifaceItem}});
788 }
789 Node::json["Members"] = ifaceArray;
790 Node::json["Members@odata.count"] = ifaceArray.size();
791 Node::json["@odata.id"] =
792 "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces";
793 res.jsonValue = Node::json;
794 } else {
795 // No success, best what we can do is return INTERNALL ERROR
796 res.result(boost::beast::http::status::internal_server_error);
797 }
798 res.end();
799 });
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100800 }
801
802 // Ethernet Provider object
803 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700804 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100805};
806
807/**
808 * EthernetInterface derived class for delivering Ethernet Schema
809 */
810class EthernetInterface : public Node {
811 public:
812 /*
813 * Default Constructor
814 */
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100815 // TODO(Pawel) Remove line from below, where we assume that there is only one
816 // manager called openbmc This shall be generic, but requires to update
817 // GetSubroutes method
818 EthernetInterface(CrowApp &app)
819 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
820 std::string()) {
821 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
822 Node::json["@odata.context"] =
823 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
824 Node::json["Name"] = "Manager Ethernet Interface";
825 Node::json["Description"] = "Management Network Interface";
826
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200827 entityPrivileges = {
828 {boost::beast::http::verb::get, {{"Login"}}},
829 {boost::beast::http::verb::head, {{"Login"}}},
830 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
831 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
832 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
833 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100834 }
835
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200836 // TODO(kkowalsk) Find a suitable class/namespace for this
837 static void handleVlanPatch(const std::string &ifaceId,
838 const nlohmann::json &input,
839 const EthernetInterfaceData &eth_data,
840 const std::string &pathPrefix,
841 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200842 if (!input.is_object()) {
843 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700844 asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200845 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200846 return;
847 }
848
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200849 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
850 nlohmann::json &paramsJson =
851 (pathPrefix == "/")
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700852 ? asyncResp->res.jsonValue
853 : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>(
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200854 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200855 bool inputVlanEnabled;
856 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200857
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200858 json_util::Result inputVlanEnabledState = json_util::getBool(
859 "VLANEnable", input, inputVlanEnabled,
860 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700861 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200862 json_util::Result inputVlanIdState = json_util::getUnsigned(
863 "VLANId", input, inputVlanId,
864 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700865 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200866 bool inputInvalid = false;
867
868 // Do not proceed if fields in VLAN object were of wrong type
869 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
870 inputVlanIdState == json_util::Result::WRONG_TYPE) {
871 return;
872 }
873
874 // Verify input
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700875 if (eth_data.vlanId == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200876 // This interface is not a VLAN. Cannot do anything with it
877 // TODO(kkowalsk) Change this message
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700878 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200879 messages::propertyMissing("VLANEnable"),
880 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200881
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200882 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200883 } else {
884 // Load actual data into field values if they were not provided
885 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
886 inputVlanEnabled = true;
887 }
888
889 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700890 inputVlanId = *eth_data.vlanId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200891 }
892 }
893
894 // Do not proceed if input has not been valid
895 if (inputInvalid) {
896 return;
897 }
898
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200899 // VLAN is configured on the interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700900 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200901 // Change VLAN Id
902 paramsJson["VLANId"] = inputVlanId;
903 OnDemandEthernetProvider::changeVlanId(
904 ifaceId, static_cast<uint32_t>(inputVlanId),
Ed Tanousa434f2b2018-07-27 13:04:22 -0700905 [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200906 const boost::system::error_code ec) {
907 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700908 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200909 messages::internalError(), pathPrefix);
910 } else {
911 paramsJson["VLANEnable"] = true;
912 }
913 });
914 } else if (inputVlanEnabled == false) {
915 // Disable VLAN
916 OnDemandEthernetProvider::disableVlan(
Ed Tanousa434f2b2018-07-27 13:04:22 -0700917 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200918 const boost::system::error_code ec) {
919 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700920 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200921 messages::internalError(), pathPrefix);
922 } else {
923 paramsJson["VLANEnable"] = false;
924 }
925 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200926 }
927 }
928
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200929 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200930 void handleHostnamePatch(const nlohmann::json &input,
931 const EthernetInterfaceData &eth_data,
932 const std::shared_ptr<AsyncResp> &asyncResp) {
933 if (input.is_string()) {
934 std::string newHostname = input.get<std::string>();
935
936 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
937 // Change hostname
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700938 ethernetProvider.setHostName(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200939 newHostname,
940 [asyncResp, newHostname](const boost::system::error_code ec) {
941 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700942 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200943 messages::internalError(),
944 "/HostName");
945 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700946 asyncResp->res.jsonValue["HostName"] = newHostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200947 }
948 });
949 }
950 } else {
951 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700952 asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200953 messages::propertyValueTypeError(input.dump(), "HostName"),
954 "/HostName");
955 }
956 }
957
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200958 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
959 const std::vector<IPv4AddressData> &ipv4_data,
960 const std::shared_ptr<AsyncResp> &asyncResp) {
961 if (!input.is_array()) {
962 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700963 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200964 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
965 "/IPv4Addresses");
966 return;
967 }
968
969 // According to Redfish PATCH definition, size must be at least equal
970 if (input.size() < ipv4_data.size()) {
971 // TODO(kkowalsk) This should be a message indicating that not enough
972 // data has been provided
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700973 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200974 messages::internalError(), "/IPv4Addresses");
975 return;
976 }
977
978 json_util::Result addressFieldState;
979 json_util::Result subnetMaskFieldState;
980 json_util::Result addressOriginFieldState;
981 json_util::Result gatewayFieldState;
982 const std::string *addressFieldValue;
983 const std::string *subnetMaskFieldValue;
984 const std::string *addressOriginFieldValue = nullptr;
985 const std::string *gatewayFieldValue;
986 uint8_t subnetMaskAsPrefixLength;
987 std::string addressOriginInDBusFormat;
988
989 bool errorDetected = false;
990 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
991 // Check that entry is not of some unexpected type
992 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
993 // Invalid object type
994 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700995 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200996 messages::propertyValueTypeError(input[entryIdx].dump(),
997 "IPv4Address"),
998 "/IPv4Addresses/" + std::to_string(entryIdx));
999
1000 continue;
1001 }
1002
1003 // Try to load fields
1004 addressFieldState = json_util::getString(
1005 "Address", input[entryIdx], addressFieldValue,
1006 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001007 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001008 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1009 subnetMaskFieldState = json_util::getString(
1010 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1011 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001012 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001013 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1014 addressOriginFieldState = json_util::getString(
1015 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1016 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001017 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001018 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1019 gatewayFieldState = json_util::getString(
1020 "Gateway", input[entryIdx], gatewayFieldValue,
1021 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001022 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001023 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1024
1025 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1026 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1027 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1028 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1029 return;
1030 }
1031
1032 if (addressFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001033 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001034 errorDetected = true;
1035 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001036 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001037 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1038 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1039 }
1040
1041 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001042 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001043 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1044 errorDetected = true;
1045 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001046 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001047 messages::propertyValueFormatError(*subnetMaskFieldValue,
1048 "SubnetMask"),
1049 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1050 }
1051
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001052 // get Address origin in proper format
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001053 addressOriginInDBusFormat =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001054 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001055 addressOriginFieldValue, true, false);
1056
1057 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1058 addressOriginInDBusFormat.empty()) {
1059 errorDetected = true;
1060 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001061 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001062 messages::propertyValueNotInList(*addressOriginFieldValue,
1063 "AddressOrigin"),
1064 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1065 }
1066
1067 if (gatewayFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001068 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001069 errorDetected = true;
1070 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001071 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001072 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1073 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1074 }
1075
1076 // If any error occured do not proceed with current entry, but do not
1077 // end loop
1078 if (errorDetected) {
1079 errorDetected = false;
1080 continue;
1081 }
1082
1083 if (entryIdx >= ipv4_data.size()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001084 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx];
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001085
1086 // Verify that all field were provided
1087 if (addressFieldState == json_util::Result::NOT_EXIST) {
1088 errorDetected = true;
1089 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001090 asyncResp->res.jsonValue, messages::propertyMissing("Address"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001091 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1092 }
1093
1094 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1095 errorDetected = true;
1096 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001097 asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001098 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1099 }
1100
1101 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1102 errorDetected = true;
1103 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001104 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001105 messages::propertyMissing("AddressOrigin"),
1106 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1107 }
1108
1109 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1110 errorDetected = true;
1111 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001112 asyncResp->res.jsonValue, messages::propertyMissing("Gateway"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001113 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1114 }
1115
1116 // If any error occured do not proceed with current entry, but do not
1117 // end loop
1118 if (errorDetected) {
1119 errorDetected = false;
1120 continue;
1121 }
1122
1123 // Create IPv4 with provided data
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001124 ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength,
1125 *gatewayFieldValue, *addressFieldValue,
1126 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001127 } else {
1128 // Existing object that should be modified/deleted/remain unchanged
1129 if (input[entryIdx].is_null()) {
1130 // Object should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001131 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx,
1132 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001133 } else if (input[entryIdx].is_object()) {
1134 if (input[entryIdx].size() == 0) {
1135 // Object shall remain unchanged
1136 continue;
1137 }
1138
1139 // Apply changes
1140 if (addressFieldState == json_util::Result::SUCCESS &&
1141 ipv4_data[entryIdx].address != nullptr &&
1142 *ipv4_data[entryIdx].address != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001143 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001144 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1145 *addressFieldValue, asyncResp);
1146 }
1147
1148 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1149 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001150 ethernetProvider.changeIPv4SubnetMaskProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001151 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1152 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1153 }
1154
1155 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1156 ipv4_data[entryIdx].origin != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001157 ethernetProvider.changeIPv4Origin(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001158 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1159 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1160 }
1161
1162 if (gatewayFieldState == json_util::Result::SUCCESS &&
1163 ipv4_data[entryIdx].gateway != nullptr &&
1164 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001165 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001166 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1167 *gatewayFieldValue, asyncResp);
1168 }
1169 }
1170 }
1171 }
1172 }
1173
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001174 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001175 const std::string &ifaceId, const EthernetInterfaceData &eth_data,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001176 const std::vector<IPv4AddressData> &ipv4_data) {
1177 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001178 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001179
1180 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001181 jsonResponse["Id"] = ifaceId;
1182 jsonResponse["@odata.id"] =
1183 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001184
1185 // ... then the one from DBus, regarding eth iface...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001186 if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001187
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001188 if (eth_data.macAddress != nullptr)
1189 jsonResponse["MACAddress"] = *eth_data.macAddress;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001190
1191 if (eth_data.hostname != nullptr)
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001192 jsonResponse["HostName"] = *eth_data.hostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001193
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001194 if (eth_data.vlanId != nullptr) {
1195 nlohmann::json &vlanObj = jsonResponse["VLAN"];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001196 vlanObj["VLANEnable"] = true;
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001197 vlanObj["VLANId"] = *eth_data.vlanId;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001198 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001199 nlohmann::json &vlanObj = jsonResponse["VLANs"];
Kowalski, Kamileb547732018-05-16 10:25:07 +02001200 vlanObj["@odata.id"] =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001201 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
Kowalski, Kamileb547732018-05-16 10:25:07 +02001202 "/VLANs";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001203 }
1204
1205 // ... at last, check if there are IPv4 data and prepare appropriate
1206 // collection
1207 if (ipv4_data.size() > 0) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001208 nlohmann::json ipv4Array = nlohmann::json::array();
1209 for (auto &ipv4Config : ipv4_data) {
1210 nlohmann::json jsonIpv4;
1211 if (ipv4Config.address != nullptr) {
1212 jsonIpv4["Address"] = *ipv4Config.address;
1213 if (ipv4Config.gateway != nullptr)
1214 jsonIpv4["Gateway"] = *ipv4Config.gateway;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001215
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001216 jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1217 jsonIpv4["SubnetMask"] = ipv4Config.netmask;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001218
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001219 ipv4Array.push_back(std::move(jsonIpv4));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001220 }
1221 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001222 jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001223 }
1224
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001225 return jsonResponse;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001226 }
1227
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001228 /**
1229 * Functions triggers appropriate requests on DBus
1230 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001231 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001232 const std::vector<std::string> &params) override {
1233 // TODO(Pawel) this shall be parametrized call (two params) to get
1234 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1235 // Check if there is required param, truly entering this shall be
1236 // impossible.
1237 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001238 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001239 res.end();
1240 return;
1241 }
1242
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001243 const std::string &ifaceId = params[0];
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001244
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001245 // get single eth interface data, and call the below callback for JSON
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001246 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001247 ethernetProvider.getEthernetIfaceData(
1248 ifaceId,
1249 [&, ifaceId](const bool &success, const EthernetInterfaceData &eth_data,
1250 const std::vector<IPv4AddressData> &ipv4_data) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001251 if (success) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001252 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001253 } else {
1254 // ... otherwise return error
1255 // TODO(Pawel)consider distinguish between non existing object, and
1256 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001257 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001258 res.jsonValue,
1259 messages::resourceNotFound("EthernetInterface", ifaceId));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001260 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001261 }
1262 res.end();
1263 });
1264 }
1265
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001266 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001267 const std::vector<std::string> &params) override {
1268 // TODO(Pawel) this shall be parametrized call (two params) to get
1269 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1270 // Check if there is required param, truly entering this shall be
1271 // impossible.
1272 if (params.size() != 1) {
1273 res.result(boost::beast::http::status::internal_server_error);
1274 res.end();
1275 return;
1276 }
1277
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001278 const std::string &ifaceId = params[0];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001279
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001280 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001281
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001282 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001283 return;
1284 }
1285
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001286 // get single eth interface data, and call the below callback for JSON
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001287 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001288 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001289 ifaceId, [&, ifaceId, patchReq = std::move(patchReq)](
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001290 const bool &success, const EthernetInterfaceData &eth_data,
1291 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001292 if (!success) {
1293 // ... otherwise return error
1294 // TODO(Pawel)consider distinguish between non existing object, and
1295 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001296 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001297 res.jsonValue,
1298 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001299 res.result(boost::beast::http::status::not_found);
1300 res.end();
1301
1302 return;
1303 }
1304
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001305 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001306
1307 std::shared_ptr<AsyncResp> asyncResp =
1308 std::make_shared<AsyncResp>(res);
1309
1310 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1311 ++propertyIt) {
1312 if (propertyIt.key() == "VLAN") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001313 handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001314 asyncResp);
1315 } else if (propertyIt.key() == "HostName") {
1316 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001317 } else if (propertyIt.key() == "IPv4Addresses") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001318 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001319 asyncResp);
1320 } else if (propertyIt.key() == "IPv6Addresses") {
1321 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1322 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001323 res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001324 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001325 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001326 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001327
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001328 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001329 // Field not in scope of defined fields
1330 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001331 res.jsonValue, messages::propertyUnknown(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001332 } else if (*fieldInJsonIt != *propertyIt) {
1333 // User attempted to modify non-writable field
1334 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001335 res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001336 messages::propertyNotWritable(propertyIt.key()));
1337 }
1338 }
1339 }
1340 });
1341 }
1342
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001343 // Ethernet Provider object
1344 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001345 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001346};
1347
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001348class VlanNetworkInterfaceCollection;
1349
1350/**
1351 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1352 */
1353class VlanNetworkInterface : public Node {
1354 public:
1355 /*
1356 * Default Constructor
1357 */
1358 template <typename CrowApp>
1359 // TODO(Pawel) Remove line from below, where we assume that there is only one
1360 // manager called openbmc This shall be generic, but requires to update
1361 // GetSubroutes method
1362 VlanNetworkInterface(CrowApp &app)
Kowalski, Kamileb547732018-05-16 10:25:07 +02001363 : Node(
1364 app,
1365 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1366 std::string(), std::string()) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001367 Node::json["@odata.type"] =
1368 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1369 Node::json["@odata.context"] =
1370 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1371 Node::json["Name"] = "VLAN Network Interface";
1372
1373 entityPrivileges = {
1374 {boost::beast::http::verb::get, {{"Login"}}},
1375 {boost::beast::http::verb::head, {{"Login"}}},
1376 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1377 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1378 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1379 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1380 }
1381
1382 private:
1383 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001384 const std::string &parent_ifaceId, const std::string &ifaceId,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001385 const EthernetInterfaceData &eth_data,
1386 const std::vector<IPv4AddressData> &ipv4_data) {
1387 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001388 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001389
1390 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001391 jsonResponse["Id"] = ifaceId;
1392 jsonResponse["@odata.id"] =
1393 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId +
1394 "/VLANs/" + ifaceId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001395
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001396 jsonResponse["VLANEnable"] = true;
1397 jsonResponse["VLANId"] = *eth_data.vlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001398
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001399 return jsonResponse;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001400 }
1401
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001402 bool verifyNames(crow::Response &res, const std::string &parent,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001403 const std::string &iface) {
1404 if (!boost::starts_with(iface, parent + "_")) {
1405 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001406 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001407 messages::resourceNotFound("VLAN Network Interface", iface));
1408 res.result(boost::beast::http::status::not_found);
1409 res.end();
1410
1411 return false;
1412 } else {
1413 return true;
1414 }
1415 }
1416
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001417 /**
1418 * Functions triggers appropriate requests on DBus
1419 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001420 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001421 const std::vector<std::string> &params) override {
1422 // TODO(Pawel) this shall be parametrized call (two params) to get
1423 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1424 // Check if there is required param, truly entering this shall be
1425 // impossible.
1426 if (params.size() != 2) {
1427 res.result(boost::beast::http::status::internal_server_error);
1428 res.end();
1429 return;
1430 }
1431
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001432 const std::string &parentIfaceId = params[0];
1433 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001434
Ed Tanousa434f2b2018-07-27 13:04:22 -07001435 if (!verifyNames(res, parentIfaceId, ifaceId)) {
1436 return;
1437 }
1438
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001439 // Get single eth interface data, and call the below callback for JSON
1440 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001441 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001442 ifaceId, [&, parentIfaceId, ifaceId](
1443 const bool &success, const EthernetInterfaceData &eth_data,
1444 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001445 if (success && eth_data.vlanId != nullptr) {
Ed Tanousa434f2b2018-07-27 13:04:22 -07001446 res.jsonValue =
1447 parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001448 } else {
1449 // ... otherwise return error
Kowalski, Kamileb547732018-05-16 10:25:07 +02001450 // TODO(Pawel)consider distinguish between non existing object, and
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001451 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001452 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001453 res.jsonValue,
1454 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001455 res.result(boost::beast::http::status::not_found);
1456 }
1457 res.end();
1458 });
1459 }
1460
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001461 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001462 const std::vector<std::string> &params) override {
1463 if (params.size() != 2) {
1464 res.result(boost::beast::http::status::internal_server_error);
1465 res.end();
1466 return;
1467 }
1468
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001469 const std::string &parent_ifaceId = params[0];
1470 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001471
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001472 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001473 return;
1474 }
1475
1476 nlohmann::json patchReq;
1477
1478 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1479 return;
1480 }
1481
1482 // Get single eth interface data, and call the below callback for JSON
1483 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001484 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001485 ifaceId, [&, parent_ifaceId, ifaceId, patchReq = std::move(patchReq)](
1486 const bool &success, const EthernetInterfaceData &eth_data,
1487 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001488 if (!success) {
1489 // ... otherwise return error
1490 // TODO(Pawel)consider distinguish between non existing object,
1491 // and
1492 // other errors
1493 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001494 res.jsonValue,
1495 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001496 res.result(boost::beast::http::status::not_found);
1497 res.end();
1498
1499 return;
1500 }
1501
Ed Tanousa434f2b2018-07-27 13:04:22 -07001502 res.jsonValue =
1503 parseInterfaceData(parent_ifaceId, ifaceId, eth_data, ipv4_data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001504
1505 std::shared_ptr<AsyncResp> asyncResp =
1506 std::make_shared<AsyncResp>(res);
1507
1508 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1509 ++propertyIt) {
1510 if (propertyIt.key() != "VLANEnable" &&
1511 propertyIt.key() != "VLANId") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001512 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001513
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001514 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001515 // Field not in scope of defined fields
1516 messages::addMessageToJsonRoot(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001517 res.jsonValue, messages::propertyUnknown(propertyIt.key()));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001518 } else if (*fieldInJsonIt != *propertyIt) {
1519 // User attempted to modify non-writable field
1520 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001521 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001522 messages::propertyNotWritable(propertyIt.key()));
1523 }
1524 }
1525 }
1526
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001527 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/",
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001528 asyncResp);
1529 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001530 }
1531
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001532 void doDelete(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001533 const std::vector<std::string> &params) override {
1534 if (params.size() != 2) {
1535 res.result(boost::beast::http::status::internal_server_error);
1536 res.end();
1537 return;
1538 }
1539
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001540 const std::string &parent_ifaceId = params[0];
1541 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001542
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001543 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001544 return;
1545 }
1546
1547 // Get single eth interface data, and call the below callback for JSON
1548 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001549 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001550 ifaceId, [&, parent_ifaceId, ifaceId](
1551 const bool &success, const EthernetInterfaceData &eth_data,
1552 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001553 if (success && eth_data.vlanId != nullptr) {
1554 res.jsonValue = parseInterfaceData(parent_ifaceId, ifaceId,
Ed Tanousa434f2b2018-07-27 13:04:22 -07001555 eth_data, ipv4_data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001556
1557 // Disable VLAN
1558 OnDemandEthernetProvider::disableVlan(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001559 ifaceId, [&](const boost::system::error_code ec) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001560 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001561 res.jsonValue = nlohmann::json::object();
1562 messages::addMessageToErrorJson(res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001563 messages::internalError());
1564 res.result(
1565 boost::beast::http::status::internal_server_error);
1566 }
1567 res.end();
1568 });
1569 } else {
1570 // ... otherwise return error
1571 // TODO(Pawel)consider distinguish between non existing object,
1572 // and
1573 // other errors
1574 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001575 res.jsonValue,
1576 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001577 res.result(boost::beast::http::status::not_found);
1578 res.end();
1579 }
1580 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001581 }
1582
1583 /**
1584 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1585 * method, to maintain consistency of returned data, as Collection's doPost
1586 * should return data for created member which should match member's doGet
1587 * result in 100%.
1588 */
1589 friend VlanNetworkInterfaceCollection;
1590
1591 // Ethernet Provider object
1592 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001593 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001594};
1595
1596/**
1597 * VlanNetworkInterfaceCollection derived class for delivering
1598 * VLANNetworkInterface Collection Schema
1599 */
1600class VlanNetworkInterfaceCollection : public Node {
1601 public:
1602 template <typename CrowApp>
1603 // TODO(Pawel) Remove line from below, where we assume that there is only one
1604 // manager called openbmc This shall be generic, but requires to update
1605 // GetSubroutes method
1606 VlanNetworkInterfaceCollection(CrowApp &app)
1607 : Node(app,
1608 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1609 std::string()),
1610 memberVlan(app) {
1611 Node::json["@odata.type"] =
1612 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1613 Node::json["@odata.context"] =
1614 "/redfish/v1/$metadata"
1615 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1616 Node::json["Name"] = "VLAN Network Interface Collection";
1617
1618 entityPrivileges = {
1619 {boost::beast::http::verb::get, {{"Login"}}},
1620 {boost::beast::http::verb::head, {{"Login"}}},
1621 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1622 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1623 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1624 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1625 }
1626
1627 private:
1628 /**
1629 * Functions triggers appropriate requests on DBus
1630 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001631 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001632 const std::vector<std::string> &params) override {
1633 if (params.size() != 1) {
1634 // This means there is a problem with the router
1635 res.result(boost::beast::http::status::internal_server_error);
1636 res.end();
1637
1638 return;
1639 }
1640
1641 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1642 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001643 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001644 std::string rootInterfaceName = params[0];
1645
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001646 // get eth interface list, and call the below callback for JSON preparation
Ed Tanousa434f2b2018-07-27 13:04:22 -07001647 ethernetProvider.getEthernetIfaceList(
1648 [&, managerId{std::move(managerId)},
1649 rootInterfaceName{std::move(rootInterfaceName)}](
1650 const bool &success, const std::vector<std::string> &iface_list) {
1651 if (success) {
1652 bool rootInterfaceFound = false;
1653 nlohmann::json ifaceArray = nlohmann::json::array();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001654
Ed Tanousa434f2b2018-07-27 13:04:22 -07001655 for (const std::string &ifaceItem : iface_list) {
1656 if (ifaceItem == rootInterfaceName) {
1657 rootInterfaceFound = true;
1658 } else if (boost::starts_with(ifaceItem,
1659 rootInterfaceName + "_")) {
1660 ifaceArray.push_back(
1661 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
1662 "/EthernetInterfaces/" +
1663 rootInterfaceName + "/VLANs/" +
1664 ifaceItem}});
1665 }
1666 }
1667
1668 if (rootInterfaceFound) {
1669 Node::json["Members"] = ifaceArray;
1670 Node::json["Members@odata.count"] = ifaceArray.size();
1671 Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId +
1672 "/EthernetInterfaces/" +
1673 rootInterfaceName + "/VLANs";
1674 res.jsonValue = Node::json;
1675 } else {
1676 messages::addMessageToErrorJson(
1677 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1678 rootInterfaceName));
1679 res.result(boost::beast::http::status::not_found);
1680 res.end();
1681 }
1682 } else {
1683 // No success, best what we can do is return INTERNALL ERROR
1684 res.result(boost::beast::http::status::internal_server_error);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001685 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001686 res.end();
Ed Tanousa434f2b2018-07-27 13:04:22 -07001687 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001688 }
1689
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001690 void doPost(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001691 const std::vector<std::string> &params) override {
1692 if (params.size() != 1) {
1693 // This means there is a problem with the router
1694 res.result(boost::beast::http::status::internal_server_error);
1695 res.end();
1696 return;
1697 }
1698
1699 nlohmann::json postReq;
1700
1701 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1702 return;
1703 }
1704
1705 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1706 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001707 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001708 std::string rootInterfaceName = params[0];
1709 uint64_t vlanId;
1710 bool errorDetected;
1711
1712 if (json_util::getUnsigned(
1713 "VLANId", postReq, vlanId,
1714 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1715 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001716 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001717 res.end();
1718 return;
1719 }
1720
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001721 // get eth interface list, and call the below callback for JSON preparation
Ed Tanousa434f2b2018-07-27 13:04:22 -07001722 ethernetProvider.getEthernetIfaceList(
1723 [&, managerId{std::move(managerId)},
1724 rootInterfaceName{std::move(rootInterfaceName)}](
1725 const bool &success, const std::vector<std::string> &iface_list) {
1726 if (success) {
1727 bool rootInterfaceFound = false;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001728
Ed Tanousa434f2b2018-07-27 13:04:22 -07001729 for (const std::string &ifaceItem : iface_list) {
1730 if (ifaceItem == rootInterfaceName) {
1731 rootInterfaceFound = true;
1732 break;
1733 }
1734 }
1735
1736 if (rootInterfaceFound) {
1737 ethernetProvider.createVlan(
1738 rootInterfaceName, vlanId,
1739 [&, vlanId, rootInterfaceName,
1740 req{std::move(req)}](const boost::system::error_code ec) {
1741 if (ec) {
1742 messages::addMessageToErrorJson(
1743 res.jsonValue, messages::internalError());
1744 res.end();
1745 } else {
1746 memberVlan.doGet(
1747 res, req,
1748 {rootInterfaceName,
1749 rootInterfaceName + "_" + std::to_string(vlanId)});
1750 }
1751 });
1752 } else {
1753 messages::addMessageToErrorJson(
1754 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1755 rootInterfaceName));
1756 res.result(boost::beast::http::status::not_found);
1757 res.end();
1758 }
1759 } else {
1760 // No success, best what we can do is return INTERNALL ERROR
1761 res.result(boost::beast::http::status::internal_server_error);
1762 res.end();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001763 }
Ed Tanousa434f2b2018-07-27 13:04:22 -07001764 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001765 }
1766
1767 // Ethernet Provider object
1768 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001769 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001770 VlanNetworkInterface memberVlan;
1771};
1772
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001773} // namespace redfish