blob: 6c2bf6c01efcb7549796f188a3418c01e79734bd [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;
327 for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) {
328 // Use strtol instead of stroi to avoid exceptions
329 long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10);
330
331 // endPtr should point to the end of the string, otherwise given string
332 // is not 100% number
333 if (*endPtr != '\0') {
334 return false;
335 }
336
337 // Value should be contained in byte
338 if (value < 0 || value > 255) {
339 return false;
340 }
341
342 if (bits != nullptr) {
343 // Mask has to be continuous between bytes
344 if (previousValue != 255 && value != 0) {
345 return false;
346 }
347
348 // Mask has to be continuous inside bytes
349 firstZeroInByteHit = false;
350
351 // Count bits
352 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
353 if (value & (1 << bitIdx)) {
354 if (firstZeroInByteHit) {
355 // Continuity not preserved
356 return false;
357 } else {
358 (*bits)++;
359 }
360 } else {
361 firstZeroInByteHit = true;
362 }
363 }
364 }
365
366 previousValue = value;
367 }
368
369 return true;
370 }
371
372 /**
373 * @brief Changes IPv4 address type property (Address, Gateway)
374 *
375 * @param[in] ifaceId Id of interface whose IP should be modified
376 * @param[in] ipIdx Index of IP in input array that should be modified
377 * @param[in] ipHash DBus Hash id of modified IP
378 * @param[in] name Name of field in JSON representation
379 * @param[in] newValue New value that should be written
380 * @param[io] asyncResp Response object that will be returned to client
381 *
382 * @return true if give IP is valid and has been sent do D-Bus, false
383 * otherwise
384 */
385 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
386 const std::string &ipHash,
387 const std::string &name,
388 const std::string &newValue,
389 const std::shared_ptr<AsyncResp> &asyncResp) {
390 auto callback = [
391 asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
392 newValue{std::move(newValue)}
393 ](const boost::system::error_code ec) {
394 if (ec) {
395 messages::addMessageToJson(
396 asyncResp->res.json_value, messages::internalError(),
397 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
398 } else {
399 asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue;
400 }
401 };
402
403 crow::connections::system_bus->async_method_call(
404 std::move(callback), "xyz.openbmc_project.Network",
405 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
406 "org.freedesktop.DBus.Properties", "Set",
407 "xyz.openbmc_project.Network.IP", name,
408 sdbusplus::message::variant<std::string>(newValue));
409 };
410
411 /**
412 * @brief Changes IPv4 address origin property
413 *
414 * @param[in] ifaceId Id of interface whose IP should be modified
415 * @param[in] ipIdx Index of IP in input array that should be modified
416 * @param[in] ipHash DBus Hash id of modified IP
417 * @param[in] newValue New value in Redfish format
418 * @param[in] newValueDbus New value in D-Bus format
419 * @param[io] asyncResp Response object that will be returned to client
420 *
421 * @return true if give IP is valid and has been sent do D-Bus, false
422 * otherwise
423 */
424 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
425 const std::string &ipHash, const std::string &newValue,
426 const std::string &newValueDbus,
427 const std::shared_ptr<AsyncResp> &asyncResp) {
428 auto callback =
429 [ asyncResp, ipIdx{std::move(ipIdx)},
430 newValue{std::move(newValue)} ](const boost::system::error_code ec) {
431 if (ec) {
432 messages::addMessageToJson(
433 asyncResp->res.json_value, messages::internalError(),
434 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
435 } else {
436 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] =
437 newValue;
438 }
439 };
440
441 crow::connections::system_bus->async_method_call(
442 std::move(callback), "xyz.openbmc_project.Network",
443 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
444 "org.freedesktop.DBus.Properties", "Set",
445 "xyz.openbmc_project.Network.IP", "Origin",
446 sdbusplus::message::variant<std::string>(newValueDbus));
447 };
448
449 /**
450 * @brief Modifies SubnetMask for given IP
451 *
452 * @param[in] ifaceId Id of interface whose IP should be modified
453 * @param[in] ipIdx Index of IP in input array that should be modified
454 * @param[in] ipHash DBus Hash id of modified IP
455 * @param[in] newValueStr Mask in dot notation as string
456 * @param[in] newValue Mask as PrefixLength in bitcount
457 * @param[io] asyncResp Response object that will be returned to client
458 *
459 * @return None
460 */
461 void changeIPv4SubnetMaskProperty(
462 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
463 const std::string &newValueStr, uint8_t &newValue,
464 const std::shared_ptr<AsyncResp> &asyncResp) {
465 auto callback = [
466 asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
467 ](const boost::system::error_code ec) {
468 if (ec) {
469 messages::addMessageToJson(
470 asyncResp->res.json_value, messages::internalError(),
471 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
472 } else {
473 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] =
474 newValueStr;
475 }
476 };
477
478 crow::connections::system_bus->async_method_call(
479 std::move(callback), "xyz.openbmc_project.Network",
480 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
481 "org.freedesktop.DBus.Properties", "Set",
482 "xyz.openbmc_project.Network.IP", "PrefixLength",
483 sdbusplus::message::variant<uint8_t>(newValue));
484 };
485
486 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200487 * @brief Disables VLAN with given ifaceId
488 *
489 * @param[in] ifaceId Id of VLAN interface that should be disabled
490 * @param[in] callback Function that will be called after the operation
491 *
492 * @return None.
493 */
494 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200495 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200496 crow::connections::system_bus->async_method_call(
497 callback, "xyz.openbmc_project.Network",
498 std::string("/xyz/openbmc_project/network/") + ifaceId,
499 "xyz.openbmc_project.Object.Delete", "Delete");
500 };
501
502 /**
503 * @brief Sets given HostName of the machine through D-Bus
504 *
505 * @param[in] newHostname New name that HostName will be changed to
506 * @param[in] callback Function that will be called after the operation
507 *
508 * @return None.
509 */
510 template <typename CallbackFunc>
511 void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
512 crow::connections::system_bus->async_method_call(
513 callback, "xyz.openbmc_project.Network",
514 "/xyz/openbmc_project/network/config",
515 "org.freedesktop.DBus.Properties", "Set",
516 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
517 sdbusplus::message::variant<std::string>(newHostname));
518 };
519
520 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200521 * @brief Deletes given IPv4
522 *
523 * @param[in] ifaceId Id of interface whose IP should be deleted
524 * @param[in] ipIdx Index of IP in input array that should be deleted
525 * @param[in] ipHash DBus Hash id of IP that should be deleted
526 * @param[io] asyncResp Response object that will be returned to client
527 *
528 * @return None
529 */
530 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
531 unsigned int ipIdx,
532 const std::shared_ptr<AsyncResp> &asyncResp) {
533 crow::connections::system_bus->async_method_call(
534 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
535 const boost::system::error_code ec) {
536 if (ec) {
537 messages::addMessageToJson(
538 asyncResp->res.json_value, messages::internalError(),
539 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
540 } else {
541 asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr;
542 }
543 },
544 "xyz.openbmc_project.Network",
545 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
546 "xyz.openbmc_project.Object.Delete", "Delete");
547 }
548
549 /**
550 * @brief Creates IPv4 with given data
551 *
552 * @param[in] ifaceId Id of interface whose IP should be deleted
553 * @param[in] ipIdx Index of IP in input array that should be deleted
554 * @param[in] ipHash DBus Hash id of IP that should be deleted
555 * @param[io] asyncResp Response object that will be returned to client
556 *
557 * @return None
558 */
559 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
560 uint8_t subnetMask, const std::string &gateway,
561 const std::string &address,
562 const std::shared_ptr<AsyncResp> &asyncResp) {
563 auto createIpHandler = [
564 ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
565 ](const boost::system::error_code ec) {
566 if (ec) {
567 messages::addMessageToJson(
568 asyncResp->res.json_value, messages::internalError(),
569 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
570 }
571 };
572
573 crow::connections::system_bus->async_method_call(
574 std::move(createIpHandler), "xyz.openbmc_project.Network",
575 "/xyz/openbmc_project/network/" + ifaceId,
576 "xyz.openbmc_project.Network.IP.Create", "IP",
577 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
578 gateway);
579 }
580
581 /**
582 * @brief Translates Address Origin value from D-Bus to Redfish format and
583 * vice-versa
584 *
585 * @param[in] inputOrigin Input value that should be translated
586 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
587 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse
588 *
589 * @return Empty string in case of failure, translated value otherwise
590 */
591 std::string translateAddressOriginBetweenDBusAndRedfish(
592 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
593 // Invalid pointer
594 if (inputOrigin == nullptr) {
595 return "";
596 }
597
598 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
599 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
600
601 std::array<std::pair<const char *, const char *>, 6> translationTable{
602 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
603 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
604 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
605 "IPv4LinkLocal"},
606 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
607 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
608 "LinkLocal"},
609 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
610
611 for (unsigned int i = 0; i < translationTable.size(); i++) {
612 // Skip unrelated
613 if (isIPv4 && i >= firstIPv6OnlyIdx) break;
614 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
615
616 // When translating D-Bus to Redfish compare input to first element
617 if (isFromDBus && translationTable[i].first == *inputOrigin)
618 return translationTable[i].second;
619
620 // When translating Redfish to D-Bus compare input to second element
621 if (!isFromDBus && translationTable[i].second == *inputOrigin)
622 return translationTable[i].first;
623 }
624
625 // If we are still here, that means that value has not been found
626 return "";
627 }
628
629 /**
630 * Function that retrieves all properties for given Ethernet Interface
631 * Object
632 * from EntityManager Network Manager
633 * @param ethiface_id a eth interface id to query on DBus
634 * @param callback a function that shall be called to convert Dbus output
635 * into JSON
636 */
637 template <typename CallbackFunc>
638 void getEthernetIfaceData(const std::string &ethiface_id,
639 CallbackFunc &&callback) {
640 crow::connections::system_bus->async_method_call(
641 [
642 this, ethiface_id{std::move(ethiface_id)},
643 callback{std::move(callback)}
644 ](const boost::system::error_code error_code,
645 const GetManagedObjectsType &resp) {
646
647 EthernetInterfaceData eth_data{};
648 std::vector<IPv4AddressData> ipv4_data;
649 ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
650
651 if (error_code) {
652 // Something wrong on DBus, the error_code is not important at
653 // this moment, just return success=false, and empty output. Since
654 // size of vector may vary depending on information from Network
655 // Manager, and empty output could not be treated same way as
656 // error.
657 callback(false, eth_data, ipv4_data);
658 return;
659 }
660
661 extractEthernetInterfaceData(ethiface_id, resp, eth_data);
662 extractIPv4Data(ethiface_id, resp, ipv4_data);
663
664 // Fix global GW
665 for (IPv4AddressData &ipv4 : ipv4_data) {
666 if ((ipv4.global) &&
667 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
668 ipv4.gateway = eth_data.default_gateway;
669 }
670 }
671
672 // Finally make a callback with usefull data
673 callback(true, eth_data, ipv4_data);
674 },
675 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
676 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
677 };
678
679 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100680 * Function that retrieves all Ethernet Interfaces available through Network
681 * Manager
682 * @param callback a function that shall be called to convert Dbus output into
683 * JSON.
684 */
685 template <typename CallbackFunc>
686 void getEthernetIfaceList(CallbackFunc &&callback) {
687 crow::connections::system_bus->async_method_call(
688 [ this, callback{std::move(callback)} ](
689 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700690 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100691 // Callback requires vector<string> to retrieve all available ethernet
692 // interfaces
693 std::vector<std::string> iface_list;
694 iface_list.reserve(resp.size());
695 if (error_code) {
696 // Something wrong on DBus, the error_code is not important at this
697 // moment, just return success=false, and empty output. Since size
698 // of vector may vary depending on information from Network Manager,
699 // and empty output could not be treated same way as error.
700 callback(false, iface_list);
701 return;
702 }
703
704 // Iterate over all retrieved ObjectPaths.
705 for (auto &objpath : resp) {
706 // And all interfaces available for certain ObjectPath.
707 for (auto &interface : objpath.second) {
708 // If interface is xyz.openbmc_project.Network.EthernetInterface,
709 // this is what we're looking for.
710 if (interface.first ==
711 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700712 // Cut out everyting until last "/", ...
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200713 const std::string &iface_id =
714 static_cast<const std::string &>(objpath.first);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100715 std::size_t last_pos = iface_id.rfind("/");
716 if (last_pos != std::string::npos) {
717 // and put it into output vector.
718 iface_list.emplace_back(iface_id.substr(last_pos + 1));
719 }
720 }
721 }
722 }
Gunnar Mills274fad52018-06-13 15:45:36 -0500723 // Finally make a callback with useful data
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100724 callback(true, iface_list);
725 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700726 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
727 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100728 };
729};
730
731/**
732 * EthernetCollection derived class for delivering Ethernet Collection Schema
733 */
734class EthernetCollection : public Node {
735 public:
736 template <typename CrowApp>
737 // TODO(Pawel) Remove line from below, where we assume that there is only one
738 // manager called openbmc This shall be generic, but requires to update
739 // GetSubroutes method
740 EthernetCollection(CrowApp &app)
741 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
742 Node::json["@odata.type"] =
743 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
744 Node::json["@odata.context"] =
745 "/redfish/v1/"
746 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
747 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
748 Node::json["Name"] = "Ethernet Network Interface Collection";
749 Node::json["Description"] =
750 "Collection of EthernetInterfaces for this Manager";
751
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200752 entityPrivileges = {
753 {boost::beast::http::verb::get, {{"Login"}}},
754 {boost::beast::http::verb::head, {{"Login"}}},
755 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
756 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
757 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
758 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100759 }
760
761 private:
762 /**
763 * Functions triggers appropriate requests on DBus
764 */
765 void doGet(crow::response &res, const crow::request &req,
766 const std::vector<std::string> &params) override {
767 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
768 // any Manager, not only hardcoded 'openbmc'.
769 std::string manager_id = "openbmc";
770
771 // Get eth interface list, and call the below callback for JSON preparation
772 ethernet_provider.getEthernetIfaceList(
773 [&, manager_id{std::move(manager_id)} ](
774 const bool &success, const std::vector<std::string> &iface_list) {
775 if (success) {
776 nlohmann::json iface_array = nlohmann::json::array();
777 for (const std::string &iface_item : iface_list) {
778 iface_array.push_back(
779 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
780 "/EthernetInterfaces/" + iface_item}});
781 }
782 Node::json["Members"] = iface_array;
783 Node::json["Members@odata.count"] = iface_array.size();
784 Node::json["@odata.id"] =
785 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
786 res.json_value = Node::json;
787 } else {
788 // No success, best what we can do is return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700789 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100790 }
791 res.end();
792 });
793 }
794
795 // Ethernet Provider object
796 // TODO(Pawel) consider move it to singleton
797 OnDemandEthernetProvider ethernet_provider;
798};
799
800/**
801 * EthernetInterface derived class for delivering Ethernet Schema
802 */
803class EthernetInterface : public Node {
804 public:
805 /*
806 * Default Constructor
807 */
808 template <typename CrowApp>
809 // TODO(Pawel) Remove line from below, where we assume that there is only one
810 // manager called openbmc This shall be generic, but requires to update
811 // GetSubroutes method
812 EthernetInterface(CrowApp &app)
813 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
814 std::string()) {
815 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
816 Node::json["@odata.context"] =
817 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
818 Node::json["Name"] = "Manager Ethernet Interface";
819 Node::json["Description"] = "Management Network Interface";
820
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200821 entityPrivileges = {
822 {boost::beast::http::verb::get, {{"Login"}}},
823 {boost::beast::http::verb::head, {{"Login"}}},
824 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
825 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
826 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
827 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100828 }
829
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200830 // TODO(kkowalsk) Find a suitable class/namespace for this
831 static void handleVlanPatch(const std::string &ifaceId,
832 const nlohmann::json &input,
833 const EthernetInterfaceData &eth_data,
834 const std::string &pathPrefix,
835 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200836 if (!input.is_object()) {
837 messages::addMessageToJson(
838 asyncResp->res.json_value,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200839 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200840 return;
841 }
842
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200843 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
844 nlohmann::json &paramsJson =
845 (pathPrefix == "/")
846 ? asyncResp->res.json_value
847 : asyncResp->res.json_value[nlohmann::json_pointer<nlohmann::json>(
848 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200849 bool inputVlanEnabled;
850 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200851
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200852 json_util::Result inputVlanEnabledState = json_util::getBool(
853 "VLANEnable", input, inputVlanEnabled,
854 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200855 asyncResp->res.json_value, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200856 json_util::Result inputVlanIdState = json_util::getUnsigned(
857 "VLANId", input, inputVlanId,
858 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200859 asyncResp->res.json_value, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200860 bool inputInvalid = false;
861
862 // Do not proceed if fields in VLAN object were of wrong type
863 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
864 inputVlanIdState == json_util::Result::WRONG_TYPE) {
865 return;
866 }
867
868 // Verify input
869 if (eth_data.vlan_id == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200870 // This interface is not a VLAN. Cannot do anything with it
871 // TODO(kkowalsk) Change this message
872 messages::addMessageToJson(asyncResp->res.json_value,
873 messages::propertyMissing("VLANEnable"),
874 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200875
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200876 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200877 } else {
878 // Load actual data into field values if they were not provided
879 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
880 inputVlanEnabled = true;
881 }
882
883 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
884 inputVlanId = *eth_data.vlan_id;
885 }
886 }
887
888 // Do not proceed if input has not been valid
889 if (inputInvalid) {
890 return;
891 }
892
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200893 // VLAN is configured on the interface
894 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
895 // Change VLAN Id
896 paramsJson["VLANId"] = inputVlanId;
897 OnDemandEthernetProvider::changeVlanId(
898 ifaceId, static_cast<uint32_t>(inputVlanId),
899 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
900 const boost::system::error_code ec) {
901 if (ec) {
902 messages::addMessageToJson(asyncResp->res.json_value,
903 messages::internalError(), pathPrefix);
904 } else {
905 paramsJson["VLANEnable"] = true;
906 }
907 });
908 } else if (inputVlanEnabled == false) {
909 // Disable VLAN
910 OnDemandEthernetProvider::disableVlan(
911 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
912 const boost::system::error_code ec) {
913 if (ec) {
914 messages::addMessageToJson(asyncResp->res.json_value,
915 messages::internalError(), pathPrefix);
916 } else {
917 paramsJson["VLANEnable"] = false;
918 }
919 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200920 }
921 }
922
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200923 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200924 void handleHostnamePatch(const nlohmann::json &input,
925 const EthernetInterfaceData &eth_data,
926 const std::shared_ptr<AsyncResp> &asyncResp) {
927 if (input.is_string()) {
928 std::string newHostname = input.get<std::string>();
929
930 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
931 // Change hostname
932 ethernet_provider.setHostName(
933 newHostname,
934 [asyncResp, newHostname](const boost::system::error_code ec) {
935 if (ec) {
936 messages::addMessageToJson(asyncResp->res.json_value,
937 messages::internalError(),
938 "/HostName");
939 } else {
940 asyncResp->res.json_value["HostName"] = newHostname;
941 }
942 });
943 }
944 } else {
945 messages::addMessageToJson(
946 asyncResp->res.json_value,
947 messages::propertyValueTypeError(input.dump(), "HostName"),
948 "/HostName");
949 }
950 }
951
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200952 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
953 const std::vector<IPv4AddressData> &ipv4_data,
954 const std::shared_ptr<AsyncResp> &asyncResp) {
955 if (!input.is_array()) {
956 messages::addMessageToJson(
957 asyncResp->res.json_value,
958 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
959 "/IPv4Addresses");
960 return;
961 }
962
963 // According to Redfish PATCH definition, size must be at least equal
964 if (input.size() < ipv4_data.size()) {
965 // TODO(kkowalsk) This should be a message indicating that not enough
966 // data has been provided
967 messages::addMessageToJson(asyncResp->res.json_value,
968 messages::internalError(), "/IPv4Addresses");
969 return;
970 }
971
972 json_util::Result addressFieldState;
973 json_util::Result subnetMaskFieldState;
974 json_util::Result addressOriginFieldState;
975 json_util::Result gatewayFieldState;
976 const std::string *addressFieldValue;
977 const std::string *subnetMaskFieldValue;
978 const std::string *addressOriginFieldValue = nullptr;
979 const std::string *gatewayFieldValue;
980 uint8_t subnetMaskAsPrefixLength;
981 std::string addressOriginInDBusFormat;
982
983 bool errorDetected = false;
984 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
985 // Check that entry is not of some unexpected type
986 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
987 // Invalid object type
988 messages::addMessageToJson(
989 asyncResp->res.json_value,
990 messages::propertyValueTypeError(input[entryIdx].dump(),
991 "IPv4Address"),
992 "/IPv4Addresses/" + std::to_string(entryIdx));
993
994 continue;
995 }
996
997 // Try to load fields
998 addressFieldState = json_util::getString(
999 "Address", input[entryIdx], addressFieldValue,
1000 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1001 asyncResp->res.json_value,
1002 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1003 subnetMaskFieldState = json_util::getString(
1004 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1005 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1006 asyncResp->res.json_value,
1007 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1008 addressOriginFieldState = json_util::getString(
1009 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1010 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1011 asyncResp->res.json_value,
1012 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1013 gatewayFieldState = json_util::getString(
1014 "Gateway", input[entryIdx], gatewayFieldValue,
1015 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1016 asyncResp->res.json_value,
1017 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1018
1019 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1020 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1021 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1022 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1023 return;
1024 }
1025
1026 if (addressFieldState == json_util::Result::SUCCESS &&
1027 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1028 errorDetected = true;
1029 messages::addMessageToJson(
1030 asyncResp->res.json_value,
1031 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1032 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1033 }
1034
1035 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1036 !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1037 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1038 errorDetected = true;
1039 messages::addMessageToJson(
1040 asyncResp->res.json_value,
1041 messages::propertyValueFormatError(*subnetMaskFieldValue,
1042 "SubnetMask"),
1043 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1044 }
1045
1046 // Get Address origin in proper format
1047 addressOriginInDBusFormat =
1048 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1049 addressOriginFieldValue, true, false);
1050
1051 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1052 addressOriginInDBusFormat.empty()) {
1053 errorDetected = true;
1054 messages::addMessageToJson(
1055 asyncResp->res.json_value,
1056 messages::propertyValueNotInList(*addressOriginFieldValue,
1057 "AddressOrigin"),
1058 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1059 }
1060
1061 if (gatewayFieldState == json_util::Result::SUCCESS &&
1062 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1063 errorDetected = true;
1064 messages::addMessageToJson(
1065 asyncResp->res.json_value,
1066 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1067 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1068 }
1069
1070 // If any error occured do not proceed with current entry, but do not
1071 // end loop
1072 if (errorDetected) {
1073 errorDetected = false;
1074 continue;
1075 }
1076
1077 if (entryIdx >= ipv4_data.size()) {
1078 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1079
1080 // Verify that all field were provided
1081 if (addressFieldState == json_util::Result::NOT_EXIST) {
1082 errorDetected = true;
1083 messages::addMessageToJson(
1084 asyncResp->res.json_value, messages::propertyMissing("Address"),
1085 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1086 }
1087
1088 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1089 errorDetected = true;
1090 messages::addMessageToJson(
1091 asyncResp->res.json_value,
1092 messages::propertyMissing("SubnetMask"),
1093 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1094 }
1095
1096 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1097 errorDetected = true;
1098 messages::addMessageToJson(
1099 asyncResp->res.json_value,
1100 messages::propertyMissing("AddressOrigin"),
1101 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1102 }
1103
1104 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1105 errorDetected = true;
1106 messages::addMessageToJson(
1107 asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1108 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1109 }
1110
1111 // If any error occured do not proceed with current entry, but do not
1112 // end loop
1113 if (errorDetected) {
1114 errorDetected = false;
1115 continue;
1116 }
1117
1118 // Create IPv4 with provided data
1119 ethernet_provider.createIPv4(
1120 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1121 *addressFieldValue, asyncResp);
1122 } else {
1123 // Existing object that should be modified/deleted/remain unchanged
1124 if (input[entryIdx].is_null()) {
1125 // Object should be deleted
1126 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1127 entryIdx, asyncResp);
1128 } else if (input[entryIdx].is_object()) {
1129 if (input[entryIdx].size() == 0) {
1130 // Object shall remain unchanged
1131 continue;
1132 }
1133
1134 // Apply changes
1135 if (addressFieldState == json_util::Result::SUCCESS &&
1136 ipv4_data[entryIdx].address != nullptr &&
1137 *ipv4_data[entryIdx].address != *addressFieldValue) {
1138 ethernet_provider.changeIPv4AddressProperty(
1139 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1140 *addressFieldValue, asyncResp);
1141 }
1142
1143 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1144 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1145 ethernet_provider.changeIPv4SubnetMaskProperty(
1146 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1147 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1148 }
1149
1150 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1151 ipv4_data[entryIdx].origin != *addressFieldValue) {
1152 ethernet_provider.changeIPv4Origin(
1153 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1154 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1155 }
1156
1157 if (gatewayFieldState == json_util::Result::SUCCESS &&
1158 ipv4_data[entryIdx].gateway != nullptr &&
1159 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1160 ethernet_provider.changeIPv4AddressProperty(
1161 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1162 *gatewayFieldValue, asyncResp);
1163 }
1164 }
1165 }
1166 }
1167 }
1168
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001169 nlohmann::json parseInterfaceData(
1170 const std::string &iface_id, const EthernetInterfaceData &eth_data,
1171 const std::vector<IPv4AddressData> &ipv4_data) {
1172 // Copy JSON object to avoid race condition
1173 nlohmann::json json_response(Node::json);
1174
1175 // Fill out obvious data...
1176 json_response["Id"] = iface_id;
1177 json_response["@odata.id"] =
1178 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1179
1180 // ... then the one from DBus, regarding eth iface...
1181 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1182
1183 if (eth_data.mac_address != nullptr)
1184 json_response["MACAddress"] = *eth_data.mac_address;
1185
1186 if (eth_data.hostname != nullptr)
1187 json_response["HostName"] = *eth_data.hostname;
1188
1189 if (eth_data.vlan_id != nullptr) {
1190 nlohmann::json &vlanObj = json_response["VLAN"];
1191 vlanObj["VLANEnable"] = true;
1192 vlanObj["VLANId"] = *eth_data.vlan_id;
1193 }
1194
1195 // ... at last, check if there are IPv4 data and prepare appropriate
1196 // collection
1197 if (ipv4_data.size() > 0) {
1198 nlohmann::json ipv4_array = nlohmann::json::array();
1199 for (auto &ipv4_config : ipv4_data) {
1200 nlohmann::json json_ipv4;
1201 if (ipv4_config.address != nullptr) {
1202 json_ipv4["Address"] = *ipv4_config.address;
1203 if (ipv4_config.gateway != nullptr)
1204 json_ipv4["Gateway"] = *ipv4_config.gateway;
1205
1206 json_ipv4["AddressOrigin"] = ipv4_config.origin;
1207 json_ipv4["SubnetMask"] = ipv4_config.netmask;
1208
1209 ipv4_array.push_back(std::move(json_ipv4));
1210 }
1211 }
1212 json_response["IPv4Addresses"] = std::move(ipv4_array);
1213 }
1214
1215 return json_response;
1216 }
1217
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001218 /**
1219 * Functions triggers appropriate requests on DBus
1220 */
1221 void doGet(crow::response &res, const crow::request &req,
1222 const std::vector<std::string> &params) override {
1223 // TODO(Pawel) this shall be parametrized call (two params) to get
1224 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1225 // Check if there is required param, truly entering this shall be
1226 // impossible.
1227 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001228 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001229 res.end();
1230 return;
1231 }
1232
1233 const std::string &iface_id = params[0];
1234
1235 // Get single eth interface data, and call the below callback for JSON
1236 // preparation
1237 ethernet_provider.getEthernetIfaceData(
1238 iface_id, [&, iface_id](const bool &success,
1239 const EthernetInterfaceData &eth_data,
1240 const std::vector<IPv4AddressData> &ipv4_data) {
1241 if (success) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001242 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001243 } else {
1244 // ... otherwise return error
1245 // TODO(Pawel)consider distinguish between non existing object, and
1246 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001247 messages::addMessageToErrorJson(
1248 res.json_value,
1249 messages::resourceNotFound("EthernetInterface", iface_id));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001250 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001251 }
1252 res.end();
1253 });
1254 }
1255
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001256 void doPatch(crow::response &res, const crow::request &req,
1257 const std::vector<std::string> &params) override {
1258 // TODO(Pawel) this shall be parametrized call (two params) to get
1259 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1260 // Check if there is required param, truly entering this shall be
1261 // impossible.
1262 if (params.size() != 1) {
1263 res.result(boost::beast::http::status::internal_server_error);
1264 res.end();
1265 return;
1266 }
1267
1268 const std::string &iface_id = params[0];
1269
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001270 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001271
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001272 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001273 return;
1274 }
1275
1276 // Get single eth interface data, and call the below callback for JSON
1277 // preparation
1278 ethernet_provider.getEthernetIfaceData(
1279 iface_id,
1280 [&, iface_id, patchReq = std::move(patchReq) ](
1281 const bool &success, const EthernetInterfaceData &eth_data,
1282 const std::vector<IPv4AddressData> &ipv4_data) {
1283 if (!success) {
1284 // ... otherwise return error
1285 // TODO(Pawel)consider distinguish between non existing object, and
1286 // other errors
1287 res.result(boost::beast::http::status::not_found);
1288 res.end();
1289
1290 return;
1291 }
1292
1293 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1294
1295 std::shared_ptr<AsyncResp> asyncResp =
1296 std::make_shared<AsyncResp>(res);
1297
1298 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1299 ++propertyIt) {
1300 if (propertyIt.key() == "VLAN") {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001301 handleVlanPatch(iface_id, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001302 asyncResp);
1303 } else if (propertyIt.key() == "HostName") {
1304 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001305 } else if (propertyIt.key() == "IPv4Addresses") {
1306 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1307 asyncResp);
1308 } else if (propertyIt.key() == "IPv6Addresses") {
1309 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1310 messages::addMessageToJsonRoot(
1311 res.json_value,
1312 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001313 } else {
1314 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1315
1316 if (fieldInJsonIt == res.json_value.end()) {
1317 // Field not in scope of defined fields
1318 messages::addMessageToJsonRoot(
1319 res.json_value,
1320 messages::propertyUnknown(propertyIt.key()));
1321 } else if (*fieldInJsonIt != *propertyIt) {
1322 // User attempted to modify non-writable field
1323 messages::addMessageToJsonRoot(
1324 res.json_value,
1325 messages::propertyNotWritable(propertyIt.key()));
1326 }
1327 }
1328 }
1329 });
1330 }
1331
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001332 // Ethernet Provider object
1333 // TODO(Pawel) consider move it to singleton
1334 OnDemandEthernetProvider ethernet_provider;
1335};
1336
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001337class VlanNetworkInterfaceCollection;
1338
1339/**
1340 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1341 */
1342class VlanNetworkInterface : public Node {
1343 public:
1344 /*
1345 * Default Constructor
1346 */
1347 template <typename CrowApp>
1348 // TODO(Pawel) Remove line from below, where we assume that there is only one
1349 // manager called openbmc This shall be generic, but requires to update
1350 // GetSubroutes method
1351 VlanNetworkInterface(CrowApp &app)
1352 : Node(app,
1353 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/"
1354 "<str>",
1355 std::string(), std::string()) {
1356 Node::json["@odata.type"] =
1357 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1358 Node::json["@odata.context"] =
1359 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1360 Node::json["Name"] = "VLAN Network Interface";
1361
1362 entityPrivileges = {
1363 {boost::beast::http::verb::get, {{"Login"}}},
1364 {boost::beast::http::verb::head, {{"Login"}}},
1365 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1366 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1367 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1368 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1369 }
1370
1371 private:
1372 nlohmann::json parseInterfaceData(
1373 const std::string &parent_iface_id, const std::string &iface_id,
1374 const EthernetInterfaceData &eth_data,
1375 const std::vector<IPv4AddressData> &ipv4_data) {
1376 // Copy JSON object to avoid race condition
1377 nlohmann::json json_response(Node::json);
1378
1379 // Fill out obvious data...
1380 json_response["Id"] = iface_id;
1381 json_response["@odata.id"] =
1382 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_iface_id +
1383 "/VLANs/" + iface_id;
1384
1385 json_response["VLANEnable"] = true;
1386 json_response["VLANId"] = *eth_data.vlan_id;
1387
1388 return json_response;
1389 }
1390
1391 bool verifyNames(crow::response &res, const std::string &parent,
1392 const std::string &iface) {
1393 if (!boost::starts_with(iface, parent + "_")) {
1394 messages::addMessageToErrorJson(
1395 res.json_value,
1396 messages::resourceNotFound("VLAN Network Interface", iface));
1397 res.result(boost::beast::http::status::bad_request);
1398 res.end();
1399
1400 return false;
1401 } else {
1402 return true;
1403 }
1404 }
1405
1406 /**
1407 * Functions triggers appropriate requests on DBus
1408 */
1409 void doGet(crow::response &res, const crow::request &req,
1410 const std::vector<std::string> &params) override {
1411 // TODO(Pawel) this shall be parametrized call (two params) to get
1412 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1413 // Check if there is required param, truly entering this shall be
1414 // impossible.
1415 if (params.size() != 2) {
1416 res.result(boost::beast::http::status::internal_server_error);
1417 res.end();
1418 return;
1419 }
1420
1421 const std::string &parent_iface_id = params[0];
1422 const std::string &iface_id = params[1];
1423
1424 if (!verifyNames(res, parent_iface_id, iface_id)) {
1425 return;
1426 }
1427
1428 // Get single eth interface data, and call the below callback for JSON
1429 // preparation
1430 ethernet_provider.getEthernetIfaceData(
1431 iface_id,
1432 [&, parent_iface_id, iface_id](
1433 const bool &success, const EthernetInterfaceData &eth_data,
1434 const std::vector<IPv4AddressData> &ipv4_data) {
1435 if (success && eth_data.vlan_id != nullptr) {
1436 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1437 eth_data, ipv4_data);
1438 } else {
1439 // ... otherwise return error
1440 // TODO(Pawel)consider distinguish between non existing object,
1441 // and
1442 // other errors
1443 res.result(boost::beast::http::status::not_found);
1444 }
1445 res.end();
1446 });
1447 }
1448
1449 void doPatch(crow::response &res, const crow::request &req,
1450 const std::vector<std::string> &params) override {
1451 if (params.size() != 2) {
1452 res.result(boost::beast::http::status::internal_server_error);
1453 res.end();
1454 return;
1455 }
1456
1457 const std::string &parent_iface_id = params[0];
1458 const std::string &iface_id = params[1];
1459
1460 if (!verifyNames(res, parent_iface_id, iface_id)) {
1461 return;
1462 }
1463
1464 nlohmann::json patchReq;
1465
1466 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1467 return;
1468 }
1469
1470 // Get single eth interface data, and call the below callback for JSON
1471 // preparation
1472 ethernet_provider.getEthernetIfaceData(
1473 iface_id,
1474 [&, parent_iface_id, iface_id, patchReq = std::move(patchReq) ](
1475 const bool &success, const EthernetInterfaceData &eth_data,
1476 const std::vector<IPv4AddressData> &ipv4_data) {
1477 if (!success) {
1478 // ... otherwise return error
1479 // TODO(Pawel)consider distinguish between non existing object,
1480 // and
1481 // other errors
1482 res.result(boost::beast::http::status::not_found);
1483 res.end();
1484
1485 return;
1486 }
1487
1488 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1489 eth_data, ipv4_data);
1490
1491 std::shared_ptr<AsyncResp> asyncResp =
1492 std::make_shared<AsyncResp>(res);
1493
1494 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1495 ++propertyIt) {
1496 if (propertyIt.key() != "VLANEnable" &&
1497 propertyIt.key() != "VLANId") {
1498 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1499
1500 if (fieldInJsonIt == res.json_value.end()) {
1501 // Field not in scope of defined fields
1502 messages::addMessageToJsonRoot(
1503 res.json_value,
1504 messages::propertyUnknown(propertyIt.key()));
1505 } else if (*fieldInJsonIt != *propertyIt) {
1506 // User attempted to modify non-writable field
1507 messages::addMessageToJsonRoot(
1508 res.json_value,
1509 messages::propertyNotWritable(propertyIt.key()));
1510 }
1511 }
1512 }
1513
1514 EthernetInterface::handleVlanPatch(iface_id, patchReq, eth_data, "/",
1515 asyncResp);
1516 });
1517 }
1518
1519 void doDelete(crow::response &res, const crow::request &req,
1520 const std::vector<std::string> &params) override {
1521 if (params.size() != 2) {
1522 res.result(boost::beast::http::status::internal_server_error);
1523 res.end();
1524 return;
1525 }
1526
1527 const std::string &parent_iface_id = params[0];
1528 const std::string &iface_id = params[1];
1529
1530 if (!verifyNames(res, parent_iface_id, iface_id)) {
1531 return;
1532 }
1533
1534 // Get single eth interface data, and call the below callback for JSON
1535 // preparation
1536 ethernet_provider.getEthernetIfaceData(
1537 iface_id,
1538 [&, parent_iface_id, iface_id](
1539 const bool &success, const EthernetInterfaceData &eth_data,
1540 const std::vector<IPv4AddressData> &ipv4_data) {
1541 if (success && eth_data.vlan_id != nullptr) {
1542 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1543 eth_data, ipv4_data);
1544
1545 // Disable VLAN
1546 OnDemandEthernetProvider::disableVlan(
1547 iface_id, [&](const boost::system::error_code ec) {
1548 if (ec) {
1549 res.json_value = nlohmann::json::object();
1550 messages::addMessageToErrorJson(res.json_value,
1551 messages::internalError());
1552 res.result(
1553 boost::beast::http::status::internal_server_error);
1554 }
1555 res.end();
1556 });
1557 } else {
1558 // ... otherwise return error
1559 // TODO(Pawel)consider distinguish between non existing object,
1560 // and
1561 // other errors
1562
1563 res.result(boost::beast::http::status::not_found);
1564 res.end();
1565 }
1566 });
1567 }
1568
1569 /**
1570 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1571 * method, to maintain consistency of returned data, as Collection's doPost
1572 * should return data for created member which should match member's doGet
1573 * result in 100%.
1574 */
1575 friend VlanNetworkInterfaceCollection;
1576
1577 // Ethernet Provider object
1578 // TODO(Pawel) consider move it to singleton
1579 OnDemandEthernetProvider ethernet_provider;
1580};
1581
1582/**
1583 * VlanNetworkInterfaceCollection derived class for delivering
1584 * VLANNetworkInterface Collection Schema
1585 */
1586class VlanNetworkInterfaceCollection : public Node {
1587 public:
1588 template <typename CrowApp>
1589 // TODO(Pawel) Remove line from below, where we assume that there is only one
1590 // manager called openbmc This shall be generic, but requires to update
1591 // GetSubroutes method
1592 VlanNetworkInterfaceCollection(CrowApp &app)
1593 : Node(app,
1594 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1595 std::string()),
1596 memberVlan(app) {
1597 Node::json["@odata.type"] =
1598 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1599 Node::json["@odata.context"] =
1600 "/redfish/v1/$metadata"
1601 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1602 Node::json["Name"] = "VLAN Network Interface Collection";
1603
1604 entityPrivileges = {
1605 {boost::beast::http::verb::get, {{"Login"}}},
1606 {boost::beast::http::verb::head, {{"Login"}}},
1607 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1608 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1609 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1610 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1611 }
1612
1613 private:
1614 /**
1615 * Functions triggers appropriate requests on DBus
1616 */
1617 void doGet(crow::response &res, const crow::request &req,
1618 const std::vector<std::string> &params) override {
1619 if (params.size() != 1) {
1620 // This means there is a problem with the router
1621 res.result(boost::beast::http::status::internal_server_error);
1622 res.end();
1623
1624 return;
1625 }
1626
1627 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1628 // any Manager, not only hardcoded 'openbmc'.
1629 std::string manager_id = "openbmc";
1630 std::string rootInterfaceName = params[0];
1631
1632 // Get eth interface list, and call the below callback for JSON preparation
1633 ethernet_provider.getEthernetIfaceList([
1634 &, manager_id{std::move(manager_id)},
1635 rootInterfaceName{std::move(rootInterfaceName)}
1636 ](const bool &success, const std::vector<std::string> &iface_list) {
1637 if (success) {
1638 bool rootInterfaceFound = false;
1639 nlohmann::json iface_array = nlohmann::json::array();
1640
1641 for (const std::string &iface_item : iface_list) {
1642 if (iface_item == rootInterfaceName) {
1643 rootInterfaceFound = true;
1644 } else if (boost::starts_with(iface_item, rootInterfaceName + "_")) {
1645 iface_array.push_back(
1646 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
1647 "/EthernetInterfaces/" + rootInterfaceName +
1648 "/VLANs/" + iface_item}});
1649 }
1650 }
1651
1652 if (rootInterfaceFound) {
1653 Node::json["Members"] = iface_array;
1654 Node::json["Members@odata.count"] = iface_array.size();
1655 Node::json["@odata.id"] = "/redfish/v1/Managers/" + manager_id +
1656 "/EthernetInterfaces/" + rootInterfaceName +
1657 "/VLANs";
1658 res.json_value = Node::json;
1659 } else {
1660 messages::addMessageToErrorJson(
1661 res.json_value, messages::resourceNotFound("EthernetInterface",
1662 rootInterfaceName));
1663 res.result(boost::beast::http::status::not_found);
1664 res.end();
1665 }
1666 } else {
1667 // No success, best what we can do is return INTERNALL ERROR
1668 res.result(boost::beast::http::status::internal_server_error);
1669 }
1670 res.end();
1671 });
1672 }
1673
1674 void doPost(crow::response &res, const crow::request &req,
1675 const std::vector<std::string> &params) override {
1676 if (params.size() != 1) {
1677 // This means there is a problem with the router
1678 res.result(boost::beast::http::status::internal_server_error);
1679 res.end();
1680 return;
1681 }
1682
1683 nlohmann::json postReq;
1684
1685 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1686 return;
1687 }
1688
1689 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1690 // any Manager, not only hardcoded 'openbmc'.
1691 std::string manager_id = "openbmc";
1692 std::string rootInterfaceName = params[0];
1693 uint64_t vlanId;
1694 bool errorDetected;
1695
1696 if (json_util::getUnsigned(
1697 "VLANId", postReq, vlanId,
1698 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1699 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1700 res.json_value, "/VLANId") != json_util::Result::SUCCESS) {
1701 res.end();
1702 return;
1703 }
1704
1705 // Get eth interface list, and call the below callback for JSON preparation
1706 ethernet_provider.getEthernetIfaceList([
1707 &, manager_id{std::move(manager_id)},
1708 rootInterfaceName{std::move(rootInterfaceName)}
1709 ](const bool &success, const std::vector<std::string> &iface_list) {
1710 if (success) {
1711 bool rootInterfaceFound = false;
1712
1713 for (const std::string &iface_item : iface_list) {
1714 if (iface_item == rootInterfaceName) {
1715 rootInterfaceFound = true;
1716 break;
1717 }
1718 }
1719
1720 if (rootInterfaceFound) {
1721 ethernet_provider.createVlan(
1722 rootInterfaceName, vlanId,
1723 [&, vlanId, rootInterfaceName,
1724 req{std::move(req)} ](const boost::system::error_code ec) {
1725 if (ec) {
1726 messages::addMessageToErrorJson(res.json_value,
1727 messages::internalError());
1728 res.end();
1729 } else {
1730 memberVlan.doGet(
1731 res, req,
1732 {rootInterfaceName,
1733 rootInterfaceName + "_" + std::to_string(vlanId)});
1734 }
1735 });
1736 } else {
1737 messages::addMessageToErrorJson(
1738 res.json_value, messages::resourceNotFound("EthernetInterface",
1739 rootInterfaceName));
1740 res.result(boost::beast::http::status::not_found);
1741 res.end();
1742 }
1743 } else {
1744 // No success, best what we can do is return INTERNALL ERROR
1745 res.result(boost::beast::http::status::internal_server_error);
1746 res.end();
1747 }
1748 });
1749 }
1750
1751 // Ethernet Provider object
1752 // TODO(Pawel) consider move it to singleton
1753 OnDemandEthernetProvider ethernet_provider;
1754 VlanNetworkInterface memberVlan;
1755};
1756
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001757} // namespace redfish