blob: a3b1701d3639e3957cef965660a729e81beb8646 [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>
289 void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
290 CallbackFunc &&callback) {
291 crow::connections::system_bus->async_method_call(
292 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;
326 for (uint8_t byteIdx = 0; byteIdx < ipV4AddressSectionsCount; byteIdx++) {
327 // Use strtol instead of stroi to avoid exceptions
328 long value = std::strtol(bytesInMask[byteIdx].c_str(), &endPtr, 10);
329
330 // endPtr should point to the end of the string, otherwise given string
331 // is not 100% number
332 if (*endPtr != '\0') {
333 return false;
334 }
335
336 // Value should be contained in byte
337 if (value < 0 || value > 255) {
338 return false;
339 }
340
341 if (bits != nullptr) {
342 // Mask has to be continuous between bytes
343 if (previousValue != 255 && value != 0) {
344 return false;
345 }
346
347 // Mask has to be continuous inside bytes
348 firstZeroInByteHit = false;
349
350 // Count bits
351 for (int bitIdx = 7; bitIdx >= 0; bitIdx--) {
352 if (value & (1 << bitIdx)) {
353 if (firstZeroInByteHit) {
354 // Continuity not preserved
355 return false;
356 } else {
357 (*bits)++;
358 }
359 } else {
360 firstZeroInByteHit = true;
361 }
362 }
363 }
364
365 previousValue = value;
366 }
367
368 return true;
369 }
370
371 /**
372 * @brief Changes IPv4 address type property (Address, Gateway)
373 *
374 * @param[in] ifaceId Id of interface whose IP should be modified
375 * @param[in] ipIdx Index of IP in input array that should be modified
376 * @param[in] ipHash DBus Hash id of modified IP
377 * @param[in] name Name of field in JSON representation
378 * @param[in] newValue New value that should be written
379 * @param[io] asyncResp Response object that will be returned to client
380 *
381 * @return true if give IP is valid and has been sent do D-Bus, false
382 * otherwise
383 */
384 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
385 const std::string &ipHash,
386 const std::string &name,
387 const std::string &newValue,
388 const std::shared_ptr<AsyncResp> &asyncResp) {
389 auto callback = [
390 asyncResp, ipIdx{std::move(ipIdx)}, name{std::move(name)},
391 newValue{std::move(newValue)}
392 ](const boost::system::error_code ec) {
393 if (ec) {
394 messages::addMessageToJson(
395 asyncResp->res.json_value, messages::internalError(),
396 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
397 } else {
398 asyncResp->res.json_value["IPv4Addresses"][ipIdx][name] = newValue;
399 }
400 };
401
402 crow::connections::system_bus->async_method_call(
403 std::move(callback), "xyz.openbmc_project.Network",
404 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
405 "org.freedesktop.DBus.Properties", "Set",
406 "xyz.openbmc_project.Network.IP", name,
407 sdbusplus::message::variant<std::string>(newValue));
408 };
409
410 /**
411 * @brief Changes IPv4 address origin property
412 *
413 * @param[in] ifaceId Id of interface whose IP should be modified
414 * @param[in] ipIdx Index of IP in input array that should be modified
415 * @param[in] ipHash DBus Hash id of modified IP
416 * @param[in] newValue New value in Redfish format
417 * @param[in] newValueDbus New value in D-Bus format
418 * @param[io] asyncResp Response object that will be returned to client
419 *
420 * @return true if give IP is valid and has been sent do D-Bus, false
421 * otherwise
422 */
423 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
424 const std::string &ipHash, const std::string &newValue,
425 const std::string &newValueDbus,
426 const std::shared_ptr<AsyncResp> &asyncResp) {
427 auto callback =
428 [ asyncResp, ipIdx{std::move(ipIdx)},
429 newValue{std::move(newValue)} ](const boost::system::error_code ec) {
430 if (ec) {
431 messages::addMessageToJson(
432 asyncResp->res.json_value, messages::internalError(),
433 "/IPv4Addresses/" + std::to_string(ipIdx) + "/AddressOrigin");
434 } else {
435 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["AddressOrigin"] =
436 newValue;
437 }
438 };
439
440 crow::connections::system_bus->async_method_call(
441 std::move(callback), "xyz.openbmc_project.Network",
442 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
443 "org.freedesktop.DBus.Properties", "Set",
444 "xyz.openbmc_project.Network.IP", "Origin",
445 sdbusplus::message::variant<std::string>(newValueDbus));
446 };
447
448 /**
449 * @brief Modifies SubnetMask for given IP
450 *
451 * @param[in] ifaceId Id of interface whose IP should be modified
452 * @param[in] ipIdx Index of IP in input array that should be modified
453 * @param[in] ipHash DBus Hash id of modified IP
454 * @param[in] newValueStr Mask in dot notation as string
455 * @param[in] newValue Mask as PrefixLength in bitcount
456 * @param[io] asyncResp Response object that will be returned to client
457 *
458 * @return None
459 */
460 void changeIPv4SubnetMaskProperty(
461 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
462 const std::string &newValueStr, uint8_t &newValue,
463 const std::shared_ptr<AsyncResp> &asyncResp) {
464 auto callback = [
465 asyncResp, ipIdx{std::move(ipIdx)}, newValueStr{std::move(newValueStr)}
466 ](const boost::system::error_code ec) {
467 if (ec) {
468 messages::addMessageToJson(
469 asyncResp->res.json_value, messages::internalError(),
470 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
471 } else {
472 asyncResp->res.json_value["IPv4Addresses"][ipIdx]["SubnetMask"] =
473 newValueStr;
474 }
475 };
476
477 crow::connections::system_bus->async_method_call(
478 std::move(callback), "xyz.openbmc_project.Network",
479 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
480 "org.freedesktop.DBus.Properties", "Set",
481 "xyz.openbmc_project.Network.IP", "PrefixLength",
482 sdbusplus::message::variant<uint8_t>(newValue));
483 };
484
485 /**
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200486 * @brief Disables VLAN with given ifaceId
487 *
488 * @param[in] ifaceId Id of VLAN interface that should be disabled
489 * @param[in] callback Function that will be called after the operation
490 *
491 * @return None.
492 */
493 template <typename CallbackFunc>
494 void disableVlan(const std::string &ifaceId, CallbackFunc &&callback) {
495 crow::connections::system_bus->async_method_call(
496 callback, "xyz.openbmc_project.Network",
497 std::string("/xyz/openbmc_project/network/") + ifaceId,
498 "xyz.openbmc_project.Object.Delete", "Delete");
499 };
500
501 /**
502 * @brief Sets given HostName of the machine through D-Bus
503 *
504 * @param[in] newHostname New name that HostName will be changed to
505 * @param[in] callback Function that will be called after the operation
506 *
507 * @return None.
508 */
509 template <typename CallbackFunc>
510 void setHostName(const std::string &newHostname, CallbackFunc &&callback) {
511 crow::connections::system_bus->async_method_call(
512 callback, "xyz.openbmc_project.Network",
513 "/xyz/openbmc_project/network/config",
514 "org.freedesktop.DBus.Properties", "Set",
515 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
516 sdbusplus::message::variant<std::string>(newHostname));
517 };
518
519 /**
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200520 * @brief Deletes given IPv4
521 *
522 * @param[in] ifaceId Id of interface whose IP should be deleted
523 * @param[in] ipIdx Index of IP in input array that should be deleted
524 * @param[in] ipHash DBus Hash id of IP that should be deleted
525 * @param[io] asyncResp Response object that will be returned to client
526 *
527 * @return None
528 */
529 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
530 unsigned int ipIdx,
531 const std::shared_ptr<AsyncResp> &asyncResp) {
532 crow::connections::system_bus->async_method_call(
533 [ ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)} ](
534 const boost::system::error_code ec) {
535 if (ec) {
536 messages::addMessageToJson(
537 asyncResp->res.json_value, messages::internalError(),
538 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
539 } else {
540 asyncResp->res.json_value["IPv4Addresses"][ipIdx] = nullptr;
541 }
542 },
543 "xyz.openbmc_project.Network",
544 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
545 "xyz.openbmc_project.Object.Delete", "Delete");
546 }
547
548 /**
549 * @brief Creates IPv4 with given data
550 *
551 * @param[in] ifaceId Id of interface whose IP should be deleted
552 * @param[in] ipIdx Index of IP in input array that should be deleted
553 * @param[in] ipHash DBus Hash id of IP that should be deleted
554 * @param[io] asyncResp Response object that will be returned to client
555 *
556 * @return None
557 */
558 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
559 uint8_t subnetMask, const std::string &gateway,
560 const std::string &address,
561 const std::shared_ptr<AsyncResp> &asyncResp) {
562 auto createIpHandler = [
563 ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}
564 ](const boost::system::error_code ec) {
565 if (ec) {
566 messages::addMessageToJson(
567 asyncResp->res.json_value, messages::internalError(),
568 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
569 }
570 };
571
572 crow::connections::system_bus->async_method_call(
573 std::move(createIpHandler), "xyz.openbmc_project.Network",
574 "/xyz/openbmc_project/network/" + ifaceId,
575 "xyz.openbmc_project.Network.IP.Create", "IP",
576 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
577 gateway);
578 }
579
580 /**
581 * @brief Translates Address Origin value from D-Bus to Redfish format and
582 * vice-versa
583 *
584 * @param[in] inputOrigin Input value that should be translated
585 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
586 * @param[in] isFromDBus True for DBus->Redfish conversion, false for reverse
587 *
588 * @return Empty string in case of failure, translated value otherwise
589 */
590 std::string translateAddressOriginBetweenDBusAndRedfish(
591 const std::string *inputOrigin, bool isIPv4, bool isFromDBus) {
592 // Invalid pointer
593 if (inputOrigin == nullptr) {
594 return "";
595 }
596
597 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
598 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
599
600 std::array<std::pair<const char *, const char *>, 6> translationTable{
601 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
602 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
603 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
604 "IPv4LinkLocal"},
605 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
606 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
607 "LinkLocal"},
608 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
609
610 for (unsigned int i = 0; i < translationTable.size(); i++) {
611 // Skip unrelated
612 if (isIPv4 && i >= firstIPv6OnlyIdx) break;
613 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx) continue;
614
615 // When translating D-Bus to Redfish compare input to first element
616 if (isFromDBus && translationTable[i].first == *inputOrigin)
617 return translationTable[i].second;
618
619 // When translating Redfish to D-Bus compare input to second element
620 if (!isFromDBus && translationTable[i].second == *inputOrigin)
621 return translationTable[i].first;
622 }
623
624 // If we are still here, that means that value has not been found
625 return "";
626 }
627
628 /**
629 * Function that retrieves all properties for given Ethernet Interface
630 * Object
631 * from EntityManager Network Manager
632 * @param ethiface_id a eth interface id to query on DBus
633 * @param callback a function that shall be called to convert Dbus output
634 * into JSON
635 */
636 template <typename CallbackFunc>
637 void getEthernetIfaceData(const std::string &ethiface_id,
638 CallbackFunc &&callback) {
639 crow::connections::system_bus->async_method_call(
640 [
641 this, ethiface_id{std::move(ethiface_id)},
642 callback{std::move(callback)}
643 ](const boost::system::error_code error_code,
644 const GetManagedObjectsType &resp) {
645
646 EthernetInterfaceData eth_data{};
647 std::vector<IPv4AddressData> ipv4_data;
648 ipv4_data.reserve(MAX_IPV4_ADDRESSES_PER_INTERFACE);
649
650 if (error_code) {
651 // Something wrong on DBus, the error_code is not important at
652 // this moment, just return success=false, and empty output. Since
653 // size of vector may vary depending on information from Network
654 // Manager, and empty output could not be treated same way as
655 // error.
656 callback(false, eth_data, ipv4_data);
657 return;
658 }
659
660 extractEthernetInterfaceData(ethiface_id, resp, eth_data);
661 extractIPv4Data(ethiface_id, resp, ipv4_data);
662
663 // Fix global GW
664 for (IPv4AddressData &ipv4 : ipv4_data) {
665 if ((ipv4.global) &&
666 ((ipv4.gateway == nullptr) || (*ipv4.gateway == "0.0.0.0"))) {
667 ipv4.gateway = eth_data.default_gateway;
668 }
669 }
670
671 // Finally make a callback with usefull data
672 callback(true, eth_data, ipv4_data);
673 },
674 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
675 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
676 };
677
678 /**
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100679 * Function that retrieves all Ethernet Interfaces available through Network
680 * Manager
681 * @param callback a function that shall be called to convert Dbus output into
682 * JSON.
683 */
684 template <typename CallbackFunc>
685 void getEthernetIfaceList(CallbackFunc &&callback) {
686 crow::connections::system_bus->async_method_call(
687 [ this, callback{std::move(callback)} ](
688 const boost::system::error_code error_code,
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700689 GetManagedObjectsType &resp) {
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100690 // Callback requires vector<string> to retrieve all available ethernet
691 // interfaces
692 std::vector<std::string> iface_list;
693 iface_list.reserve(resp.size());
694 if (error_code) {
695 // Something wrong on DBus, the error_code is not important at this
696 // moment, just return success=false, and empty output. Since size
697 // of vector may vary depending on information from Network Manager,
698 // and empty output could not be treated same way as error.
699 callback(false, iface_list);
700 return;
701 }
702
703 // Iterate over all retrieved ObjectPaths.
704 for (auto &objpath : resp) {
705 // And all interfaces available for certain ObjectPath.
706 for (auto &interface : objpath.second) {
707 // If interface is xyz.openbmc_project.Network.EthernetInterface,
708 // this is what we're looking for.
709 if (interface.first ==
710 "xyz.openbmc_project.Network.EthernetInterface") {
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700711 // Cut out everyting until last "/", ...
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200712 const std::string &iface_id =
713 static_cast<const std::string &>(objpath.first);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100714 std::size_t last_pos = iface_id.rfind("/");
715 if (last_pos != std::string::npos) {
716 // and put it into output vector.
717 iface_list.emplace_back(iface_id.substr(last_pos + 1));
718 }
719 }
720 }
721 }
Gunnar Mills274fad52018-06-13 15:45:36 -0500722 // Finally make a callback with useful data
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100723 callback(true, iface_list);
724 },
Ed Tanousaa2e59c2018-04-12 12:17:20 -0700725 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
726 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100727 };
728};
729
730/**
731 * EthernetCollection derived class for delivering Ethernet Collection Schema
732 */
733class EthernetCollection : public Node {
734 public:
735 template <typename CrowApp>
736 // TODO(Pawel) Remove line from below, where we assume that there is only one
737 // manager called openbmc This shall be generic, but requires to update
738 // GetSubroutes method
739 EthernetCollection(CrowApp &app)
740 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/") {
741 Node::json["@odata.type"] =
742 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
743 Node::json["@odata.context"] =
744 "/redfish/v1/"
745 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
746 Node::json["@odata.id"] = "/redfish/v1/Managers/openbmc/EthernetInterfaces";
747 Node::json["Name"] = "Ethernet Network Interface Collection";
748 Node::json["Description"] =
749 "Collection of EthernetInterfaces for this Manager";
750
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200751 entityPrivileges = {
752 {boost::beast::http::verb::get, {{"Login"}}},
753 {boost::beast::http::verb::head, {{"Login"}}},
754 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
755 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
756 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
757 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100758 }
759
760 private:
761 /**
762 * Functions triggers appropriate requests on DBus
763 */
764 void doGet(crow::response &res, const crow::request &req,
765 const std::vector<std::string> &params) override {
766 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces for
767 // any Manager, not only hardcoded 'openbmc'.
768 std::string manager_id = "openbmc";
769
770 // Get eth interface list, and call the below callback for JSON preparation
771 ethernet_provider.getEthernetIfaceList(
772 [&, manager_id{std::move(manager_id)} ](
773 const bool &success, const std::vector<std::string> &iface_list) {
774 if (success) {
775 nlohmann::json iface_array = nlohmann::json::array();
776 for (const std::string &iface_item : iface_list) {
777 iface_array.push_back(
778 {{"@odata.id", "/redfish/v1/Managers/" + manager_id +
779 "/EthernetInterfaces/" + iface_item}});
780 }
781 Node::json["Members"] = iface_array;
782 Node::json["Members@odata.count"] = iface_array.size();
783 Node::json["@odata.id"] =
784 "/redfish/v1/Managers/" + manager_id + "/EthernetInterfaces";
785 res.json_value = Node::json;
786 } else {
787 // No success, best what we can do is return INTERNALL ERROR
Ed Tanouse0d918b2018-03-27 17:41:04 -0700788 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100789 }
790 res.end();
791 });
792 }
793
794 // Ethernet Provider object
795 // TODO(Pawel) consider move it to singleton
796 OnDemandEthernetProvider ethernet_provider;
797};
798
799/**
800 * EthernetInterface derived class for delivering Ethernet Schema
801 */
802class EthernetInterface : public Node {
803 public:
804 /*
805 * Default Constructor
806 */
807 template <typename CrowApp>
808 // TODO(Pawel) Remove line from below, where we assume that there is only one
809 // manager called openbmc This shall be generic, but requires to update
810 // GetSubroutes method
811 EthernetInterface(CrowApp &app)
812 : Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
813 std::string()) {
814 Node::json["@odata.type"] = "#EthernetInterface.v1_2_0.EthernetInterface";
815 Node::json["@odata.context"] =
816 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
817 Node::json["Name"] = "Manager Ethernet Interface";
818 Node::json["Description"] = "Management Network Interface";
819
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200820 entityPrivileges = {
821 {boost::beast::http::verb::get, {{"Login"}}},
822 {boost::beast::http::verb::head, {{"Login"}}},
823 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
824 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
825 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
826 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100827 }
828
829 private:
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200830 void handleVlanPatch(const std::string &ifaceId, const nlohmann::json &input,
831 const EthernetInterfaceData &eth_data,
832 const std::shared_ptr<AsyncResp> &asyncResp) {
833 if (!input.is_object()) {
834 messages::addMessageToJson(
835 asyncResp->res.json_value,
836 messages::propertyValueTypeError(input.dump(), "VLAN"), "/VLAN");
837 return;
838 }
839
840 bool inputVlanEnabled;
841 uint64_t inputVlanId;
842 json_util::Result inputVlanEnabledState = json_util::getBool(
843 "VLANEnable", input, inputVlanEnabled,
844 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
845 asyncResp->res.json_value, std::string("/VLAN/VLANEnable"));
846 json_util::Result inputVlanIdState = json_util::getUnsigned(
847 "VLANId", input, inputVlanId,
848 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
849 asyncResp->res.json_value, std::string("/VLAN/VLANId"));
850 bool inputInvalid = false;
851
852 // Do not proceed if fields in VLAN object were of wrong type
853 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
854 inputVlanIdState == json_util::Result::WRONG_TYPE) {
855 return;
856 }
857
858 // Verify input
859 if (eth_data.vlan_id == nullptr) {
860 // VLAN is currently disabled. User can only create/enable it. Change of
861 // VLANId is prohibited, and disable request (VLANEnabled == false) will
862 // not have any effect.
863 if (inputVlanEnabledState == json_util::Result::SUCCESS &&
864 inputVlanEnabled == true) {
865 // Creation requested, user should also provide ID for new VLAN
866 if (inputVlanIdState != json_util::Result::SUCCESS) {
867 messages::addMessageToJson(asyncResp->res.json_value,
868 messages::propertyMissing("VLANId"),
869 "/VLAN");
870 inputInvalid = true;
871 }
872 } else if (inputVlanIdState == json_util::Result::SUCCESS) {
873 // VLAN is disabled, but user requested modification. This is not valid.
874 messages::addMessageToJson(
875 asyncResp->res.json_value,
876 messages::actionParameterNotSupported("VLANId", "change VLAN Id"),
877 "/VLAN");
878
879 messages::addMessageToJson(asyncResp->res.json_value,
880 messages::propertyMissing("VLANEnable"),
881 "/VLAN");
882
883 inputInvalid = true;
884 }
885 } else {
886 // Load actual data into field values if they were not provided
887 if (inputVlanEnabledState == json_util::Result::NOT_EXIST) {
888 inputVlanEnabled = true;
889 }
890
891 if (inputVlanIdState == json_util::Result::NOT_EXIST) {
892 inputVlanId = *eth_data.vlan_id;
893 }
894 }
895
896 // Do not proceed if input has not been valid
897 if (inputInvalid) {
898 return;
899 }
900
901 auto vlanEnabledAfterOperation =
902 [asyncResp](const boost::system::error_code ec) {
903 if (ec) {
904 messages::addMessageToJson(asyncResp->res.json_value,
905 messages::internalError(), "/VLAN");
906 } else {
907 asyncResp->res.json_value["VLAN"]["VLANEnable"] = true;
908 }
909 };
910
911 if (eth_data.vlan_id == nullptr) {
912 if (inputVlanEnabled == true) {
913 ethernet_provider.createVlan(ifaceId, inputVlanId,
914 std::move(vlanEnabledAfterOperation));
915 asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
916 }
917 } else {
918 // VLAN is configured on the interface
919 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlan_id) {
920 // Change VLAN Id
921 asyncResp->res.json_value["VLAN"]["VLANId"] = inputVlanId;
922 ethernet_provider.changeVlanId(ifaceId,
923 static_cast<uint32_t>(inputVlanId),
924 std::move(vlanEnabledAfterOperation));
925 } else if (inputVlanEnabled == false) {
926 // Disable VLAN
927 ethernet_provider.disableVlan(
928 ifaceId, [asyncResp](const boost::system::error_code ec) {
929 if (ec) {
930 messages::addMessageToJson(asyncResp->res.json_value,
931 messages::internalError(), "/VLAN");
932 } else {
933 asyncResp->res.json_value["VLAN"]["VLANEnable"] = false;
934 }
935 });
936 }
937 }
938 }
939
940 void handleHostnamePatch(const nlohmann::json &input,
941 const EthernetInterfaceData &eth_data,
942 const std::shared_ptr<AsyncResp> &asyncResp) {
943 if (input.is_string()) {
944 std::string newHostname = input.get<std::string>();
945
946 if (eth_data.hostname == nullptr || newHostname != *eth_data.hostname) {
947 // Change hostname
948 ethernet_provider.setHostName(
949 newHostname,
950 [asyncResp, newHostname](const boost::system::error_code ec) {
951 if (ec) {
952 messages::addMessageToJson(asyncResp->res.json_value,
953 messages::internalError(),
954 "/HostName");
955 } else {
956 asyncResp->res.json_value["HostName"] = newHostname;
957 }
958 });
959 }
960 } else {
961 messages::addMessageToJson(
962 asyncResp->res.json_value,
963 messages::propertyValueTypeError(input.dump(), "HostName"),
964 "/HostName");
965 }
966 }
967
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200968 void handleIPv4Patch(const std::string &ifaceId, const nlohmann::json &input,
969 const std::vector<IPv4AddressData> &ipv4_data,
970 const std::shared_ptr<AsyncResp> &asyncResp) {
971 if (!input.is_array()) {
972 messages::addMessageToJson(
973 asyncResp->res.json_value,
974 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
975 "/IPv4Addresses");
976 return;
977 }
978
979 // According to Redfish PATCH definition, size must be at least equal
980 if (input.size() < ipv4_data.size()) {
981 // TODO(kkowalsk) This should be a message indicating that not enough
982 // data has been provided
983 messages::addMessageToJson(asyncResp->res.json_value,
984 messages::internalError(), "/IPv4Addresses");
985 return;
986 }
987
988 json_util::Result addressFieldState;
989 json_util::Result subnetMaskFieldState;
990 json_util::Result addressOriginFieldState;
991 json_util::Result gatewayFieldState;
992 const std::string *addressFieldValue;
993 const std::string *subnetMaskFieldValue;
994 const std::string *addressOriginFieldValue = nullptr;
995 const std::string *gatewayFieldValue;
996 uint8_t subnetMaskAsPrefixLength;
997 std::string addressOriginInDBusFormat;
998
999 bool errorDetected = false;
1000 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++) {
1001 // Check that entry is not of some unexpected type
1002 if (!input[entryIdx].is_object() && !input[entryIdx].is_null()) {
1003 // Invalid object type
1004 messages::addMessageToJson(
1005 asyncResp->res.json_value,
1006 messages::propertyValueTypeError(input[entryIdx].dump(),
1007 "IPv4Address"),
1008 "/IPv4Addresses/" + std::to_string(entryIdx));
1009
1010 continue;
1011 }
1012
1013 // Try to load fields
1014 addressFieldState = json_util::getString(
1015 "Address", input[entryIdx], addressFieldValue,
1016 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1017 asyncResp->res.json_value,
1018 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1019 subnetMaskFieldState = json_util::getString(
1020 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1021 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1022 asyncResp->res.json_value,
1023 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1024 addressOriginFieldState = json_util::getString(
1025 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1026 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1027 asyncResp->res.json_value,
1028 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1029 gatewayFieldState = json_util::getString(
1030 "Gateway", input[entryIdx], gatewayFieldValue,
1031 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1032 asyncResp->res.json_value,
1033 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1034
1035 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1036 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1037 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1038 gatewayFieldState == json_util::Result::WRONG_TYPE) {
1039 return;
1040 }
1041
1042 if (addressFieldState == json_util::Result::SUCCESS &&
1043 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*addressFieldValue)) {
1044 errorDetected = true;
1045 messages::addMessageToJson(
1046 asyncResp->res.json_value,
1047 messages::propertyValueFormatError(*addressFieldValue, "Address"),
1048 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1049 }
1050
1051 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1052 !ethernet_provider.ipv4VerifyIpAndGetBitcount(
1053 *subnetMaskFieldValue, &subnetMaskAsPrefixLength)) {
1054 errorDetected = true;
1055 messages::addMessageToJson(
1056 asyncResp->res.json_value,
1057 messages::propertyValueFormatError(*subnetMaskFieldValue,
1058 "SubnetMask"),
1059 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1060 }
1061
1062 // Get Address origin in proper format
1063 addressOriginInDBusFormat =
1064 ethernet_provider.translateAddressOriginBetweenDBusAndRedfish(
1065 addressOriginFieldValue, true, false);
1066
1067 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1068 addressOriginInDBusFormat.empty()) {
1069 errorDetected = true;
1070 messages::addMessageToJson(
1071 asyncResp->res.json_value,
1072 messages::propertyValueNotInList(*addressOriginFieldValue,
1073 "AddressOrigin"),
1074 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1075 }
1076
1077 if (gatewayFieldState == json_util::Result::SUCCESS &&
1078 !ethernet_provider.ipv4VerifyIpAndGetBitcount(*gatewayFieldValue)) {
1079 errorDetected = true;
1080 messages::addMessageToJson(
1081 asyncResp->res.json_value,
1082 messages::propertyValueFormatError(*gatewayFieldValue, "Gateway"),
1083 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1084 }
1085
1086 // If any error occured do not proceed with current entry, but do not
1087 // end loop
1088 if (errorDetected) {
1089 errorDetected = false;
1090 continue;
1091 }
1092
1093 if (entryIdx >= ipv4_data.size()) {
1094 asyncResp->res.json_value["IPv4Addresses"][entryIdx] = input[entryIdx];
1095
1096 // Verify that all field were provided
1097 if (addressFieldState == json_util::Result::NOT_EXIST) {
1098 errorDetected = true;
1099 messages::addMessageToJson(
1100 asyncResp->res.json_value, messages::propertyMissing("Address"),
1101 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1102 }
1103
1104 if (subnetMaskFieldState == json_util::Result::NOT_EXIST) {
1105 errorDetected = true;
1106 messages::addMessageToJson(
1107 asyncResp->res.json_value,
1108 messages::propertyMissing("SubnetMask"),
1109 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1110 }
1111
1112 if (addressOriginFieldState == json_util::Result::NOT_EXIST) {
1113 errorDetected = true;
1114 messages::addMessageToJson(
1115 asyncResp->res.json_value,
1116 messages::propertyMissing("AddressOrigin"),
1117 "/IPv4Addresses/" + std::to_string(entryIdx) + "/AddressOrigin");
1118 }
1119
1120 if (gatewayFieldState == json_util::Result::NOT_EXIST) {
1121 errorDetected = true;
1122 messages::addMessageToJson(
1123 asyncResp->res.json_value, messages::propertyMissing("Gateway"),
1124 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1125 }
1126
1127 // If any error occured do not proceed with current entry, but do not
1128 // end loop
1129 if (errorDetected) {
1130 errorDetected = false;
1131 continue;
1132 }
1133
1134 // Create IPv4 with provided data
1135 ethernet_provider.createIPv4(
1136 ifaceId, entryIdx, subnetMaskAsPrefixLength, *gatewayFieldValue,
1137 *addressFieldValue, asyncResp);
1138 } else {
1139 // Existing object that should be modified/deleted/remain unchanged
1140 if (input[entryIdx].is_null()) {
1141 // Object should be deleted
1142 ethernet_provider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1143 entryIdx, asyncResp);
1144 } else if (input[entryIdx].is_object()) {
1145 if (input[entryIdx].size() == 0) {
1146 // Object shall remain unchanged
1147 continue;
1148 }
1149
1150 // Apply changes
1151 if (addressFieldState == json_util::Result::SUCCESS &&
1152 ipv4_data[entryIdx].address != nullptr &&
1153 *ipv4_data[entryIdx].address != *addressFieldValue) {
1154 ethernet_provider.changeIPv4AddressProperty(
1155 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Address",
1156 *addressFieldValue, asyncResp);
1157 }
1158
1159 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1160 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue) {
1161 ethernet_provider.changeIPv4SubnetMaskProperty(
1162 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1163 *subnetMaskFieldValue, subnetMaskAsPrefixLength, asyncResp);
1164 }
1165
1166 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1167 ipv4_data[entryIdx].origin != *addressFieldValue) {
1168 ethernet_provider.changeIPv4Origin(
1169 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1170 *addressOriginFieldValue, addressOriginInDBusFormat, asyncResp);
1171 }
1172
1173 if (gatewayFieldState == json_util::Result::SUCCESS &&
1174 ipv4_data[entryIdx].gateway != nullptr &&
1175 *ipv4_data[entryIdx].gateway != *gatewayFieldValue) {
1176 ethernet_provider.changeIPv4AddressProperty(
1177 ifaceId, entryIdx, ipv4_data[entryIdx].id, "Gateway",
1178 *gatewayFieldValue, asyncResp);
1179 }
1180 }
1181 }
1182 }
1183 }
1184
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001185 nlohmann::json parseInterfaceData(
1186 const std::string &iface_id, const EthernetInterfaceData &eth_data,
1187 const std::vector<IPv4AddressData> &ipv4_data) {
1188 // Copy JSON object to avoid race condition
1189 nlohmann::json json_response(Node::json);
1190
1191 // Fill out obvious data...
1192 json_response["Id"] = iface_id;
1193 json_response["@odata.id"] =
1194 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + iface_id;
1195
1196 // ... then the one from DBus, regarding eth iface...
1197 if (eth_data.speed != nullptr) json_response["SpeedMbps"] = *eth_data.speed;
1198
1199 if (eth_data.mac_address != nullptr)
1200 json_response["MACAddress"] = *eth_data.mac_address;
1201
1202 if (eth_data.hostname != nullptr)
1203 json_response["HostName"] = *eth_data.hostname;
1204
1205 if (eth_data.vlan_id != nullptr) {
1206 nlohmann::json &vlanObj = json_response["VLAN"];
1207 vlanObj["VLANEnable"] = true;
1208 vlanObj["VLANId"] = *eth_data.vlan_id;
1209 }
1210
1211 // ... at last, check if there are IPv4 data and prepare appropriate
1212 // collection
1213 if (ipv4_data.size() > 0) {
1214 nlohmann::json ipv4_array = nlohmann::json::array();
1215 for (auto &ipv4_config : ipv4_data) {
1216 nlohmann::json json_ipv4;
1217 if (ipv4_config.address != nullptr) {
1218 json_ipv4["Address"] = *ipv4_config.address;
1219 if (ipv4_config.gateway != nullptr)
1220 json_ipv4["Gateway"] = *ipv4_config.gateway;
1221
1222 json_ipv4["AddressOrigin"] = ipv4_config.origin;
1223 json_ipv4["SubnetMask"] = ipv4_config.netmask;
1224
1225 ipv4_array.push_back(std::move(json_ipv4));
1226 }
1227 }
1228 json_response["IPv4Addresses"] = std::move(ipv4_array);
1229 }
1230
1231 return json_response;
1232 }
1233
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001234 /**
1235 * Functions triggers appropriate requests on DBus
1236 */
1237 void doGet(crow::response &res, const crow::request &req,
1238 const std::vector<std::string> &params) override {
1239 // TODO(Pawel) this shall be parametrized call (two params) to get
1240 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1241 // Check if there is required param, truly entering this shall be
1242 // impossible.
1243 if (params.size() != 1) {
Ed Tanouse0d918b2018-03-27 17:41:04 -07001244 res.result(boost::beast::http::status::internal_server_error);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001245 res.end();
1246 return;
1247 }
1248
1249 const std::string &iface_id = params[0];
1250
1251 // Get single eth interface data, and call the below callback for JSON
1252 // preparation
1253 ethernet_provider.getEthernetIfaceData(
1254 iface_id, [&, iface_id](const bool &success,
1255 const EthernetInterfaceData &eth_data,
1256 const std::vector<IPv4AddressData> &ipv4_data) {
1257 if (success) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001258 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001259 } else {
1260 // ... otherwise return error
1261 // TODO(Pawel)consider distinguish between non existing object, and
1262 // other errors
Ed Tanouse0d918b2018-03-27 17:41:04 -07001263 res.result(boost::beast::http::status::not_found);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001264 }
1265 res.end();
1266 });
1267 }
1268
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001269 void doPatch(crow::response &res, const crow::request &req,
1270 const std::vector<std::string> &params) override {
1271 // TODO(Pawel) this shall be parametrized call (two params) to get
1272 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1273 // Check if there is required param, truly entering this shall be
1274 // impossible.
1275 if (params.size() != 1) {
1276 res.result(boost::beast::http::status::internal_server_error);
1277 res.end();
1278 return;
1279 }
1280
1281 const std::string &iface_id = params[0];
1282
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001283 nlohmann::json patchReq;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001284
Kowalski, Kamil179db1d2018-04-23 11:12:41 +02001285 if (!json_util::processJsonFromRequest(res, req, patchReq)) {
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001286 return;
1287 }
1288
1289 // Get single eth interface data, and call the below callback for JSON
1290 // preparation
1291 ethernet_provider.getEthernetIfaceData(
1292 iface_id,
1293 [&, iface_id, patchReq = std::move(patchReq) ](
1294 const bool &success, const EthernetInterfaceData &eth_data,
1295 const std::vector<IPv4AddressData> &ipv4_data) {
1296 if (!success) {
1297 // ... otherwise return error
1298 // TODO(Pawel)consider distinguish between non existing object, and
1299 // other errors
1300 res.result(boost::beast::http::status::not_found);
1301 res.end();
1302
1303 return;
1304 }
1305
1306 res.json_value = parseInterfaceData(iface_id, eth_data, ipv4_data);
1307
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") {
1314 handleVlanPatch(iface_id, propertyIt.value(), eth_data,
1315 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") {
1319 handleIPv4Patch(iface_id, propertyIt.value(), ipv4_data,
1320 asyncResp);
1321 } else if (propertyIt.key() == "IPv6Addresses") {
1322 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1323 messages::addMessageToJsonRoot(
1324 res.json_value,
1325 messages::propertyNotWritable(propertyIt.key()));
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001326 } else {
1327 auto fieldInJsonIt = res.json_value.find(propertyIt.key());
1328
1329 if (fieldInJsonIt == res.json_value.end()) {
1330 // Field not in scope of defined fields
1331 messages::addMessageToJsonRoot(
1332 res.json_value,
1333 messages::propertyUnknown(propertyIt.key()));
1334 } else if (*fieldInJsonIt != *propertyIt) {
1335 // User attempted to modify non-writable field
1336 messages::addMessageToJsonRoot(
1337 res.json_value,
1338 messages::propertyNotWritable(propertyIt.key()));
1339 }
1340 }
1341 }
1342 });
1343 }
1344
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001345 // Ethernet Provider object
1346 // TODO(Pawel) consider move it to singleton
1347 OnDemandEthernetProvider ethernet_provider;
1348};
1349
1350} // namespace redfish