blob: c008be6b60124158f162bf8c6c097f9eefd5b218 [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
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200665 // Find interface
666 if (resp.find("/xyz/openbmc_project/network/" + ethiface_id) ==
667 resp.end()) {
668 // Interface has not been found
669 callback(false, eth_data, ipv4_data);
670 return;
671 }
672
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200673 extractEthernetInterfaceData(ethiface_id, resp, eth_data);
674 extractIPv4Data(ethiface_id, resp, ipv4_data);
675
676 // Fix global GW
677 for (IPv4AddressData &ipv4 : ipv4_data) {
678 if ((ipv4.global) &&
679 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
680 ipv4.gateway = eth_data.default_gateway;
681 }
682 }
683
684 // Finally make a callback with usefull data
685 callback(true, eth_data, ipv4_data);
686 },
687 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
688 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
689 };
690
691 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100692 * Function that retrieves all Ethernet Interfaces available through Network
693 * Manager
694 * @param callback a function that shall be called to convert Dbus output into
695 * JSON.
696 */
697 template <typename CallbackFunc>
698 void getEthernetIfaceList(CallbackFunc &&callback) {
699 crow::connections::system_bus->async_method_call(
700 [ this, callback{std::move(callback)} ](
701 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700702 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100703 // Callback requires vector<string> to retrieve all available ethernet
704 // interfaces
705 std::vector<std::string> iface_list;
706 iface_list.reserve(resp.size());
707 if (error_code) {
708 // Something wrong on DBus, the error_code is not important at this
709 // moment, just return success=false, and empty output. Since size
710 // of vector may vary depending on information from Network Manager,
711 // and empty output could not be treated same way as error.
712 callback(false, iface_list);
713 return;
714 }
715
716 // Iterate over all retrieved ObjectPaths.
717 for (auto &objpath : resp) {
718 // And all interfaces available for certain ObjectPath.
719 for (auto &interface : objpath.second) {
720 // If interface is xyz.openbmc_project.Network.EthernetInterface,
721 // this is what we're looking for.
722 if (interface.first ==
723 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700724 // Cut out everyting until last "/", ...
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200725 const std::string &iface_id =
726 static_cast<const std::string &>(objpath.first);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100727 std::size_t last_pos = iface_id.rfind("/");
728 if (last_pos != std::string::npos) {
729 // and put it into output vector.
730 iface_list.emplace_back(iface_id.substr(last_pos + 1));
731 }
732 }
733 }
734 }
Gunnar Mills274fad52018-06-13 15:45:36 -0500735 // Finally make a callback with useful data
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100736 callback(true, iface_list);
737 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700738 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
739 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100740 };
741};
742
743/**
744 * EthernetCollection derived class for delivering Ethernet Collection Schema
745 */
746class EthernetCollection : public Node {
747 public:
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100748 // TODO(Pawel) Remove line from below, where we assume that there is only one
749 // manager called openbmc This shall be generic, but requires to update
750 // GetSubroutes method
751 EthernetCollection(CrowApp &app)
752 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
753 Node::json["@odata.type"] =
754 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
755 Node::json["@odata.context"] =
756 "/redfish/v1/"
757 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
758 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
759 Node::json["Name"] = "Ethernet Network Interface Collection";
760 Node::json["Description"] =
761 "Collection of EthernetInterfaces for this Manager";
762
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200763 entityPrivileges = {
764 {boost::beast::http::verb::get, {{"Login"}}},
765 {boost::beast::http::verb::head, {{"Login"}}},
766 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
767 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
768 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
769 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100770 }
771
772 private:
773 /**
774 * Functions triggers appropriate requests on DBus
775 */
776 void doGet(crow::response &res, const crow::request &req,
777 const std::vector<std::string> &params) override {
778 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
779 // any Manager, not only hardcoded 'openbmc'.
780 std::string manager_id = "openbmc";
781
782 // Get eth interface list, and call the below callback for JSON preparation
783 ethernet_provider.getEthernetIfaceList(
784 [&, manager_id{std::move(manager_id)} ](
785 const bool &success, const std::vector<std::string> &iface_list) {
786 if (success) {
787 nlohmann::json iface_array = nlohmann::json::array();
788 for (const std::string &iface_item : iface_list) {
789 iface_array.push_back(
790 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
791 "/EthernetInterfaces/" + iface_item}});
792 }
793 Node::json["Members"] = iface_array;
794 Node::json["Members@odata.count"] = iface_array.size();
795 Node::json["@odata.id"] =
796 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
797 res.json_value = Node::json;
798 } else {
799 // No success, best what we can do is return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700800 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100801 }
802 res.end();
803 });
804 }
805
806 // Ethernet Provider object
807 // TODO(Pawel) consider move it to singleton
808 OnDemandEthernetProvider ethernet_provider;
809};
810
811/**
812 * EthernetInterface derived class for delivering Ethernet Schema
813 */
814class EthernetInterface : public Node {
815 public:
816 /*
817 * Default Constructor
818 */
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100819 // TODO(Pawel) Remove line from below, where we assume that there is only one
820 // manager called openbmc This shall be generic, but requires to update
821 // GetSubroutes method
822 EthernetInterface(CrowApp &app)
823 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
824 std::string()) {
825 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
826 Node::json["@odata.context"] =
827 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
828 Node::json["Name"] = "Manager Ethernet Interface";
829 Node::json["Description"] = "Management Network Interface";
830
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200831 entityPrivileges = {
832 {boost::beast::http::verb::get, {{"Login"}}},
833 {boost::beast::http::verb::head, {{"Login"}}},
834 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
835 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
836 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
837 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100838 }
839
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200840 // TODO(kkowalsk) Find a suitable class/namespace for this
841 static void handleVlanPatch(const std::string &ifaceId,
842 const nlohmann::json &input,
843 const EthernetInterfaceData &eth_data,
844 const std::string &pathPrefix,
845 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200846 if (!input.is_object()) {
847 messages::addMessageToJson(
848 asyncResp->res.json_value,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200849 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200850 return;
851 }
852
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200853 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
854 nlohmann::json &paramsJson =
855 (pathPrefix == "/")
856 ? asyncResp->res.json_value
857 : asyncResp->res.json_value[nlohmann::json_pointer<nlohmann::json>(
858 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200859 bool inputVlanEnabled;
860 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200861
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200862 json_util::Result inputVlanEnabledState = json_util::getBool(
863 "VLANEnable", input, inputVlanEnabled,
864 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200865 asyncResp->res.json_value, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200866 json_util::Result inputVlanIdState = json_util::getUnsigned(
867 "VLANId", input, inputVlanId,
868 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200869 asyncResp->res.json_value, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200870 bool inputInvalid = false;
871
872 // Do not proceed if fields in VLAN object were of wrong type
873 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
874 inputVlanIdState == json_util::Result::WRONG_TYPE) {
875 return;
876 }
877
878 // Verify input
879 if (eth_data.vlan_id == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200880 // This interface is not a VLAN. Cannot do anything with it
881 // TODO(kkowalsk) Change this message
882 messages::addMessageToJson(asyncResp->res.json_value,
883 messages::propertyMissing("VLANEnable"),
884 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200885
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200886 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200887 } else {
888 // Load actual data into field values if they were not provided
889 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
890 inputVlanEnabled = true;
891 }
892
893 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
894 inputVlanId = *eth_data.vlan_id;
895 }
896 }
897
898 // Do not proceed if input has not been valid
899 if (inputInvalid) {
900 return;
901 }
902
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200903 // VLAN is configured on the interface
904 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
905 // Change VLAN Id
906 paramsJson["VLANId"] = inputVlanId;
907 OnDemandEthernetProvider::changeVlanId(
908 ifaceId, static_cast<uint32_t>(inputVlanId),
909 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
910 const boost::system::error_code ec) {
911 if (ec) {
912 messages::addMessageToJson(asyncResp->res.json_value,
913 messages::internalError(), pathPrefix);
914 } else {
915 paramsJson["VLANEnable"] = true;
916 }
917 });
918 } else if (inputVlanEnabled == false) {
919 // Disable VLAN
920 OnDemandEthernetProvider::disableVlan(
921 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
922 const boost::system::error_code ec) {
923 if (ec) {
924 messages::addMessageToJson(asyncResp->res.json_value,
925 messages::internalError(), pathPrefix);
926 } else {
927 paramsJson["VLANEnable"] = false;
928 }
929 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200930 }
931 }
932
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200933 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200934 void handleHostnamePatch(const nlohmann::json &input,
935 const EthernetInterfaceData &eth_data,
936 const std::shared_ptr<AsyncResp> &asyncResp) {
937 if (input.is_string()) {
938 std::string newHostname = input.get<std::string>();
939
940 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
941 // Change hostname
942 ethernet_provider.setHostName(
943 newHostname,
944 [asyncResp, newHostname](const boost::system::error_code ec) {
945 if (ec) {
946 messages::addMessageToJson(asyncResp->res.json_value,
947 messages::internalError(),
948 "/HostName");
949 } else {
950 asyncResp->res.json_value["HostName"] = newHostname;
951 }
952 });
953 }
954 } else {
955 messages::addMessageToJson(
956 asyncResp->res.json_value,
957 messages::propertyValueTypeError(input.dump(), "HostName"),
958 "/HostName");
959 }
960 }
961
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200962 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
963 const std::vector<IPv4AddressData> &ipv4_data,
964 const std::shared_ptr<AsyncResp> &asyncResp) {
965 if (!input.is_array()) {
966 messages::addMessageToJson(
967 asyncResp->res.json_value,
968 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
969 "/IPv4Addresses");
970 return;
971 }
972
973 // According to Redfish PATCH definition, size must be at least equal
974 if (input.size() < ipv4_data.size()) {
975 // TODO(kkowalsk) This should be a message indicating that not enough
976 // data has been provided
977 messages::addMessageToJson(asyncResp->res.json_value,
978 messages::internalError(), "/IPv4Addresses");
979 return;
980 }
981
982 json_util::Result addressFieldState;
983 json_util::Result subnetMaskFieldState;
984 json_util::Result addressOriginFieldState;
985 json_util::Result gatewayFieldState;
986 const std::string *addressFieldValue;
987 const std::string *subnetMaskFieldValue;
988 const std::string *addressOriginFieldValue = nullptr;
989 const std::string *gatewayFieldValue;
990 uint8_t subnetMaskAsPrefixLength;
991 std::string addressOriginInDBusFormat;
992
993 bool errorDetected = false;
994 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
995 // Check that entry is not of some unexpected type
996 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
997 // Invalid object type
998 messages::addMessageToJson(
999 asyncResp->res.json_value,
1000 messages::propertyValueTypeError(input[entryIdx].dump(),
1001 "IPv4Address"),
1002 "/IPv4Addresses/" + std::to_string(entryIdx));
1003
1004 continue;
1005 }
1006
1007 // Try to load fields
1008 addressFieldState = json_util::getString(
1009 "Address", input[entryIdx], addressFieldValue,
1010 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1011 asyncResp->res.json_value,
1012 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1013 subnetMaskFieldState = json_util::getString(
1014 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1015 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1016 asyncResp->res.json_value,
1017 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1018 addressOriginFieldState = json_util::getString(
1019 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1020 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1021 asyncResp->res.json_value,
1022 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1023 gatewayFieldState = json_util::getString(
1024 "Gateway", input[entryIdx], gatewayFieldValue,
1025 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1026 asyncResp->res.json_value,
1027 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1028
1029 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1030 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1031 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1032 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1033 return;
1034 }
1035
1036 if (addressFieldState == json_util::Result::SUCCESS &&
1037 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1038 errorDetected = true;
1039 messages::addMessageToJson(
1040 asyncResp->res.json_value,
1041 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1042 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1043 }
1044
1045 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1046 !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1047 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1048 errorDetected = true;
1049 messages::addMessageToJson(
1050 asyncResp->res.json_value,
1051 messages::propertyValueFormatError(*subnetMaskFieldValue,
1052 "SubnetMask"),
1053 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1054 }
1055
1056 // Get Address origin in proper format
1057 addressOriginInDBusFormat =
1058 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1059 addressOriginFieldValue, true, false);
1060
1061 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1062 addressOriginInDBusFormat.empty()) {
1063 errorDetected = true;
1064 messages::addMessageToJson(
1065 asyncResp->res.json_value,
1066 messages::propertyValueNotInList(*addressOriginFieldValue,
1067 "AddressOrigin"),
1068 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1069 }
1070
1071 if (gatewayFieldState == json_util::Result::SUCCESS &&
1072 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1073 errorDetected = true;
1074 messages::addMessageToJson(
1075 asyncResp->res.json_value,
1076 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1077 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1078 }
1079
1080 // If any error occured do not proceed with current entry, but do not
1081 // end loop
1082 if (errorDetected) {
1083 errorDetected = false;
1084 continue;
1085 }
1086
1087 if (entryIdx >= ipv4_data.size()) {
1088 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1089
1090 // Verify that all field were provided
1091 if (addressFieldState == json_util::Result::NOT_EXIST) {
1092 errorDetected = true;
1093 messages::addMessageToJson(
1094 asyncResp->res.json_value, messages::propertyMissing("Address"),
1095 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1096 }
1097
1098 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1099 errorDetected = true;
1100 messages::addMessageToJson(
1101 asyncResp->res.json_value,
1102 messages::propertyMissing("SubnetMask"),
1103 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1104 }
1105
1106 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1107 errorDetected = true;
1108 messages::addMessageToJson(
1109 asyncResp->res.json_value,
1110 messages::propertyMissing("AddressOrigin"),
1111 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1112 }
1113
1114 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1115 errorDetected = true;
1116 messages::addMessageToJson(
1117 asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1118 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1119 }
1120
1121 // If any error occured do not proceed with current entry, but do not
1122 // end loop
1123 if (errorDetected) {
1124 errorDetected = false;
1125 continue;
1126 }
1127
1128 // Create IPv4 with provided data
1129 ethernet_provider.createIPv4(
1130 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1131 *addressFieldValue, asyncResp);
1132 } else {
1133 // Existing object that should be modified/deleted/remain unchanged
1134 if (input[entryIdx].is_null()) {
1135 // Object should be deleted
1136 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1137 entryIdx, asyncResp);
1138 } else if (input[entryIdx].is_object()) {
1139 if (input[entryIdx].size() == 0) {
1140 // Object shall remain unchanged
1141 continue;
1142 }
1143
1144 // Apply changes
1145 if (addressFieldState == json_util::Result::SUCCESS &&
1146 ipv4_data[entryIdx].address != nullptr &&
1147 *ipv4_data[entryIdx].address != *addressFieldValue) {
1148 ethernet_provider.changeIPv4AddressProperty(
1149 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1150 *addressFieldValue, asyncResp);
1151 }
1152
1153 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1154 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1155 ethernet_provider.changeIPv4SubnetMaskProperty(
1156 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1157 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1158 }
1159
1160 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1161 ipv4_data[entryIdx].origin != *addressFieldValue) {
1162 ethernet_provider.changeIPv4Origin(
1163 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1164 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1165 }
1166
1167 if (gatewayFieldState == json_util::Result::SUCCESS &&
1168 ipv4_data[entryIdx].gateway != nullptr &&
1169 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1170 ethernet_provider.changeIPv4AddressProperty(
1171 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1172 *gatewayFieldValue, asyncResp);
1173 }
1174 }
1175 }
1176 }
1177 }
1178
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001179 nlohmann::json parseInterfaceData(
1180 const std::string &iface_id, const EthernetInterfaceData &eth_data,
1181 const std::vector<IPv4AddressData> &ipv4_data) {
1182 // Copy JSON object to avoid race condition
1183 nlohmann::json json_response(Node::json);
1184
1185 // Fill out obvious data...
1186 json_response["Id"] = iface_id;
1187 json_response["@odata.id"] =
1188 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1189
1190 // ... then the one from DBus, regarding eth iface...
1191 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1192
1193 if (eth_data.mac_address != nullptr)
1194 json_response["MACAddress"] = *eth_data.mac_address;
1195
1196 if (eth_data.hostname != nullptr)
1197 json_response["HostName"] = *eth_data.hostname;
1198
1199 if (eth_data.vlan_id != nullptr) {
1200 nlohmann::json &vlanObj = json_response["VLAN"];
1201 vlanObj["VLANEnable"] = true;
1202 vlanObj["VLANId"] = *eth_data.vlan_id;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001203 } else {
1204 nlohmann::json &vlanObj = json_response["VLANs"];
1205 vlanObj["@odata.id"] =
1206 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id +
1207 "/VLANs";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001208 }
1209
1210 // ... at last, check if there are IPv4 data and prepare appropriate
1211 // collection
1212 if (ipv4_data.size() > 0) {
1213 nlohmann::json ipv4_array = nlohmann::json::array();
1214 for (auto &ipv4_config : ipv4_data) {
1215 nlohmann::json json_ipv4;
1216 if (ipv4_config.address != nullptr) {
1217 json_ipv4["Address"] = *ipv4_config.address;
1218 if (ipv4_config.gateway != nullptr)
1219 json_ipv4["Gateway"] = *ipv4_config.gateway;
1220
1221 json_ipv4["AddressOrigin"] = ipv4_config.origin;
1222 json_ipv4["SubnetMask"] = ipv4_config.netmask;
1223
1224 ipv4_array.push_back(std::move(json_ipv4));
1225 }
1226 }
1227 json_response["IPv4Addresses"] = std::move(ipv4_array);
1228 }
1229
1230 return json_response;
1231 }
1232
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001233 /**
1234 * Functions triggers appropriate requests on DBus
1235 */
1236 void doGet(crow::response &res, const crow::request &req,
1237 const std::vector<std::string> &params) override {
1238 // TODO(Pawel) this shall be parametrized call (two params) to get
1239 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1240 // Check if there is required param, truly entering this shall be
1241 // impossible.
1242 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001243 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001244 res.end();
1245 return;
1246 }
1247
1248 const std::string &iface_id = params[0];
1249
1250 // Get single eth interface data, and call the below callback for JSON
1251 // preparation
1252 ethernet_provider.getEthernetIfaceData(
1253 iface_id, [&, iface_id](const bool &success,
1254 const EthernetInterfaceData &eth_data,
1255 const std::vector<IPv4AddressData> &ipv4_data) {
1256 if (success) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001257 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001258 } else {
1259 // ... otherwise return error
1260 // TODO(Pawel)consider distinguish between non existing object, and
1261 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001262 messages::addMessageToErrorJson(
1263 res.json_value,
1264 messages::resourceNotFound("EthernetInterface", iface_id));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001265 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001266 }
1267 res.end();
1268 });
1269 }
1270
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001271 void doPatch(crow::response &res, const crow::request &req,
1272 const std::vector<std::string> &params) override {
1273 // TODO(Pawel) this shall be parametrized call (two params) to get
1274 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1275 // Check if there is required param, truly entering this shall be
1276 // impossible.
1277 if (params.size() != 1) {
1278 res.result(boost::beast::http::status::internal_server_error);
1279 res.end();
1280 return;
1281 }
1282
1283 const std::string &iface_id = params[0];
1284
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001285 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001286
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001287 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001288 return;
1289 }
1290
1291 // Get single eth interface data, and call the below callback for JSON
1292 // preparation
1293 ethernet_provider.getEthernetIfaceData(
1294 iface_id,
1295 [&, iface_id, patchReq = std::move(patchReq) ](
1296 const bool &success, const EthernetInterfaceData &eth_data,
1297 const std::vector<IPv4AddressData> &ipv4_data) {
1298 if (!success) {
1299 // ... otherwise return error
1300 // TODO(Pawel)consider distinguish between non existing object, and
1301 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001302 messages::addMessageToErrorJson(
1303 res.json_value,
1304 messages::resourceNotFound("VLAN Network Interface", iface_id));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001305 res.result(boost::beast::http::status::not_found);
1306 res.end();
1307
1308 return;
1309 }
1310
1311 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1312
1313 std::shared_ptr<AsyncResp> asyncResp =
1314 std::make_shared<AsyncResp>(res);
1315
1316 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1317 ++propertyIt) {
1318 if (propertyIt.key() == "VLAN") {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001319 handleVlanPatch(iface_id, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001320 asyncResp);
1321 } else if (propertyIt.key() == "HostName") {
1322 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001323 } else if (propertyIt.key() == "IPv4Addresses") {
1324 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1325 asyncResp);
1326 } else if (propertyIt.key() == "IPv6Addresses") {
1327 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1328 messages::addMessageToJsonRoot(
1329 res.json_value,
1330 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001331 } else {
1332 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1333
1334 if (fieldInJsonIt == res.json_value.end()) {
1335 // Field not in scope of defined fields
1336 messages::addMessageToJsonRoot(
1337 res.json_value,
1338 messages::propertyUnknown(propertyIt.key()));
1339 } else if (*fieldInJsonIt != *propertyIt) {
1340 // User attempted to modify non-writable field
1341 messages::addMessageToJsonRoot(
1342 res.json_value,
1343 messages::propertyNotWritable(propertyIt.key()));
1344 }
1345 }
1346 }
1347 });
1348 }
1349
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001350 // Ethernet Provider object
1351 // TODO(Pawel) consider move it to singleton
1352 OnDemandEthernetProvider ethernet_provider;
1353};
1354
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001355class VlanNetworkInterfaceCollection;
1356
1357/**
1358 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1359 */
1360class VlanNetworkInterface : public Node {
1361 public:
1362 /*
1363 * Default Constructor
1364 */
1365 template <typename CrowApp>
1366 // TODO(Pawel) Remove line from below, where we assume that there is only one
1367 // manager called openbmc This shall be generic, but requires to update
1368 // GetSubroutes method
1369 VlanNetworkInterface(CrowApp &app)
Kowalski, Kamileb547732018-05-16 10:25:07 +02001370 : Node(
1371 app,
1372 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1373 std::string(), std::string()) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001374 Node::json["@odata.type"] =
1375 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1376 Node::json["@odata.context"] =
1377 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1378 Node::json["Name"] = "VLAN Network Interface";
1379
1380 entityPrivileges = {
1381 {boost::beast::http::verb::get, {{"Login"}}},
1382 {boost::beast::http::verb::head, {{"Login"}}},
1383 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1384 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1385 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1386 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1387 }
1388
1389 private:
1390 nlohmann::json parseInterfaceData(
1391 const std::string &parent_iface_id, const std::string &iface_id,
1392 const EthernetInterfaceData &eth_data,
1393 const std::vector<IPv4AddressData> &ipv4_data) {
1394 // Copy JSON object to avoid race condition
1395 nlohmann::json json_response(Node::json);
1396
Kowalski, Kamileb547732018-05-16 10:25:07 +02001397 if (eth_data.vlan_id == nullptr) {
1398 // Interface not a VLAN - abort
1399 messages::addMessageToErrorJson(json_response, messages::internalError());
1400 return json_response;
1401 }
1402
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001403 // Fill out obvious data...
1404 json_response["Id"] = iface_id;
1405 json_response["@odata.id"] =
1406 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_iface_id +
1407 "/VLANs/" + iface_id;
1408
1409 json_response["VLANEnable"] = true;
1410 json_response["VLANId"] = *eth_data.vlan_id;
1411
1412 return json_response;
1413 }
1414
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001415 bool verifyNames(crow::response &res, const std::string &parent,
1416 const std::string &iface) {
1417 if (!boost::starts_with(iface, parent + "_")) {
1418 messages::addMessageToErrorJson(
1419 res.json_value,
1420 messages::resourceNotFound("VLAN Network Interface", iface));
1421 res.result(boost::beast::http::status::not_found);
1422 res.end();
1423
1424 return false;
1425 } else {
1426 return true;
1427 }
1428 }
1429
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001430 /**
1431 * Functions triggers appropriate requests on DBus
1432 */
1433 void doGet(crow::response &res, const crow::request &req,
1434 const std::vector<std::string> &params) override {
1435 // TODO(Pawel) this shall be parametrized call (two params) to get
1436 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1437 // Check if there is required param, truly entering this shall be
1438 // impossible.
1439 if (params.size() != 2) {
1440 res.result(boost::beast::http::status::internal_server_error);
1441 res.end();
1442 return;
1443 }
1444
1445 const std::string &parent_iface_id = params[0];
1446 const std::string &iface_id = params[1];
1447
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001448 // Get single eth interface data, and call the below callback for JSON
1449 // preparation
1450 ethernet_provider.getEthernetIfaceData(
1451 iface_id,
1452 [&, parent_iface_id, iface_id](
1453 const bool &success, const EthernetInterfaceData &eth_data,
1454 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamileb547732018-05-16 10:25:07 +02001455 if (success) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001456 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1457 eth_data, ipv4_data);
1458 } else {
1459 // ... otherwise return error
Kowalski, Kamileb547732018-05-16 10:25:07 +02001460 // TODO(Pawel)consider distinguish between non existing object, and
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001461 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001462 messages::addMessageToErrorJson(
1463 res.json_value,
1464 messages::resourceNotFound("VLAN Network Interface", iface_id));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001465 res.result(boost::beast::http::status::not_found);
1466 }
1467 res.end();
1468 });
1469 }
1470
1471 void doPatch(crow::response &res, const crow::request &req,
1472 const std::vector<std::string> &params) override {
1473 if (params.size() != 2) {
1474 res.result(boost::beast::http::status::internal_server_error);
1475 res.end();
1476 return;
1477 }
1478
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001479 const std::string &parent_iface_id = params[0];
1480 const std::string &iface_id = params[1];
1481
1482 if (!verifyNames(res, parent_iface_id, iface_id)) {
1483 return;
1484 }
1485
1486 nlohmann::json patchReq;
1487
1488 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1489 return;
1490 }
1491
1492 // Get single eth interface data, and call the below callback for JSON
1493 // preparation
1494 ethernet_provider.getEthernetIfaceData(
1495 iface_id,
1496 [&, parent_iface_id, iface_id, patchReq = std::move(patchReq) ](
1497 const bool &success, const EthernetInterfaceData &eth_data,
1498 const std::vector<IPv4AddressData> &ipv4_data) {
1499 if (!success) {
1500 // ... otherwise return error
1501 // TODO(Pawel)consider distinguish between non existing object,
1502 // and
1503 // other errors
1504 messages::addMessageToErrorJson(
1505 res.json_value,
1506 messages::resourceNotFound("VLAN Network Interface", iface_id));
1507 res.result(boost::beast::http::status::not_found);
1508 res.end();
1509
1510 return;
1511 }
1512
1513 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1514 eth_data, ipv4_data);
1515
1516 std::shared_ptr<AsyncResp> asyncResp =
1517 std::make_shared<AsyncResp>(res);
1518
1519 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1520 ++propertyIt) {
1521 if (propertyIt.key() != "VLANEnable" &&
1522 propertyIt.key() != "VLANId") {
1523 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1524
1525 if (fieldInJsonIt == res.json_value.end()) {
1526 // Field not in scope of defined fields
1527 messages::addMessageToJsonRoot(
1528 res.json_value,
1529 messages::propertyUnknown(propertyIt.key()));
1530 } else if (*fieldInJsonIt != *propertyIt) {
1531 // User attempted to modify non-writable field
1532 messages::addMessageToJsonRoot(
1533 res.json_value,
1534 messages::propertyNotWritable(propertyIt.key()));
1535 }
1536 }
1537 }
1538
1539 EthernetInterface::handleVlanPatch(iface_id, patchReq, eth_data, "/",
1540 asyncResp);
1541 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001542 }
1543
1544 void doDelete(crow::response &res, const crow::request &req,
1545 const std::vector<std::string> &params) override {
1546 if (params.size() != 2) {
1547 res.result(boost::beast::http::status::internal_server_error);
1548 res.end();
1549 return;
1550 }
1551
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001552 const std::string &parent_iface_id = params[0];
1553 const std::string &iface_id = params[1];
1554
1555 if (!verifyNames(res, parent_iface_id, iface_id)) {
1556 return;
1557 }
1558
1559 // Get single eth interface data, and call the below callback for JSON
1560 // preparation
1561 ethernet_provider.getEthernetIfaceData(
1562 iface_id,
1563 [&, parent_iface_id, iface_id](
1564 const bool &success, const EthernetInterfaceData &eth_data,
1565 const std::vector<IPv4AddressData> &ipv4_data) {
1566 if (success && eth_data.vlan_id != nullptr) {
1567 res.json_value = parseInterfaceData(parent_iface_id, iface_id,
1568 eth_data, ipv4_data);
1569
1570 // Disable VLAN
1571 OnDemandEthernetProvider::disableVlan(
1572 iface_id, [&](const boost::system::error_code ec) {
1573 if (ec) {
1574 res.json_value = nlohmann::json::object();
1575 messages::addMessageToErrorJson(res.json_value,
1576 messages::internalError());
1577 res.result(
1578 boost::beast::http::status::internal_server_error);
1579 }
1580 res.end();
1581 });
1582 } else {
1583 // ... otherwise return error
1584 // TODO(Pawel)consider distinguish between non existing object,
1585 // and
1586 // other errors
1587 messages::addMessageToErrorJson(
1588 res.json_value,
1589 messages::resourceNotFound("VLAN Network Interface", iface_id));
1590 res.result(boost::beast::http::status::not_found);
1591 res.end();
1592 }
1593 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001594 }
1595
1596 /**
1597 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1598 * method, to maintain consistency of returned data, as Collection's doPost
1599 * should return data for created member which should match member's doGet
1600 * result in 100%.
1601 */
1602 friend VlanNetworkInterfaceCollection;
1603
1604 // Ethernet Provider object
1605 // TODO(Pawel) consider move it to singleton
1606 OnDemandEthernetProvider ethernet_provider;
1607};
1608
1609/**
1610 * VlanNetworkInterfaceCollection derived class for delivering
1611 * VLANNetworkInterface Collection Schema
1612 */
1613class VlanNetworkInterfaceCollection : public Node {
1614 public:
1615 template <typename CrowApp>
1616 // TODO(Pawel) Remove line from below, where we assume that there is only one
1617 // manager called openbmc This shall be generic, but requires to update
1618 // GetSubroutes method
1619 VlanNetworkInterfaceCollection(CrowApp &app)
1620 : Node(app,
1621 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1622 std::string()),
1623 memberVlan(app) {
1624 Node::json["@odata.type"] =
1625 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1626 Node::json["@odata.context"] =
1627 "/redfish/v1/$metadata"
1628 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1629 Node::json["Name"] = "VLAN Network Interface Collection";
1630
1631 entityPrivileges = {
1632 {boost::beast::http::verb::get, {{"Login"}}},
1633 {boost::beast::http::verb::head, {{"Login"}}},
1634 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1635 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1636 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1637 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1638 }
1639
1640 private:
1641 /**
1642 * Functions triggers appropriate requests on DBus
1643 */
1644 void doGet(crow::response &res, const crow::request &req,
1645 const std::vector<std::string> &params) override {
1646 if (params.size() != 1) {
1647 // This means there is a problem with the router
1648 res.result(boost::beast::http::status::internal_server_error);
1649 res.end();
1650
1651 return;
1652 }
1653
1654 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1655 // any Manager, not only hardcoded 'openbmc'.
1656 std::string manager_id = "openbmc";
1657 std::string rootInterfaceName = params[0];
1658
1659 // Get eth interface list, and call the below callback for JSON preparation
1660 ethernet_provider.getEthernetIfaceList([
1661 &, manager_id{std::move(manager_id)},
1662 rootInterfaceName{std::move(rootInterfaceName)}
1663 ](const bool &success, const std::vector<std::string> &iface_list) {
1664 if (success) {
1665 bool rootInterfaceFound = false;
1666 nlohmann::json iface_array = nlohmann::json::array();
1667
1668 for (const std::string &iface_item : iface_list) {
1669 if (iface_item == rootInterfaceName) {
1670 rootInterfaceFound = true;
1671 } else if (boost::starts_with(iface_item, rootInterfaceName + "_")) {
1672 iface_array.push_back(
1673 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
1674 "/EthernetInterfaces/" + rootInterfaceName +
1675 "/VLANs/" + iface_item}});
1676 }
1677 }
1678
1679 if (rootInterfaceFound) {
1680 Node::json["Members"] = iface_array;
1681 Node::json["Members@odata.count"] = iface_array.size();
1682 Node::json["@odata.id"] = "/redfish/v1/Managers/" + manager_id +
1683 "/EthernetInterfaces/" + rootInterfaceName +
1684 "/VLANs";
1685 res.json_value = Node::json;
1686 } else {
1687 messages::addMessageToErrorJson(
1688 res.json_value, messages::resourceNotFound("EthernetInterface",
1689 rootInterfaceName));
1690 res.result(boost::beast::http::status::not_found);
1691 res.end();
1692 }
1693 } else {
1694 // No success, best what we can do is return INTERNALL ERROR
1695 res.result(boost::beast::http::status::internal_server_error);
1696 }
1697 res.end();
1698 });
1699 }
1700
1701 void doPost(crow::response &res, const crow::request &req,
1702 const std::vector<std::string> &params) override {
1703 if (params.size() != 1) {
1704 // This means there is a problem with the router
1705 res.result(boost::beast::http::status::internal_server_error);
1706 res.end();
1707 return;
1708 }
1709
1710 nlohmann::json postReq;
1711
1712 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1713 return;
1714 }
1715
1716 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1717 // any Manager, not only hardcoded 'openbmc'.
1718 std::string manager_id = "openbmc";
1719 std::string rootInterfaceName = params[0];
1720 uint64_t vlanId;
1721 bool errorDetected;
1722
1723 if (json_util::getUnsigned(
1724 "VLANId", postReq, vlanId,
1725 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1726 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1727 res.json_value, "/VLANId") != json_util::Result::SUCCESS) {
1728 res.end();
1729 return;
1730 }
1731
1732 // Get eth interface list, and call the below callback for JSON preparation
1733 ethernet_provider.getEthernetIfaceList([
1734 &, manager_id{std::move(manager_id)},
1735 rootInterfaceName{std::move(rootInterfaceName)}
1736 ](const bool &success, const std::vector<std::string> &iface_list) {
1737 if (success) {
1738 bool rootInterfaceFound = false;
1739
1740 for (const std::string &iface_item : iface_list) {
1741 if (iface_item == rootInterfaceName) {
1742 rootInterfaceFound = true;
1743 break;
1744 }
1745 }
1746
1747 if (rootInterfaceFound) {
1748 ethernet_provider.createVlan(
1749 rootInterfaceName, vlanId,
1750 [&, vlanId, rootInterfaceName,
1751 req{std::move(req)} ](const boost::system::error_code ec) {
1752 if (ec) {
1753 messages::addMessageToErrorJson(res.json_value,
1754 messages::internalError());
1755 res.end();
1756 } else {
1757 memberVlan.doGet(
1758 res, req,
1759 {rootInterfaceName,
1760 rootInterfaceName + "_" + std::to_string(vlanId)});
1761 }
1762 });
1763 } else {
1764 messages::addMessageToErrorJson(
1765 res.json_value, messages::resourceNotFound("EthernetInterface",
1766 rootInterfaceName));
1767 res.result(boost::beast::http::status::not_found);
1768 res.end();
1769 }
1770 } else {
1771 // No success, best what we can do is return INTERNALL ERROR
1772 res.result(boost::beast::http::status::internal_server_error);
1773 res.end();
1774 }
1775 });
1776 }
1777
1778 // Ethernet Provider object
1779 // TODO(Pawel) consider move it to singleton
1780 OnDemandEthernetProvider ethernet_provider;
1781 VlanNetworkInterface memberVlan;
1782};
1783
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001784} // namespace redfish