blob: 0c642798b5b627d7a91f868578fce3a3c6f15cba [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
Ed Tanous1abe55e2018-09-05 08:30:59 -070018#include <boost/container/flat_map.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020019#include <dbus_singleton.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020020#include <error_messages.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020021#include <node.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020022#include <utils/json_utils.hpp>
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010023
Ed Tanous1abe55e2018-09-05 08:30:59 -070024namespace redfish
25{
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010026
27/**
28 * DBus types primitives for several generic DBus interfaces
29 * TODO(Pawel) consider move this to separate file into boost::dbus
30 */
Ed Tanousaa2e59c2018-04-12 12:17:20 -070031using PropertiesMapType = boost::container::flat_map<
32 std::string,
33 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
34 int32_t, uint32_t, int64_t, uint64_t, double>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010035
36using GetManagedObjectsType = boost::container::flat_map<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070037 sdbusplus::message::object_path,
38 boost::container::flat_map<
39 std::string,
40 boost::container::flat_map<
41 std::string, sdbusplus::message::variant<
42 std::string, bool, uint8_t, int16_t, uint16_t,
43 int32_t, uint32_t, int64_t, uint64_t, double>>>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010044
45/**
46 * Structure for keeping IPv4 data required by Redfish
47 * TODO(Pawel) consider change everything to ptr, or to non-ptr values.
48 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070049struct IPv4AddressData
50{
51 std::string id;
52 const std::string *address;
53 const std::string *domain;
54 const std::string *gateway;
55 std::string netmask;
56 std::string origin;
57 bool global;
58 /**
59 * @brief Operator< to enable sorting
60 *
61 * @param[in] obj Object to compare with
62 *
63 * @return This object id < supplied object id
64 */
65 bool operator<(const IPv4AddressData &obj) const
66 {
67 return (id < obj.id);
68 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010069};
70
71/**
72 * Structure for keeping basic single Ethernet Interface information
73 * available from DBus
74 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070075struct EthernetInterfaceData
76{
77 const unsigned int *speed;
78 const bool *autoNeg;
79 const std::string *hostname;
80 const std::string *defaultGateway;
81 const std::string *macAddress;
82 const unsigned int *vlanId;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010083};
84
85/**
86 * OnDemandEthernetProvider
Gunnar Mills274fad52018-06-13 15:45:36 -050087 * Ethernet provider class that retrieves data directly from dbus, before
88 * setting it into JSON output. This does not cache any data.
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010089 *
90 * TODO(Pawel)
91 * This perhaps shall be different file, which has to be chosen on compile time
92 * depending on OEM needs
93 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070094class OnDemandEthernetProvider
95{
96 private:
97 // Consts that may have influence on EthernetProvider performance/memory
98 // usage
99 const size_t maxIpV4AddressesPerInterface = 10;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100100
Ed Tanous1abe55e2018-09-05 08:30:59 -0700101 // Helper function that allows to extract GetAllPropertiesType from
102 // GetManagedObjectsType, based on object path, and interface name
103 const PropertiesMapType *extractInterfaceProperties(
104 const sdbusplus::message::object_path &objpath,
105 const std::string &interface, const GetManagedObjectsType &dbus_data)
106 {
107 const auto &dbusObj = dbus_data.find(objpath);
108 if (dbusObj != dbus_data.end())
109 {
110 const auto &iface = dbusObj->second.find(interface);
111 if (iface != dbusObj->second.end())
112 {
113 return &iface->second;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100114 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100115 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700116 return nullptr;
117 }
118
119 // Helper Wrapper that does inline object_path conversion from string
120 // into sdbusplus::message::object_path type
121 inline const PropertiesMapType *
122 extractInterfaceProperties(const std::string &objpath,
123 const std::string &interface,
124 const GetManagedObjectsType &dbus_data)
125 {
126 const auto &dbusObj = sdbusplus::message::object_path{objpath};
127 return extractInterfaceProperties(dbusObj, interface, dbus_data);
128 }
129
130 // Helper function that allows to get pointer to the property from
131 // GetAllPropertiesType native, or extracted by GetAllPropertiesType
132 template <typename T>
133 inline T const *const extractProperty(const PropertiesMapType &properties,
134 const std::string &name)
135 {
136 const auto &property = properties.find(name);
137 if (property != properties.end())
138 {
139 return mapbox::getPtr<const T>(property->second);
140 }
141 return nullptr;
142 }
143 // TODO(Pawel) Consider to move the above functions to dbus
144 // generic_interfaces.hpp
145
146 // Helper function that extracts data from several dbus objects and several
147 // interfaces required by single ethernet interface instance
148 void extractEthernetInterfaceData(const std::string &ethifaceId,
149 const GetManagedObjectsType &dbus_data,
150 EthernetInterfaceData &eth_data)
151 {
152 // Extract data that contains MAC Address
153 const PropertiesMapType *macProperties = extractInterfaceProperties(
154 "/xyz/openbmc_project/network/" + ethifaceId,
155 "xyz.openbmc_project.Network.MACAddress", dbus_data);
156
157 if (macProperties != nullptr)
158 {
159 eth_data.macAddress =
160 extractProperty<std::string>(*macProperties, "MACAddress");
161 }
162
163 const PropertiesMapType *vlanProperties = extractInterfaceProperties(
164 "/xyz/openbmc_project/network/" + ethifaceId,
165 "xyz.openbmc_project.Network.VLAN", dbus_data);
166
167 if (vlanProperties != nullptr)
168 {
169 eth_data.vlanId =
170 extractProperty<unsigned int>(*vlanProperties, "Id");
171 }
172
173 // Extract data that contains link information (auto negotiation and
174 // speed)
175 const PropertiesMapType *ethProperties = extractInterfaceProperties(
176 "/xyz/openbmc_project/network/" + ethifaceId,
177 "xyz.openbmc_project.Network.EthernetInterface", dbus_data);
178
179 if (ethProperties != nullptr)
180 {
181 eth_data.autoNeg = extractProperty<bool>(*ethProperties, "AutoNeg");
182 eth_data.speed =
183 extractProperty<unsigned int>(*ethProperties, "Speed");
184 }
185
186 // Extract data that contains network config (HostName and DefaultGW)
187 const PropertiesMapType *configProperties = extractInterfaceProperties(
188 "/xyz/openbmc_project/network/config",
189 "xyz.openbmc_project.Network.SystemConfiguration", dbus_data);
190
191 if (configProperties != nullptr)
192 {
193 eth_data.hostname =
194 extractProperty<std::string>(*configProperties, "HostName");
195 eth_data.defaultGateway = extractProperty<std::string>(
196 *configProperties, "DefaultGateway");
197 }
198 }
199
200 // Helper function that changes bits netmask notation (i.e. /24)
201 // into full dot notation
202 inline std::string getNetmask(unsigned int bits)
203 {
204 uint32_t value = 0xffffffff << (32 - bits);
205 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
206 std::to_string((value >> 16) & 0xff) + "." +
207 std::to_string((value >> 8) & 0xff) + "." +
208 std::to_string(value & 0xff);
209 return netmask;
210 }
211
212 // Helper function that extracts data for single ethernet ipv4 address
213 void extractIPv4Data(const std::string &ethifaceId,
214 const GetManagedObjectsType &dbus_data,
215 std::vector<IPv4AddressData> &ipv4_config)
216 {
217 const std::string pathStart =
218 "/xyz/openbmc_project/network/" + ethifaceId + "/ipv4/";
219
220 // Since there might be several IPv4 configurations aligned with
221 // single ethernet interface, loop over all of them
222 for (auto &objpath : dbus_data)
223 {
224 // Check if proper patter for object path appears
225 if (boost::starts_with(
226 static_cast<const std::string &>(objpath.first), pathStart))
227 {
228 // and get approrpiate interface
229 const auto &interface =
230 objpath.second.find("xyz.openbmc_project.Network.IP");
231 if (interface != objpath.second.end())
232 {
233 // Make a properties 'shortcut', to make everything more
234 // readable
235 const PropertiesMapType &properties = interface->second;
236 // Instance IPv4AddressData structure, and set as
237 // appropriate
238 IPv4AddressData ipv4Address;
239
240 ipv4Address.id =
241 static_cast<const std::string &>(objpath.first)
242 .substr(pathStart.size());
243
244 // IPv4 address
245 ipv4Address.address =
246 extractProperty<std::string>(properties, "Address");
247 // IPv4 gateway
248 ipv4Address.gateway =
249 extractProperty<std::string>(properties, "Gateway");
250
251 // Origin is kind of DBus object so fetch pointer...
252 const std::string *origin =
253 extractProperty<std::string>(properties, "Origin");
254 if (origin != nullptr)
255 {
256 ipv4Address.origin =
257 translateAddressOriginBetweenDBusAndRedfish(
258 origin, true, true);
259 }
260
261 // Netmask is presented as PrefixLength
262 const auto *mask =
263 extractProperty<uint8_t>(properties, "PrefixLength");
264 if (mask != nullptr)
265 {
266 // convert it to the string
267 ipv4Address.netmask = getNetmask(*mask);
268 }
269
270 // Attach IPv4 only if address is present
271 if (ipv4Address.address != nullptr)
272 {
273 // Check if given address is local, or global
274 if (boost::starts_with(*ipv4Address.address, "169.254"))
275 {
276 ipv4Address.global = false;
277 }
278 else
279 {
280 ipv4Address.global = true;
281 }
282 ipv4_config.emplace_back(std::move(ipv4Address));
283 }
284 }
285 }
286 }
287
288 /**
289 * We have to sort this vector and ensure that order of IPv4 addresses
290 * is consistent between GETs to allow modification and deletion in
291 * PATCHes
292 */
293 std::sort(ipv4_config.begin(), ipv4_config.end());
294 }
295
296 static const constexpr int ipV4AddressSectionsCount = 4;
297
298 public:
299 /**
300 * @brief Creates VLAN for given interface with given Id through D-Bus
301 *
302 * @param[in] ifaceId Id of interface for which VLAN will be created
303 * @param[in] inputVlanId ID of the new VLAN
304 * @param[in] callback Function that will be called after the operation
305 *
306 * @return None.
307 */
308 template <typename CallbackFunc>
309 void createVlan(const std::string &ifaceId, const uint64_t &inputVlanId,
310 CallbackFunc &&callback)
311 {
312 crow::connections::systemBus->async_method_call(
313 callback, "xyz.openbmc_project.Network",
314 "/xyz/openbmc_project/network",
315 "xyz.openbmc_project.Network.VLAN.Create", "VLAN", ifaceId,
316 static_cast<uint32_t>(inputVlanId));
317 };
318
319 /**
320 * @brief Sets given Id on the given VLAN interface through D-Bus
321 *
322 * @param[in] ifaceId Id of VLAN interface that should be modified
323 * @param[in] inputVlanId New ID of the VLAN
324 * @param[in] callback Function that will be called after the operation
325 *
326 * @return None.
327 */
328 template <typename CallbackFunc>
329 static void changeVlanId(const std::string &ifaceId,
330 const uint32_t &inputVlanId,
331 CallbackFunc &&callback)
332 {
333 crow::connections::systemBus->async_method_call(
334 callback, "xyz.openbmc_project.Network",
335 std::string("/xyz/openbmc_project/network/") + ifaceId,
336 "org.freedesktop.DBus.Properties", "Set",
337 "xyz.openbmc_project.Network.VLAN", "Id",
338 sdbusplus::message::variant<uint32_t>(inputVlanId));
339 };
340
341 /**
342 * @brief Helper function that verifies IP address to check if it is in
343 * proper format. If bits pointer is provided, also calculates active
344 * bit count for Subnet Mask.
345 *
346 * @param[in] ip IP that will be verified
347 * @param[out] bits Calculated mask in bits notation
348 *
349 * @return true in case of success, false otherwise
350 */
351 bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
352 uint8_t *bits = nullptr)
353 {
354 std::vector<std::string> bytesInMask;
355
356 boost::split(bytesInMask, ip, boost::is_any_of("."));
357
358 if (bytesInMask.size() != ipV4AddressSectionsCount)
359 {
360 return false;
361 }
362
363 if (bits != nullptr)
364 {
365 *bits = 0;
366 }
367
368 char *endPtr;
369 long previousValue = 255;
370 bool firstZeroInByteHit;
371 for (const std::string &byte : bytesInMask)
372 {
373 if (byte.empty())
374 {
375 return false;
376 }
377
378 // Use strtol instead of stroi to avoid exceptions
379 long value = std::strtol(byte.c_str(), &endPtr, 10);
380
381 // endPtr should point to the end of the string, otherwise given
382 // string is not 100% number
383 if (*endPtr != '\0')
384 {
385 return false;
386 }
387
388 // Value should be contained in byte
389 if (value < 0 || value > 255)
390 {
391 return false;
392 }
393
394 if (bits != nullptr)
395 {
396 // Mask has to be continuous between bytes
397 if (previousValue != 255 && value != 0)
398 {
399 return false;
400 }
401
402 // Mask has to be continuous inside bytes
403 firstZeroInByteHit = false;
404
405 // Count bits
406 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
407 {
408 if (value & (1 << bitIdx))
409 {
410 if (firstZeroInByteHit)
411 {
412 // Continuity not preserved
413 return false;
414 }
415 else
416 {
417 (*bits)++;
418 }
419 }
420 else
421 {
422 firstZeroInByteHit = true;
423 }
424 }
425 }
426
427 previousValue = value;
428 }
429
430 return true;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100431 }
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200432
433 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 * @brief Changes IPv4 address type property (Address, Gateway)
435 *
436 * @param[in] ifaceId Id of interface whose IP should be modified
437 * @param[in] ipIdx index of IP in input array that should be modified
438 * @param[in] ipHash DBus Hash id of modified IP
439 * @param[in] name Name of field in JSON representation
440 * @param[in] newValue New value that should be written
441 * @param[io] asyncResp Response object that will be returned to client
442 *
443 * @return true if give IP is valid and has been sent do D-Bus, false
444 * otherwise
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200445 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
447 const std::string &ipHash,
448 const std::string &name,
449 const std::string &newValue,
450 const std::shared_ptr<AsyncResp> &asyncResp)
451 {
452 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
453 name{std::move(name)}, newValue{std::move(newValue)}](
454 const boost::system::error_code ec) {
455 if (ec)
456 {
457 messages::addMessageToJson(
458 asyncResp->res.jsonValue, messages::internalError(),
459 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200460 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700461 else
462 {
463 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] =
464 newValue;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200465 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200467
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 crow::connections::systemBus->async_method_call(
469 std::move(callback), "xyz.openbmc_project.Network",
470 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
471 "org.freedesktop.DBus.Properties", "Set",
472 "xyz.openbmc_project.Network.IP", name,
473 sdbusplus::message::variant<std::string>(newValue));
474 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200475
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 /**
477 * @brief Changes IPv4 address origin property
478 *
479 * @param[in] ifaceId Id of interface whose IP should be modified
480 * @param[in] ipIdx index of IP in input array that should be
481 * modified
482 * @param[in] ipHash DBus Hash id of modified IP
483 * @param[in] newValue New value in Redfish format
484 * @param[in] newValueDbus New value in D-Bus format
485 * @param[io] asyncResp Response object that will be returned to client
486 *
487 * @return true if give IP is valid and has been sent do D-Bus, false
488 * otherwise
489 */
490 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
491 const std::string &ipHash,
492 const std::string &newValue,
493 const std::string &newValueDbus,
494 const std::shared_ptr<AsyncResp> &asyncResp)
495 {
496 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
497 newValue{std::move(newValue)}](
498 const boost::system::error_code ec) {
499 if (ec)
500 {
501 messages::addMessageToJson(
502 asyncResp->res.jsonValue, messages::internalError(),
503 "/IPv4Addresses/" + std::to_string(ipIdx) +
504 "/AddressOrigin");
505 }
506 else
507 {
508 asyncResp->res
509 .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
510 newValue;
511 }
512 };
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100513
Ed Tanous1abe55e2018-09-05 08:30:59 -0700514 crow::connections::systemBus->async_method_call(
515 std::move(callback), "xyz.openbmc_project.Network",
516 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
517 "org.freedesktop.DBus.Properties", "Set",
518 "xyz.openbmc_project.Network.IP", "Origin",
519 sdbusplus::message::variant<std::string>(newValueDbus));
520 };
521
522 /**
523 * @brief Modifies SubnetMask for given IP
524 *
525 * @param[in] ifaceId Id of interface whose IP should be modified
526 * @param[in] ipIdx index of IP in input array that should be
527 * modified
528 * @param[in] ipHash DBus Hash id of modified IP
529 * @param[in] newValueStr Mask in dot notation as string
530 * @param[in] newValue Mask as PrefixLength in bitcount
531 * @param[io] asyncResp Response object that will be returned to client
532 *
533 * @return None
534 */
535 void changeIPv4SubnetMaskProperty(
536 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
537 const std::string &newValueStr, uint8_t &newValue,
538 const std::shared_ptr<AsyncResp> &asyncResp)
539 {
540 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
541 newValueStr{std::move(newValueStr)}](
542 const boost::system::error_code ec) {
543 if (ec)
544 {
545 messages::addMessageToJson(
546 asyncResp->res.jsonValue, messages::internalError(),
547 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
548 }
549 else
550 {
551 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
552 newValueStr;
553 }
554 };
555
556 crow::connections::systemBus->async_method_call(
557 std::move(callback), "xyz.openbmc_project.Network",
558 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
559 "org.freedesktop.DBus.Properties", "Set",
560 "xyz.openbmc_project.Network.IP", "PrefixLength",
561 sdbusplus::message::variant<uint8_t>(newValue));
562 };
563
564 /**
565 * @brief Disables VLAN with given ifaceId
566 *
567 * @param[in] ifaceId Id of VLAN interface that should be disabled
568 * @param[in] callback Function that will be called after the operation
569 *
570 * @return None.
571 */
572 template <typename CallbackFunc>
573 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback)
574 {
575 crow::connections::systemBus->async_method_call(
576 callback, "xyz.openbmc_project.Network",
577 std::string("/xyz/openbmc_project/network/") + ifaceId,
578 "xyz.openbmc_project.Object.Delete", "Delete");
579 };
580
581 /**
582 * @brief Sets given HostName of the machine through D-Bus
583 *
584 * @param[in] newHostname New name that HostName will be changed to
585 * @param[in] callback Function that will be called after the operation
586 *
587 * @return None.
588 */
589 template <typename CallbackFunc>
590 void setHostName(const std::string &newHostname, CallbackFunc &&callback)
591 {
592 crow::connections::systemBus->async_method_call(
593 callback, "xyz.openbmc_project.Network",
594 "/xyz/openbmc_project/network/config",
595 "org.freedesktop.DBus.Properties", "Set",
596 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
597 sdbusplus::message::variant<std::string>(newHostname));
598 };
599
600 /**
601 * @brief Deletes given IPv4
602 *
603 * @param[in] ifaceId Id of interface whose IP should be deleted
604 * @param[in] ipIdx index of IP in input array that should be deleted
605 * @param[in] ipHash DBus Hash id of IP that should be deleted
606 * @param[io] asyncResp Response object that will be returned to client
607 *
608 * @return None
609 */
610 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
611 unsigned int ipIdx,
612 const std::shared_ptr<AsyncResp> &asyncResp)
613 {
614 crow::connections::systemBus->async_method_call(
615 [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}](
616 const boost::system::error_code ec) {
617 if (ec)
618 {
619 messages::addMessageToJson(
620 asyncResp->res.jsonValue, messages::internalError(),
621 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100622 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 else
624 {
625 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
626 }
627 },
628 "xyz.openbmc_project.Network",
629 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
630 "xyz.openbmc_project.Object.Delete", "Delete");
631 }
632
633 /**
634 * @brief Creates IPv4 with given data
635 *
636 * @param[in] ifaceId Id of interface whose IP should be deleted
637 * @param[in] ipIdx index of IP in input array that should be deleted
638 * @param[in] ipHash DBus Hash id of IP that should be deleted
639 * @param[io] asyncResp Response object that will be returned to client
640 *
641 * @return None
642 */
643 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
644 uint8_t subnetMask, const std::string &gateway,
645 const std::string &address,
646 const std::shared_ptr<AsyncResp> &asyncResp)
647 {
648 auto createIpHandler = [ipIdx{std::move(ipIdx)},
649 asyncResp{std::move(asyncResp)}](
650 const boost::system::error_code ec) {
651 if (ec)
652 {
653 messages::addMessageToJson(
654 asyncResp->res.jsonValue, messages::internalError(),
655 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100656 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700657 };
658
659 crow::connections::systemBus->async_method_call(
660 std::move(createIpHandler), "xyz.openbmc_project.Network",
661 "/xyz/openbmc_project/network/" + ifaceId,
662 "xyz.openbmc_project.Network.IP.Create", "IP",
663 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
664 gateway);
665 }
666
667 /**
668 * @brief Translates Address Origin value from D-Bus to Redfish format and
669 * vice-versa
670 *
671 * @param[in] inputOrigin Input value that should be translated
672 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
673 * @param[in] isFromDBus True for DBus->Redfish conversion, false for
674 * reverse
675 *
676 * @return Empty string in case of failure, translated value otherwise
677 */
678 std::string translateAddressOriginBetweenDBusAndRedfish(
679 const std::string *inputOrigin, bool isIPv4, bool isFromDBus)
680 {
681 // Invalid pointer
682 if (inputOrigin == nullptr)
683 {
684 return "";
685 }
686
687 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
688 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
689
690 std::array<std::pair<const char *, const char *>, 6> translationTable{
691 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
692 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
693 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
694 "IPv4LinkLocal"},
695 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
696 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
697 "LinkLocal"},
698 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
699
700 for (unsigned int i = 0; i < translationTable.size(); i++)
701 {
702 // Skip unrelated
703 if (isIPv4 && i >= firstIPv6OnlyIdx)
704 break;
705 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx)
706 continue;
707
708 // When translating D-Bus to Redfish compare input to first element
709 if (isFromDBus && translationTable[i].first == *inputOrigin)
710 return translationTable[i].second;
711
712 // When translating Redfish to D-Bus compare input to second element
713 if (!isFromDBus && translationTable[i].second == *inputOrigin)
714 return translationTable[i].first;
715 }
716
717 // If we are still here, that means that value has not been found
718 return "";
719 }
720
721 /**
722 * Function that retrieves all properties for given Ethernet Interface
723 * Object
724 * from EntityManager Network Manager
725 * @param ethifaceId a eth interface id to query on DBus
726 * @param callback a function that shall be called to convert Dbus output
727 * into JSON
728 */
729 template <typename CallbackFunc>
730 void getEthernetIfaceData(const std::string &ethifaceId,
731 CallbackFunc &&callback)
732 {
733 crow::connections::systemBus->async_method_call(
734 [this, ethifaceId{std::move(ethifaceId)},
735 callback{std::move(callback)}](
736 const boost::system::error_code error_code,
737 const GetManagedObjectsType &resp) {
738 EthernetInterfaceData ethData{};
739 std::vector<IPv4AddressData> ipv4Data;
740 ipv4Data.reserve(maxIpV4AddressesPerInterface);
741
742 if (error_code)
743 {
744 // Something wrong on DBus, the error_code is not important
745 // at this moment, just return success=false, and empty
746 // output. Since size of vector may vary depending on
747 // information from Network Manager, and empty output could
748 // not be treated same way as error.
749 callback(false, ethData, ipv4Data);
750 return;
751 }
752
753 // Find interface
754 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
755 resp.end())
756 {
757 // Interface has not been found
758 callback(false, ethData, ipv4Data);
759 return;
760 }
761
762 extractEthernetInterfaceData(ethifaceId, resp, ethData);
763 extractIPv4Data(ethifaceId, resp, ipv4Data);
764
765 // Fix global GW
766 for (IPv4AddressData &ipv4 : ipv4Data)
767 {
768 if ((ipv4.global) && ((ipv4.gateway == nullptr) ||
769 (*ipv4.gateway == "0.0.0.0")))
770 {
771 ipv4.gateway = ethData.defaultGateway;
772 }
773 }
774
775 // Finally make a callback with useful data
776 callback(true, ethData, ipv4Data);
777 },
778 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
779 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
780 };
781
782 /**
783 * Function that retrieves all Ethernet Interfaces available through Network
784 * Manager
785 * @param callback a function that shall be called to convert Dbus output
786 * into JSON.
787 */
788 template <typename CallbackFunc>
789 void getEthernetIfaceList(CallbackFunc &&callback)
790 {
791 crow::connections::systemBus->async_method_call(
792 [this, callback{std::move(callback)}](
793 const boost::system::error_code error_code,
794 GetManagedObjectsType &resp) {
795 // Callback requires vector<string> to retrieve all available
796 // ethernet interfaces
797 std::vector<std::string> ifaceList;
798 ifaceList.reserve(resp.size());
799 if (error_code)
800 {
801 // Something wrong on DBus, the error_code is not important
802 // at this moment, just return success=false, and empty
803 // output. Since size of vector may vary depending on
804 // information from Network Manager, and empty output could
805 // not be treated same way as error.
806 callback(false, ifaceList);
807 return;
808 }
809
810 // Iterate over all retrieved ObjectPaths.
811 for (auto &objpath : resp)
812 {
813 // And all interfaces available for certain ObjectPath.
814 for (auto &interface : objpath.second)
815 {
816 // If interface is
817 // xyz.openbmc_project.Network.EthernetInterface, this
818 // is what we're looking for.
819 if (interface.first ==
820 "xyz.openbmc_project.Network.EthernetInterface")
821 {
822 // Cut out everything until last "/", ...
823 const std::string &ifaceId =
824 static_cast<const std::string &>(objpath.first);
825 std::size_t lastPos = ifaceId.rfind("/");
826 if (lastPos != std::string::npos)
827 {
828 // and put it into output vector.
829 ifaceList.emplace_back(
830 ifaceId.substr(lastPos + 1));
831 }
832 }
833 }
834 }
835 // Finally make a callback with useful data
836 callback(true, ifaceList);
837 },
838 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
839 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
840 };
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100841};
842
843/**
844 * EthernetCollection derived class for delivering Ethernet Collection Schema
845 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846class EthernetCollection : public Node
847{
848 public:
849 // TODO(Pawel) Remove line from below, where we assume that there is only
850 // one manager called openbmc This shall be generic, but requires to update
851 // GetSubroutes method
852 EthernetCollection(CrowApp &app) :
853 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/")
854 {
855 Node::json["@odata.type"] =
856 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
857 Node::json["@odata.context"] =
858 "/redfish/v1/"
859 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700860 Node::json["@odata.id"] =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 "/redfish/v1/Managers/openbmc/EthernetInterfaces";
862 Node::json["Name"] = "Ethernet Network Interface Collection";
863 Node::json["Description"] =
864 "Collection of EthernetInterfaces for this Manager";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100865
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 entityPrivileges = {
867 {boost::beast::http::verb::get, {{"Login"}}},
868 {boost::beast::http::verb::head, {{"Login"}}},
869 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
870 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
871 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
872 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
873 }
874
875 private:
876 /**
877 * Functions triggers appropriate requests on DBus
878 */
879 void doGet(crow::Response &res, const crow::Request &req,
880 const std::vector<std::string> &params) override
881 {
882 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
883 // for any Manager, not only hardcoded 'openbmc'.
884 std::string managerId = "openbmc";
885
886 // get eth interface list, and call the below callback for JSON
887 // preparation
888 ethernetProvider.getEthernetIfaceList(
889 [&, managerId{std::move(managerId)}](
890 const bool &success,
891 const std::vector<std::string> &iface_list) {
892 if (success)
893 {
894 nlohmann::json ifaceArray = nlohmann::json::array();
895 for (const std::string &ifaceItem : iface_list)
896 {
897 ifaceArray.push_back(
898 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
899 "/EthernetInterfaces/" +
900 ifaceItem}});
901 }
902 Node::json["Members"] = ifaceArray;
903 Node::json["Members@odata.count"] = ifaceArray.size();
904 Node::json["@odata.id"] = "/redfish/v1/Managers/" +
905 managerId + "/EthernetInterfaces";
906 res.jsonValue = Node::json;
907 }
908 else
909 {
910 // No success, best what we can do is return INTERNALL ERROR
911 res.result(
912 boost::beast::http::status::internal_server_error);
913 }
914 res.end();
915 });
916 }
917
918 // Ethernet Provider object
919 // TODO(Pawel) consider move it to singleton
920 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100921};
922
923/**
924 * EthernetInterface derived class for delivering Ethernet Schema
925 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926class EthernetInterface : public Node
927{
928 public:
929 /*
930 * Default Constructor
931 */
932 // TODO(Pawel) Remove line from below, where we assume that there is only
933 // one manager called openbmc This shall be generic, but requires to update
934 // GetSubroutes method
935 EthernetInterface(CrowApp &app) :
936 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
937 std::string())
938 {
939 Node::json["@odata.type"] =
940 "#EthernetInterface.v1_2_0.EthernetInterface";
941 Node::json["@odata.context"] =
942 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
943 Node::json["Name"] = "Manager Ethernet Interface";
944 Node::json["Description"] = "Management Network Interface";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100945
Ed Tanous1abe55e2018-09-05 08:30:59 -0700946 entityPrivileges = {
947 {boost::beast::http::verb::get, {{"Login"}}},
948 {boost::beast::http::verb::head, {{"Login"}}},
949 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
950 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
951 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
952 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200953 }
954
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955 // TODO(kkowalsk) Find a suitable class/namespace for this
956 static void handleVlanPatch(const std::string &ifaceId,
957 const nlohmann::json &input,
958 const EthernetInterfaceData &eth_data,
959 const std::string &pathPrefix,
960 const std::shared_ptr<AsyncResp> &asyncResp)
961 {
962 if (!input.is_object())
963 {
964 messages::addMessageToJson(
965 asyncResp->res.jsonValue,
966 messages::propertyValueTypeError(input.dump(), "VLAN"),
967 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200968 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700969 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200970
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
972 nlohmann::json &paramsJson =
973 (pathPrefix == "/")
974 ? asyncResp->res.jsonValue
975 : asyncResp->res
976 .jsonValue[nlohmann::json_pointer<nlohmann::json>(
977 pathPrefix)];
978 bool inputVlanEnabled;
979 uint64_t inputVlanId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200980
Ed Tanous1abe55e2018-09-05 08:30:59 -0700981 json_util::Result inputVlanEnabledState = json_util::getBool(
982 "VLANEnable", input, inputVlanEnabled,
983 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
984 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
985 json_util::Result inputVlanIdState = json_util::getUnsigned(
986 "VLANId", input, inputVlanId,
987 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
988 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
989 bool inputInvalid = false;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200990
Ed Tanous1abe55e2018-09-05 08:30:59 -0700991 // Do not proceed if fields in VLAN object were of wrong type
992 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
993 inputVlanIdState == json_util::Result::WRONG_TYPE)
994 {
995 return;
996 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200997
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 // Verify input
999 if (eth_data.vlanId == nullptr)
1000 {
1001 // This interface is not a VLAN. Cannot do anything with it
1002 // TODO(kkowalsk) Change this message
1003 messages::addMessageToJson(asyncResp->res.jsonValue,
1004 messages::propertyMissing("VLANEnable"),
1005 pathPrefix);
1006
1007 inputInvalid = true;
1008 }
1009 else
1010 {
1011 // Load actual data into field values if they were not provided
1012 if (inputVlanEnabledState == json_util::Result::NOT_EXIST)
1013 {
1014 inputVlanEnabled = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001015 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001016
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 if (inputVlanIdState == json_util::Result::NOT_EXIST)
1018 {
1019 inputVlanId = *eth_data.vlanId;
1020 }
1021 }
1022
1023 // Do not proceed if input has not been valid
1024 if (inputInvalid)
1025 {
1026 return;
1027 }
1028
1029 // VLAN is configured on the interface
1030 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId)
1031 {
1032 // Change VLAN Id
1033 paramsJson["VLANId"] = inputVlanId;
1034 OnDemandEthernetProvider::changeVlanId(
1035 ifaceId, static_cast<uint32_t>(inputVlanId),
1036 [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1037 const boost::system::error_code ec) {
1038 if (ec)
1039 {
1040 messages::addMessageToJson(asyncResp->res.jsonValue,
1041 messages::internalError(),
1042 pathPrefix);
1043 }
1044 else
1045 {
1046 paramsJson["VLANEnable"] = true;
1047 }
1048 });
1049 }
1050 else if (inputVlanEnabled == false)
1051 {
1052 // Disable VLAN
1053 OnDemandEthernetProvider::disableVlan(
1054 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1055 const boost::system::error_code ec) {
1056 if (ec)
1057 {
1058 messages::addMessageToJson(asyncResp->res.jsonValue,
1059 messages::internalError(),
1060 pathPrefix);
1061 }
1062 else
1063 {
1064 paramsJson["VLANEnable"] = false;
1065 }
1066 });
1067 }
1068 }
1069
1070 private:
1071 void handleHostnamePatch(const nlohmann::json &input,
1072 const EthernetInterfaceData &eth_data,
1073 const std::shared_ptr<AsyncResp> &asyncResp)
1074 {
1075 if (input.is_string())
1076 {
1077 std::string newHostname = input.get<std::string>();
1078
1079 if (eth_data.hostname == nullptr ||
1080 newHostname != *eth_data.hostname)
1081 {
1082 // Change hostname
1083 ethernetProvider.setHostName(
1084 newHostname, [asyncResp, newHostname](
1085 const boost::system::error_code ec) {
1086 if (ec)
1087 {
1088 messages::addMessageToJson(
1089 asyncResp->res.jsonValue,
1090 messages::internalError(), "/HostName");
1091 }
1092 else
1093 {
1094 asyncResp->res.jsonValue["HostName"] = newHostname;
1095 }
1096 });
1097 }
1098 }
1099 else
1100 {
1101 messages::addMessageToJson(
1102 asyncResp->res.jsonValue,
1103 messages::propertyValueTypeError(input.dump(), "HostName"),
1104 "/HostName");
1105 }
1106 }
1107
1108 void handleIPv4Patch(const std::string &ifaceId,
1109 const nlohmann::json &input,
1110 const std::vector<IPv4AddressData> &ipv4_data,
1111 const std::shared_ptr<AsyncResp> &asyncResp)
1112 {
1113 if (!input.is_array())
1114 {
1115 messages::addMessageToJson(
1116 asyncResp->res.jsonValue,
1117 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
1118 "/IPv4Addresses");
1119 return;
1120 }
1121
1122 // According to Redfish PATCH definition, size must be at least equal
1123 if (input.size() < ipv4_data.size())
1124 {
1125 // TODO(kkowalsk) This should be a message indicating that not
1126 // enough data has been provided
1127 messages::addMessageToJson(asyncResp->res.jsonValue,
1128 messages::internalError(),
1129 "/IPv4Addresses");
1130 return;
1131 }
1132
1133 json_util::Result addressFieldState;
1134 json_util::Result subnetMaskFieldState;
1135 json_util::Result addressOriginFieldState;
1136 json_util::Result gatewayFieldState;
1137 const std::string *addressFieldValue;
1138 const std::string *subnetMaskFieldValue;
1139 const std::string *addressOriginFieldValue = nullptr;
1140 const std::string *gatewayFieldValue;
1141 uint8_t subnetMaskAsPrefixLength;
1142 std::string addressOriginInDBusFormat;
1143
1144 bool errorDetected = false;
1145 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++)
1146 {
1147 // Check that entry is not of some unexpected type
1148 if (!input[entryIdx].is_object() && !input[entryIdx].is_null())
1149 {
1150 // Invalid object type
1151 messages::addMessageToJson(
1152 asyncResp->res.jsonValue,
1153 messages::propertyValueTypeError(input[entryIdx].dump(),
1154 "IPv4Address"),
1155 "/IPv4Addresses/" + std::to_string(entryIdx));
1156
1157 continue;
1158 }
1159
1160 // Try to load fields
1161 addressFieldState = json_util::getString(
1162 "Address", input[entryIdx], addressFieldValue,
1163 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1164 asyncResp->res.jsonValue,
1165 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1166 subnetMaskFieldState = json_util::getString(
1167 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1168 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1169 asyncResp->res.jsonValue,
1170 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1171 addressOriginFieldState = json_util::getString(
1172 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1173 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1174 asyncResp->res.jsonValue,
1175 "/IPv4Addresses/" + std::to_string(entryIdx) +
1176 "/AddressOrigin");
1177 gatewayFieldState = json_util::getString(
1178 "Gateway", input[entryIdx], gatewayFieldValue,
1179 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1180 asyncResp->res.jsonValue,
1181 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1182
1183 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1184 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1185 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1186 gatewayFieldState == json_util::Result::WRONG_TYPE)
1187 {
1188 return;
1189 }
1190
1191 if (addressFieldState == json_util::Result::SUCCESS &&
1192 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1193 *addressFieldValue))
1194 {
1195 errorDetected = true;
1196 messages::addMessageToJson(
1197 asyncResp->res.jsonValue,
1198 messages::propertyValueFormatError(*addressFieldValue,
1199 "Address"),
1200 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1201 }
1202
1203 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1204 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1205 *subnetMaskFieldValue, &subnetMaskAsPrefixLength))
1206 {
1207 errorDetected = true;
1208 messages::addMessageToJson(
1209 asyncResp->res.jsonValue,
1210 messages::propertyValueFormatError(*subnetMaskFieldValue,
1211 "SubnetMask"),
1212 "/IPv4Addresses/" + std::to_string(entryIdx) +
1213 "/SubnetMask");
1214 }
1215
1216 // get Address origin in proper format
1217 addressOriginInDBusFormat =
1218 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
1219 addressOriginFieldValue, true, false);
1220
1221 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1222 addressOriginInDBusFormat.empty())
1223 {
1224 errorDetected = true;
1225 messages::addMessageToJson(
1226 asyncResp->res.jsonValue,
1227 messages::propertyValueNotInList(*addressOriginFieldValue,
1228 "AddressOrigin"),
1229 "/IPv4Addresses/" + std::to_string(entryIdx) +
1230 "/AddressOrigin");
1231 }
1232
1233 if (gatewayFieldState == json_util::Result::SUCCESS &&
1234 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1235 *gatewayFieldValue))
1236 {
1237 errorDetected = true;
1238 messages::addMessageToJson(
1239 asyncResp->res.jsonValue,
1240 messages::propertyValueFormatError(*gatewayFieldValue,
1241 "Gateway"),
1242 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1243 }
1244
1245 // If any error occured do not proceed with current entry, but do
1246 // not end loop
1247 if (errorDetected)
1248 {
1249 errorDetected = false;
1250 continue;
1251 }
1252
1253 if (entryIdx >= ipv4_data.size())
1254 {
1255 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1256 input[entryIdx];
1257
1258 // Verify that all field were provided
1259 if (addressFieldState == json_util::Result::NOT_EXIST)
1260 {
1261 errorDetected = true;
1262 messages::addMessageToJson(
1263 asyncResp->res.jsonValue,
1264 messages::propertyMissing("Address"),
1265 "/IPv4Addresses/" + std::to_string(entryIdx) +
1266 "/Address");
1267 }
1268
1269 if (subnetMaskFieldState == json_util::Result::NOT_EXIST)
1270 {
1271 errorDetected = true;
1272 messages::addMessageToJson(
1273 asyncResp->res.jsonValue,
1274 messages::propertyMissing("SubnetMask"),
1275 "/IPv4Addresses/" + std::to_string(entryIdx) +
1276 "/SubnetMask");
1277 }
1278
1279 if (addressOriginFieldState == json_util::Result::NOT_EXIST)
1280 {
1281 errorDetected = true;
1282 messages::addMessageToJson(
1283 asyncResp->res.jsonValue,
1284 messages::propertyMissing("AddressOrigin"),
1285 "/IPv4Addresses/" + std::to_string(entryIdx) +
1286 "/AddressOrigin");
1287 }
1288
1289 if (gatewayFieldState == json_util::Result::NOT_EXIST)
1290 {
1291 errorDetected = true;
1292 messages::addMessageToJson(
1293 asyncResp->res.jsonValue,
1294 messages::propertyMissing("Gateway"),
1295 "/IPv4Addresses/" + std::to_string(entryIdx) +
1296 "/Gateway");
1297 }
1298
1299 // If any error occured do not proceed with current entry, but
1300 // do not end loop
1301 if (errorDetected)
1302 {
1303 errorDetected = false;
1304 continue;
1305 }
1306
1307 // Create IPv4 with provided data
1308 ethernetProvider.createIPv4(
1309 ifaceId, entryIdx, subnetMaskAsPrefixLength,
1310 *gatewayFieldValue, *addressFieldValue, asyncResp);
1311 }
1312 else
1313 {
1314 // Existing object that should be modified/deleted/remain
1315 // unchanged
1316 if (input[entryIdx].is_null())
1317 {
1318 // Object should be deleted
1319 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1320 entryIdx, asyncResp);
1321 }
1322 else if (input[entryIdx].is_object())
1323 {
1324 if (input[entryIdx].size() == 0)
1325 {
1326 // Object shall remain unchanged
1327 continue;
1328 }
1329
1330 // Apply changes
1331 if (addressFieldState == json_util::Result::SUCCESS &&
1332 ipv4_data[entryIdx].address != nullptr &&
1333 *ipv4_data[entryIdx].address != *addressFieldValue)
1334 {
1335 ethernetProvider.changeIPv4AddressProperty(
1336 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1337 "Address", *addressFieldValue, asyncResp);
1338 }
1339
1340 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1341 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue)
1342 {
1343 ethernetProvider.changeIPv4SubnetMaskProperty(
1344 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1345 *subnetMaskFieldValue, subnetMaskAsPrefixLength,
1346 asyncResp);
1347 }
1348
1349 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1350 ipv4_data[entryIdx].origin != *addressFieldValue)
1351 {
1352 ethernetProvider.changeIPv4Origin(
1353 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1354 *addressOriginFieldValue, addressOriginInDBusFormat,
1355 asyncResp);
1356 }
1357
1358 if (gatewayFieldState == json_util::Result::SUCCESS &&
1359 ipv4_data[entryIdx].gateway != nullptr &&
1360 *ipv4_data[entryIdx].gateway != *gatewayFieldValue)
1361 {
1362 ethernetProvider.changeIPv4AddressProperty(
1363 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1364 "Gateway", *gatewayFieldValue, asyncResp);
1365 }
1366 }
1367 }
1368 }
1369 }
1370
1371 nlohmann::json
1372 parseInterfaceData(const std::string &ifaceId,
1373 const EthernetInterfaceData &eth_data,
1374 const std::vector<IPv4AddressData> &ipv4_data)
1375 {
1376 // Copy JSON object to avoid race condition
1377 nlohmann::json jsonResponse(Node::json);
1378
1379 // Fill out obvious data...
1380 jsonResponse["Id"] = ifaceId;
1381 jsonResponse["@odata.id"] =
1382 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
1383
1384 // ... then the one from DBus, regarding eth iface...
1385 if (eth_data.speed != nullptr)
1386 jsonResponse["SpeedMbps"] = *eth_data.speed;
1387
1388 if (eth_data.macAddress != nullptr)
1389 jsonResponse["MACAddress"] = *eth_data.macAddress;
1390
1391 if (eth_data.hostname != nullptr)
1392 jsonResponse["HostName"] = *eth_data.hostname;
1393
1394 if (eth_data.vlanId != nullptr)
1395 {
1396 nlohmann::json &vlanObj = jsonResponse["VLAN"];
1397 vlanObj["VLANEnable"] = true;
1398 vlanObj["VLANId"] = *eth_data.vlanId;
1399 }
1400 else
1401 {
1402 nlohmann::json &vlanObj = jsonResponse["VLANs"];
1403 vlanObj["@odata.id"] =
1404 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
1405 "/VLANs";
1406 }
1407
1408 // ... at last, check if there are IPv4 data and prepare appropriate
1409 // collection
1410 if (ipv4_data.size() > 0)
1411 {
1412 nlohmann::json ipv4Array = nlohmann::json::array();
1413 for (auto &ipv4Config : ipv4_data)
1414 {
1415 nlohmann::json jsonIpv4;
1416 if (ipv4Config.address != nullptr)
1417 {
1418 jsonIpv4["Address"] = *ipv4Config.address;
1419 if (ipv4Config.gateway != nullptr)
1420 jsonIpv4["Gateway"] = *ipv4Config.gateway;
1421
1422 jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1423 jsonIpv4["SubnetMask"] = ipv4Config.netmask;
1424
1425 ipv4Array.push_back(std::move(jsonIpv4));
1426 }
1427 }
1428 jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
1429 }
1430
1431 return jsonResponse;
1432 }
1433
1434 /**
1435 * Functions triggers appropriate requests on DBus
1436 */
1437 void doGet(crow::Response &res, const crow::Request &req,
1438 const std::vector<std::string> &params) override
1439 {
1440 // TODO(Pawel) this shall be parametrized call (two params) to get
1441 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1442 // Check if there is required param, truly entering this shall be
1443 // impossible.
1444 if (params.size() != 1)
1445 {
1446 res.result(boost::beast::http::status::internal_server_error);
1447 res.end();
1448 return;
1449 }
1450
1451 const std::string &ifaceId = params[0];
1452
1453 // get single eth interface data, and call the below callback for JSON
1454 // preparation
1455 ethernetProvider.getEthernetIfaceData(
1456 ifaceId,
1457 [&, ifaceId](const bool &success,
1458 const EthernetInterfaceData &eth_data,
1459 const std::vector<IPv4AddressData> &ipv4_data) {
1460 if (success)
1461 {
1462 res.jsonValue =
1463 parseInterfaceData(ifaceId, eth_data, ipv4_data);
1464 }
1465 else
1466 {
1467 // ... otherwise return error
1468 // TODO(Pawel)consider distinguish between non existing
1469 // object, and other errors
1470 messages::addMessageToErrorJson(
1471 res.jsonValue, messages::resourceNotFound(
1472 "EthernetInterface", ifaceId));
1473 res.result(boost::beast::http::status::not_found);
1474 }
1475 res.end();
1476 });
1477 }
1478
1479 void doPatch(crow::Response &res, const crow::Request &req,
1480 const std::vector<std::string> &params) override
1481 {
1482 // TODO(Pawel) this shall be parametrized call (two params) to get
1483 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1484 // Check if there is required param, truly entering this shall be
1485 // impossible.
1486 if (params.size() != 1)
1487 {
1488 res.result(boost::beast::http::status::internal_server_error);
1489 res.end();
1490 return;
1491 }
1492
1493 const std::string &ifaceId = params[0];
1494
1495 nlohmann::json patchReq;
1496
1497 if (!json_util::processJsonFromRequest(res, req, patchReq))
1498 {
1499 return;
1500 }
1501
1502 // get single eth interface data, and call the below callback for JSON
1503 // preparation
1504 ethernetProvider.getEthernetIfaceData(
1505 ifaceId,
1506 [&, ifaceId, patchReq = std::move(patchReq)](
1507 const bool &success, const EthernetInterfaceData &eth_data,
1508 const std::vector<IPv4AddressData> &ipv4_data) {
1509 if (!success)
1510 {
1511 // ... otherwise return error
1512 // TODO(Pawel)consider distinguish between non existing
1513 // object, and other errors
1514 messages::addMessageToErrorJson(
1515 res.jsonValue, messages::resourceNotFound(
1516 "VLAN Network Interface", ifaceId));
1517 res.result(boost::beast::http::status::not_found);
1518 res.end();
1519
1520 return;
1521 }
1522
1523 res.jsonValue =
1524 parseInterfaceData(ifaceId, eth_data, ipv4_data);
1525
1526 std::shared_ptr<AsyncResp> asyncResp =
1527 std::make_shared<AsyncResp>(res);
1528
1529 for (auto propertyIt = patchReq.begin();
1530 propertyIt != patchReq.end(); ++propertyIt)
1531 {
1532 if (propertyIt.key() == "VLAN")
1533 {
1534 handleVlanPatch(ifaceId, propertyIt.value(), eth_data,
1535 "/VLAN", asyncResp);
1536 }
1537 else if (propertyIt.key() == "HostName")
1538 {
1539 handleHostnamePatch(propertyIt.value(), eth_data,
1540 asyncResp);
1541 }
1542 else if (propertyIt.key() == "IPv4Addresses")
1543 {
1544 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
1545 asyncResp);
1546 }
1547 else if (propertyIt.key() == "IPv6Addresses")
1548 {
1549 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1550 messages::addMessageToJsonRoot(
1551 res.jsonValue,
1552 messages::propertyNotWritable(propertyIt.key()));
1553 }
1554 else
1555 {
1556 auto fieldInJsonIt =
1557 res.jsonValue.find(propertyIt.key());
1558
1559 if (fieldInJsonIt == res.jsonValue.end())
1560 {
1561 // Field not in scope of defined fields
1562 messages::addMessageToJsonRoot(
1563 res.jsonValue,
1564 messages::propertyUnknown(propertyIt.key()));
1565 }
1566 else if (*fieldInJsonIt != *propertyIt)
1567 {
1568 // User attempted to modify non-writable field
1569 messages::addMessageToJsonRoot(
1570 res.jsonValue, messages::propertyNotWritable(
1571 propertyIt.key()));
1572 }
1573 }
1574 }
1575 });
1576 }
1577
1578 // Ethernet Provider object
1579 // TODO(Pawel) consider move it to singleton
1580 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001581};
1582
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001583class VlanNetworkInterfaceCollection;
1584
1585/**
1586 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1587 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588class VlanNetworkInterface : public Node
1589{
1590 public:
1591 /*
1592 * Default Constructor
1593 */
1594 template <typename CrowApp>
1595 // TODO(Pawel) Remove line from below, where we assume that there is only
1596 // one manager called openbmc This shall be generic, but requires to update
1597 // GetSubroutes method
1598 VlanNetworkInterface(CrowApp &app) :
1599 Node(
Kowalski, Kamileb547732018-05-16 10:25:07 +02001600 app,
1601 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602 std::string(), std::string())
1603 {
1604 Node::json["@odata.type"] =
1605 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1606 Node::json["@odata.context"] =
1607 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1608 Node::json["Name"] = "VLAN Network Interface";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001609
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610 entityPrivileges = {
1611 {boost::beast::http::verb::get, {{"Login"}}},
1612 {boost::beast::http::verb::head, {{"Login"}}},
1613 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1614 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1615 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1616 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001617 }
1618
Ed Tanous1abe55e2018-09-05 08:30:59 -07001619 private:
1620 nlohmann::json
1621 parseInterfaceData(const std::string &parent_ifaceId,
1622 const std::string &ifaceId,
1623 const EthernetInterfaceData &eth_data,
1624 const std::vector<IPv4AddressData> &ipv4_data)
1625 {
1626 // Copy JSON object to avoid race condition
1627 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001628
Ed Tanous1abe55e2018-09-05 08:30:59 -07001629 // Fill out obvious data...
1630 jsonResponse["Id"] = ifaceId;
1631 jsonResponse["@odata.id"] =
1632 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" +
1633 parent_ifaceId + "/VLANs/" + ifaceId;
1634
1635 jsonResponse["VLANEnable"] = true;
1636 jsonResponse["VLANId"] = *eth_data.vlanId;
1637
1638 return jsonResponse;
Ed Tanousa434f2b2018-07-27 13:04:22 -07001639 }
1640
Ed Tanous1abe55e2018-09-05 08:30:59 -07001641 bool verifyNames(crow::Response &res, const std::string &parent,
1642 const std::string &iface)
1643 {
1644 if (!boost::starts_with(iface, parent + "_"))
1645 {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001646 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001647 res.jsonValue,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001648 messages::resourceNotFound("VLAN Network Interface", iface));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001649 res.result(boost::beast::http::status::not_found);
1650 res.end();
1651
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652 return false;
1653 }
1654 else
1655 {
1656 return true;
1657 }
1658 }
1659
1660 /**
1661 * Functions triggers appropriate requests on DBus
1662 */
1663 void doGet(crow::Response &res, const crow::Request &req,
1664 const std::vector<std::string> &params) override
1665 {
1666 // TODO(Pawel) this shall be parametrized call (two params) to get
1667 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1668 // Check if there is required param, truly entering this shall be
1669 // impossible.
1670 if (params.size() != 2)
1671 {
1672 res.result(boost::beast::http::status::internal_server_error);
1673 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001674 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001675 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001676
Ed Tanous1abe55e2018-09-05 08:30:59 -07001677 const std::string &parentIfaceId = params[0];
1678 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001679
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 if (!verifyNames(res, parentIfaceId, ifaceId))
1681 {
1682 return;
1683 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001684
Ed Tanous1abe55e2018-09-05 08:30:59 -07001685 // Get single eth interface data, and call the below callback for JSON
1686 // preparation
1687 ethernetProvider.getEthernetIfaceData(
1688 ifaceId, [&, parentIfaceId,
1689 ifaceId](const bool &success,
1690 const EthernetInterfaceData &eth_data,
1691 const std::vector<IPv4AddressData> &ipv4_data) {
1692 if (success && eth_data.vlanId != nullptr)
1693 {
1694 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1695 eth_data, ipv4_data);
1696 }
1697 else
1698 {
1699 // ... otherwise return error
1700 // TODO(Pawel)consider distinguish between non existing
1701 // object, and other errors
1702 messages::addMessageToErrorJson(
1703 res.jsonValue, messages::resourceNotFound(
1704 "VLAN Network Interface", ifaceId));
1705 res.result(boost::beast::http::status::not_found);
1706 }
1707 res.end();
1708 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001709 }
1710
Ed Tanous1abe55e2018-09-05 08:30:59 -07001711 void doPatch(crow::Response &res, const crow::Request &req,
1712 const std::vector<std::string> &params) override
1713 {
1714 if (params.size() != 2)
1715 {
1716 res.result(boost::beast::http::status::internal_server_error);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001717 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001718 return;
1719 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001720
Ed Tanous1abe55e2018-09-05 08:30:59 -07001721 const std::string &parentIfaceId = params[0];
1722 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001723
Ed Tanous1abe55e2018-09-05 08:30:59 -07001724 if (!verifyNames(res, parentIfaceId, ifaceId))
1725 {
1726 return;
1727 }
1728
1729 nlohmann::json patchReq;
1730
1731 if (!json_util::processJsonFromRequest(res, req, patchReq))
1732 {
1733 return;
1734 }
1735
1736 // Get single eth interface data, and call the below callback for JSON
1737 // preparation
1738 ethernetProvider.getEthernetIfaceData(
1739 ifaceId,
1740 [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)](
1741 const bool &success, const EthernetInterfaceData &eth_data,
1742 const std::vector<IPv4AddressData> &ipv4_data) {
1743 if (!success)
1744 {
1745 // ... otherwise return error
1746 // TODO(Pawel)consider distinguish between non existing
1747 // object, and other errors
1748 messages::addMessageToErrorJson(
1749 res.jsonValue, messages::resourceNotFound(
1750 "VLAN Network Interface", ifaceId));
1751 res.result(boost::beast::http::status::not_found);
1752 res.end();
1753
1754 return;
1755 }
1756
1757 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1758 eth_data, ipv4_data);
1759
1760 std::shared_ptr<AsyncResp> asyncResp =
1761 std::make_shared<AsyncResp>(res);
1762
1763 for (auto propertyIt = patchReq.begin();
1764 propertyIt != patchReq.end(); ++propertyIt)
1765 {
1766 if (propertyIt.key() != "VLANEnable" &&
1767 propertyIt.key() != "VLANId")
1768 {
1769 auto fieldInJsonIt =
1770 res.jsonValue.find(propertyIt.key());
1771
1772 if (fieldInJsonIt == res.jsonValue.end())
1773 {
1774 // Field not in scope of defined fields
1775 messages::addMessageToJsonRoot(
1776 res.jsonValue,
1777 messages::propertyUnknown(propertyIt.key()));
1778 }
1779 else if (*fieldInJsonIt != *propertyIt)
1780 {
1781 // User attempted to modify non-writable field
1782 messages::addMessageToJsonRoot(
1783 res.jsonValue, messages::propertyNotWritable(
1784 propertyIt.key()));
1785 }
1786 }
1787 }
1788
1789 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data,
1790 "/", asyncResp);
1791 });
1792 }
1793
1794 void doDelete(crow::Response &res, const crow::Request &req,
1795 const std::vector<std::string> &params) override
1796 {
1797 if (params.size() != 2)
1798 {
1799 res.result(boost::beast::http::status::internal_server_error);
1800 res.end();
1801 return;
1802 }
1803
1804 const std::string &parentIfaceId = params[0];
1805 const std::string &ifaceId = params[1];
1806
1807 if (!verifyNames(res, parentIfaceId, ifaceId))
1808 {
1809 return;
1810 }
1811
1812 // Get single eth interface data, and call the below callback for JSON
1813 // preparation
1814 ethernetProvider.getEthernetIfaceData(
1815 ifaceId, [&, parentIfaceId,
1816 ifaceId](const bool &success,
1817 const EthernetInterfaceData &eth_data,
1818 const std::vector<IPv4AddressData> &ipv4_data) {
1819 if (success && eth_data.vlanId != nullptr)
1820 {
1821 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1822 eth_data, ipv4_data);
1823
1824 // Disable VLAN
1825 OnDemandEthernetProvider::disableVlan(
1826 ifaceId, [&](const boost::system::error_code ec) {
1827 if (ec)
1828 {
1829 res.jsonValue = nlohmann::json::object();
1830 messages::addMessageToErrorJson(
1831 res.jsonValue, messages::internalError());
1832 res.result(boost::beast::http::status::
1833 internal_server_error);
1834 }
1835 res.end();
1836 });
1837 }
1838 else
1839 {
1840 // ... otherwise return error
1841 // TODO(Pawel)consider distinguish between non existing
1842 // object, and other errors
1843 messages::addMessageToErrorJson(
1844 res.jsonValue, messages::resourceNotFound(
1845 "VLAN Network Interface", ifaceId));
1846 res.result(boost::beast::http::status::not_found);
1847 res.end();
1848 }
1849 });
1850 }
1851
1852 /**
1853 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1854 * method, to maintain consistency of returned data, as Collection's doPost
1855 * should return data for created member which should match member's doGet
1856 * result in 100%.
1857 */
1858 friend VlanNetworkInterfaceCollection;
1859
1860 // Ethernet Provider object
1861 // TODO(Pawel) consider move it to singleton
1862 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001863};
1864
1865/**
1866 * VlanNetworkInterfaceCollection derived class for delivering
1867 * VLANNetworkInterface Collection Schema
1868 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001869class VlanNetworkInterfaceCollection : public Node
1870{
1871 public:
1872 template <typename CrowApp>
1873 // TODO(Pawel) Remove line from below, where we assume that there is only
1874 // one manager called openbmc This shall be generic, but requires to update
1875 // GetSubroutes method
1876 VlanNetworkInterfaceCollection(CrowApp &app) :
1877 Node(app,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001878 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1879 std::string()),
Ed Tanous1abe55e2018-09-05 08:30:59 -07001880 memberVlan(app)
1881 {
1882 Node::json["@odata.type"] =
1883 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1884 Node::json["@odata.context"] =
1885 "/redfish/v1/$metadata"
1886 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1887 Node::json["Name"] = "VLAN Network Interface Collection";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001888
Ed Tanous1abe55e2018-09-05 08:30:59 -07001889 entityPrivileges = {
1890 {boost::beast::http::verb::get, {{"Login"}}},
1891 {boost::beast::http::verb::head, {{"Login"}}},
1892 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1893 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1894 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1895 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001896 }
1897
Ed Tanous1abe55e2018-09-05 08:30:59 -07001898 private:
1899 /**
1900 * Functions triggers appropriate requests on DBus
1901 */
1902 void doGet(crow::Response &res, const crow::Request &req,
1903 const std::vector<std::string> &params) override
1904 {
1905 if (params.size() != 1)
1906 {
1907 // This means there is a problem with the router
1908 res.result(boost::beast::http::status::internal_server_error);
1909 res.end();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001910
Ed Tanous1abe55e2018-09-05 08:30:59 -07001911 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001912 }
1913
Ed Tanous1abe55e2018-09-05 08:30:59 -07001914 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1915 // for any Manager, not only hardcoded 'openbmc'.
1916 std::string managerId = "openbmc";
1917 std::string rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001918
Ed Tanous1abe55e2018-09-05 08:30:59 -07001919 // get eth interface list, and call the below callback for JSON
1920 // preparation
1921 ethernetProvider.getEthernetIfaceList(
1922 [&, managerId{std::move(managerId)},
1923 rootInterfaceName{std::move(rootInterfaceName)}](
1924 const bool &success,
1925 const std::vector<std::string> &iface_list) {
1926 if (success)
1927 {
1928 bool rootInterfaceFound = false;
1929 nlohmann::json ifaceArray = nlohmann::json::array();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001930
Ed Tanous1abe55e2018-09-05 08:30:59 -07001931 for (const std::string &ifaceItem : iface_list)
1932 {
1933 if (ifaceItem == rootInterfaceName)
1934 {
1935 rootInterfaceFound = true;
1936 }
1937 else if (boost::starts_with(ifaceItem,
1938 rootInterfaceName + "_"))
1939 {
1940 ifaceArray.push_back(
1941 {{"@odata.id", "/redfish/v1/Managers/" +
1942 managerId +
1943 "/EthernetInterfaces/" +
1944 rootInterfaceName +
1945 "/VLANs/" + ifaceItem}});
1946 }
1947 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001948
Ed Tanous1abe55e2018-09-05 08:30:59 -07001949 if (rootInterfaceFound)
1950 {
1951 Node::json["Members"] = ifaceArray;
1952 Node::json["Members@odata.count"] = ifaceArray.size();
1953 Node::json["@odata.id"] = "/redfish/v1/Managers/" +
1954 managerId +
1955 "/EthernetInterfaces/" +
1956 rootInterfaceName + "/VLANs";
1957 res.jsonValue = Node::json;
1958 }
1959 else
1960 {
1961 messages::addMessageToErrorJson(
1962 res.jsonValue,
1963 messages::resourceNotFound("EthernetInterface",
1964 rootInterfaceName));
1965 res.result(boost::beast::http::status::not_found);
1966 res.end();
1967 }
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001968 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001969 else
1970 {
1971 // No success, best what we can do is return INTERNALL ERROR
1972 res.result(
1973 boost::beast::http::status::internal_server_error);
1974 }
1975 res.end();
1976 });
1977 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001978
Ed Tanous1abe55e2018-09-05 08:30:59 -07001979 void doPost(crow::Response &res, const crow::Request &req,
1980 const std::vector<std::string> &params) override
1981 {
1982 if (params.size() != 1)
1983 {
1984 // This means there is a problem with the router
1985 res.result(boost::beast::http::status::internal_server_error);
1986 res.end();
1987 return;
1988 }
1989
1990 nlohmann::json postReq;
1991
1992 if (!json_util::processJsonFromRequest(res, req, postReq))
1993 {
1994 return;
1995 }
1996
1997 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1998 // for any Manager, not only hardcoded 'openbmc'.
1999 std::string managerId = "openbmc";
2000 std::string rootInterfaceName = params[0];
2001 uint64_t vlanId;
2002 bool errorDetected;
2003
2004 if (json_util::getUnsigned(
2005 "VLANId", postReq, vlanId,
2006 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
2007 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
2008 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS)
2009 {
2010 res.end();
2011 return;
2012 }
2013
2014 // get eth interface list, and call the below callback for JSON
2015 // preparation
2016 ethernetProvider.getEthernetIfaceList(
2017 [&, managerId{std::move(managerId)},
2018 rootInterfaceName{std::move(rootInterfaceName)}](
2019 const bool &success,
2020 const std::vector<std::string> &iface_list) {
2021 if (success)
2022 {
2023 bool rootInterfaceFound = false;
2024
2025 for (const std::string &ifaceItem : iface_list)
2026 {
2027 if (ifaceItem == rootInterfaceName)
2028 {
2029 rootInterfaceFound = true;
2030 break;
2031 }
2032 }
2033
2034 if (rootInterfaceFound)
2035 {
2036 ethernetProvider.createVlan(
2037 rootInterfaceName, vlanId,
2038 [&, vlanId, rootInterfaceName, req{std::move(req)}](
2039 const boost::system::error_code ec) {
2040 if (ec)
2041 {
2042 messages::addMessageToErrorJson(
2043 res.jsonValue,
2044 messages::internalError());
2045 res.end();
2046 }
2047 else
2048 {
2049 memberVlan.doGet(
2050 res, req,
2051 {rootInterfaceName,
2052 rootInterfaceName + "_" +
2053 std::to_string(vlanId)});
2054 }
2055 });
2056 }
2057 else
2058 {
2059 messages::addMessageToErrorJson(
2060 res.jsonValue,
2061 messages::resourceNotFound("EthernetInterface",
2062 rootInterfaceName));
2063 res.result(boost::beast::http::status::not_found);
2064 res.end();
2065 }
2066 }
2067 else
2068 {
2069 // No success, best what we can do is return INTERNALL ERROR
2070 res.result(
2071 boost::beast::http::status::internal_server_error);
2072 res.end();
2073 }
2074 });
2075 }
2076
2077 // Ethernet Provider object
2078 // TODO(Pawel) consider move it to singleton
2079 OnDemandEthernetProvider ethernetProvider;
2080 VlanNetworkInterface memberVlan;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02002081};
2082
Ed Tanous1abe55e2018-09-05 08:30:59 -07002083} // namespace redfish