blob: aa8d66f1bd431af8ff3819d7b21147de98002789 [file] [log] [blame]
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020018#include <dbus_singleton.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020019#include <error_messages.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020020#include <node.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020021#include <utils/json_utils.hpp>
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010022#include <boost/container/flat_map.hpp>
23
24namespace redfish {
25
26/**
27 * DBus types primitives for several generic DBus interfaces
28 * TODO(Pawel) consider move this to separate file into boost::dbus
29 */
Ed Tanousaa2e59c2018-04-12 12:17:20 -070030using PropertiesMapType = boost::container::flat_map<
31 std::string,
32 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
33 int32_t, uint32_t, int64_t, uint64_t, double>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010034
35using GetManagedObjectsType = boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070036 sdbusplus::message::object_path,
37 boost::container::flat_map<
38 std::string,
39 boost::container::flat_map<
40 std::string, sdbusplus::message::variant<
41 std::string, bool, uint8_t, int16_t, uint16_t,
42 int32_t, uint32_t, int64_t, uint64_t, double>>>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010043
44/**
45 * Structure for keeping IPv4 data required by Redfish
46 * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
47 */
48struct IPv4AddressData {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020049 std::string id;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010050 const std::string *address;
51 const std::string *domain;
52 const std::string *gateway;
53 std::string netmask;
54 std::string origin;
55 bool global;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020056 /**
57 * @brief Operator< to enable sorting
58 *
59 * @param[in] obj Object to compare with
60 *
61 * @return This object id < supplied object id
62 */
63 bool operator<(const IPv4AddressData &obj) const { return (id < obj.id); }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010064};
65
66/**
67 * Structure for keeping basic single Ethernet Interface information
68 * available from DBus
69 */
70struct EthernetInterfaceData {
71 const unsigned int *speed;
Ed Tanous55c7b7a2018-05-22 15:27:24 -070072 const bool *autoNeg;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010073 const std::string *hostname;
Ed Tanous55c7b7a2018-05-22 15:27:24 -070074 const std::string *defaultGateway;
75 const std::string *macAddress;
76 const unsigned int *vlanId;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010077};
78
79/**
80 * OnDemandEthernetProvider
Gunnar Mills274fad52018-06-13 15:45:36 -050081 * Ethernet provider class that retrieves data directly from dbus, before
82 * setting it into JSON output. This does not cache any data.
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010083 *
84 * TODO(Pawel)
85 * This perhaps shall be different file, which has to be chosen on compile time
86 * depending on OEM needs
87 */
88class OnDemandEthernetProvider {
89 private:
90 // Consts that may have influence on EthernetProvider performance/memory usage
Ed Tanous55c7b7a2018-05-22 15:27:24 -070091 const size_t maxIpV4AddressesPerInterface = 10;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010092
93 // Helper function that allows to extract GetAllPropertiesType from
94 // GetManagedObjectsType, based on object path, and interface name
95 const PropertiesMapType *extractInterfaceProperties(
Ed Tanousaa2e59c2018-04-12 12:17:20 -070096 const sdbusplus::message::object_path &objpath,
97 const std::string &interface, const GetManagedObjectsType &dbus_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070098 const auto &dbusObj = dbus_data.find(objpath);
99 if (dbusObj != dbus_data.end()) {
100 const auto &iface = dbusObj->second.find(interface);
101 if (iface != dbusObj->second.end()) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100102 return &iface->second;
103 }
104 }
105 return nullptr;
106 }
107
108 // Helper Wrapper that does inline object_path conversion from string
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700109 // into sdbusplus::message::object_path type
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100110 inline const PropertiesMapType *extractInterfaceProperties(
111 const std::string &objpath, const std::string &interface,
112 const GetManagedObjectsType &dbus_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700113 const auto &dbusObj = sdbusplus::message::object_path{objpath};
114 return extractInterfaceProperties(dbusObj, interface, dbus_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100115 }
116
117 // Helper function that allows to get pointer to the property from
118 // GetAllPropertiesType native, or extracted by GetAllPropertiesType
119 template <typename T>
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700120 inline T const *const extractProperty(const PropertiesMapType &properties,
121 const std::string &name) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100122 const auto &property = properties.find(name);
123 if (property != properties.end()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700124 return mapbox::getPtr<const T>(property->second);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100125 }
126 return nullptr;
127 }
128 // TODO(Pawel) Consider to move the above functions to dbus
129 // generic_interfaces.hpp
130
131 // Helper function that extracts data from several dbus objects and several
132 // interfaces required by single ethernet interface instance
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700133 void extractEthernetInterfaceData(const std::string &ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100134 const GetManagedObjectsType &dbus_data,
135 EthernetInterfaceData &eth_data) {
136 // Extract data that contains MAC Address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700137 const PropertiesMapType *macProperties = extractInterfaceProperties(
138 "/xyz/openbmc_project/network/" + ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100139 "xyz.openbmc_project.Network.MACAddress", dbus_data);
140
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700141 if (macProperties != nullptr) {
142 eth_data.macAddress =
143 extractProperty<std::string>(*macProperties, "MACAddress");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100144 }
145
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700146 const PropertiesMapType *vlanProperties = extractInterfaceProperties(
147 "/xyz/openbmc_project/network/" + ethifaceId,
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +0200148 "xyz.openbmc_project.Network.VLAN", dbus_data);
149
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700150 if (vlanProperties != nullptr) {
151 eth_data.vlanId = extractProperty<unsigned int>(*vlanProperties, "Id");
Kowalski, Kamilc7070ac2018-03-27 15:01:02 +0200152 }
153
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100154 // Extract data that contains link information (auto negotiation and speed)
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700155 const PropertiesMapType *ethProperties = extractInterfaceProperties(
156 "/xyz/openbmc_project/network/" + ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100157 "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
158
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700159 if (ethProperties != nullptr) {
160 eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
161 eth_data.speed = extractProperty<unsigned int>(*ethProperties, "Speed");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100162 }
163
164 // Extract data that contains network config (HostName and DefaultGW)
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700165 const PropertiesMapType *configProperties = extractInterfaceProperties(
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100166 "/xyz/openbmc_project/network/config",
167 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
168
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700169 if (configProperties != nullptr) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100170 eth_data.hostname =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700171 extractProperty<std::string>(*configProperties, "HostName");
172 eth_data.defaultGateway =
173 extractProperty<std::string>(*configProperties, "DefaultGateway");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100174 }
175 }
176
177 // Helper function that changes bits netmask notation (i.e. /24)
178 // into full dot notation
179 inline std::string getNetmask(unsigned int bits) {
180 uint32_t value = 0xffffffff << (32 - bits);
181 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
182 std::to_string((value >> 16) & 0xff) + "." +
183 std::to_string((value >> 8) & 0xff) + "." +
184 std::to_string(value & 0xff);
185 return netmask;
186 }
187
188 // Helper function that extracts data for single ethernet ipv4 address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700189 void extractIPv4Data(const std::string &ethifaceId,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100190 const GetManagedObjectsType &dbus_data,
191 std::vector<IPv4AddressData> &ipv4_config) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200192 const std::string pathStart =
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700193 "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200194
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100195 // Since there might be several IPv4 configurations aligned with
196 // single ethernet interface, loop over all of them
197 for (auto &objpath : dbus_data) {
Gunnar Mills274fad52018-06-13 15:45:36 -0500198 // Check if proper patter for object path appears
Ed Tanousa434f2b2018-07-27 13:04:22 -0700199 if (boost::starts_with(static_cast<const std::string &>(objpath.first),
200 pathStart)) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100201 // and get approrpiate interface
202 const auto &interface =
203 objpath.second.find("xyz.openbmc_project.Network.IP");
204 if (interface != objpath.second.end()) {
205 // Make a properties 'shortcut', to make everything more readable
206 const PropertiesMapType &properties = interface->second;
207 // Instance IPv4AddressData structure, and set as appropriate
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700208 IPv4AddressData ipv4Address;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200209
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700210 ipv4Address.id = static_cast<const std::string &>(objpath.first)
211 .substr(pathStart.size());
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200212
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100213 // IPv4 address
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700214 ipv4Address.address =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100215 extractProperty<std::string>(properties, "Address");
216 // IPv4 gateway
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700217 ipv4Address.gateway =
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100218 extractProperty<std::string>(properties, "Gateway");
219
220 // Origin is kind of DBus object so fetch pointer...
221 const std::string *origin =
222 extractProperty<std::string>(properties, "Origin");
223 if (origin != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700224 ipv4Address.origin =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200225 translateAddressOriginBetweenDBusAndRedfish(origin, true, true);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100226 }
227
228 // Netmask is presented as PrefixLength
229 const auto *mask =
230 extractProperty<uint8_t>(properties, "PrefixLength");
231 if (mask != nullptr) {
232 // convert it to the string
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700233 ipv4Address.netmask = getNetmask(*mask);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100234 }
235
236 // Attach IPv4 only if address is present
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700237 if (ipv4Address.address != nullptr) {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700238 // Check if given address is local, or global
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700239 if (boost::starts_with(*ipv4Address.address, "169.254")) {
240 ipv4Address.global = false;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100241 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700242 ipv4Address.global = true;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100243 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700244 ipv4_config.emplace_back(std::move(ipv4Address));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100245 }
246 }
247 }
248 }
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200249
250 /**
251 * We have to sort this vector and ensure that order of IPv4 addresses
252 * is consistent between GETs to allow modification and deletion in PATCHes
253 */
254 std::sort(ipv4_config.begin(), ipv4_config.end());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100255 }
256
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200257 static const constexpr int ipV4AddressSectionsCount = 4;
258
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100259 public:
260 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200261 * @brief Creates VLAN for given interface with given Id through D-Bus
262 *
263 * @param[in] ifaceId Id of interface for which VLAN will be created
264 * @param[in] inputVlanId ID of the new VLAN
265 * @param[in] callback Function that will be called after the operation
266 *
267 * @return None.
268 */
269 template <typename CallbackFunc>
270 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
271 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700272 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200273 callback, "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
274 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
275 static_cast<uint32_t>(inputVlanId));
276 };
277
278 /**
279 * @brief Sets given Id on the given VLAN interface through D-Bus
280 *
281 * @param[in] ifaceId Id of VLAN interface that should be modified
282 * @param[in] inputVlanId New ID of the VLAN
283 * @param[in] callback Function that will be called after the operation
284 *
285 * @return None.
286 */
287 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200288 static void changeVlanId(const std::string &ifaceId,
289 const uint32_t &inputVlanId,
290 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700291 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200292 callback, "xyz.openbmc_project.Network",
293 std::string("/xyz/openbmc_project/network/") + ifaceId,
294 "org.freedesktop.DBus.Properties", "Set",
295 "xyz.openbmc_project.Network.VLAN", "Id",
296 sdbusplus::message::variant<uint32_t>(inputVlanId));
297 };
298
299 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200300 * @brief Helper function that verifies IP address to check if it is in
301 * proper format. If bits pointer is provided, also calculates active
302 * bit count for Subnet Mask.
303 *
304 * @param[in] ip IP that will be verified
305 * @param[out] bits Calculated mask in bits notation
306 *
307 * @return true in case of success, false otherwise
308 */
309 bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
310 uint8_t *bits = nullptr) {
311 std::vector<std::string> bytesInMask;
312
313 boost::split(bytesInMask, ip, boost::is_any_of("."));
314
315 if (bytesInMask.size() != ipV4AddressSectionsCount) {
316 return false;
317 }
318
319 if (bits != nullptr) {
320 *bits = 0;
321 }
322
323 char *endPtr;
324 long previousValue = 255;
325 bool firstZeroInByteHit;
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200326 for (const std::string &byte : bytesInMask) {
327 if (byte.empty()) {
328 return false;
329 }
330
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200331 // Use strtol instead of stroi to avoid exceptions
Kowalski, Kamil1db9ca32018-06-06 11:35:43 +0200332 long value = std::strtol(byte.c_str(), &endPtr, 10);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200333
334 // endPtr should point to the end of the string, otherwise given string
335 // is not 100% number
336 if (*endPtr != '\0') {
337 return false;
338 }
339
340 // Value should be contained in byte
341 if (value < 0 || value > 255) {
342 return false;
343 }
344
345 if (bits != nullptr) {
346 // Mask has to be continuous between bytes
347 if (previousValue != 255 && value != 0) {
348 return false;
349 }
350
351 // Mask has to be continuous inside bytes
352 firstZeroInByteHit = false;
353
354 // Count bits
355 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
356 if (value & (1 << bitIdx)) {
357 if (firstZeroInByteHit) {
358 // Continuity not preserved
359 return false;
360 } else {
361 (*bits)++;
362 }
363 } else {
364 firstZeroInByteHit = true;
365 }
366 }
367 }
368
369 previousValue = value;
370 }
371
372 return true;
373 }
374
375 /**
376 * @brief Changes IPv4 address type property (Address, Gateway)
377 *
378 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700379 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200380 * @param[in] ipHash DBus Hash id of modified IP
381 * @param[in] name Name of field in JSON representation
382 * @param[in] newValue New value that should be written
383 * @param[io] asyncResp Response object that will be returned to client
384 *
385 * @return true if give IP is valid and has been sent do D-Bus, false
386 * otherwise
387 */
388 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
389 const std::string &ipHash,
390 const std::string &name,
391 const std::string &newValue,
392 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700393 auto callback = [
394 asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
395 newValue{std::move(newValue)}
396 ](const boost::system::error_code ec) {
397 if (ec) {
398 messages::addMessageToJson(
399 asyncResp->res.jsonValue, messages::internalError(),
400 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
401 } else {
402 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
403 }
404 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200405
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700406 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200407 std::move(callback), "xyz.openbmc_project.Network",
408 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
409 "org.freedesktop.DBus.Properties", "Set",
410 "xyz.openbmc_project.Network.IP", name,
411 sdbusplus::message::variant<std::string>(newValue));
412 };
413
414 /**
415 * @brief Changes IPv4 address origin property
416 *
417 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700418 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200419 * @param[in] ipHash DBus Hash id of modified IP
420 * @param[in] newValue New value in Redfish format
421 * @param[in] newValueDbus New value in D-Bus format
422 * @param[io] asyncResp Response object that will be returned to client
423 *
424 * @return true if give IP is valid and has been sent do D-Bus, false
425 * otherwise
426 */
427 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
428 const std::string &ipHash, const std::string &newValue,
429 const std::string &newValueDbus,
430 const std::shared_ptr<AsyncResp> &asyncResp) {
431 auto callback =
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700432 [ asyncResp, ipIdx{std::move(ipIdx)},
433 newValue{std::move(newValue)} ](const boost::system::error_code ec) {
434 if (ec) {
435 messages::addMessageToJson(
436 asyncResp->res.jsonValue, messages::internalError(),
437 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
438 } else {
439 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
440 newValue;
441 }
442 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200443
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700444 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200445 std::move(callback), "xyz.openbmc_project.Network",
446 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
447 "org.freedesktop.DBus.Properties", "Set",
448 "xyz.openbmc_project.Network.IP", "Origin",
449 sdbusplus::message::variant<std::string>(newValueDbus));
450 };
451
452 /**
453 * @brief Modifies SubnetMask for given IP
454 *
455 * @param[in] ifaceId Id of interface whose IP should be modified
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700456 * @param[in] ipIdx index of IP in input array that should be modified
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200457 * @param[in] ipHash DBus Hash id of modified IP
458 * @param[in] newValueStr Mask in dot notation as string
459 * @param[in] newValue Mask as PrefixLength in bitcount
460 * @param[io] asyncResp Response object that will be returned to client
461 *
462 * @return None
463 */
464 void changeIPv4SubnetMaskProperty(
465 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
466 const std::string &newValueStr, uint8_t &newValue,
467 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700468 auto callback = [
469 asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
470 ](const boost::system::error_code ec) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200471 if (ec) {
472 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700473 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200474 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
475 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700476 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200477 newValueStr;
478 }
479 };
480
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700481 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200482 std::move(callback), "xyz.openbmc_project.Network",
483 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
484 "org.freedesktop.DBus.Properties", "Set",
485 "xyz.openbmc_project.Network.IP", "PrefixLength",
486 sdbusplus::message::variant<uint8_t>(newValue));
487 };
488
489 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200490 * @brief Disables VLAN with given ifaceId
491 *
492 * @param[in] ifaceId Id of VLAN interface that should be disabled
493 * @param[in] callback Function that will be called after the operation
494 *
495 * @return None.
496 */
497 template <typename CallbackFunc>
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200498 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700499 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200500 callback, "xyz.openbmc_project.Network",
501 std::string("/xyz/openbmc_project/network/") + ifaceId,
502 "xyz.openbmc_project.Object.Delete", "Delete");
503 };
504
505 /**
506 * @brief Sets given HostName of the machine through D-Bus
507 *
508 * @param[in] newHostname New name that HostName will be changed to
509 * @param[in] callback Function that will be called after the operation
510 *
511 * @return None.
512 */
513 template <typename CallbackFunc>
514 void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700515 crow::connections::systemBus->async_method_call(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200516 callback, "xyz.openbmc_project.Network",
517 "/xyz/openbmc_project/network/config",
518 "org.freedesktop.DBus.Properties", "Set",
519 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
520 sdbusplus::message::variant<std::string>(newHostname));
521 };
522
523 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200524 * @brief Deletes given IPv4
525 *
526 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700527 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200528 * @param[in] ipHash DBus Hash id of IP that should be deleted
529 * @param[io] asyncResp Response object that will be returned to client
530 *
531 * @return None
532 */
533 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
534 unsigned int ipIdx,
535 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700536 crow::connections::systemBus->async_method_call(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700537 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
538 const boost::system::error_code ec) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200539 if (ec) {
540 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700541 asyncResp->res.jsonValue, messages::internalError(),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200542 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
543 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700544 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200545 }
546 },
547 "xyz.openbmc_project.Network",
548 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
549 "xyz.openbmc_project.Object.Delete", "Delete");
550 }
551
552 /**
553 * @brief Creates IPv4 with given data
554 *
555 * @param[in] ifaceId Id of interface whose IP should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700556 * @param[in] ipIdx index of IP in input array that should be deleted
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200557 * @param[in] ipHash DBus Hash id of IP that should be deleted
558 * @param[io] asyncResp Response object that will be returned to client
559 *
560 * @return None
561 */
562 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
563 uint8_t subnetMask, const std::string &gateway,
564 const std::string &address,
565 const std::shared_ptr<AsyncResp> &asyncResp) {
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700566 auto createIpHandler = [
567 ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
568 ](const boost::system::error_code ec) {
569 if (ec) {
570 messages::addMessageToJson(
571 asyncResp->res.jsonValue, messages::internalError(),
572 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
573 }
574 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200575
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700576 crow::connections::systemBus->async_method_call(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200577 std::move(createIpHandler), "xyz.openbmc_project.Network",
578 "/xyz/openbmc_project/network/" + ifaceId,
579 "xyz.openbmc_project.Network.IP.Create", "IP",
580 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
581 gateway);
582 }
583
584 /**
585 * @brief Translates Address Origin value from D-Bus to Redfish format and
586 * vice-versa
587 *
588 * @param[in] inputOrigin Input value that should be translated
589 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
590 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse
591 *
592 * @return Empty string in case of failure, translated value otherwise
593 */
594 std::string translateAddressOriginBetweenDBusAndRedfish(
595 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
596 // Invalid pointer
597 if (inputOrigin == nullptr) {
598 return "";
599 }
600
601 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
602 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
603
604 std::array<std::pair<const char *, const char *>, 6> translationTable{
605 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
606 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
607 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
608 "IPv4LinkLocal"},
609 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
610 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
611 "LinkLocal"},
612 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
613
614 for (unsigned int i = 0; i < translationTable.size(); i++) {
615 // Skip unrelated
616 if (isIPv4 && i >= firstIPv6OnlyIdx) break;
617 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
618
619 // When translating D-Bus to Redfish compare input to first element
620 if (isFromDBus && translationTable[i].first == *inputOrigin)
621 return translationTable[i].second;
622
623 // When translating Redfish to D-Bus compare input to second element
624 if (!isFromDBus && translationTable[i].second == *inputOrigin)
625 return translationTable[i].first;
626 }
627
628 // If we are still here, that means that value has not been found
629 return "";
630 }
631
632 /**
633 * Function that retrieves all properties for given Ethernet Interface
634 * Object
635 * from EntityManager Network Manager
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700636 * @param ethifaceId a eth interface id to query on DBus
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200637 * @param callback a function that shall be called to convert Dbus output
638 * into JSON
639 */
640 template <typename CallbackFunc>
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700641 void getEthernetIfaceData(const std::string &ethifaceId,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200642 CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700643 crow::connections::systemBus->async_method_call(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700644 [
645 this, ethifaceId{std::move(ethifaceId)}, callback{std::move(callback)}
646 ](const boost::system::error_code error_code,
647 const GetManagedObjectsType &resp) {
648
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700649 EthernetInterfaceData ethData{};
650 std::vector<IPv4AddressData> ipv4Data;
651 ipv4Data.reserve(maxIpV4AddressesPerInterface);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200652
653 if (error_code) {
654 // Something wrong on DBus, the error_code is not important at
655 // this moment, just return success=false, and empty output. Since
656 // size of vector may vary depending on information from Network
657 // Manager, and empty output could not be treated same way as
658 // error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700659 callback(false, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200660 return;
661 }
662
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200663 // Find interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700664 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200665 resp.end()) {
666 // Interface has not been found
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700667 callback(false, ethData, ipv4Data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +0200668 return;
669 }
670
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700671 extractEthernetInterfaceData(ethifaceId, resp, ethData);
672 extractIPv4Data(ethifaceId, resp, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200673
674 // Fix global GW
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700675 for (IPv4AddressData &ipv4 : ipv4Data) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200676 if ((ipv4.global) &&
677 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700678 ipv4.gateway = ethData.defaultGateway;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200679 }
680 }
681
Ed Tanousa434f2b2018-07-27 13:04:22 -0700682 // Finally make a callback with useful data
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700683 callback(true, ethData, ipv4Data);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200684 },
685 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
686 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
687 };
688
689 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100690 * Function that retrieves all Ethernet Interfaces available through Network
691 * Manager
692 * @param callback a function that shall be called to convert Dbus output into
693 * JSON.
694 */
695 template <typename CallbackFunc>
696 void getEthernetIfaceList(CallbackFunc &&callback) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700697 crow::connections::systemBus->async_method_call(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700698 [ this, callback{std::move(callback)} ](
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100699 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700700 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100701 // Callback requires vector<string> to retrieve all available ethernet
702 // interfaces
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700703 std::vector<std::string> ifaceList;
704 ifaceList.reserve(resp.size());
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100705 if (error_code) {
706 // Something wrong on DBus, the error_code is not important at this
707 // moment, just return success=false, and empty output. Since size
708 // of vector may vary depending on information from Network Manager,
709 // and empty output could not be treated same way as error.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700710 callback(false, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100711 return;
712 }
713
714 // Iterate over all retrieved ObjectPaths.
715 for (auto &objpath : resp) {
716 // And all interfaces available for certain ObjectPath.
717 for (auto &interface : objpath.second) {
718 // If interface is xyz.openbmc_project.Network.EthernetInterface,
719 // this is what we're looking for.
720 if (interface.first ==
721 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousa434f2b2018-07-27 13:04:22 -0700722 // Cut out everything until last "/", ...
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700723 const std::string &ifaceId =
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200724 static_cast<const std::string &>(objpath.first);
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700725 std::size_t lastPos = ifaceId.rfind("/");
726 if (lastPos != std::string::npos) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100727 // and put it into output vector.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700728 ifaceList.emplace_back(ifaceId.substr(lastPos + 1));
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100729 }
730 }
731 }
732 }
Ed Tanousa434f2b2018-07-27 13:04:22 -0700733 // Finally make a callback with useful data
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700734 callback(true, ifaceList);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100735 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700736 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
737 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100738 };
739};
740
741/**
742 * EthernetCollection derived class for delivering Ethernet Collection Schema
743 */
744class EthernetCollection : public Node {
745 public:
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100746 // TODO(Pawel) Remove line from below, where we assume that there is only one
747 // manager called openbmc This shall be generic, but requires to update
748 // GetSubroutes method
749 EthernetCollection(CrowApp &app)
750 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
751 Node::json["@odata.type"] =
752 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
753 Node::json["@odata.context"] =
754 "/redfish/v1/"
755 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
756 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
757 Node::json["Name"] = "Ethernet Network Interface Collection";
758 Node::json["Description"] =
759 "Collection of EthernetInterfaces for this Manager";
760
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200761 entityPrivileges = {
762 {boost::beast::http::verb::get, {{"Login"}}},
763 {boost::beast::http::verb::head, {{"Login"}}},
764 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
765 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
766 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
767 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100768 }
769
770 private:
771 /**
772 * Functions triggers appropriate requests on DBus
773 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700774 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100775 const std::vector<std::string> &params) override {
776 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
777 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700778 std::string managerId = "openbmc";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100779
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700780 // get eth interface list, and call the below callback for JSON preparation
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700781 ethernetProvider.getEthernetIfaceList([&, managerId{std::move(managerId)} ](
782 const bool &success, const std::vector<std::string> &iface_list) {
783 if (success) {
784 nlohmann::json ifaceArray = nlohmann::json::array();
785 for (const std::string &ifaceItem : iface_list) {
786 ifaceArray.push_back(
787 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
788 "/EthernetInterfaces/" + ifaceItem}});
789 }
790 Node::json["Members"] = ifaceArray;
791 Node::json["Members@odata.count"] = ifaceArray.size();
792 Node::json["@odata.id"] =
793 "/redfish/v1/Managers/" + managerId + "/EthernetInterfaces";
794 res.jsonValue = Node::json;
795 } else {
796 // No success, best what we can do is return INTERNALL ERROR
797 res.result(boost::beast::http::status::internal_server_error);
798 }
799 res.end();
800 });
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100801 }
802
803 // Ethernet Provider object
804 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700805 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100806};
807
808/**
809 * EthernetInterface derived class for delivering Ethernet Schema
810 */
811class EthernetInterface : public Node {
812 public:
813 /*
814 * Default Constructor
815 */
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100816 // TODO(Pawel) Remove line from below, where we assume that there is only one
817 // manager called openbmc This shall be generic, but requires to update
818 // GetSubroutes method
819 EthernetInterface(CrowApp &app)
820 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
821 std::string()) {
822 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
823 Node::json["@odata.context"] =
824 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
825 Node::json["Name"] = "Manager Ethernet Interface";
826 Node::json["Description"] = "Management Network Interface";
827
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200828 entityPrivileges = {
829 {boost::beast::http::verb::get, {{"Login"}}},
830 {boost::beast::http::verb::head, {{"Login"}}},
831 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
832 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
833 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
834 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100835 }
836
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200837 // TODO(kkowalsk) Find a suitable class/namespace for this
838 static void handleVlanPatch(const std::string &ifaceId,
839 const nlohmann::json &input,
840 const EthernetInterfaceData &eth_data,
841 const std::string &pathPrefix,
842 const std::shared_ptr<AsyncResp> &asyncResp) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200843 if (!input.is_object()) {
844 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700845 asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200846 messages::propertyValueTypeError(input.dump(), "VLAN"), pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200847 return;
848 }
849
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200850 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
851 nlohmann::json &paramsJson =
852 (pathPrefix == "/")
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700853 ? asyncResp->res.jsonValue
854 : asyncResp->res.jsonValue[nlohmann::json_pointer<nlohmann::json>(
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200855 pathPrefix)];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200856 bool inputVlanEnabled;
857 uint64_t inputVlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200858
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200859 json_util::Result inputVlanEnabledState = json_util::getBool(
860 "VLANEnable", input, inputVlanEnabled,
861 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700862 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200863 json_util::Result inputVlanIdState = json_util::getUnsigned(
864 "VLANId", input, inputVlanId,
865 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700866 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200867 bool inputInvalid = false;
868
869 // Do not proceed if fields in VLAN object were of wrong type
870 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
871 inputVlanIdState == json_util::Result::WRONG_TYPE) {
872 return;
873 }
874
875 // Verify input
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700876 if (eth_data.vlanId == nullptr) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200877 // This interface is not a VLAN. Cannot do anything with it
878 // TODO(kkowalsk) Change this message
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700879 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200880 messages::propertyMissing("VLANEnable"),
881 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200882
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200883 inputInvalid = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200884 } else {
885 // Load actual data into field values if they were not provided
886 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
887 inputVlanEnabled = true;
888 }
889
890 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700891 inputVlanId = *eth_data.vlanId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200892 }
893 }
894
895 // Do not proceed if input has not been valid
896 if (inputInvalid) {
897 return;
898 }
899
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200900 // VLAN is configured on the interface
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700901 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200902 // Change VLAN Id
903 paramsJson["VLANId"] = inputVlanId;
904 OnDemandEthernetProvider::changeVlanId(
905 ifaceId, static_cast<uint32_t>(inputVlanId),
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700906 [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200907 const boost::system::error_code ec) {
908 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700909 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200910 messages::internalError(), pathPrefix);
911 } else {
912 paramsJson["VLANEnable"] = true;
913 }
914 });
915 } else if (inputVlanEnabled == false) {
916 // Disable VLAN
917 OnDemandEthernetProvider::disableVlan(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700918 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)} ](
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200919 const boost::system::error_code ec) {
920 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700921 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200922 messages::internalError(), pathPrefix);
923 } else {
924 paramsJson["VLANEnable"] = false;
925 }
926 });
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200927 }
928 }
929
Kowalski, Kamile439f0f2018-05-21 08:13:57 +0200930 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200931 void handleHostnamePatch(const nlohmann::json &input,
932 const EthernetInterfaceData &eth_data,
933 const std::shared_ptr<AsyncResp> &asyncResp) {
934 if (input.is_string()) {
935 std::string newHostname = input.get<std::string>();
936
937 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
938 // Change hostname
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700939 ethernetProvider.setHostName(
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200940 newHostname,
941 [asyncResp, newHostname](const boost::system::error_code ec) {
942 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700943 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200944 messages::internalError(),
945 "/HostName");
946 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700947 asyncResp->res.jsonValue["HostName"] = newHostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200948 }
949 });
950 }
951 } else {
952 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700953 asyncResp->res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200954 messages::propertyValueTypeError(input.dump(), "HostName"),
955 "/HostName");
956 }
957 }
958
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200959 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
960 const std::vector<IPv4AddressData> &ipv4_data,
961 const std::shared_ptr<AsyncResp> &asyncResp) {
962 if (!input.is_array()) {
963 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700964 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200965 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
966 "/IPv4Addresses");
967 return;
968 }
969
970 // According to Redfish PATCH definition, size must be at least equal
971 if (input.size() < ipv4_data.size()) {
972 // TODO(kkowalsk) This should be a message indicating that not enough
973 // data has been provided
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700974 messages::addMessageToJson(asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200975 messages::internalError(), "/IPv4Addresses");
976 return;
977 }
978
979 json_util::Result addressFieldState;
980 json_util::Result subnetMaskFieldState;
981 json_util::Result addressOriginFieldState;
982 json_util::Result gatewayFieldState;
983 const std::string *addressFieldValue;
984 const std::string *subnetMaskFieldValue;
985 const std::string *addressOriginFieldValue = nullptr;
986 const std::string *gatewayFieldValue;
987 uint8_t subnetMaskAsPrefixLength;
988 std::string addressOriginInDBusFormat;
989
990 bool errorDetected = false;
991 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
992 // Check that entry is not of some unexpected type
993 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
994 // Invalid object type
995 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700996 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200997 messages::propertyValueTypeError(input[entryIdx].dump(),
998 "IPv4Address"),
999 "/IPv4Addresses/" + std::to_string(entryIdx));
1000
1001 continue;
1002 }
1003
1004 // Try to load fields
1005 addressFieldState = json_util::getString(
1006 "Address", input[entryIdx], addressFieldValue,
1007 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001008 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001009 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1010 subnetMaskFieldState = json_util::getString(
1011 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1012 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001013 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001014 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1015 addressOriginFieldState = json_util::getString(
1016 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1017 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001018 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001019 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1020 gatewayFieldState = json_util::getString(
1021 "Gateway", input[entryIdx], gatewayFieldValue,
1022 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001023 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001024 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1025
1026 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1027 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1028 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1029 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1030 return;
1031 }
1032
1033 if (addressFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001034 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001035 errorDetected = true;
1036 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001037 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001038 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1039 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1040 }
1041
1042 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001043 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001044 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1045 errorDetected = true;
1046 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001047 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001048 messages::propertyValueFormatError(*subnetMaskFieldValue,
1049 "SubnetMask"),
1050 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1051 }
1052
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001053 // get Address origin in proper format
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001054 addressOriginInDBusFormat =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001055 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001056 addressOriginFieldValue, true, false);
1057
1058 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1059 addressOriginInDBusFormat.empty()) {
1060 errorDetected = true;
1061 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001062 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001063 messages::propertyValueNotInList(*addressOriginFieldValue,
1064 "AddressOrigin"),
1065 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1066 }
1067
1068 if (gatewayFieldState == json_util::Result::SUCCESS &&
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001069 !ethernetProvider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001070 errorDetected = true;
1071 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001072 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001073 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1074 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1075 }
1076
1077 // If any error occured do not proceed with current entry, but do not
1078 // end loop
1079 if (errorDetected) {
1080 errorDetected = false;
1081 continue;
1082 }
1083
1084 if (entryIdx >= ipv4_data.size()) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001085 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = input[entryIdx];
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001086
1087 // Verify that all field were provided
1088 if (addressFieldState == json_util::Result::NOT_EXIST) {
1089 errorDetected = true;
1090 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001091 asyncResp->res.jsonValue, messages::propertyMissing("Address"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001092 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1093 }
1094
1095 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1096 errorDetected = true;
1097 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001098 asyncResp->res.jsonValue, messages::propertyMissing("SubnetMask"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001099 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1100 }
1101
1102 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1103 errorDetected = true;
1104 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001105 asyncResp->res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001106 messages::propertyMissing("AddressOrigin"),
1107 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1108 }
1109
1110 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1111 errorDetected = true;
1112 messages::addMessageToJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001113 asyncResp->res.jsonValue, messages::propertyMissing("Gateway"),
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001114 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1115 }
1116
1117 // If any error occured do not proceed with current entry, but do not
1118 // end loop
1119 if (errorDetected) {
1120 errorDetected = false;
1121 continue;
1122 }
1123
1124 // Create IPv4 with provided data
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001125 ethernetProvider.createIPv4(ifaceId, entryIdx, subnetMaskAsPrefixLength,
1126 *gatewayFieldValue, *addressFieldValue,
1127 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001128 } else {
1129 // Existing object that should be modified/deleted/remain unchanged
1130 if (input[entryIdx].is_null()) {
1131 // Object should be deleted
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001132 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id, entryIdx,
1133 asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001134 } else if (input[entryIdx].is_object()) {
1135 if (input[entryIdx].size() == 0) {
1136 // Object shall remain unchanged
1137 continue;
1138 }
1139
1140 // Apply changes
1141 if (addressFieldState == json_util::Result::SUCCESS &&
1142 ipv4_data[entryIdx].address != nullptr &&
1143 *ipv4_data[entryIdx].address != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001144 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001145 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1146 *addressFieldValue, asyncResp);
1147 }
1148
1149 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1150 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001151 ethernetProvider.changeIPv4SubnetMaskProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001152 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1153 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1154 }
1155
1156 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1157 ipv4_data[entryIdx].origin != *addressFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001158 ethernetProvider.changeIPv4Origin(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001159 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1160 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1161 }
1162
1163 if (gatewayFieldState == json_util::Result::SUCCESS &&
1164 ipv4_data[entryIdx].gateway != nullptr &&
1165 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001166 ethernetProvider.changeIPv4AddressProperty(
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001167 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1168 *gatewayFieldValue, asyncResp);
1169 }
1170 }
1171 }
1172 }
1173 }
1174
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001175 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001176 const std::string &ifaceId, const EthernetInterfaceData &eth_data,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001177 const std::vector<IPv4AddressData> &ipv4_data) {
1178 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001179 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001180
1181 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001182 jsonResponse["Id"] = ifaceId;
1183 jsonResponse["@odata.id"] =
1184 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001185
1186 // ... then the one from DBus, regarding eth iface...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001187 if (eth_data.speed != nullptr) jsonResponse["SpeedMbps"] = *eth_data.speed;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001188
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001189 if (eth_data.macAddress != nullptr)
1190 jsonResponse["MACAddress"] = *eth_data.macAddress;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001191
1192 if (eth_data.hostname != nullptr)
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001193 jsonResponse["HostName"] = *eth_data.hostname;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001194
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001195 if (eth_data.vlanId != nullptr) {
1196 nlohmann::json &vlanObj = jsonResponse["VLAN"];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001197 vlanObj["VLANEnable"] = true;
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001198 vlanObj["VLANId"] = *eth_data.vlanId;
Kowalski, Kamileb547732018-05-16 10:25:07 +02001199 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001200 nlohmann::json &vlanObj = jsonResponse["VLANs"];
Kowalski, Kamileb547732018-05-16 10:25:07 +02001201 vlanObj["@odata.id"] =
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001202 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
Kowalski, Kamileb547732018-05-16 10:25:07 +02001203 "/VLANs";
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001204 }
1205
1206 // ... at last, check if there are IPv4 data and prepare appropriate
1207 // collection
1208 if (ipv4_data.size() > 0) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001209 nlohmann::json ipv4Array = nlohmann::json::array();
1210 for (auto &ipv4Config : ipv4_data) {
1211 nlohmann::json jsonIpv4;
1212 if (ipv4Config.address != nullptr) {
1213 jsonIpv4["Address"] = *ipv4Config.address;
1214 if (ipv4Config.gateway != nullptr)
1215 jsonIpv4["Gateway"] = *ipv4Config.gateway;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001216
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001217 jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1218 jsonIpv4["SubnetMask"] = ipv4Config.netmask;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001219
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001220 ipv4Array.push_back(std::move(jsonIpv4));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001221 }
1222 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001223 jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001224 }
1225
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001226 return jsonResponse;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001227 }
1228
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001229 /**
1230 * Functions triggers appropriate requests on DBus
1231 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001232 void doGet(crow::Response &res, const crow::Request &req,
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001233 const std::vector<std::string> &params) override {
1234 // TODO(Pawel) this shall be parametrized call (two params) to get
1235 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1236 // Check if there is required param, truly entering this shall be
1237 // impossible.
1238 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001239 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001240 res.end();
1241 return;
1242 }
1243
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001244 const std::string &ifaceId = params[0];
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001245
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001246 // get single eth interface data, and call the below callback for JSON
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001247 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001248 ethernetProvider.getEthernetIfaceData(
1249 ifaceId,
1250 [&, ifaceId](const bool &success, const EthernetInterfaceData &eth_data,
1251 const std::vector<IPv4AddressData> &ipv4_data) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001252 if (success) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001253 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001254 } else {
1255 // ... otherwise return error
1256 // TODO(Pawel)consider distinguish between non existing object, and
1257 // other errors
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001258 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001259 res.jsonValue,
1260 messages::resourceNotFound("EthernetInterface", ifaceId));
Ed Tanouse0d918b2018-03-27 17:41:04 -07001261 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001262 }
1263 res.end();
1264 });
1265 }
1266
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001267 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001268 const std::vector<std::string> &params) override {
1269 // TODO(Pawel) this shall be parametrized call (two params) to get
1270 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1271 // Check if there is required param, truly entering this shall be
1272 // impossible.
1273 if (params.size() != 1) {
1274 res.result(boost::beast::http::status::internal_server_error);
1275 res.end();
1276 return;
1277 }
1278
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001279 const std::string &ifaceId = params[0];
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001280
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001281 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001282
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001283 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001284 return;
1285 }
1286
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001287 // get single eth interface data, and call the below callback for JSON
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001288 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001289 ethernetProvider.getEthernetIfaceData(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001290 ifaceId, [&, ifaceId, patchReq = std::move(patchReq) ](
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001291 const bool &success, const EthernetInterfaceData &eth_data,
1292 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001293 if (!success) {
1294 // ... otherwise return error
1295 // TODO(Pawel)consider distinguish between non existing object, and
1296 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001297 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001298 res.jsonValue,
1299 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001300 res.result(boost::beast::http::status::not_found);
1301 res.end();
1302
1303 return;
1304 }
1305
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001306 res.jsonValue = parseInterfaceData(ifaceId, eth_data, ipv4_data);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001307
1308 std::shared_ptr<AsyncResp> asyncResp =
1309 std::make_shared<AsyncResp>(res);
1310
1311 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1312 ++propertyIt) {
1313 if (propertyIt.key() == "VLAN") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001314 handleVlanPatch(ifaceId, propertyIt.value(), eth_data, "/VLAN",
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001315 asyncResp);
1316 } else if (propertyIt.key() == "HostName") {
1317 handleHostnamePatch(propertyIt.value(), eth_data, asyncResp);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001318 } else if (propertyIt.key() == "IPv4Addresses") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001319 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001320 asyncResp);
1321 } else if (propertyIt.key() == "IPv6Addresses") {
1322 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1323 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001324 res.jsonValue,
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001325 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001326 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001327 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001328
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001329 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001330 // Field not in scope of defined fields
1331 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001332 res.jsonValue, messages::propertyUnknown(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001333 } else if (*fieldInJsonIt != *propertyIt) {
1334 // User attempted to modify non-writable field
1335 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001336 res.jsonValue,
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001337 messages::propertyNotWritable(propertyIt.key()));
1338 }
1339 }
1340 }
1341 });
1342 }
1343
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001344 // Ethernet Provider object
1345 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001346 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001347};
1348
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001349class VlanNetworkInterfaceCollection;
1350
1351/**
1352 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1353 */
1354class VlanNetworkInterface : public Node {
1355 public:
1356 /*
1357 * Default Constructor
1358 */
1359 template <typename CrowApp>
1360 // TODO(Pawel) Remove line from below, where we assume that there is only one
1361 // manager called openbmc This shall be generic, but requires to update
1362 // GetSubroutes method
1363 VlanNetworkInterface(CrowApp &app)
Kowalski, Kamileb547732018-05-16 10:25:07 +02001364 : Node(
1365 app,
1366 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
1367 std::string(), std::string()) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001368 Node::json["@odata.type"] =
1369 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1370 Node::json["@odata.context"] =
1371 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1372 Node::json["Name"] = "VLAN Network Interface";
1373
1374 entityPrivileges = {
1375 {boost::beast::http::verb::get, {{"Login"}}},
1376 {boost::beast::http::verb::head, {{"Login"}}},
1377 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1378 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1379 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1380 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1381 }
1382
1383 private:
1384 nlohmann::json parseInterfaceData(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001385 const std::string &parent_ifaceId, const std::string &ifaceId,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001386 const EthernetInterfaceData &eth_data,
1387 const std::vector<IPv4AddressData> &ipv4_data) {
1388 // Copy JSON object to avoid race condition
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001389 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001390
1391 // Fill out obvious data...
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001392 jsonResponse["Id"] = ifaceId;
1393 jsonResponse["@odata.id"] =
1394 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + parent_ifaceId +
1395 "/VLANs/" + ifaceId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001396
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001397 jsonResponse["VLANEnable"] = true;
1398 jsonResponse["VLANId"] = *eth_data.vlanId;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001399
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001400 return jsonResponse;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001401 }
1402
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001403 bool verifyNames(crow::Response &res, const std::string &parent,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001404 const std::string &iface) {
1405 if (!boost::starts_with(iface, parent + "_")) {
1406 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001407 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001408 messages::resourceNotFound("VLAN Network Interface", iface));
1409 res.result(boost::beast::http::status::not_found);
1410 res.end();
1411
1412 return false;
1413 } else {
1414 return true;
1415 }
1416 }
1417
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001418 /**
1419 * Functions triggers appropriate requests on DBus
1420 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001421 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001422 const std::vector<std::string> &params) override {
1423 // TODO(Pawel) this shall be parametrized call (two params) to get
1424 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1425 // Check if there is required param, truly entering this shall be
1426 // impossible.
1427 if (params.size() != 2) {
1428 res.result(boost::beast::http::status::internal_server_error);
1429 res.end();
1430 return;
1431 }
1432
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001433 const std::string &parentIfaceId = params[0];
1434 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001435
Ed Tanousa434f2b2018-07-27 13:04:22 -07001436 if (!verifyNames(res, parentIfaceId, ifaceId)) {
1437 return;
1438 }
1439
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001440 // Get single eth interface data, and call the below callback for JSON
1441 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001442 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001443 ifaceId, [&, parentIfaceId, ifaceId](
1444 const bool &success, const EthernetInterfaceData &eth_data,
1445 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001446 if (success && eth_data.vlanId != nullptr) {
Ed Tanousa434f2b2018-07-27 13:04:22 -07001447 res.jsonValue =
1448 parseInterfaceData(parentIfaceId, ifaceId, eth_data, ipv4_data);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001449 } else {
1450 // ... otherwise return error
Kowalski, Kamileb547732018-05-16 10:25:07 +02001451 // TODO(Pawel)consider distinguish between non existing object, and
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001452 // other errors
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001453 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001454 res.jsonValue,
1455 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001456 res.result(boost::beast::http::status::not_found);
1457 }
1458 res.end();
1459 });
1460 }
1461
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001462 void doPatch(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001463 const std::vector<std::string> &params) override {
1464 if (params.size() != 2) {
1465 res.result(boost::beast::http::status::internal_server_error);
1466 res.end();
1467 return;
1468 }
1469
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001470 const std::string &parent_ifaceId = params[0];
1471 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001472
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001473 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001474 return;
1475 }
1476
1477 nlohmann::json patchReq;
1478
1479 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
1480 return;
1481 }
1482
1483 // Get single eth interface data, and call the below callback for JSON
1484 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001485 ethernetProvider.getEthernetIfaceData(
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001486 ifaceId, [&, parent_ifaceId, ifaceId, patchReq = std::move(patchReq) ](
Ed Tanousa434f2b2018-07-27 13:04:22 -07001487 const bool &success, const EthernetInterfaceData &eth_data,
1488 const std::vector<IPv4AddressData> &ipv4_data) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001489 if (!success) {
1490 // ... otherwise return error
1491 // TODO(Pawel)consider distinguish between non existing object,
1492 // and
1493 // other errors
1494 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001495 res.jsonValue,
1496 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001497 res.result(boost::beast::http::status::not_found);
1498 res.end();
1499
1500 return;
1501 }
1502
Ed Tanousa434f2b2018-07-27 13:04:22 -07001503 res.jsonValue =
1504 parseInterfaceData(parent_ifaceId, ifaceId, eth_data, ipv4_data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001505
1506 std::shared_ptr<AsyncResp> asyncResp =
1507 std::make_shared<AsyncResp>(res);
1508
1509 for (auto propertyIt = patchReq.begin(); propertyIt != patchReq.end();
1510 ++propertyIt) {
1511 if (propertyIt.key() != "VLANEnable" &&
1512 propertyIt.key() != "VLANId") {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001513 auto fieldInJsonIt = res.jsonValue.find(propertyIt.key());
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001514
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001515 if (fieldInJsonIt == res.jsonValue.end()) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001516 // Field not in scope of defined fields
1517 messages::addMessageToJsonRoot(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001518 res.jsonValue, messages::propertyUnknown(propertyIt.key()));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001519 } else if (*fieldInJsonIt != *propertyIt) {
1520 // User attempted to modify non-writable field
1521 messages::addMessageToJsonRoot(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001522 res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001523 messages::propertyNotWritable(propertyIt.key()));
1524 }
1525 }
1526 }
1527
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001528 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data, "/",
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001529 asyncResp);
1530 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001531 }
1532
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001533 void doDelete(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001534 const std::vector<std::string> &params) override {
1535 if (params.size() != 2) {
1536 res.result(boost::beast::http::status::internal_server_error);
1537 res.end();
1538 return;
1539 }
1540
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001541 const std::string &parent_ifaceId = params[0];
1542 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001543
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001544 if (!verifyNames(res, parent_ifaceId, ifaceId)) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001545 return;
1546 }
1547
1548 // Get single eth interface data, and call the below callback for JSON
1549 // preparation
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001550 ethernetProvider.getEthernetIfaceData(
Ed Tanousa434f2b2018-07-27 13:04:22 -07001551 ifaceId, [&, parent_ifaceId, ifaceId](
1552 const bool &success, const EthernetInterfaceData &eth_data,
1553 const std::vector<IPv4AddressData> &ipv4_data) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001554 if (success && eth_data.vlanId != nullptr) {
1555 res.jsonValue = parseInterfaceData(parent_ifaceId, ifaceId,
Ed Tanousa434f2b2018-07-27 13:04:22 -07001556 eth_data, ipv4_data);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001557
1558 // Disable VLAN
1559 OnDemandEthernetProvider::disableVlan(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001560 ifaceId, [&](const boost::system::error_code ec) {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001561 if (ec) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001562 res.jsonValue = nlohmann::json::object();
1563 messages::addMessageToErrorJson(res.jsonValue,
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001564 messages::internalError());
1565 res.result(
1566 boost::beast::http::status::internal_server_error);
1567 }
1568 res.end();
1569 });
1570 } else {
1571 // ... otherwise return error
1572 // TODO(Pawel)consider distinguish between non existing object,
1573 // and
1574 // other errors
1575 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001576 res.jsonValue,
1577 messages::resourceNotFound("VLAN Network Interface", ifaceId));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001578 res.result(boost::beast::http::status::not_found);
1579 res.end();
1580 }
1581 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001582 }
1583
1584 /**
1585 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1586 * method, to maintain consistency of returned data, as Collection's doPost
1587 * should return data for created member which should match member's doGet
1588 * result in 100%.
1589 */
1590 friend VlanNetworkInterfaceCollection;
1591
1592 // Ethernet Provider object
1593 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001594 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001595};
1596
1597/**
1598 * VlanNetworkInterfaceCollection derived class for delivering
1599 * VLANNetworkInterface Collection Schema
1600 */
1601class VlanNetworkInterfaceCollection : public Node {
1602 public:
1603 template <typename CrowApp>
1604 // TODO(Pawel) Remove line from below, where we assume that there is only one
1605 // manager called openbmc This shall be generic, but requires to update
1606 // GetSubroutes method
1607 VlanNetworkInterfaceCollection(CrowApp &app)
1608 : Node(app,
1609 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1610 std::string()),
1611 memberVlan(app) {
1612 Node::json["@odata.type"] =
1613 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1614 Node::json["@odata.context"] =
1615 "/redfish/v1/$metadata"
1616 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1617 Node::json["Name"] = "VLAN Network Interface Collection";
1618
1619 entityPrivileges = {
1620 {boost::beast::http::verb::get, {{"Login"}}},
1621 {boost::beast::http::verb::head, {{"Login"}}},
1622 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1623 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1624 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1625 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1626 }
1627
1628 private:
1629 /**
1630 * Functions triggers appropriate requests on DBus
1631 */
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001632 void doGet(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001633 const std::vector<std::string> &params) override {
1634 if (params.size() != 1) {
1635 // This means there is a problem with the router
1636 res.result(boost::beast::http::status::internal_server_error);
1637 res.end();
1638
1639 return;
1640 }
1641
1642 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1643 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001644 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001645 std::string rootInterfaceName = params[0];
1646
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001647 // get eth interface list, and call the below callback for JSON preparation
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001648 ethernetProvider.getEthernetIfaceList([
1649 &, managerId{std::move(managerId)},
1650 rootInterfaceName{std::move(rootInterfaceName)}
1651 ](const bool &success, const std::vector<std::string> &iface_list) {
1652 if (success) {
1653 bool rootInterfaceFound = false;
1654 nlohmann::json ifaceArray = nlohmann::json::array();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001655
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001656 for (const std::string &ifaceItem : iface_list) {
1657 if (ifaceItem == rootInterfaceName) {
1658 rootInterfaceFound = true;
1659 } else if (boost::starts_with(ifaceItem, rootInterfaceName + "_")) {
1660 ifaceArray.push_back(
1661 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
1662 "/EthernetInterfaces/" + rootInterfaceName +
1663 "/VLANs/" + ifaceItem}});
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001664 }
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001665 }
1666
1667 if (rootInterfaceFound) {
1668 Node::json["Members"] = ifaceArray;
1669 Node::json["Members@odata.count"] = ifaceArray.size();
1670 Node::json["@odata.id"] = "/redfish/v1/Managers/" + managerId +
1671 "/EthernetInterfaces/" + rootInterfaceName +
1672 "/VLANs";
1673 res.jsonValue = Node::json;
1674 } else {
1675 messages::addMessageToErrorJson(
1676 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1677 rootInterfaceName));
1678 res.result(boost::beast::http::status::not_found);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001679 res.end();
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001680 }
1681 } else {
1682 // No success, best what we can do is return INTERNALL ERROR
1683 res.result(boost::beast::http::status::internal_server_error);
1684 }
1685 res.end();
1686 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001687 }
1688
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001689 void doPost(crow::Response &res, const crow::Request &req,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001690 const std::vector<std::string> &params) override {
1691 if (params.size() != 1) {
1692 // This means there is a problem with the router
1693 res.result(boost::beast::http::status::internal_server_error);
1694 res.end();
1695 return;
1696 }
1697
1698 nlohmann::json postReq;
1699
1700 if (!json_util::processJsonFromRequest(res, req, postReq)) {
1701 return;
1702 }
1703
1704 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
1705 // any Manager, not only hardcoded 'openbmc'.
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001706 std::string managerId = "openbmc";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001707 std::string rootInterfaceName = params[0];
1708 uint64_t vlanId;
1709 bool errorDetected;
1710
1711 if (json_util::getUnsigned(
1712 "VLANId", postReq, vlanId,
1713 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
1714 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001715 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS) {
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001716 res.end();
1717 return;
1718 }
1719
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001720 // get eth interface list, and call the below callback for JSON preparation
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001721 ethernetProvider.getEthernetIfaceList([
1722 &, managerId{std::move(managerId)},
1723 rootInterfaceName{std::move(rootInterfaceName)}
1724 ](const bool &success, const std::vector<std::string> &iface_list) {
1725 if (success) {
1726 bool rootInterfaceFound = false;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001727
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001728 for (const std::string &ifaceItem : iface_list) {
1729 if (ifaceItem == rootInterfaceName) {
1730 rootInterfaceFound = true;
1731 break;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001732 }
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001733 }
1734
1735 if (rootInterfaceFound) {
1736 ethernetProvider.createVlan(
1737 rootInterfaceName, vlanId,
1738 [&, vlanId, rootInterfaceName,
1739 req{std::move(req)} ](const boost::system::error_code ec) {
1740 if (ec) {
1741 messages::addMessageToErrorJson(res.jsonValue,
1742 messages::internalError());
1743 res.end();
1744 } else {
1745 memberVlan.doGet(
1746 res, req,
1747 {rootInterfaceName,
1748 rootInterfaceName + "_" + std::to_string(vlanId)});
1749 }
1750 });
1751 } else {
1752 messages::addMessageToErrorJson(
1753 res.jsonValue, messages::resourceNotFound("EthernetInterface",
1754 rootInterfaceName));
1755 res.result(boost::beast::http::status::not_found);
1756 res.end();
1757 }
1758 } else {
1759 // No success, best what we can do is return INTERNALL ERROR
1760 res.result(boost::beast::http::status::internal_server_error);
1761 res.end();
1762 }
1763 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001764 }
1765
1766 // Ethernet Provider object
1767 // TODO(Pawel) consider move it to singleton
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001768 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001769 VlanNetworkInterface memberVlan;
1770};
1771
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001772} // namespace redfish