blob: 630ebd16c5b5a40f9c89337127608396d3340805 [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:
740 template <typename CrowApp>
741 // TODO(Pawel) Remove line from below, where we assume that there is only one
742 // manager called openbmc This shall be generic, but requires to update
743 // GetSubroutes method
744 EthernetCollection(CrowApp &app)
745 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
746 Node::json["@odata.type"] =
747 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
748 Node::json["@odata.context"] =
749 "/redfish/v1/"
750 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
751 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
752 Node::json["Name"] = "Ethernet Network Interface Collection";
753 Node::json["Description"] =
754 "Collection of EthernetInterfaces for this Manager";
755
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200756 entityPrivileges = {
757 {boost::beast::http::verb::get, {{"Login"}}},
758 {boost::beast::http::verb::head, {{"Login"}}},
759 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
760 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
761 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
762 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100763 }
764
765 private:
766 /**
767 * Functions triggers appropriate requests on DBus
768 */
769 void doGet(crow::response &res, const crow::request &req,
770 const std::vector<std::string> &params) override {
771 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
772 // any Manager, not only hardcoded 'openbmc'.
773 std::string manager_id = "openbmc";
774
775 // Get eth interface list, and call the below callback for JSON preparation
776 ethernet_provider.getEthernetIfaceList(
777 [&, manager_id{std::move(manager_id)} ](
778 const bool &success, const std::vector<std::string> &iface_list) {
779 if (success) {
780 nlohmann::json iface_array = nlohmann::json::array();
781 for (const std::string &iface_item : iface_list) {
782 iface_array.push_back(
783 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
784 "/EthernetInterfaces/" + iface_item}});
785 }
786 Node::json["Members"] = iface_array;
787 Node::json["Members@odata.count"] = iface_array.size();
788 Node::json["@odata.id"] =
789 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
790 res.json_value = Node::json;
791 } else {
792 // No success, best what we can do is return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700793 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100794 }
795 res.end();
796 });
797 }
798
799 // Ethernet Provider object
800 // TODO(Pawel) consider move it to singleton
801 OnDemandEthernetProvider ethernet_provider;
802};
803
804/**
805 * EthernetInterface derived class for delivering Ethernet Schema
806 */
807class EthernetInterface : public Node {
808 public:
809 /*
810 * Default Constructor
811 */
812 template <typename CrowApp>
813 // TODO(Pawel) Remove line from below, where we assume that there is only one
814 // manager called openbmc This shall be generic, but requires to update
815 // GetSubroutes method
816 EthernetInterface(CrowApp &app)
817 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
818 std::string()) {
819 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
820 Node::json["@odata.context"] =
821 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
822 Node::json["Name"] = "Manager Ethernet Interface";
823 Node::json["Description"] = "Management Network Interface";
824
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200825 entityPrivileges = {
826 {boost::beast::http::verb::get, {{"Login"}}},
827 {boost::beast::http::verb::head, {{"Login"}}},
828 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
829 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
830 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
831 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100832 }
833
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200834 // TODO(kkowalsk) Find a suitable class/namespace for this
835 static void handleVlanPatch(const std::string &ifaceId,
836 const nlohmann::json &input,
837 const EthernetInterfaceData &eth_data,
838 const std::string &pathPrefix,
839 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200840 if (!input.is_object()) {
841 messages::addMessageToJson(
842 asyncResp->res.json_value,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200843 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200844 return;
845 }
846
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200847 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
848 nlohmann::json &paramsJson =
849 (pathPrefix == "/")
850 ? asyncResp->res.json_value
851 : asyncResp->res.json_value[nlohmann::json_pointer<nlohmann::json>(
852 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200853 bool inputVlanEnabled;
854 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200855
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200856 json_util::Result inputVlanEnabledState = json_util::getBool(
857 "VLANEnable", input, inputVlanEnabled,
858 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200859 asyncResp->res.json_value, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200860 json_util::Result inputVlanIdState = json_util::getUnsigned(
861 "VLANId", input, inputVlanId,
862 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200863 asyncResp->res.json_value, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200864 bool inputInvalid = false;
865
866 // Do not proceed if fields in VLAN object were of wrong type
867 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
868 inputVlanIdState == json_util::Result::WRONG_TYPE) {
869 return;
870 }
871
872 // Verify input
873 if (eth_data.vlan_id == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200874 // This interface is not a VLAN. Cannot do anything with it
875 // TODO(kkowalsk) Change this message
876 messages::addMessageToJson(asyncResp->res.json_value,
877 messages::propertyMissing("VLANEnable"),
878 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200879
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200880 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200881 } else {
882 // Load actual data into field values if they were not provided
883 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
884 inputVlanEnabled = true;
885 }
886
887 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
888 inputVlanId = *eth_data.vlan_id;
889 }
890 }
891
892 // Do not proceed if input has not been valid
893 if (inputInvalid) {
894 return;
895 }
896
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200897 // VLAN is configured on the interface
898 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
899 // Change VLAN Id
900 paramsJson["VLANId"] = inputVlanId;
901 OnDemandEthernetProvider::changeVlanId(
902 ifaceId, static_cast<uint32_t>(inputVlanId),
903 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
904 const boost::system::error_code ec) {
905 if (ec) {
906 messages::addMessageToJson(asyncResp->res.json_value,
907 messages::internalError(), pathPrefix);
908 } else {
909 paramsJson["VLANEnable"] = true;
910 }
911 });
912 } else if (inputVlanEnabled == false) {
913 // Disable VLAN
914 OnDemandEthernetProvider::disableVlan(
915 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
916 const boost::system::error_code ec) {
917 if (ec) {
918 messages::addMessageToJson(asyncResp->res.json_value,
919 messages::internalError(), pathPrefix);
920 } else {
921 paramsJson["VLANEnable"] = false;
922 }
923 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200924 }
925 }
926
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200927 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200928 void handleHostnamePatch(const nlohmann::json &input,
929 const EthernetInterfaceData &eth_data,
930 const std::shared_ptr<AsyncResp> &asyncResp) {
931 if (input.is_string()) {
932 std::string newHostname = input.get<std::string>();
933
934 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
935 // Change hostname
936 ethernet_provider.setHostName(
937 newHostname,
938 [asyncResp, newHostname](const boost::system::error_code ec) {
939 if (ec) {
940 messages::addMessageToJson(asyncResp->res.json_value,
941 messages::internalError(),
942 "/HostName");
943 } else {
944 asyncResp->res.json_value["HostName"] = newHostname;
945 }
946 });
947 }
948 } else {
949 messages::addMessageToJson(
950 asyncResp->res.json_value,
951 messages::propertyValueTypeError(input.dump(), "HostName"),
952 "/HostName");
953 }
954 }
955
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200956 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
957 const std::vector<IPv4AddressData> &ipv4_data,
958 const std::shared_ptr<AsyncResp> &asyncResp) {
959 if (!input.is_array()) {
960 messages::addMessageToJson(
961 asyncResp->res.json_value,
962 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
963 "/IPv4Addresses");
964 return;
965 }
966
967 // According to Redfish PATCH definition, size must be at least equal
968 if (input.size() < ipv4_data.size()) {
969 // TODO(kkowalsk) This should be a message indicating that not enough
970 // data has been provided
971 messages::addMessageToJson(asyncResp->res.json_value,
972 messages::internalError(), "/IPv4Addresses");
973 return;
974 }
975
976 json_util::Result addressFieldState;
977 json_util::Result subnetMaskFieldState;
978 json_util::Result addressOriginFieldState;
979 json_util::Result gatewayFieldState;
980 const std::string *addressFieldValue;
981 const std::string *subnetMaskFieldValue;
982 const std::string *addressOriginFieldValue = nullptr;
983 const std::string *gatewayFieldValue;
984 uint8_t subnetMaskAsPrefixLength;
985 std::string addressOriginInDBusFormat;
986
987 bool errorDetected = false;
988 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
989 // Check that entry is not of some unexpected type
990 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
991 // Invalid object type
992 messages::addMessageToJson(
993 asyncResp->res.json_value,
994 messages::propertyValueTypeError(input[entryIdx].dump(),
995 "IPv4Address"),
996 "/IPv4Addresses/" + std::to_string(entryIdx));
997
998 continue;
999 }
1000
1001 // Try to load fields
1002 addressFieldState = json_util::getString(
1003 "Address", input[entryIdx], addressFieldValue,
1004 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1005 asyncResp->res.json_value,
1006 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1007 subnetMaskFieldState = json_util::getString(
1008 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1009 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1010 asyncResp->res.json_value,
1011 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1012 addressOriginFieldState = json_util::getString(
1013 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1014 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1015 asyncResp->res.json_value,
1016 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1017 gatewayFieldState = json_util::getString(
1018 "Gateway", input[entryIdx], gatewayFieldValue,
1019 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1020 asyncResp->res.json_value,
1021 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1022
1023 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1024 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1025 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1026 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1027 return;
1028 }
1029
1030 if (addressFieldState == json_util::Result::SUCCESS &&
1031 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1032 errorDetected = true;
1033 messages::addMessageToJson(
1034 asyncResp->res.json_value,
1035 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1036 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1037 }
1038
1039 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1040 !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1041 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1042 errorDetected = true;
1043 messages::addMessageToJson(
1044 asyncResp->res.json_value,
1045 messages::propertyValueFormatError(*subnetMaskFieldValue,
1046 "SubnetMask"),
1047 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1048 }
1049
1050 // Get Address origin in proper format
1051 addressOriginInDBusFormat =
1052 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1053 addressOriginFieldValue, true, false);
1054
1055 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1056 addressOriginInDBusFormat.empty()) {
1057 errorDetected = true;
1058 messages::addMessageToJson(
1059 asyncResp->res.json_value,
1060 messages::propertyValueNotInList(*addressOriginFieldValue,
1061 "AddressOrigin"),
1062 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1063 }
1064
1065 if (gatewayFieldState == json_util::Result::SUCCESS &&
1066 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1067 errorDetected = true;
1068 messages::addMessageToJson(
1069 asyncResp->res.json_value,
1070 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1071 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1072 }
1073
1074 // If any error occured do not proceed with current entry, but do not
1075 // end loop
1076 if (errorDetected) {
1077 errorDetected = false;
1078 continue;
1079 }
1080
1081 if (entryIdx >= ipv4_data.size()) {
1082 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1083
1084 // Verify that all field were provided
1085 if (addressFieldState == json_util::Result::NOT_EXIST) {
1086 errorDetected = true;
1087 messages::addMessageToJson(
1088 asyncResp->res.json_value, messages::propertyMissing("Address"),
1089 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1090 }
1091
1092 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1093 errorDetected = true;
1094 messages::addMessageToJson(
1095 asyncResp->res.json_value,
1096 messages::propertyMissing("SubnetMask"),
1097 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1098 }
1099
1100 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1101 errorDetected = true;
1102 messages::addMessageToJson(
1103 asyncResp->res.json_value,
1104 messages::propertyMissing("AddressOrigin"),
1105 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1106 }
1107
1108 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1109 errorDetected = true;
1110 messages::addMessageToJson(
1111 asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1112 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1113 }
1114
1115 // If any error occured do not proceed with current entry, but do not
1116 // end loop
1117 if (errorDetected) {
1118 errorDetected = false;
1119 continue;
1120 }
1121
1122 // Create IPv4 with provided data
1123 ethernet_provider.createIPv4(
1124 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1125 *addressFieldValue, asyncResp);
1126 } else {
1127 // Existing object that should be modified/deleted/remain unchanged
1128 if (input[entryIdx].is_null()) {
1129 // Object should be deleted
1130 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1131 entryIdx, asyncResp);
1132 } else if (input[entryIdx].is_object()) {
1133 if (input[entryIdx].size() == 0) {
1134 // Object shall remain unchanged
1135 continue;
1136 }
1137
1138 // Apply changes
1139 if (addressFieldState == json_util::Result::SUCCESS &&
1140 ipv4_data[entryIdx].address != nullptr &&
1141 *ipv4_data[entryIdx].address != *addressFieldValue) {
1142 ethernet_provider.changeIPv4AddressProperty(
1143 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1144 *addressFieldValue, asyncResp);
1145 }
1146
1147 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1148 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1149 ethernet_provider.changeIPv4SubnetMaskProperty(
1150 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1151 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1152 }
1153
1154 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1155 ipv4_data[entryIdx].origin != *addressFieldValue) {
1156 ethernet_provider.changeIPv4Origin(
1157 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1158 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1159 }
1160
1161 if (gatewayFieldState == json_util::Result::SUCCESS &&
1162 ipv4_data[entryIdx].gateway != nullptr &&
1163 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1164 ethernet_provider.changeIPv4AddressProperty(
1165 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1166 *gatewayFieldValue, asyncResp);
1167 }
1168 }
1169 }
1170 }
1171 }
1172
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001173 nlohmann::json parseInterfaceData(
1174 const std::string &iface_id, const EthernetInterfaceData &eth_data,
1175 const std::vector<IPv4AddressData> &ipv4_data) {
1176 // Copy JSON object to avoid race condition
1177 nlohmann::json json_response(Node::json);
1178
1179 // Fill out obvious data...
1180 json_response["Id"] = iface_id;
1181 json_response["@odata.id"] =
1182 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1183
1184 // ... then the one from DBus, regarding eth iface...
1185 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1186
1187 if (eth_data.mac_address != nullptr)
1188 json_response["MACAddress"] = *eth_data.mac_address;
1189
1190 if (eth_data.hostname != nullptr)
1191 json_response["HostName"] = *eth_data.hostname;
1192
1193 if (eth_data.vlan_id != nullptr) {
1194 nlohmann::json &vlanObj = json_response["VLAN"];
1195 vlanObj["VLANEnable"] = true;
1196 vlanObj["VLANId"] = *eth_data.vlan_id;
1197 }
1198
1199 // ... at last, check if there are IPv4 data and prepare appropriate
1200 // collection
1201 if (ipv4_data.size() > 0) {
1202 nlohmann::json ipv4_array = nlohmann::json::array();
1203 for (auto &ipv4_config : ipv4_data) {
1204 nlohmann::json json_ipv4;
1205 if (ipv4_config.address != nullptr) {
1206 json_ipv4["Address"] = *ipv4_config.address;
1207 if (ipv4_config.gateway != nullptr)
1208 json_ipv4["Gateway"] = *ipv4_config.gateway;
1209
1210 json_ipv4["AddressOrigin"] = ipv4_config.origin;
1211 json_ipv4["SubnetMask"] = ipv4_config.netmask;
1212
1213 ipv4_array.push_back(std::move(json_ipv4));
1214 }
1215 }
1216 json_response["IPv4Addresses"] = std::move(ipv4_array);
1217 }
1218
1219 return json_response;
1220 }
1221
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001222 /**
1223 * Functions triggers appropriate requests on DBus
1224 */
1225 void doGet(crow::response &res, const crow::request &req,
1226 const std::vector<std::string> &params) override {
1227 // TODO(Pawel) this shall be parametrized call (two params) to get
1228 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1229 // Check if there is required param, truly entering this shall be
1230 // impossible.
1231 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001232 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001233 res.end();
1234 return;
1235 }
1236
1237 const std::string &iface_id = params[0];
1238
1239 // Get single eth interface data, and call the below callback for JSON
1240 // preparation
1241 ethernet_provider.getEthernetIfaceData(
1242 iface_id, [&, iface_id](const bool &success,
1243 const EthernetInterfaceData &eth_data,
1244 const std::vector<IPv4AddressData> &ipv4_data) {
1245 if (success) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001246 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001247 } else {
1248 // ... otherwise return error
1249 // TODO(Pawel)consider distinguish between non existing object, and
1250 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001251 messages::addMessageToErrorJson(
1252 res.json_value,
1253 messages::resourceNotFound("EthernetInterface", iface_id));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001254 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001255 }
1256 res.end();
1257 });
1258 }
1259
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001260 void doPatch(crow::response &res, const crow::request &req,
1261 const std::vector<std::string> &params) override {
1262 // TODO(Pawel) this shall be parametrized call (two params) to get
1263 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1264 // Check if there is required param, truly entering this shall be
1265 // impossible.
1266 if (params.size() != 1) {
1267 res.result(boost::beast::http::status::internal_server_error);
1268 res.end();
1269 return;
1270 }
1271
1272 const std::string &iface_id = params[0];
1273
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001274 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001275
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001276 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001277 return;
1278 }
1279
1280 // Get single eth interface data, and call the below callback for JSON
1281 // preparation
1282 ethernet_provider.getEthernetIfaceData(
1283 iface_id,
1284 [&, iface_id, patchReq = std::move(patchReq) ](
1285 const bool &success, const EthernetInterfaceData &eth_data,
1286 const std::vector<IPv4AddressData> &ipv4_data) {
1287 if (!success) {
1288 // ... otherwise return error
1289 // TODO(Pawel)consider distinguish between non existing object, and
1290 // other errors
1291 res.result(boost::beast::http::status::not_found);
1292 res.end();
1293
1294 return;
1295 }
1296
1297 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1298
1299 std::shared_ptr<AsyncResp> asyncResp =
1300 std::make_shared<AsyncResp>(res);
1301
1302 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1303 ++propertyIt) {
1304 if (propertyIt.key() == "VLAN") {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001305 handleVlanPatch(iface_id, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001306 asyncResp);
1307 } else if (propertyIt.key() == "HostName") {
1308 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001309 } else if (propertyIt.key() == "IPv4Addresses") {
1310 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1311 asyncResp);
1312 } else if (propertyIt.key() == "IPv6Addresses") {
1313 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1314 messages::addMessageToJsonRoot(
1315 res.json_value,
1316 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001317 } else {
1318 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1319
1320 if (fieldInJsonIt == res.json_value.end()) {
1321 // Field not in scope of defined fields
1322 messages::addMessageToJsonRoot(
1323 res.json_value,
1324 messages::propertyUnknown(propertyIt.key()));
1325 } else if (*fieldInJsonIt != *propertyIt) {
1326 // User attempted to modify non-writable field
1327 messages::addMessageToJsonRoot(
1328 res.json_value,
1329 messages::propertyNotWritable(propertyIt.key()));
1330 }
1331 }
1332 }
1333 });
1334 }
1335
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001336 // Ethernet Provider object
1337 // TODO(Pawel) consider move it to singleton
1338 OnDemandEthernetProvider ethernet_provider;
1339};
1340
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001341class VlanNetworkInterfaceCollection;
1342
1343/**
1344 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1345 */
1346class VlanNetworkInterface : public Node {
1347 public:
1348 /*
1349 * Default Constructor
1350 */
1351 template <typename CrowApp>
1352 // TODO(Pawel) Remove line from below, where we assume that there is only one
1353 // manager called openbmc This shall be generic, but requires to update
1354 // GetSubroutes method
1355 VlanNetworkInterface(CrowApp &app)
1356 : Node(app,
1357 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/"
1358 "<str>",
1359 std::string(), std::string()) {
1360 Node::json["@odata.type"] =
1361 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1362 Node::json["@odata.context"] =
1363 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1364 Node::json["Name"] = "VLAN Network Interface";
1365
1366 entityPrivileges = {
1367 {boost::beast::http::verb::get, {{"Login"}}},
1368 {boost::beast::http::verb::head, {{"Login"}}},
1369 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1370 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1371 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1372 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1373 }
1374
1375 private:
1376 nlohmann::json parseInterfaceData(
1377 const std::string &parent_iface_id, const std::string &iface_id,
1378 const EthernetInterfaceData &eth_data,
1379 const std::vector<IPv4AddressData> &ipv4_data) {
1380 // Copy JSON object to avoid race condition
1381 nlohmann::json json_response(Node::json);
1382
1383 // Fill out obvious data...
1384 json_response["Id"] = iface_id;
1385 json_response["@odata.id"] =
1386 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_iface_id +
1387 "/VLANs/" + iface_id;
1388
1389 json_response["VLANEnable"] = true;
1390 json_response["VLANId"] = *eth_data.vlan_id;
1391
1392 return json_response;
1393 }
1394
1395 bool verifyNames(crow::response &res, const std::string &parent,
1396 const std::string &iface) {
1397 if (!boost::starts_with(iface, parent + "_")) {
1398 messages::addMessageToErrorJson(
1399 res.json_value,
1400 messages::resourceNotFound("VLAN Network Interface", iface));
1401 res.result(boost::beast::http::status::bad_request);
1402 res.end();
1403
1404 return false;
1405 } else {
1406 return true;
1407 }
1408 }
1409
1410 /**
1411 * Functions triggers appropriate requests on DBus
1412 */
1413 void doGet(crow::response &res, const crow::request &req,
1414 const std::vector<std::string> &params) override {
1415 // TODO(Pawel) this shall be parametrized call (two params) to get
1416 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1417 // Check if there is required param, truly entering this shall be
1418 // impossible.
1419 if (params.size() != 2) {
1420 res.result(boost::beast::http::status::internal_server_error);
1421 res.end();
1422 return;
1423 }
1424
1425 const std::string &parent_iface_id = params[0];
1426 const std::string &iface_id = params[1];
1427
1428 if (!verifyNames(res, parent_iface_id, iface_id)) {
1429 return;
1430 }
1431
1432 // Get single eth interface data, and call the below callback for JSON
1433 // preparation
1434 ethernet_provider.getEthernetIfaceData(
1435 iface_id,
1436 [&, parent_iface_id, iface_id](
1437 const bool &success, const EthernetInterfaceData &eth_data,
1438 const std::vector<IPv4AddressData> &ipv4_data) {
1439 if (success && eth_data.vlan_id != nullptr) {
1440 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1441 eth_data, ipv4_data);
1442 } else {
1443 // ... otherwise return error
1444 // TODO(Pawel)consider distinguish between non existing object,
1445 // and
1446 // other errors
1447 res.result(boost::beast::http::status::not_found);
1448 }
1449 res.end();
1450 });
1451 }
1452
1453 void doPatch(crow::response &res, const crow::request &req,
1454 const std::vector<std::string> &params) override {
1455 if (params.size() != 2) {
1456 res.result(boost::beast::http::status::internal_server_error);
1457 res.end();
1458 return;
1459 }
1460
1461 const std::string &parent_iface_id = params[0];
1462 const std::string &iface_id = params[1];
1463
1464 if (!verifyNames(res, parent_iface_id, iface_id)) {
1465 return;
1466 }
1467
1468 nlohmann::json patchReq;
1469
1470 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1471 return;
1472 }
1473
1474 // Get single eth interface data, and call the below callback for JSON
1475 // preparation
1476 ethernet_provider.getEthernetIfaceData(
1477 iface_id,
1478 [&, parent_iface_id, iface_id, patchReq = std::move(patchReq) ](
1479 const bool &success, const EthernetInterfaceData &eth_data,
1480 const std::vector<IPv4AddressData> &ipv4_data) {
1481 if (!success) {
1482 // ... otherwise return error
1483 // TODO(Pawel)consider distinguish between non existing object,
1484 // and
1485 // other errors
1486 res.result(boost::beast::http::status::not_found);
1487 res.end();
1488
1489 return;
1490 }
1491
1492 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1493 eth_data, ipv4_data);
1494
1495 std::shared_ptr<AsyncResp> asyncResp =
1496 std::make_shared<AsyncResp>(res);
1497
1498 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1499 ++propertyIt) {
1500 if (propertyIt.key() != "VLANEnable" &&
1501 propertyIt.key() != "VLANId") {
1502 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1503
1504 if (fieldInJsonIt == res.json_value.end()) {
1505 // Field not in scope of defined fields
1506 messages::addMessageToJsonRoot(
1507 res.json_value,
1508 messages::propertyUnknown(propertyIt.key()));
1509 } else if (*fieldInJsonIt != *propertyIt) {
1510 // User attempted to modify non-writable field
1511 messages::addMessageToJsonRoot(
1512 res.json_value,
1513 messages::propertyNotWritable(propertyIt.key()));
1514 }
1515 }
1516 }
1517
1518 EthernetInterface::handleVlanPatch(iface_id, patchReq, eth_data, "/",
1519 asyncResp);
1520 });
1521 }
1522
1523 void doDelete(crow::response &res, const crow::request &req,
1524 const std::vector<std::string> &params) override {
1525 if (params.size() != 2) {
1526 res.result(boost::beast::http::status::internal_server_error);
1527 res.end();
1528 return;
1529 }
1530
1531 const std::string &parent_iface_id = params[0];
1532 const std::string &iface_id = params[1];
1533
1534 if (!verifyNames(res, parent_iface_id, iface_id)) {
1535 return;
1536 }
1537
1538 // Get single eth interface data, and call the below callback for JSON
1539 // preparation
1540 ethernet_provider.getEthernetIfaceData(
1541 iface_id,
1542 [&, parent_iface_id, iface_id](
1543 const bool &success, const EthernetInterfaceData &eth_data,
1544 const std::vector<IPv4AddressData> &ipv4_data) {
1545 if (success && eth_data.vlan_id != nullptr) {
1546 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1547 eth_data, ipv4_data);
1548
1549 // Disable VLAN
1550 OnDemandEthernetProvider::disableVlan(
1551 iface_id, [&](const boost::system::error_code ec) {
1552 if (ec) {
1553 res.json_value = nlohmann::json::object();
1554 messages::addMessageToErrorJson(res.json_value,
1555 messages::internalError());
1556 res.result(
1557 boost::beast::http::status::internal_server_error);
1558 }
1559 res.end();
1560 });
1561 } else {
1562 // ... otherwise return error
1563 // TODO(Pawel)consider distinguish between non existing object,
1564 // and
1565 // other errors
1566
1567 res.result(boost::beast::http::status::not_found);
1568 res.end();
1569 }
1570 });
1571 }
1572
1573 /**
1574 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1575 * method, to maintain consistency of returned data, as Collection's doPost
1576 * should return data for created member which should match member's doGet
1577 * result in 100%.
1578 */
1579 friend VlanNetworkInterfaceCollection;
1580
1581 // Ethernet Provider object
1582 // TODO(Pawel) consider move it to singleton
1583 OnDemandEthernetProvider ethernet_provider;
1584};
1585
1586/**
1587 * VlanNetworkInterfaceCollection derived class for delivering
1588 * VLANNetworkInterface Collection Schema
1589 */
1590class VlanNetworkInterfaceCollection : public Node {
1591 public:
1592 template <typename CrowApp>
1593 // TODO(Pawel) Remove line from below, where we assume that there is only one
1594 // manager called openbmc This shall be generic, but requires to update
1595 // GetSubroutes method
1596 VlanNetworkInterfaceCollection(CrowApp &app)
1597 : Node(app,
1598 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1599 std::string()),
1600 memberVlan(app) {
1601 Node::json["@odata.type"] =
1602 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1603 Node::json["@odata.context"] =
1604 "/redfish/v1/$metadata"
1605 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1606 Node::json["Name"] = "VLAN Network Interface Collection";
1607
1608 entityPrivileges = {
1609 {boost::beast::http::verb::get, {{"Login"}}},
1610 {boost::beast::http::verb::head, {{"Login"}}},
1611 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1612 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1613 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1614 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1615 }
1616
1617 private:
1618 /**
1619 * Functions triggers appropriate requests on DBus
1620 */
1621 void doGet(crow::response &res, const crow::request &req,
1622 const std::vector<std::string> &params) override {
1623 if (params.size() != 1) {
1624 // This means there is a problem with the router
1625 res.result(boost::beast::http::status::internal_server_error);
1626 res.end();
1627
1628 return;
1629 }
1630
1631 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1632 // any Manager, not only hardcoded 'openbmc'.
1633 std::string manager_id = "openbmc";
1634 std::string rootInterfaceName = params[0];
1635
1636 // Get eth interface list, and call the below callback for JSON preparation
1637 ethernet_provider.getEthernetIfaceList([
1638 &, manager_id{std::move(manager_id)},
1639 rootInterfaceName{std::move(rootInterfaceName)}
1640 ](const bool &success, const std::vector<std::string> &iface_list) {
1641 if (success) {
1642 bool rootInterfaceFound = false;
1643 nlohmann::json iface_array = nlohmann::json::array();
1644
1645 for (const std::string &iface_item : iface_list) {
1646 if (iface_item == rootInterfaceName) {
1647 rootInterfaceFound = true;
1648 } else if (boost::starts_with(iface_item, rootInterfaceName + "_")) {
1649 iface_array.push_back(
1650 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
1651 "/EthernetInterfaces/" + rootInterfaceName +
1652 "/VLANs/" + iface_item}});
1653 }
1654 }
1655
1656 if (rootInterfaceFound) {
1657 Node::json["Members"] = iface_array;
1658 Node::json["Members@odata.count"] = iface_array.size();
1659 Node::json["@odata.id"] = "/redfish/v1/Managers/" + manager_id +
1660 "/EthernetInterfaces/" + rootInterfaceName +
1661 "/VLANs";
1662 res.json_value = Node::json;
1663 } else {
1664 messages::addMessageToErrorJson(
1665 res.json_value, messages::resourceNotFound("EthernetInterface",
1666 rootInterfaceName));
1667 res.result(boost::beast::http::status::not_found);
1668 res.end();
1669 }
1670 } else {
1671 // No success, best what we can do is return INTERNALL ERROR
1672 res.result(boost::beast::http::status::internal_server_error);
1673 }
1674 res.end();
1675 });
1676 }
1677
1678 void doPost(crow::response &res, const crow::request &req,
1679 const std::vector<std::string> &params) override {
1680 if (params.size() != 1) {
1681 // This means there is a problem with the router
1682 res.result(boost::beast::http::status::internal_server_error);
1683 res.end();
1684 return;
1685 }
1686
1687 nlohmann::json postReq;
1688
1689 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1690 return;
1691 }
1692
1693 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1694 // any Manager, not only hardcoded 'openbmc'.
1695 std::string manager_id = "openbmc";
1696 std::string rootInterfaceName = params[0];
1697 uint64_t vlanId;
1698 bool errorDetected;
1699
1700 if (json_util::getUnsigned(
1701 "VLANId", postReq, vlanId,
1702 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1703 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1704 res.json_value, "/VLANId") != json_util::Result::SUCCESS) {
1705 res.end();
1706 return;
1707 }
1708
1709 // Get eth interface list, and call the below callback for JSON preparation
1710 ethernet_provider.getEthernetIfaceList([
1711 &, manager_id{std::move(manager_id)},
1712 rootInterfaceName{std::move(rootInterfaceName)}
1713 ](const bool &success, const std::vector<std::string> &iface_list) {
1714 if (success) {
1715 bool rootInterfaceFound = false;
1716
1717 for (const std::string &iface_item : iface_list) {
1718 if (iface_item == rootInterfaceName) {
1719 rootInterfaceFound = true;
1720 break;
1721 }
1722 }
1723
1724 if (rootInterfaceFound) {
1725 ethernet_provider.createVlan(
1726 rootInterfaceName, vlanId,
1727 [&, vlanId, rootInterfaceName,
1728 req{std::move(req)} ](const boost::system::error_code ec) {
1729 if (ec) {
1730 messages::addMessageToErrorJson(res.json_value,
1731 messages::internalError());
1732 res.end();
1733 } else {
1734 memberVlan.doGet(
1735 res, req,
1736 {rootInterfaceName,
1737 rootInterfaceName + "_" + std::to_string(vlanId)});
1738 }
1739 });
1740 } else {
1741 messages::addMessageToErrorJson(
1742 res.json_value, messages::resourceNotFound("EthernetInterface",
1743 rootInterfaceName));
1744 res.result(boost::beast::http::status::not_found);
1745 res.end();
1746 }
1747 } else {
1748 // No success, best what we can do is return INTERNALL ERROR
1749 res.result(boost::beast::http::status::internal_server_error);
1750 res.end();
1751 }
1752 });
1753 }
1754
1755 // Ethernet Provider object
1756 // TODO(Pawel) consider move it to singleton
1757 OnDemandEthernetProvider ethernet_provider;
1758 VlanNetworkInterface memberVlan;
1759};
1760
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001761} // namespace redfish