blob: 3bbcff4fa56ce4d65fa199816f141dcab36fe163 [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;
72 const bool *auto_neg;
73 const std::string *hostname;
74 const std::string *default_gateway;
75 const std::string *mac_address;
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +020076 const unsigned int *vlan_id;
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
91 const size_t MAX_IPV4_ADDRESSES_PER_INTERFACE = 10;
92
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) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010098 const auto &dbus_obj = dbus_data.find(objpath);
99 if (dbus_obj != dbus_data.end()) {
100 const auto &iface = dbus_obj->second.find(interface);
101 if (iface != dbus_obj->second.end()) {
102 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 Tanousaa2e59c2018-04-12 12:17:20 -0700113 const auto &dbus_obj = sdbusplus::message::object_path{objpath};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100114 return extractInterfaceProperties(dbus_obj, interface, dbus_data);
115 }
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 Tanousaa2e59c2018-04-12 12:17:20 -0700124 return mapbox::get_ptr<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
133 void extractEthernetInterfaceData(const std::string &ethiface_id,
134 const GetManagedObjectsType &dbus_data,
135 EthernetInterfaceData &eth_data) {
136 // Extract data that contains MAC Address
137 const PropertiesMapType *mac_properties = extractInterfaceProperties(
138 "/xyz/openbmc_project/network/" + ethiface_id,
139 "xyz.openbmc_project.Network.MACAddress", dbus_data);
140
141 if (mac_properties != nullptr) {
142 eth_data.mac_address =
143 extractProperty<std::string>(*mac_properties, "MACAddress");
144 }
145
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +0200146 const PropertiesMapType *vlan_properties = extractInterfaceProperties(
147 "/xyz/openbmc_project/network/" + ethiface_id,
148 "xyz.openbmc_project.Network.VLAN", dbus_data);
149
150 if (vlan_properties != nullptr) {
151 eth_data.vlan_id = extractProperty<unsigned int>(*vlan_properties, "Id");
152 }
153
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100154 // Extract data that contains link information (auto negotiation and speed)
155 const PropertiesMapType *eth_properties = extractInterfaceProperties(
156 "/xyz/openbmc_project/network/" + ethiface_id,
157 "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
158
159 if (eth_properties != nullptr) {
160 eth_data.auto_neg = extractProperty<bool>(*eth_properties, "AutoNeg");
161 eth_data.speed = extractProperty<unsigned int>(*eth_properties, "Speed");
162 }
163
164 // Extract data that contains network config (HostName and DefaultGW)
165 const PropertiesMapType *config_properties = extractInterfaceProperties(
166 "/xyz/openbmc_project/network/config",
167 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
168
169 if (config_properties != nullptr) {
170 eth_data.hostname =
171 extractProperty<std::string>(*config_properties, "HostName");
172 eth_data.default_gateway =
173 extractProperty<std::string>(*config_properties, "DefaultGateway");
174 }
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
189 void extractIPv4Data(const std::string &ethiface_id,
190 const GetManagedObjectsType &dbus_data,
191 std::vector<IPv4AddressData> &ipv4_config) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200192 const std::string pathStart =
193 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
194
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),
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100201 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/")) {
202 // 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
209 IPv4AddressData ipv4_address;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200210
211 ipv4_address.id = static_cast<const std::string &>(objpath.first)
212 .substr(pathStart.size());
213
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100214 // IPv4 address
215 ipv4_address.address =
216 extractProperty<std::string>(properties, "Address");
217 // IPv4 gateway
218 ipv4_address.gateway =
219 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) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200225 ipv4_address.origin =
226 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
234 ipv4_address.netmask = getNetmask(*mask);
235 }
236
237 // Attach IPv4 only if address is present
238 if (ipv4_address.address != nullptr) {
Gunnar Mills274fad52018-06-13 15:45:36 -0500239 // Check if given address is local, or global
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100240 if (boost::starts_with(*ipv4_address.address, "169.254")) {
241 ipv4_address.global = false;
242 } else {
243 ipv4_address.global = true;
244 }
245 ipv4_config.emplace_back(std::move(ipv4_address));
246 }
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) {
273 crow::connections::system_bus->async_method_call(
274 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) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200292 crow::connections::system_bus->async_method_call(
293 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
380 * @param[in] ipIdx Index of IP in input array that should be modified
381 * @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(
400 asyncResp->res.json_value, messages::internalError(),
401 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
402 } else {
403 asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue;
404 }
405 };
406
407 crow::connections::system_bus->async_method_call(
408 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
419 * @param[in] ipIdx Index of IP in input array that should be modified
420 * @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(
437 asyncResp->res.json_value, messages::internalError(),
438 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
439 } else {
440 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] =
441 newValue;
442 }
443 };
444
445 crow::connections::system_bus->async_method_call(
446 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
457 * @param[in] ipIdx Index of IP in input array that should be modified
458 * @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(
474 asyncResp->res.json_value, messages::internalError(),
475 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
476 } else {
477 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] =
478 newValueStr;
479 }
480 };
481
482 crow::connections::system_bus->async_method_call(
483 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) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200500 crow::connections::system_bus->async_method_call(
501 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) {
516 crow::connections::system_bus->async_method_call(
517 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
528 * @param[in] ipIdx Index of IP in input array that should be deleted
529 * @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) {
537 crow::connections::system_bus->async_method_call(
538 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
539 const boost::system::error_code ec) {
540 if (ec) {
541 messages::addMessageToJson(
542 asyncResp->res.json_value, messages::internalError(),
543 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
544 } else {
545 asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr;
546 }
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
557 * @param[in] ipIdx Index of IP in input array that should be deleted
558 * @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(
572 asyncResp->res.json_value, messages::internalError(),
573 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
574 }
575 };
576
577 crow::connections::system_bus->async_method_call(
578 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
637 * @param ethiface_id a eth interface id to query on DBus
638 * @param callback a function that shall be called to convert Dbus output
639 * into JSON
640 */
641 template <typename CallbackFunc>
642 void getEthernetIfaceData(const std::string &ethiface_id,
643 CallbackFunc &&callback) {
644 crow::connections::system_bus->async_method_call(
645 [
646 this, ethiface_id{std::move(ethiface_id)},
647 callback{std::move(callback)}
648 ](const boost::system::error_code error_code,
649 const GetManagedObjectsType &resp) {
650
651 EthernetInterfaceData eth_data{};
652 std::vector<IPv4AddressData> ipv4_data;
653 ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
654
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.
661 callback(false, eth_data, ipv4_data);
662 return;
663 }
664
665 extractEthernetInterfaceData(ethiface_id, resp, eth_data);
666 extractIPv4Data(ethiface_id, resp, ipv4_data);
667
668 // Fix global GW
669 for (IPv4AddressData &ipv4 : ipv4_data) {
670 if ((ipv4.global) &&
671 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
672 ipv4.gateway = eth_data.default_gateway;
673 }
674 }
675
676 // Finally make a callback with usefull data
677 callback(true, eth_data, ipv4_data);
678 },
679 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
680 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
681 };
682
683 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100684 * Function that retrieves all Ethernet Interfaces available through Network
685 * Manager
686 * @param callback a function that shall be called to convert Dbus output into
687 * JSON.
688 */
689 template <typename CallbackFunc>
690 void getEthernetIfaceList(CallbackFunc &&callback) {
691 crow::connections::system_bus->async_method_call(
692 [ this, callback{std::move(callback)} ](
693 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700694 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100695 // Callback requires vector<string> to retrieve all available ethernet
696 // interfaces
697 std::vector<std::string> iface_list;
698 iface_list.reserve(resp.size());
699 if (error_code) {
700 // Something wrong on DBus, the error_code is not important at this
701 // moment, just return success=false, and empty output. Since size
702 // of vector may vary depending on information from Network Manager,
703 // and empty output could not be treated same way as error.
704 callback(false, iface_list);
705 return;
706 }
707
708 // Iterate over all retrieved ObjectPaths.
709 for (auto &objpath : resp) {
710 // And all interfaces available for certain ObjectPath.
711 for (auto &interface : objpath.second) {
712 // If interface is xyz.openbmc_project.Network.EthernetInterface,
713 // this is what we're looking for.
714 if (interface.first ==
715 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700716 // Cut out everyting until last "/", ...
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200717 const std::string &iface_id =
718 static_cast<const std::string &>(objpath.first);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100719 std::size_t last_pos = iface_id.rfind("/");
720 if (last_pos != std::string::npos) {
721 // and put it into output vector.
722 iface_list.emplace_back(iface_id.substr(last_pos + 1));
723 }
724 }
725 }
726 }
Gunnar Mills274fad52018-06-13 15:45:36 -0500727 // Finally make a callback with useful data
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100728 callback(true, iface_list);
729 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700730 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
731 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100732 };
733};
734
735/**
736 * EthernetCollection derived class for delivering Ethernet Collection Schema
737 */
738class EthernetCollection : public Node {
739 public:
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100740 // TODO(Pawel) Remove line from below, where we assume that there is only one
741 // manager called openbmc This shall be generic, but requires to update
742 // GetSubroutes method
743 EthernetCollection(CrowApp &app)
744 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
745 Node::json["@odata.type"] =
746 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
747 Node::json["@odata.context"] =
748 "/redfish/v1/"
749 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
750 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
751 Node::json["Name"] = "Ethernet Network Interface Collection";
752 Node::json["Description"] =
753 "Collection of EthernetInterfaces for this Manager";
754
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200755 entityPrivileges = {
756 {boost::beast::http::verb::get, {{"Login"}}},
757 {boost::beast::http::verb::head, {{"Login"}}},
758 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
759 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
760 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
761 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100762 }
763
764 private:
765 /**
766 * Functions triggers appropriate requests on DBus
767 */
768 void doGet(crow::response &res, const crow::request &req,
769 const std::vector<std::string> &params) override {
770 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
771 // any Manager, not only hardcoded 'openbmc'.
772 std::string manager_id = "openbmc";
773
774 // Get eth interface list, and call the below callback for JSON preparation
775 ethernet_provider.getEthernetIfaceList(
776 [&, manager_id{std::move(manager_id)} ](
777 const bool &success, const std::vector<std::string> &iface_list) {
778 if (success) {
779 nlohmann::json iface_array = nlohmann::json::array();
780 for (const std::string &iface_item : iface_list) {
781 iface_array.push_back(
782 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
783 "/EthernetInterfaces/" + iface_item}});
784 }
785 Node::json["Members"] = iface_array;
786 Node::json["Members@odata.count"] = iface_array.size();
787 Node::json["@odata.id"] =
788 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
789 res.json_value = Node::json;
790 } else {
791 // No success, best what we can do is return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700792 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100793 }
794 res.end();
795 });
796 }
797
798 // Ethernet Provider object
799 // TODO(Pawel) consider move it to singleton
800 OnDemandEthernetProvider ethernet_provider;
801};
802
803/**
804 * EthernetInterface derived class for delivering Ethernet Schema
805 */
806class EthernetInterface : public Node {
807 public:
808 /*
809 * Default Constructor
810 */
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100811 // TODO(Pawel) Remove line from below, where we assume that there is only one
812 // manager called openbmc This shall be generic, but requires to update
813 // GetSubroutes method
814 EthernetInterface(CrowApp &app)
815 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
816 std::string()) {
817 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
818 Node::json["@odata.context"] =
819 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
820 Node::json["Name"] = "Manager Ethernet Interface";
821 Node::json["Description"] = "Management Network Interface";
822
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200823 entityPrivileges = {
824 {boost::beast::http::verb::get, {{"Login"}}},
825 {boost::beast::http::verb::head, {{"Login"}}},
826 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
827 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
828 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
829 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100830 }
831
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200832 // TODO(kkowalsk) Find a suitable class/namespace for this
833 static void handleVlanPatch(const std::string &ifaceId,
834 const nlohmann::json &input,
835 const EthernetInterfaceData &eth_data,
836 const std::string &pathPrefix,
837 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200838 if (!input.is_object()) {
839 messages::addMessageToJson(
840 asyncResp->res.json_value,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200841 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200842 return;
843 }
844
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200845 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
846 nlohmann::json &paramsJson =
847 (pathPrefix == "/")
848 ? asyncResp->res.json_value
849 : asyncResp->res.json_value[nlohmann::json_pointer<nlohmann::json>(
850 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200851 bool inputVlanEnabled;
852 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200853
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200854 json_util::Result inputVlanEnabledState = json_util::getBool(
855 "VLANEnable", input, inputVlanEnabled,
856 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200857 asyncResp->res.json_value, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200858 json_util::Result inputVlanIdState = json_util::getUnsigned(
859 "VLANId", input, inputVlanId,
860 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200861 asyncResp->res.json_value, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200862 bool inputInvalid = false;
863
864 // Do not proceed if fields in VLAN object were of wrong type
865 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
866 inputVlanIdState == json_util::Result::WRONG_TYPE) {
867 return;
868 }
869
870 // Verify input
871 if (eth_data.vlan_id == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200872 // This interface is not a VLAN. Cannot do anything with it
873 // TODO(kkowalsk) Change this message
874 messages::addMessageToJson(asyncResp->res.json_value,
875 messages::propertyMissing("VLANEnable"),
876 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200877
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200878 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200879 } else {
880 // Load actual data into field values if they were not provided
881 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
882 inputVlanEnabled = true;
883 }
884
885 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
886 inputVlanId = *eth_data.vlan_id;
887 }
888 }
889
890 // Do not proceed if input has not been valid
891 if (inputInvalid) {
892 return;
893 }
894
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200895 // VLAN is configured on the interface
896 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
897 // Change VLAN Id
898 paramsJson["VLANId"] = inputVlanId;
899 OnDemandEthernetProvider::changeVlanId(
900 ifaceId, static_cast<uint32_t>(inputVlanId),
901 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
902 const boost::system::error_code ec) {
903 if (ec) {
904 messages::addMessageToJson(asyncResp->res.json_value,
905 messages::internalError(), pathPrefix);
906 } else {
907 paramsJson["VLANEnable"] = true;
908 }
909 });
910 } else if (inputVlanEnabled == false) {
911 // Disable VLAN
912 OnDemandEthernetProvider::disableVlan(
913 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
914 const boost::system::error_code ec) {
915 if (ec) {
916 messages::addMessageToJson(asyncResp->res.json_value,
917 messages::internalError(), pathPrefix);
918 } else {
919 paramsJson["VLANEnable"] = false;
920 }
921 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200922 }
923 }
924
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200925 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200926 void handleHostnamePatch(const nlohmann::json &input,
927 const EthernetInterfaceData &eth_data,
928 const std::shared_ptr<AsyncResp> &asyncResp) {
929 if (input.is_string()) {
930 std::string newHostname = input.get<std::string>();
931
932 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
933 // Change hostname
934 ethernet_provider.setHostName(
935 newHostname,
936 [asyncResp, newHostname](const boost::system::error_code ec) {
937 if (ec) {
938 messages::addMessageToJson(asyncResp->res.json_value,
939 messages::internalError(),
940 "/HostName");
941 } else {
942 asyncResp->res.json_value["HostName"] = newHostname;
943 }
944 });
945 }
946 } else {
947 messages::addMessageToJson(
948 asyncResp->res.json_value,
949 messages::propertyValueTypeError(input.dump(), "HostName"),
950 "/HostName");
951 }
952 }
953
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200954 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
955 const std::vector<IPv4AddressData> &ipv4_data,
956 const std::shared_ptr<AsyncResp> &asyncResp) {
957 if (!input.is_array()) {
958 messages::addMessageToJson(
959 asyncResp->res.json_value,
960 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
961 "/IPv4Addresses");
962 return;
963 }
964
965 // According to Redfish PATCH definition, size must be at least equal
966 if (input.size() < ipv4_data.size()) {
967 // TODO(kkowalsk) This should be a message indicating that not enough
968 // data has been provided
969 messages::addMessageToJson(asyncResp->res.json_value,
970 messages::internalError(), "/IPv4Addresses");
971 return;
972 }
973
974 json_util::Result addressFieldState;
975 json_util::Result subnetMaskFieldState;
976 json_util::Result addressOriginFieldState;
977 json_util::Result gatewayFieldState;
978 const std::string *addressFieldValue;
979 const std::string *subnetMaskFieldValue;
980 const std::string *addressOriginFieldValue = nullptr;
981 const std::string *gatewayFieldValue;
982 uint8_t subnetMaskAsPrefixLength;
983 std::string addressOriginInDBusFormat;
984
985 bool errorDetected = false;
986 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
987 // Check that entry is not of some unexpected type
988 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
989 // Invalid object type
990 messages::addMessageToJson(
991 asyncResp->res.json_value,
992 messages::propertyValueTypeError(input[entryIdx].dump(),
993 "IPv4Address"),
994 "/IPv4Addresses/" + std::to_string(entryIdx));
995
996 continue;
997 }
998
999 // Try to load fields
1000 addressFieldState = json_util::getString(
1001 "Address", input[entryIdx], addressFieldValue,
1002 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1003 asyncResp->res.json_value,
1004 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1005 subnetMaskFieldState = json_util::getString(
1006 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1007 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1008 asyncResp->res.json_value,
1009 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1010 addressOriginFieldState = json_util::getString(
1011 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1012 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1013 asyncResp->res.json_value,
1014 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1015 gatewayFieldState = json_util::getString(
1016 "Gateway", input[entryIdx], gatewayFieldValue,
1017 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1018 asyncResp->res.json_value,
1019 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1020
1021 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1022 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1023 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1024 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1025 return;
1026 }
1027
1028 if (addressFieldState == json_util::Result::SUCCESS &&
1029 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1030 errorDetected = true;
1031 messages::addMessageToJson(
1032 asyncResp->res.json_value,
1033 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1034 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1035 }
1036
1037 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1038 !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1039 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1040 errorDetected = true;
1041 messages::addMessageToJson(
1042 asyncResp->res.json_value,
1043 messages::propertyValueFormatError(*subnetMaskFieldValue,
1044 "SubnetMask"),
1045 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1046 }
1047
1048 // Get Address origin in proper format
1049 addressOriginInDBusFormat =
1050 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1051 addressOriginFieldValue, true, false);
1052
1053 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1054 addressOriginInDBusFormat.empty()) {
1055 errorDetected = true;
1056 messages::addMessageToJson(
1057 asyncResp->res.json_value,
1058 messages::propertyValueNotInList(*addressOriginFieldValue,
1059 "AddressOrigin"),
1060 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1061 }
1062
1063 if (gatewayFieldState == json_util::Result::SUCCESS &&
1064 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1065 errorDetected = true;
1066 messages::addMessageToJson(
1067 asyncResp->res.json_value,
1068 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1069 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1070 }
1071
1072 // If any error occured do not proceed with current entry, but do not
1073 // end loop
1074 if (errorDetected) {
1075 errorDetected = false;
1076 continue;
1077 }
1078
1079 if (entryIdx >= ipv4_data.size()) {
1080 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1081
1082 // Verify that all field were provided
1083 if (addressFieldState == json_util::Result::NOT_EXIST) {
1084 errorDetected = true;
1085 messages::addMessageToJson(
1086 asyncResp->res.json_value, messages::propertyMissing("Address"),
1087 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1088 }
1089
1090 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1091 errorDetected = true;
1092 messages::addMessageToJson(
1093 asyncResp->res.json_value,
1094 messages::propertyMissing("SubnetMask"),
1095 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1096 }
1097
1098 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1099 errorDetected = true;
1100 messages::addMessageToJson(
1101 asyncResp->res.json_value,
1102 messages::propertyMissing("AddressOrigin"),
1103 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1104 }
1105
1106 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1107 errorDetected = true;
1108 messages::addMessageToJson(
1109 asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1110 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1111 }
1112
1113 // If any error occured do not proceed with current entry, but do not
1114 // end loop
1115 if (errorDetected) {
1116 errorDetected = false;
1117 continue;
1118 }
1119
1120 // Create IPv4 with provided data
1121 ethernet_provider.createIPv4(
1122 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1123 *addressFieldValue, asyncResp);
1124 } else {
1125 // Existing object that should be modified/deleted/remain unchanged
1126 if (input[entryIdx].is_null()) {
1127 // Object should be deleted
1128 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1129 entryIdx, asyncResp);
1130 } else if (input[entryIdx].is_object()) {
1131 if (input[entryIdx].size() == 0) {
1132 // Object shall remain unchanged
1133 continue;
1134 }
1135
1136 // Apply changes
1137 if (addressFieldState == json_util::Result::SUCCESS &&
1138 ipv4_data[entryIdx].address != nullptr &&
1139 *ipv4_data[entryIdx].address != *addressFieldValue) {
1140 ethernet_provider.changeIPv4AddressProperty(
1141 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1142 *addressFieldValue, asyncResp);
1143 }
1144
1145 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1146 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1147 ethernet_provider.changeIPv4SubnetMaskProperty(
1148 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1149 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1150 }
1151
1152 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1153 ipv4_data[entryIdx].origin != *addressFieldValue) {
1154 ethernet_provider.changeIPv4Origin(
1155 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1156 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1157 }
1158
1159 if (gatewayFieldState == json_util::Result::SUCCESS &&
1160 ipv4_data[entryIdx].gateway != nullptr &&
1161 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1162 ethernet_provider.changeIPv4AddressProperty(
1163 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1164 *gatewayFieldValue, asyncResp);
1165 }
1166 }
1167 }
1168 }
1169 }
1170
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001171 nlohmann::json parseInterfaceData(
1172 const std::string &iface_id, const EthernetInterfaceData &eth_data,
1173 const std::vector<IPv4AddressData> &ipv4_data) {
1174 // Copy JSON object to avoid race condition
1175 nlohmann::json json_response(Node::json);
1176
1177 // Fill out obvious data...
1178 json_response["Id"] = iface_id;
1179 json_response["@odata.id"] =
1180 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1181
1182 // ... then the one from DBus, regarding eth iface...
1183 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1184
1185 if (eth_data.mac_address != nullptr)
1186 json_response["MACAddress"] = *eth_data.mac_address;
1187
1188 if (eth_data.hostname != nullptr)
1189 json_response["HostName"] = *eth_data.hostname;
1190
1191 if (eth_data.vlan_id != nullptr) {
1192 nlohmann::json &vlanObj = json_response["VLAN"];
1193 vlanObj["VLANEnable"] = true;
1194 vlanObj["VLANId"] = *eth_data.vlan_id;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001195 } else {
1196 nlohmann::json &vlanObj = json_response["VLANs"];
1197 vlanObj["@odata.id"] =
1198 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id +
1199 "/VLANs";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001200 }
1201
1202 // ... at last, check if there are IPv4 data and prepare appropriate
1203 // collection
1204 if (ipv4_data.size() > 0) {
1205 nlohmann::json ipv4_array = nlohmann::json::array();
1206 for (auto &ipv4_config : ipv4_data) {
1207 nlohmann::json json_ipv4;
1208 if (ipv4_config.address != nullptr) {
1209 json_ipv4["Address"] = *ipv4_config.address;
1210 if (ipv4_config.gateway != nullptr)
1211 json_ipv4["Gateway"] = *ipv4_config.gateway;
1212
1213 json_ipv4["AddressOrigin"] = ipv4_config.origin;
1214 json_ipv4["SubnetMask"] = ipv4_config.netmask;
1215
1216 ipv4_array.push_back(std::move(json_ipv4));
1217 }
1218 }
1219 json_response["IPv4Addresses"] = std::move(ipv4_array);
1220 }
1221
1222 return json_response;
1223 }
1224
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001225 /**
1226 * Functions triggers appropriate requests on DBus
1227 */
1228 void doGet(crow::response &res, const crow::request &req,
1229 const std::vector<std::string> &params) override {
1230 // TODO(Pawel) this shall be parametrized call (two params) to get
1231 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1232 // Check if there is required param, truly entering this shall be
1233 // impossible.
1234 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001235 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001236 res.end();
1237 return;
1238 }
1239
1240 const std::string &iface_id = params[0];
1241
1242 // Get single eth interface data, and call the below callback for JSON
1243 // preparation
1244 ethernet_provider.getEthernetIfaceData(
1245 iface_id, [&, iface_id](const bool &success,
1246 const EthernetInterfaceData &eth_data,
1247 const std::vector<IPv4AddressData> &ipv4_data) {
1248 if (success) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001249 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001250 } else {
1251 // ... otherwise return error
1252 // TODO(Pawel)consider distinguish between non existing object, and
1253 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001254 messages::addMessageToErrorJson(
1255 res.json_value,
1256 messages::resourceNotFound("EthernetInterface", iface_id));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001257 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001258 }
1259 res.end();
1260 });
1261 }
1262
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001263 void doPatch(crow::response &res, const crow::request &req,
1264 const std::vector<std::string> &params) override {
1265 // TODO(Pawel) this shall be parametrized call (two params) to get
1266 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1267 // Check if there is required param, truly entering this shall be
1268 // impossible.
1269 if (params.size() != 1) {
1270 res.result(boost::beast::http::status::internal_server_error);
1271 res.end();
1272 return;
1273 }
1274
1275 const std::string &iface_id = params[0];
1276
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001277 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001278
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001279 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001280 return;
1281 }
1282
1283 // Get single eth interface data, and call the below callback for JSON
1284 // preparation
1285 ethernet_provider.getEthernetIfaceData(
1286 iface_id,
1287 [&, iface_id, patchReq = std::move(patchReq) ](
1288 const bool &success, const EthernetInterfaceData &eth_data,
1289 const std::vector<IPv4AddressData> &ipv4_data) {
1290 if (!success) {
1291 // ... otherwise return error
1292 // TODO(Pawel)consider distinguish between non existing object, and
1293 // other errors
1294 res.result(boost::beast::http::status::not_found);
1295 res.end();
1296
1297 return;
1298 }
1299
1300 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1301
1302 std::shared_ptr<AsyncResp> asyncResp =
1303 std::make_shared<AsyncResp>(res);
1304
1305 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1306 ++propertyIt) {
1307 if (propertyIt.key() == "VLAN") {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001308 handleVlanPatch(iface_id, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001309 asyncResp);
1310 } else if (propertyIt.key() == "HostName") {
1311 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001312 } else if (propertyIt.key() == "IPv4Addresses") {
1313 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1314 asyncResp);
1315 } else if (propertyIt.key() == "IPv6Addresses") {
1316 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1317 messages::addMessageToJsonRoot(
1318 res.json_value,
1319 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001320 } else {
1321 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1322
1323 if (fieldInJsonIt == res.json_value.end()) {
1324 // Field not in scope of defined fields
1325 messages::addMessageToJsonRoot(
1326 res.json_value,
1327 messages::propertyUnknown(propertyIt.key()));
1328 } else if (*fieldInJsonIt != *propertyIt) {
1329 // User attempted to modify non-writable field
1330 messages::addMessageToJsonRoot(
1331 res.json_value,
1332 messages::propertyNotWritable(propertyIt.key()));
1333 }
1334 }
1335 }
1336 });
1337 }
1338
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001339 // Ethernet Provider object
1340 // TODO(Pawel) consider move it to singleton
1341 OnDemandEthernetProvider ethernet_provider;
1342};
1343
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001344class VlanNetworkInterfaceCollection;
1345
1346/**
1347 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1348 */
1349class VlanNetworkInterface : public Node {
1350 public:
1351 /*
1352 * Default Constructor
1353 */
1354 template <typename CrowApp>
1355 // TODO(Pawel) Remove line from below, where we assume that there is only one
1356 // manager called openbmc This shall be generic, but requires to update
1357 // GetSubroutes method
1358 VlanNetworkInterface(CrowApp &app)
Kowalski, Kamileb547732018-05-16 10:25:07 +02001359 : Node(
1360 app,
1361 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1362 std::string(), std::string()) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001363 Node::json["@odata.type"] =
1364 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1365 Node::json["@odata.context"] =
1366 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1367 Node::json["Name"] = "VLAN Network Interface";
1368
1369 entityPrivileges = {
1370 {boost::beast::http::verb::get, {{"Login"}}},
1371 {boost::beast::http::verb::head, {{"Login"}}},
1372 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1373 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1374 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1375 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1376 }
1377
1378 private:
1379 nlohmann::json parseInterfaceData(
1380 const std::string &parent_iface_id, const std::string &iface_id,
1381 const EthernetInterfaceData &eth_data,
1382 const std::vector<IPv4AddressData> &ipv4_data) {
1383 // Copy JSON object to avoid race condition
1384 nlohmann::json json_response(Node::json);
1385
Kowalski, Kamileb547732018-05-16 10:25:07 +02001386 if (eth_data.vlan_id == nullptr) {
1387 // Interface not a VLAN - abort
1388 messages::addMessageToErrorJson(json_response, messages::internalError());
1389 return json_response;
1390 }
1391
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001392 // Fill out obvious data...
1393 json_response["Id"] = iface_id;
1394 json_response["@odata.id"] =
1395 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_iface_id +
1396 "/VLANs/" + iface_id;
1397
1398 json_response["VLANEnable"] = true;
1399 json_response["VLANId"] = *eth_data.vlan_id;
1400
1401 return json_response;
1402 }
1403
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001404 /**
1405 * Functions triggers appropriate requests on DBus
1406 */
1407 void doGet(crow::response &res, const crow::request &req,
1408 const std::vector<std::string> &params) override {
1409 // TODO(Pawel) this shall be parametrized call (two params) to get
1410 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1411 // Check if there is required param, truly entering this shall be
1412 // impossible.
1413 if (params.size() != 2) {
1414 res.result(boost::beast::http::status::internal_server_error);
1415 res.end();
1416 return;
1417 }
1418
1419 const std::string &parent_iface_id = params[0];
1420 const std::string &iface_id = params[1];
1421
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001422 // Get single eth interface data, and call the below callback for JSON
1423 // preparation
1424 ethernet_provider.getEthernetIfaceData(
1425 iface_id,
1426 [&, parent_iface_id, iface_id](
1427 const bool &success, const EthernetInterfaceData &eth_data,
1428 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamileb547732018-05-16 10:25:07 +02001429 if (success) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001430 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1431 eth_data, ipv4_data);
1432 } else {
1433 // ... otherwise return error
Kowalski, Kamileb547732018-05-16 10:25:07 +02001434 // TODO(Pawel)consider distinguish between non existing object, and
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001435 // other errors
1436 res.result(boost::beast::http::status::not_found);
1437 }
1438 res.end();
1439 });
1440 }
1441
1442 void doPatch(crow::response &res, const crow::request &req,
1443 const std::vector<std::string> &params) override {
1444 if (params.size() != 2) {
1445 res.result(boost::beast::http::status::internal_server_error);
1446 res.end();
1447 return;
1448 }
1449
Kowalski, Kamileb547732018-05-16 10:25:07 +02001450 // TODO(kkowalsk) Will be implement in further patchset
1451 res.result(boost::beast::http::status::method_not_allowed);
1452 res.end();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001453 }
1454
1455 void doDelete(crow::response &res, const crow::request &req,
1456 const std::vector<std::string> &params) override {
1457 if (params.size() != 2) {
1458 res.result(boost::beast::http::status::internal_server_error);
1459 res.end();
1460 return;
1461 }
1462
Kowalski, Kamileb547732018-05-16 10:25:07 +02001463 // TODO(kkowalsk) Will be implement in further patchset
1464 res.result(boost::beast::http::status::method_not_allowed);
1465 res.end();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001466 }
1467
1468 /**
1469 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1470 * method, to maintain consistency of returned data, as Collection's doPost
1471 * should return data for created member which should match member's doGet
1472 * result in 100%.
1473 */
1474 friend VlanNetworkInterfaceCollection;
1475
1476 // Ethernet Provider object
1477 // TODO(Pawel) consider move it to singleton
1478 OnDemandEthernetProvider ethernet_provider;
1479};
1480
1481/**
1482 * VlanNetworkInterfaceCollection derived class for delivering
1483 * VLANNetworkInterface Collection Schema
1484 */
1485class VlanNetworkInterfaceCollection : public Node {
1486 public:
1487 template <typename CrowApp>
1488 // TODO(Pawel) Remove line from below, where we assume that there is only one
1489 // manager called openbmc This shall be generic, but requires to update
1490 // GetSubroutes method
1491 VlanNetworkInterfaceCollection(CrowApp &app)
1492 : Node(app,
1493 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1494 std::string()),
1495 memberVlan(app) {
1496 Node::json["@odata.type"] =
1497 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1498 Node::json["@odata.context"] =
1499 "/redfish/v1/$metadata"
1500 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1501 Node::json["Name"] = "VLAN Network Interface Collection";
1502
1503 entityPrivileges = {
1504 {boost::beast::http::verb::get, {{"Login"}}},
1505 {boost::beast::http::verb::head, {{"Login"}}},
1506 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1507 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1508 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1509 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1510 }
1511
1512 private:
1513 /**
1514 * Functions triggers appropriate requests on DBus
1515 */
1516 void doGet(crow::response &res, const crow::request &req,
1517 const std::vector<std::string> &params) override {
1518 if (params.size() != 1) {
1519 // This means there is a problem with the router
1520 res.result(boost::beast::http::status::internal_server_error);
1521 res.end();
1522
1523 return;
1524 }
1525
1526 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1527 // any Manager, not only hardcoded 'openbmc'.
1528 std::string manager_id = "openbmc";
1529 std::string rootInterfaceName = params[0];
1530
1531 // Get eth interface list, and call the below callback for JSON preparation
1532 ethernet_provider.getEthernetIfaceList([
1533 &, manager_id{std::move(manager_id)},
1534 rootInterfaceName{std::move(rootInterfaceName)}
1535 ](const bool &success, const std::vector<std::string> &iface_list) {
1536 if (success) {
1537 bool rootInterfaceFound = false;
1538 nlohmann::json iface_array = nlohmann::json::array();
1539
1540 for (const std::string &iface_item : iface_list) {
1541 if (iface_item == rootInterfaceName) {
1542 rootInterfaceFound = true;
1543 } else if (boost::starts_with(iface_item, rootInterfaceName + "_")) {
1544 iface_array.push_back(
1545 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
1546 "/EthernetInterfaces/" + rootInterfaceName +
1547 "/VLANs/" + iface_item}});
1548 }
1549 }
1550
1551 if (rootInterfaceFound) {
1552 Node::json["Members"] = iface_array;
1553 Node::json["Members@odata.count"] = iface_array.size();
1554 Node::json["@odata.id"] = "/redfish/v1/Managers/" + manager_id +
1555 "/EthernetInterfaces/" + rootInterfaceName +
1556 "/VLANs";
1557 res.json_value = Node::json;
1558 } else {
1559 messages::addMessageToErrorJson(
1560 res.json_value, messages::resourceNotFound("EthernetInterface",
1561 rootInterfaceName));
1562 res.result(boost::beast::http::status::not_found);
1563 res.end();
1564 }
1565 } else {
1566 // No success, best what we can do is return INTERNALL ERROR
1567 res.result(boost::beast::http::status::internal_server_error);
1568 }
1569 res.end();
1570 });
1571 }
1572
1573 void doPost(crow::response &res, const crow::request &req,
1574 const std::vector<std::string> &params) override {
1575 if (params.size() != 1) {
1576 // This means there is a problem with the router
1577 res.result(boost::beast::http::status::internal_server_error);
1578 res.end();
1579 return;
1580 }
1581
1582 nlohmann::json postReq;
1583
1584 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1585 return;
1586 }
1587
1588 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1589 // any Manager, not only hardcoded 'openbmc'.
1590 std::string manager_id = "openbmc";
1591 std::string rootInterfaceName = params[0];
1592 uint64_t vlanId;
1593 bool errorDetected;
1594
1595 if (json_util::getUnsigned(
1596 "VLANId", postReq, vlanId,
1597 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1598 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1599 res.json_value, "/VLANId") != json_util::Result::SUCCESS) {
1600 res.end();
1601 return;
1602 }
1603
1604 // Get eth interface list, and call the below callback for JSON preparation
1605 ethernet_provider.getEthernetIfaceList([
1606 &, manager_id{std::move(manager_id)},
1607 rootInterfaceName{std::move(rootInterfaceName)}
1608 ](const bool &success, const std::vector<std::string> &iface_list) {
1609 if (success) {
1610 bool rootInterfaceFound = false;
1611
1612 for (const std::string &iface_item : iface_list) {
1613 if (iface_item == rootInterfaceName) {
1614 rootInterfaceFound = true;
1615 break;
1616 }
1617 }
1618
1619 if (rootInterfaceFound) {
1620 ethernet_provider.createVlan(
1621 rootInterfaceName, vlanId,
1622 [&, vlanId, rootInterfaceName,
1623 req{std::move(req)} ](const boost::system::error_code ec) {
1624 if (ec) {
1625 messages::addMessageToErrorJson(res.json_value,
1626 messages::internalError());
1627 res.end();
1628 } else {
1629 memberVlan.doGet(
1630 res, req,
1631 {rootInterfaceName,
1632 rootInterfaceName + "_" + std::to_string(vlanId)});
1633 }
1634 });
1635 } else {
1636 messages::addMessageToErrorJson(
1637 res.json_value, messages::resourceNotFound("EthernetInterface",
1638 rootInterfaceName));
1639 res.result(boost::beast::http::status::not_found);
1640 res.end();
1641 }
1642 } else {
1643 // No success, best what we can do is return INTERNALL ERROR
1644 res.result(boost::beast::http::status::internal_server_error);
1645 res.end();
1646 }
1647 });
1648 }
1649
1650 // Ethernet Provider object
1651 // TODO(Pawel) consider move it to singleton
1652 OnDemandEthernetProvider ethernet_provider;
1653 VlanNetworkInterface memberVlan;
1654};
1655
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001656} // namespace redfish