blob: 85af636cd481cdcd6dc7e87b7365a53fa4ab792d [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;
Ed Tanous1da66f72018-07-27 16:13:37 -0700371
Ed Tanous1abe55e2018-09-05 08:30:59 -0700372 for (const std::string &byte : bytesInMask)
373 {
374 if (byte.empty())
375 {
376 return false;
377 }
378
379 // Use strtol instead of stroi to avoid exceptions
380 long value = std::strtol(byte.c_str(), &endPtr, 10);
381
382 // endPtr should point to the end of the string, otherwise given
383 // string is not 100% number
384 if (*endPtr != '\0')
385 {
386 return false;
387 }
388
389 // Value should be contained in byte
390 if (value < 0 || value > 255)
391 {
392 return false;
393 }
394
395 if (bits != nullptr)
396 {
397 // Mask has to be continuous between bytes
398 if (previousValue != 255 && value != 0)
399 {
400 return false;
401 }
402
403 // Mask has to be continuous inside bytes
404 firstZeroInByteHit = false;
405
406 // Count bits
407 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
408 {
409 if (value & (1 << bitIdx))
410 {
411 if (firstZeroInByteHit)
412 {
413 // Continuity not preserved
414 return false;
415 }
416 else
417 {
418 (*bits)++;
419 }
420 }
421 else
422 {
423 firstZeroInByteHit = true;
424 }
425 }
426 }
427
428 previousValue = value;
429 }
430
431 return true;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100432 }
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200433
434 /**
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435 * @brief Changes IPv4 address type property (Address, Gateway)
436 *
437 * @param[in] ifaceId Id of interface whose IP should be modified
438 * @param[in] ipIdx index of IP in input array that should be modified
439 * @param[in] ipHash DBus Hash id of modified IP
440 * @param[in] name Name of field in JSON representation
441 * @param[in] newValue New value that should be written
442 * @param[io] asyncResp Response object that will be returned to client
443 *
444 * @return true if give IP is valid and has been sent do D-Bus, false
445 * otherwise
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200446 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 void changeIPv4AddressProperty(const std::string &ifaceId, int ipIdx,
448 const std::string &ipHash,
449 const std::string &name,
450 const std::string &newValue,
451 const std::shared_ptr<AsyncResp> &asyncResp)
452 {
453 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
454 name{std::move(name)}, newValue{std::move(newValue)}](
455 const boost::system::error_code ec) {
456 if (ec)
457 {
458 messages::addMessageToJson(
459 asyncResp->res.jsonValue, messages::internalError(),
460 "/IPv4Addresses/" + std::to_string(ipIdx) + "/" + name);
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200461 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 else
463 {
464 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] =
465 newValue;
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200466 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200468
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 crow::connections::systemBus->async_method_call(
470 std::move(callback), "xyz.openbmc_project.Network",
471 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
472 "org.freedesktop.DBus.Properties", "Set",
473 "xyz.openbmc_project.Network.IP", name,
474 sdbusplus::message::variant<std::string>(newValue));
475 };
Kowalski, Kamil179db1d2018-04-23 11:12:41 +0200476
Ed Tanous1abe55e2018-09-05 08:30:59 -0700477 /**
478 * @brief Changes IPv4 address origin property
479 *
480 * @param[in] ifaceId Id of interface whose IP should be modified
481 * @param[in] ipIdx index of IP in input array that should be
482 * modified
483 * @param[in] ipHash DBus Hash id of modified IP
484 * @param[in] newValue New value in Redfish format
485 * @param[in] newValueDbus New value in D-Bus format
486 * @param[io] asyncResp Response object that will be returned to client
487 *
488 * @return true if give IP is valid and has been sent do D-Bus, false
489 * otherwise
490 */
491 void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
492 const std::string &ipHash,
493 const std::string &newValue,
494 const std::string &newValueDbus,
495 const std::shared_ptr<AsyncResp> &asyncResp)
496 {
497 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
498 newValue{std::move(newValue)}](
499 const boost::system::error_code ec) {
500 if (ec)
501 {
502 messages::addMessageToJson(
503 asyncResp->res.jsonValue, messages::internalError(),
504 "/IPv4Addresses/" + std::to_string(ipIdx) +
505 "/AddressOrigin");
506 }
507 else
508 {
509 asyncResp->res
510 .jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
511 newValue;
512 }
513 };
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100514
Ed Tanous1abe55e2018-09-05 08:30:59 -0700515 crow::connections::systemBus->async_method_call(
516 std::move(callback), "xyz.openbmc_project.Network",
517 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
518 "org.freedesktop.DBus.Properties", "Set",
519 "xyz.openbmc_project.Network.IP", "Origin",
520 sdbusplus::message::variant<std::string>(newValueDbus));
521 };
522
523 /**
524 * @brief Modifies SubnetMask for given IP
525 *
526 * @param[in] ifaceId Id of interface whose IP should be modified
527 * @param[in] ipIdx index of IP in input array that should be
528 * modified
529 * @param[in] ipHash DBus Hash id of modified IP
530 * @param[in] newValueStr Mask in dot notation as string
531 * @param[in] newValue Mask as PrefixLength in bitcount
532 * @param[io] asyncResp Response object that will be returned to client
533 *
534 * @return None
535 */
536 void changeIPv4SubnetMaskProperty(
537 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
538 const std::string &newValueStr, uint8_t &newValue,
539 const std::shared_ptr<AsyncResp> &asyncResp)
540 {
541 auto callback = [asyncResp, ipIdx{std::move(ipIdx)},
542 newValueStr{std::move(newValueStr)}](
543 const boost::system::error_code ec) {
544 if (ec)
545 {
546 messages::addMessageToJson(
547 asyncResp->res.jsonValue, messages::internalError(),
548 "/IPv4Addresses/" + std::to_string(ipIdx) + "/SubnetMask");
549 }
550 else
551 {
552 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
553 newValueStr;
554 }
555 };
556
557 crow::connections::systemBus->async_method_call(
558 std::move(callback), "xyz.openbmc_project.Network",
559 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
560 "org.freedesktop.DBus.Properties", "Set",
561 "xyz.openbmc_project.Network.IP", "PrefixLength",
562 sdbusplus::message::variant<uint8_t>(newValue));
563 };
564
565 /**
566 * @brief Disables VLAN with given ifaceId
567 *
568 * @param[in] ifaceId Id of VLAN interface that should be disabled
569 * @param[in] callback Function that will be called after the operation
570 *
571 * @return None.
572 */
573 template <typename CallbackFunc>
574 static void disableVlan(const std::string &ifaceId, CallbackFunc &&callback)
575 {
576 crow::connections::systemBus->async_method_call(
577 callback, "xyz.openbmc_project.Network",
578 std::string("/xyz/openbmc_project/network/") + ifaceId,
579 "xyz.openbmc_project.Object.Delete", "Delete");
580 };
581
582 /**
583 * @brief Sets given HostName of the machine through D-Bus
584 *
585 * @param[in] newHostname New name that HostName will be changed to
586 * @param[in] callback Function that will be called after the operation
587 *
588 * @return None.
589 */
590 template <typename CallbackFunc>
591 void setHostName(const std::string &newHostname, CallbackFunc &&callback)
592 {
593 crow::connections::systemBus->async_method_call(
594 callback, "xyz.openbmc_project.Network",
595 "/xyz/openbmc_project/network/config",
596 "org.freedesktop.DBus.Properties", "Set",
597 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
598 sdbusplus::message::variant<std::string>(newHostname));
599 };
600
601 /**
602 * @brief Deletes given IPv4
603 *
604 * @param[in] ifaceId Id of interface whose IP should be deleted
605 * @param[in] ipIdx index of IP in input array that should be deleted
606 * @param[in] ipHash DBus Hash id of IP that should be deleted
607 * @param[io] asyncResp Response object that will be returned to client
608 *
609 * @return None
610 */
611 void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
612 unsigned int ipIdx,
613 const std::shared_ptr<AsyncResp> &asyncResp)
614 {
615 crow::connections::systemBus->async_method_call(
616 [ipIdx{std::move(ipIdx)}, asyncResp{std::move(asyncResp)}](
617 const boost::system::error_code ec) {
618 if (ec)
619 {
620 messages::addMessageToJson(
621 asyncResp->res.jsonValue, messages::internalError(),
622 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100623 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 else
625 {
626 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
627 }
628 },
629 "xyz.openbmc_project.Network",
630 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
631 "xyz.openbmc_project.Object.Delete", "Delete");
632 }
633
634 /**
635 * @brief Creates IPv4 with given data
636 *
637 * @param[in] ifaceId Id of interface whose IP should be deleted
638 * @param[in] ipIdx index of IP in input array that should be deleted
639 * @param[in] ipHash DBus Hash id of IP that should be deleted
640 * @param[io] asyncResp Response object that will be returned to client
641 *
642 * @return None
643 */
644 void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
645 uint8_t subnetMask, const std::string &gateway,
646 const std::string &address,
647 const std::shared_ptr<AsyncResp> &asyncResp)
648 {
649 auto createIpHandler = [ipIdx{std::move(ipIdx)},
650 asyncResp{std::move(asyncResp)}](
651 const boost::system::error_code ec) {
652 if (ec)
653 {
654 messages::addMessageToJson(
655 asyncResp->res.jsonValue, messages::internalError(),
656 "/IPv4Addresses/" + std::to_string(ipIdx) + "/");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100657 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 };
659
660 crow::connections::systemBus->async_method_call(
661 std::move(createIpHandler), "xyz.openbmc_project.Network",
662 "/xyz/openbmc_project/network/" + ifaceId,
663 "xyz.openbmc_project.Network.IP.Create", "IP",
664 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
665 gateway);
666 }
667
668 /**
669 * @brief Translates Address Origin value from D-Bus to Redfish format and
670 * vice-versa
671 *
672 * @param[in] inputOrigin Input value that should be translated
673 * @param[in] isIPv4 True for IPv4 origins, False for IPv6
674 * @param[in] isFromDBus True for DBus->Redfish conversion, false for
675 * reverse
676 *
677 * @return Empty string in case of failure, translated value otherwise
678 */
679 std::string translateAddressOriginBetweenDBusAndRedfish(
680 const std::string *inputOrigin, bool isIPv4, bool isFromDBus)
681 {
682 // Invalid pointer
683 if (inputOrigin == nullptr)
684 {
685 return "";
686 }
687
688 static const constexpr unsigned int firstIPv4OnlyIdx = 1;
689 static const constexpr unsigned int firstIPv6OnlyIdx = 3;
690
691 std::array<std::pair<const char *, const char *>, 6> translationTable{
692 {{"xyz.openbmc_project.Network.IP.AddressOrigin.Static", "Static"},
693 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCP"},
694 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
695 "IPv4LinkLocal"},
696 {"xyz.openbmc_project.Network.IP.AddressOrigin.DHCP", "DHCPv6"},
697 {"xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal",
698 "LinkLocal"},
699 {"xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC", "SLAAC"}}};
700
701 for (unsigned int i = 0; i < translationTable.size(); i++)
702 {
703 // Skip unrelated
704 if (isIPv4 && i >= firstIPv6OnlyIdx)
705 break;
706 if (!isIPv4 && i >= firstIPv4OnlyIdx && i < firstIPv6OnlyIdx)
707 continue;
708
709 // When translating D-Bus to Redfish compare input to first element
710 if (isFromDBus && translationTable[i].first == *inputOrigin)
711 return translationTable[i].second;
712
713 // When translating Redfish to D-Bus compare input to second element
714 if (!isFromDBus && translationTable[i].second == *inputOrigin)
715 return translationTable[i].first;
716 }
717
718 // If we are still here, that means that value has not been found
719 return "";
720 }
721
722 /**
723 * Function that retrieves all properties for given Ethernet Interface
724 * Object
725 * from EntityManager Network Manager
726 * @param ethifaceId a eth interface id to query on DBus
727 * @param callback a function that shall be called to convert Dbus output
728 * into JSON
729 */
730 template <typename CallbackFunc>
731 void getEthernetIfaceData(const std::string &ethifaceId,
732 CallbackFunc &&callback)
733 {
734 crow::connections::systemBus->async_method_call(
735 [this, ethifaceId{std::move(ethifaceId)},
736 callback{std::move(callback)}](
737 const boost::system::error_code error_code,
738 const GetManagedObjectsType &resp) {
739 EthernetInterfaceData ethData{};
740 std::vector<IPv4AddressData> ipv4Data;
741 ipv4Data.reserve(maxIpV4AddressesPerInterface);
742
743 if (error_code)
744 {
745 // Something wrong on DBus, the error_code is not important
746 // at this moment, just return success=false, and empty
747 // output. Since size of vector may vary depending on
748 // information from Network Manager, and empty output could
749 // not be treated same way as error.
750 callback(false, ethData, ipv4Data);
751 return;
752 }
753
754 // Find interface
755 if (resp.find("/xyz/openbmc_project/network/" + ethifaceId) ==
756 resp.end())
757 {
758 // Interface has not been found
759 callback(false, ethData, ipv4Data);
760 return;
761 }
762
763 extractEthernetInterfaceData(ethifaceId, resp, ethData);
764 extractIPv4Data(ethifaceId, resp, ipv4Data);
765
766 // Fix global GW
767 for (IPv4AddressData &ipv4 : ipv4Data)
768 {
769 if ((ipv4.global) && ((ipv4.gateway == nullptr) ||
770 (*ipv4.gateway == "0.0.0.0")))
771 {
772 ipv4.gateway = ethData.defaultGateway;
773 }
774 }
775
776 // Finally make a callback with useful data
777 callback(true, ethData, ipv4Data);
778 },
779 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
780 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
781 };
782
783 /**
784 * Function that retrieves all Ethernet Interfaces available through Network
785 * Manager
786 * @param callback a function that shall be called to convert Dbus output
787 * into JSON.
788 */
789 template <typename CallbackFunc>
790 void getEthernetIfaceList(CallbackFunc &&callback)
791 {
792 crow::connections::systemBus->async_method_call(
793 [this, callback{std::move(callback)}](
794 const boost::system::error_code error_code,
795 GetManagedObjectsType &resp) {
796 // Callback requires vector<string> to retrieve all available
797 // ethernet interfaces
798 std::vector<std::string> ifaceList;
799 ifaceList.reserve(resp.size());
800 if (error_code)
801 {
802 // Something wrong on DBus, the error_code is not important
803 // at this moment, just return success=false, and empty
804 // output. Since size of vector may vary depending on
805 // information from Network Manager, and empty output could
806 // not be treated same way as error.
807 callback(false, ifaceList);
808 return;
809 }
810
811 // Iterate over all retrieved ObjectPaths.
812 for (auto &objpath : resp)
813 {
814 // And all interfaces available for certain ObjectPath.
815 for (auto &interface : objpath.second)
816 {
817 // If interface is
818 // xyz.openbmc_project.Network.EthernetInterface, this
819 // is what we're looking for.
820 if (interface.first ==
821 "xyz.openbmc_project.Network.EthernetInterface")
822 {
823 // Cut out everything until last "/", ...
824 const std::string &ifaceId =
825 static_cast<const std::string &>(objpath.first);
826 std::size_t lastPos = ifaceId.rfind("/");
827 if (lastPos != std::string::npos)
828 {
829 // and put it into output vector.
830 ifaceList.emplace_back(
831 ifaceId.substr(lastPos + 1));
832 }
833 }
834 }
835 }
836 // Finally make a callback with useful data
837 callback(true, ifaceList);
838 },
839 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
840 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
841 };
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100842};
843
844/**
845 * EthernetCollection derived class for delivering Ethernet Collection Schema
846 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847class EthernetCollection : public Node
848{
849 public:
850 // TODO(Pawel) Remove line from below, where we assume that there is only
851 // one manager called openbmc This shall be generic, but requires to update
852 // GetSubroutes method
853 EthernetCollection(CrowApp &app) :
854 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/")
855 {
856 Node::json["@odata.type"] =
857 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
858 Node::json["@odata.context"] =
859 "/redfish/v1/"
860 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
Ed Tanous8ceb2ec2018-08-13 11:11:56 -0700861 Node::json["@odata.id"] =
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 "/redfish/v1/Managers/openbmc/EthernetInterfaces";
863 Node::json["Name"] = "Ethernet Network Interface Collection";
864 Node::json["Description"] =
865 "Collection of EthernetInterfaces for this Manager";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100866
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 entityPrivileges = {
868 {boost::beast::http::verb::get, {{"Login"}}},
869 {boost::beast::http::verb::head, {{"Login"}}},
870 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
871 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
872 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
873 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
874 }
875
876 private:
877 /**
878 * Functions triggers appropriate requests on DBus
879 */
880 void doGet(crow::Response &res, const crow::Request &req,
881 const std::vector<std::string> &params) override
882 {
883 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
884 // for any Manager, not only hardcoded 'openbmc'.
885 std::string managerId = "openbmc";
886
887 // get eth interface list, and call the below callback for JSON
888 // preparation
889 ethernetProvider.getEthernetIfaceList(
890 [&, managerId{std::move(managerId)}](
891 const bool &success,
892 const std::vector<std::string> &iface_list) {
893 if (success)
894 {
895 nlohmann::json ifaceArray = nlohmann::json::array();
896 for (const std::string &ifaceItem : iface_list)
897 {
898 ifaceArray.push_back(
899 {{"@odata.id", "/redfish/v1/Managers/" + managerId +
900 "/EthernetInterfaces/" +
901 ifaceItem}});
902 }
903 Node::json["Members"] = ifaceArray;
904 Node::json["Members@odata.count"] = ifaceArray.size();
905 Node::json["@odata.id"] = "/redfish/v1/Managers/" +
906 managerId + "/EthernetInterfaces";
907 res.jsonValue = Node::json;
908 }
909 else
910 {
911 // No success, best what we can do is return INTERNALL ERROR
912 res.result(
913 boost::beast::http::status::internal_server_error);
914 }
915 res.end();
916 });
917 }
918
919 // Ethernet Provider object
920 // TODO(Pawel) consider move it to singleton
921 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100922};
923
924/**
925 * EthernetInterface derived class for delivering Ethernet Schema
926 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927class EthernetInterface : public Node
928{
929 public:
930 /*
931 * Default Constructor
932 */
933 // TODO(Pawel) Remove line from below, where we assume that there is only
934 // one manager called openbmc This shall be generic, but requires to update
935 // GetSubroutes method
936 EthernetInterface(CrowApp &app) :
937 Node(app, "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/",
938 std::string())
939 {
940 Node::json["@odata.type"] =
941 "#EthernetInterface.v1_2_0.EthernetInterface";
942 Node::json["@odata.context"] =
943 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
944 Node::json["Name"] = "Manager Ethernet Interface";
945 Node::json["Description"] = "Management Network Interface";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100946
Ed Tanous1abe55e2018-09-05 08:30:59 -0700947 entityPrivileges = {
948 {boost::beast::http::verb::get, {{"Login"}}},
949 {boost::beast::http::verb::head, {{"Login"}}},
950 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
951 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
952 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
953 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200954 }
955
Ed Tanous1abe55e2018-09-05 08:30:59 -0700956 // TODO(kkowalsk) Find a suitable class/namespace for this
957 static void handleVlanPatch(const std::string &ifaceId,
958 const nlohmann::json &input,
959 const EthernetInterfaceData &eth_data,
960 const std::string &pathPrefix,
961 const std::shared_ptr<AsyncResp> &asyncResp)
962 {
963 if (!input.is_object())
964 {
965 messages::addMessageToJson(
966 asyncResp->res.jsonValue,
967 messages::propertyValueTypeError(input.dump(), "VLAN"),
968 pathPrefix);
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200969 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700970 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200971
Ed Tanous1abe55e2018-09-05 08:30:59 -0700972 const std::string pathStart = (pathPrefix == "/") ? "" : pathPrefix;
973 nlohmann::json &paramsJson =
974 (pathPrefix == "/")
975 ? asyncResp->res.jsonValue
976 : asyncResp->res
977 .jsonValue[nlohmann::json_pointer<nlohmann::json>(
978 pathPrefix)];
979 bool inputVlanEnabled;
980 uint64_t inputVlanId;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200981
Ed Tanous1abe55e2018-09-05 08:30:59 -0700982 json_util::Result inputVlanEnabledState = json_util::getBool(
983 "VLANEnable", input, inputVlanEnabled,
984 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
985 asyncResp->res.jsonValue, std::string(pathStart + "/VLANEnable"));
986 json_util::Result inputVlanIdState = json_util::getUnsigned(
987 "VLANId", input, inputVlanId,
988 static_cast<int>(json_util::MessageSetting::TYPE_ERROR),
989 asyncResp->res.jsonValue, std::string(pathStart + "/VLANId"));
990 bool inputInvalid = false;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200991
Ed Tanous1abe55e2018-09-05 08:30:59 -0700992 // Do not proceed if fields in VLAN object were of wrong type
993 if (inputVlanEnabledState == json_util::Result::WRONG_TYPE ||
994 inputVlanIdState == json_util::Result::WRONG_TYPE)
995 {
996 return;
997 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200998
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 // Verify input
1000 if (eth_data.vlanId == nullptr)
1001 {
1002 // This interface is not a VLAN. Cannot do anything with it
1003 // TODO(kkowalsk) Change this message
1004 messages::addMessageToJson(asyncResp->res.jsonValue,
1005 messages::propertyMissing("VLANEnable"),
1006 pathPrefix);
1007
1008 inputInvalid = true;
1009 }
1010 else
1011 {
1012 // Load actual data into field values if they were not provided
1013 if (inputVlanEnabledState == json_util::Result::NOT_EXIST)
1014 {
1015 inputVlanEnabled = true;
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001016 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +02001017
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018 if (inputVlanIdState == json_util::Result::NOT_EXIST)
1019 {
1020 inputVlanId = *eth_data.vlanId;
1021 }
1022 }
1023
1024 // Do not proceed if input has not been valid
1025 if (inputInvalid)
1026 {
1027 return;
1028 }
1029
1030 // VLAN is configured on the interface
1031 if (inputVlanEnabled == true && inputVlanId != *eth_data.vlanId)
1032 {
1033 // Change VLAN Id
1034 paramsJson["VLANId"] = inputVlanId;
1035 OnDemandEthernetProvider::changeVlanId(
1036 ifaceId, static_cast<uint32_t>(inputVlanId),
1037 [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1038 const boost::system::error_code ec) {
1039 if (ec)
1040 {
1041 messages::addMessageToJson(asyncResp->res.jsonValue,
1042 messages::internalError(),
1043 pathPrefix);
1044 }
1045 else
1046 {
1047 paramsJson["VLANEnable"] = true;
1048 }
1049 });
1050 }
1051 else if (inputVlanEnabled == false)
1052 {
1053 // Disable VLAN
1054 OnDemandEthernetProvider::disableVlan(
1055 ifaceId, [&, asyncResp, pathPrefx{std::move(pathPrefix)}](
1056 const boost::system::error_code ec) {
1057 if (ec)
1058 {
1059 messages::addMessageToJson(asyncResp->res.jsonValue,
1060 messages::internalError(),
1061 pathPrefix);
1062 }
1063 else
1064 {
1065 paramsJson["VLANEnable"] = false;
1066 }
1067 });
1068 }
1069 }
1070
1071 private:
1072 void handleHostnamePatch(const nlohmann::json &input,
1073 const EthernetInterfaceData &eth_data,
1074 const std::shared_ptr<AsyncResp> &asyncResp)
1075 {
1076 if (input.is_string())
1077 {
1078 std::string newHostname = input.get<std::string>();
1079
1080 if (eth_data.hostname == nullptr ||
1081 newHostname != *eth_data.hostname)
1082 {
1083 // Change hostname
1084 ethernetProvider.setHostName(
1085 newHostname, [asyncResp, newHostname](
1086 const boost::system::error_code ec) {
1087 if (ec)
1088 {
1089 messages::addMessageToJson(
1090 asyncResp->res.jsonValue,
1091 messages::internalError(), "/HostName");
1092 }
1093 else
1094 {
1095 asyncResp->res.jsonValue["HostName"] = newHostname;
1096 }
1097 });
1098 }
1099 }
1100 else
1101 {
1102 messages::addMessageToJson(
1103 asyncResp->res.jsonValue,
1104 messages::propertyValueTypeError(input.dump(), "HostName"),
1105 "/HostName");
1106 }
1107 }
1108
1109 void handleIPv4Patch(const std::string &ifaceId,
1110 const nlohmann::json &input,
1111 const std::vector<IPv4AddressData> &ipv4_data,
1112 const std::shared_ptr<AsyncResp> &asyncResp)
1113 {
1114 if (!input.is_array())
1115 {
1116 messages::addMessageToJson(
1117 asyncResp->res.jsonValue,
1118 messages::propertyValueTypeError(input.dump(), "IPv4Addresses"),
1119 "/IPv4Addresses");
1120 return;
1121 }
1122
1123 // According to Redfish PATCH definition, size must be at least equal
1124 if (input.size() < ipv4_data.size())
1125 {
1126 // TODO(kkowalsk) This should be a message indicating that not
1127 // enough data has been provided
1128 messages::addMessageToJson(asyncResp->res.jsonValue,
1129 messages::internalError(),
1130 "/IPv4Addresses");
1131 return;
1132 }
1133
1134 json_util::Result addressFieldState;
1135 json_util::Result subnetMaskFieldState;
1136 json_util::Result addressOriginFieldState;
1137 json_util::Result gatewayFieldState;
1138 const std::string *addressFieldValue;
1139 const std::string *subnetMaskFieldValue;
1140 const std::string *addressOriginFieldValue = nullptr;
1141 const std::string *gatewayFieldValue;
1142 uint8_t subnetMaskAsPrefixLength;
1143 std::string addressOriginInDBusFormat;
1144
1145 bool errorDetected = false;
1146 for (unsigned int entryIdx = 0; entryIdx < input.size(); entryIdx++)
1147 {
1148 // Check that entry is not of some unexpected type
1149 if (!input[entryIdx].is_object() && !input[entryIdx].is_null())
1150 {
1151 // Invalid object type
1152 messages::addMessageToJson(
1153 asyncResp->res.jsonValue,
1154 messages::propertyValueTypeError(input[entryIdx].dump(),
1155 "IPv4Address"),
1156 "/IPv4Addresses/" + std::to_string(entryIdx));
1157
1158 continue;
1159 }
1160
1161 // Try to load fields
1162 addressFieldState = json_util::getString(
1163 "Address", input[entryIdx], addressFieldValue,
1164 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1165 asyncResp->res.jsonValue,
1166 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1167 subnetMaskFieldState = json_util::getString(
1168 "SubnetMask", input[entryIdx], subnetMaskFieldValue,
1169 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1170 asyncResp->res.jsonValue,
1171 "/IPv4Addresses/" + std::to_string(entryIdx) + "/SubnetMask");
1172 addressOriginFieldState = json_util::getString(
1173 "AddressOrigin", input[entryIdx], addressOriginFieldValue,
1174 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1175 asyncResp->res.jsonValue,
1176 "/IPv4Addresses/" + std::to_string(entryIdx) +
1177 "/AddressOrigin");
1178 gatewayFieldState = json_util::getString(
1179 "Gateway", input[entryIdx], gatewayFieldValue,
1180 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
1181 asyncResp->res.jsonValue,
1182 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1183
1184 if (addressFieldState == json_util::Result::WRONG_TYPE ||
1185 subnetMaskFieldState == json_util::Result::WRONG_TYPE ||
1186 addressOriginFieldState == json_util::Result::WRONG_TYPE ||
1187 gatewayFieldState == json_util::Result::WRONG_TYPE)
1188 {
1189 return;
1190 }
1191
1192 if (addressFieldState == json_util::Result::SUCCESS &&
1193 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1194 *addressFieldValue))
1195 {
1196 errorDetected = true;
1197 messages::addMessageToJson(
1198 asyncResp->res.jsonValue,
1199 messages::propertyValueFormatError(*addressFieldValue,
1200 "Address"),
1201 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Address");
1202 }
1203
1204 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1205 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1206 *subnetMaskFieldValue, &subnetMaskAsPrefixLength))
1207 {
1208 errorDetected = true;
1209 messages::addMessageToJson(
1210 asyncResp->res.jsonValue,
1211 messages::propertyValueFormatError(*subnetMaskFieldValue,
1212 "SubnetMask"),
1213 "/IPv4Addresses/" + std::to_string(entryIdx) +
1214 "/SubnetMask");
1215 }
1216
1217 // get Address origin in proper format
1218 addressOriginInDBusFormat =
1219 ethernetProvider.translateAddressOriginBetweenDBusAndRedfish(
1220 addressOriginFieldValue, true, false);
1221
1222 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1223 addressOriginInDBusFormat.empty())
1224 {
1225 errorDetected = true;
1226 messages::addMessageToJson(
1227 asyncResp->res.jsonValue,
1228 messages::propertyValueNotInList(*addressOriginFieldValue,
1229 "AddressOrigin"),
1230 "/IPv4Addresses/" + std::to_string(entryIdx) +
1231 "/AddressOrigin");
1232 }
1233
1234 if (gatewayFieldState == json_util::Result::SUCCESS &&
1235 !ethernetProvider.ipv4VerifyIpAndGetBitcount(
1236 *gatewayFieldValue))
1237 {
1238 errorDetected = true;
1239 messages::addMessageToJson(
1240 asyncResp->res.jsonValue,
1241 messages::propertyValueFormatError(*gatewayFieldValue,
1242 "Gateway"),
1243 "/IPv4Addresses/" + std::to_string(entryIdx) + "/Gateway");
1244 }
1245
1246 // If any error occured do not proceed with current entry, but do
1247 // not end loop
1248 if (errorDetected)
1249 {
1250 errorDetected = false;
1251 continue;
1252 }
1253
1254 if (entryIdx >= ipv4_data.size())
1255 {
1256 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1257 input[entryIdx];
1258
1259 // Verify that all field were provided
1260 if (addressFieldState == json_util::Result::NOT_EXIST)
1261 {
1262 errorDetected = true;
1263 messages::addMessageToJson(
1264 asyncResp->res.jsonValue,
1265 messages::propertyMissing("Address"),
1266 "/IPv4Addresses/" + std::to_string(entryIdx) +
1267 "/Address");
1268 }
1269
1270 if (subnetMaskFieldState == json_util::Result::NOT_EXIST)
1271 {
1272 errorDetected = true;
1273 messages::addMessageToJson(
1274 asyncResp->res.jsonValue,
1275 messages::propertyMissing("SubnetMask"),
1276 "/IPv4Addresses/" + std::to_string(entryIdx) +
1277 "/SubnetMask");
1278 }
1279
1280 if (addressOriginFieldState == json_util::Result::NOT_EXIST)
1281 {
1282 errorDetected = true;
1283 messages::addMessageToJson(
1284 asyncResp->res.jsonValue,
1285 messages::propertyMissing("AddressOrigin"),
1286 "/IPv4Addresses/" + std::to_string(entryIdx) +
1287 "/AddressOrigin");
1288 }
1289
1290 if (gatewayFieldState == json_util::Result::NOT_EXIST)
1291 {
1292 errorDetected = true;
1293 messages::addMessageToJson(
1294 asyncResp->res.jsonValue,
1295 messages::propertyMissing("Gateway"),
1296 "/IPv4Addresses/" + std::to_string(entryIdx) +
1297 "/Gateway");
1298 }
1299
1300 // If any error occured do not proceed with current entry, but
1301 // do not end loop
1302 if (errorDetected)
1303 {
1304 errorDetected = false;
1305 continue;
1306 }
1307
1308 // Create IPv4 with provided data
1309 ethernetProvider.createIPv4(
1310 ifaceId, entryIdx, subnetMaskAsPrefixLength,
1311 *gatewayFieldValue, *addressFieldValue, asyncResp);
1312 }
1313 else
1314 {
1315 // Existing object that should be modified/deleted/remain
1316 // unchanged
1317 if (input[entryIdx].is_null())
1318 {
1319 // Object should be deleted
1320 ethernetProvider.deleteIPv4(ifaceId, ipv4_data[entryIdx].id,
1321 entryIdx, asyncResp);
1322 }
1323 else if (input[entryIdx].is_object())
1324 {
1325 if (input[entryIdx].size() == 0)
1326 {
1327 // Object shall remain unchanged
1328 continue;
1329 }
1330
1331 // Apply changes
1332 if (addressFieldState == json_util::Result::SUCCESS &&
1333 ipv4_data[entryIdx].address != nullptr &&
1334 *ipv4_data[entryIdx].address != *addressFieldValue)
1335 {
1336 ethernetProvider.changeIPv4AddressProperty(
1337 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1338 "Address", *addressFieldValue, asyncResp);
1339 }
1340
1341 if (subnetMaskFieldState == json_util::Result::SUCCESS &&
1342 ipv4_data[entryIdx].netmask != *subnetMaskFieldValue)
1343 {
1344 ethernetProvider.changeIPv4SubnetMaskProperty(
1345 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1346 *subnetMaskFieldValue, subnetMaskAsPrefixLength,
1347 asyncResp);
1348 }
1349
1350 if (addressOriginFieldState == json_util::Result::SUCCESS &&
1351 ipv4_data[entryIdx].origin != *addressFieldValue)
1352 {
1353 ethernetProvider.changeIPv4Origin(
1354 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1355 *addressOriginFieldValue, addressOriginInDBusFormat,
1356 asyncResp);
1357 }
1358
1359 if (gatewayFieldState == json_util::Result::SUCCESS &&
1360 ipv4_data[entryIdx].gateway != nullptr &&
1361 *ipv4_data[entryIdx].gateway != *gatewayFieldValue)
1362 {
1363 ethernetProvider.changeIPv4AddressProperty(
1364 ifaceId, entryIdx, ipv4_data[entryIdx].id,
1365 "Gateway", *gatewayFieldValue, asyncResp);
1366 }
1367 }
1368 }
1369 }
1370 }
1371
1372 nlohmann::json
1373 parseInterfaceData(const std::string &ifaceId,
1374 const EthernetInterfaceData &eth_data,
1375 const std::vector<IPv4AddressData> &ipv4_data)
1376 {
1377 // Copy JSON object to avoid race condition
1378 nlohmann::json jsonResponse(Node::json);
1379
1380 // Fill out obvious data...
1381 jsonResponse["Id"] = ifaceId;
1382 jsonResponse["@odata.id"] =
1383 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId;
1384
1385 // ... then the one from DBus, regarding eth iface...
1386 if (eth_data.speed != nullptr)
1387 jsonResponse["SpeedMbps"] = *eth_data.speed;
1388
1389 if (eth_data.macAddress != nullptr)
1390 jsonResponse["MACAddress"] = *eth_data.macAddress;
1391
1392 if (eth_data.hostname != nullptr)
1393 jsonResponse["HostName"] = *eth_data.hostname;
1394
1395 if (eth_data.vlanId != nullptr)
1396 {
1397 nlohmann::json &vlanObj = jsonResponse["VLAN"];
1398 vlanObj["VLANEnable"] = true;
1399 vlanObj["VLANId"] = *eth_data.vlanId;
1400 }
1401 else
1402 {
1403 nlohmann::json &vlanObj = jsonResponse["VLANs"];
1404 vlanObj["@odata.id"] =
1405 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" + ifaceId +
1406 "/VLANs";
1407 }
1408
1409 // ... at last, check if there are IPv4 data and prepare appropriate
1410 // collection
1411 if (ipv4_data.size() > 0)
1412 {
1413 nlohmann::json ipv4Array = nlohmann::json::array();
1414 for (auto &ipv4Config : ipv4_data)
1415 {
1416 nlohmann::json jsonIpv4;
1417 if (ipv4Config.address != nullptr)
1418 {
1419 jsonIpv4["Address"] = *ipv4Config.address;
1420 if (ipv4Config.gateway != nullptr)
1421 jsonIpv4["Gateway"] = *ipv4Config.gateway;
1422
1423 jsonIpv4["AddressOrigin"] = ipv4Config.origin;
1424 jsonIpv4["SubnetMask"] = ipv4Config.netmask;
1425
1426 ipv4Array.push_back(std::move(jsonIpv4));
1427 }
1428 }
1429 jsonResponse["IPv4Addresses"] = std::move(ipv4Array);
1430 }
1431
1432 return jsonResponse;
1433 }
1434
1435 /**
1436 * Functions triggers appropriate requests on DBus
1437 */
1438 void doGet(crow::Response &res, const crow::Request &req,
1439 const std::vector<std::string> &params) override
1440 {
1441 // TODO(Pawel) this shall be parametrized call (two params) to get
1442 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1443 // Check if there is required param, truly entering this shall be
1444 // impossible.
1445 if (params.size() != 1)
1446 {
1447 res.result(boost::beast::http::status::internal_server_error);
1448 res.end();
1449 return;
1450 }
1451
1452 const std::string &ifaceId = params[0];
1453
1454 // get single eth interface data, and call the below callback for JSON
1455 // preparation
1456 ethernetProvider.getEthernetIfaceData(
1457 ifaceId,
1458 [&, ifaceId](const bool &success,
1459 const EthernetInterfaceData &eth_data,
1460 const std::vector<IPv4AddressData> &ipv4_data) {
1461 if (success)
1462 {
1463 res.jsonValue =
1464 parseInterfaceData(ifaceId, eth_data, ipv4_data);
1465 }
1466 else
1467 {
1468 // ... otherwise return error
1469 // TODO(Pawel)consider distinguish between non existing
1470 // object, and other errors
1471 messages::addMessageToErrorJson(
1472 res.jsonValue, messages::resourceNotFound(
1473 "EthernetInterface", ifaceId));
1474 res.result(boost::beast::http::status::not_found);
1475 }
1476 res.end();
1477 });
1478 }
1479
1480 void doPatch(crow::Response &res, const crow::Request &req,
1481 const std::vector<std::string> &params) override
1482 {
1483 // TODO(Pawel) this shall be parametrized call (two params) to get
1484 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1485 // Check if there is required param, truly entering this shall be
1486 // impossible.
1487 if (params.size() != 1)
1488 {
1489 res.result(boost::beast::http::status::internal_server_error);
1490 res.end();
1491 return;
1492 }
1493
1494 const std::string &ifaceId = params[0];
1495
1496 nlohmann::json patchReq;
1497
1498 if (!json_util::processJsonFromRequest(res, req, patchReq))
1499 {
1500 return;
1501 }
1502
1503 // get single eth interface data, and call the below callback for JSON
1504 // preparation
1505 ethernetProvider.getEthernetIfaceData(
1506 ifaceId,
1507 [&, ifaceId, patchReq = std::move(patchReq)](
1508 const bool &success, const EthernetInterfaceData &eth_data,
1509 const std::vector<IPv4AddressData> &ipv4_data) {
1510 if (!success)
1511 {
1512 // ... otherwise return error
1513 // TODO(Pawel)consider distinguish between non existing
1514 // object, and other errors
1515 messages::addMessageToErrorJson(
1516 res.jsonValue, messages::resourceNotFound(
1517 "VLAN Network Interface", ifaceId));
1518 res.result(boost::beast::http::status::not_found);
1519 res.end();
1520
1521 return;
1522 }
1523
1524 res.jsonValue =
1525 parseInterfaceData(ifaceId, eth_data, ipv4_data);
1526
1527 std::shared_ptr<AsyncResp> asyncResp =
1528 std::make_shared<AsyncResp>(res);
1529
1530 for (auto propertyIt = patchReq.begin();
1531 propertyIt != patchReq.end(); ++propertyIt)
1532 {
1533 if (propertyIt.key() == "VLAN")
1534 {
1535 handleVlanPatch(ifaceId, propertyIt.value(), eth_data,
1536 "/VLAN", asyncResp);
1537 }
1538 else if (propertyIt.key() == "HostName")
1539 {
1540 handleHostnamePatch(propertyIt.value(), eth_data,
1541 asyncResp);
1542 }
1543 else if (propertyIt.key() == "IPv4Addresses")
1544 {
1545 handleIPv4Patch(ifaceId, propertyIt.value(), ipv4_data,
1546 asyncResp);
1547 }
1548 else if (propertyIt.key() == "IPv6Addresses")
1549 {
1550 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1551 messages::addMessageToJsonRoot(
1552 res.jsonValue,
1553 messages::propertyNotWritable(propertyIt.key()));
1554 }
1555 else
1556 {
1557 auto fieldInJsonIt =
1558 res.jsonValue.find(propertyIt.key());
1559
1560 if (fieldInJsonIt == res.jsonValue.end())
1561 {
1562 // Field not in scope of defined fields
1563 messages::addMessageToJsonRoot(
1564 res.jsonValue,
1565 messages::propertyUnknown(propertyIt.key()));
1566 }
1567 else if (*fieldInJsonIt != *propertyIt)
1568 {
1569 // User attempted to modify non-writable field
1570 messages::addMessageToJsonRoot(
1571 res.jsonValue, messages::propertyNotWritable(
1572 propertyIt.key()));
1573 }
1574 }
1575 }
1576 });
1577 }
1578
1579 // Ethernet Provider object
1580 // TODO(Pawel) consider move it to singleton
1581 OnDemandEthernetProvider ethernetProvider;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001582};
1583
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001584class VlanNetworkInterfaceCollection;
1585
1586/**
1587 * VlanNetworkInterface derived class for delivering VLANNetworkInterface Schema
1588 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001589class VlanNetworkInterface : public Node
1590{
1591 public:
1592 /*
1593 * Default Constructor
1594 */
1595 template <typename CrowApp>
1596 // TODO(Pawel) Remove line from below, where we assume that there is only
1597 // one manager called openbmc This shall be generic, but requires to update
1598 // GetSubroutes method
1599 VlanNetworkInterface(CrowApp &app) :
1600 Node(
Kowalski, Kamileb547732018-05-16 10:25:07 +02001601 app,
1602 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous1abe55e2018-09-05 08:30:59 -07001603 std::string(), std::string())
1604 {
1605 Node::json["@odata.type"] =
1606 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1607 Node::json["@odata.context"] =
1608 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1609 Node::json["Name"] = "VLAN Network Interface";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001610
Ed Tanous1abe55e2018-09-05 08:30:59 -07001611 entityPrivileges = {
1612 {boost::beast::http::verb::get, {{"Login"}}},
1613 {boost::beast::http::verb::head, {{"Login"}}},
1614 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1615 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1616 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1617 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001618 }
1619
Ed Tanous1abe55e2018-09-05 08:30:59 -07001620 private:
1621 nlohmann::json
1622 parseInterfaceData(const std::string &parent_ifaceId,
1623 const std::string &ifaceId,
1624 const EthernetInterfaceData &eth_data,
1625 const std::vector<IPv4AddressData> &ipv4_data)
1626 {
1627 // Copy JSON object to avoid race condition
1628 nlohmann::json jsonResponse(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001629
Ed Tanous1abe55e2018-09-05 08:30:59 -07001630 // Fill out obvious data...
1631 jsonResponse["Id"] = ifaceId;
1632 jsonResponse["@odata.id"] =
1633 "/redfish/v1/Managers/openbmc/EthernetInterfaces/" +
1634 parent_ifaceId + "/VLANs/" + ifaceId;
1635
1636 jsonResponse["VLANEnable"] = true;
1637 jsonResponse["VLANId"] = *eth_data.vlanId;
1638
1639 return jsonResponse;
Ed Tanousa434f2b2018-07-27 13:04:22 -07001640 }
1641
Ed Tanous1abe55e2018-09-05 08:30:59 -07001642 bool verifyNames(crow::Response &res, const std::string &parent,
1643 const std::string &iface)
1644 {
1645 if (!boost::starts_with(iface, parent + "_"))
1646 {
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001647 messages::addMessageToErrorJson(
Ed Tanous55c7b7a2018-05-22 15:27:24 -07001648 res.jsonValue,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001649 messages::resourceNotFound("VLAN Network Interface", iface));
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001650 res.result(boost::beast::http::status::not_found);
1651 res.end();
1652
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 return false;
1654 }
1655 else
1656 {
1657 return true;
1658 }
1659 }
1660
1661 /**
1662 * Functions triggers appropriate requests on DBus
1663 */
1664 void doGet(crow::Response &res, const crow::Request &req,
1665 const std::vector<std::string> &params) override
1666 {
1667 // TODO(Pawel) this shall be parametrized call (two params) to get
1668 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1669 // Check if there is required param, truly entering this shall be
1670 // impossible.
1671 if (params.size() != 2)
1672 {
1673 res.result(boost::beast::http::status::internal_server_error);
1674 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001675 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001676 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001677
Ed Tanous1abe55e2018-09-05 08:30:59 -07001678 const std::string &parentIfaceId = params[0];
1679 const std::string &ifaceId = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001680
Ed Tanous1abe55e2018-09-05 08:30:59 -07001681 if (!verifyNames(res, parentIfaceId, ifaceId))
1682 {
1683 return;
1684 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001685
Ed Tanous1abe55e2018-09-05 08:30:59 -07001686 // Get single eth interface data, and call the below callback for JSON
1687 // preparation
1688 ethernetProvider.getEthernetIfaceData(
1689 ifaceId, [&, parentIfaceId,
1690 ifaceId](const bool &success,
1691 const EthernetInterfaceData &eth_data,
1692 const std::vector<IPv4AddressData> &ipv4_data) {
1693 if (success && eth_data.vlanId != nullptr)
1694 {
1695 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1696 eth_data, ipv4_data);
1697 }
1698 else
1699 {
1700 // ... otherwise return error
1701 // TODO(Pawel)consider distinguish between non existing
1702 // object, and other errors
1703 messages::addMessageToErrorJson(
1704 res.jsonValue, messages::resourceNotFound(
1705 "VLAN Network Interface", ifaceId));
1706 res.result(boost::beast::http::status::not_found);
1707 }
1708 res.end();
1709 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001710 }
1711
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 void doPatch(crow::Response &res, const crow::Request &req,
1713 const std::vector<std::string> &params) override
1714 {
1715 if (params.size() != 2)
1716 {
1717 res.result(boost::beast::http::status::internal_server_error);
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001718 res.end();
Ed Tanous1abe55e2018-09-05 08:30:59 -07001719 return;
1720 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001721
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722 const std::string &parentIfaceId = params[0];
1723 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001724
Ed Tanous1abe55e2018-09-05 08:30:59 -07001725 if (!verifyNames(res, parentIfaceId, ifaceId))
1726 {
1727 return;
1728 }
1729
1730 nlohmann::json patchReq;
1731
1732 if (!json_util::processJsonFromRequest(res, req, patchReq))
1733 {
1734 return;
1735 }
1736
1737 // Get single eth interface data, and call the below callback for JSON
1738 // preparation
1739 ethernetProvider.getEthernetIfaceData(
1740 ifaceId,
1741 [&, parentIfaceId, ifaceId, patchReq = std::move(patchReq)](
1742 const bool &success, const EthernetInterfaceData &eth_data,
1743 const std::vector<IPv4AddressData> &ipv4_data) {
1744 if (!success)
1745 {
1746 // ... otherwise return error
1747 // TODO(Pawel)consider distinguish between non existing
1748 // object, and other errors
1749 messages::addMessageToErrorJson(
1750 res.jsonValue, messages::resourceNotFound(
1751 "VLAN Network Interface", ifaceId));
1752 res.result(boost::beast::http::status::not_found);
1753 res.end();
1754
1755 return;
1756 }
1757
1758 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1759 eth_data, ipv4_data);
1760
1761 std::shared_ptr<AsyncResp> asyncResp =
1762 std::make_shared<AsyncResp>(res);
1763
1764 for (auto propertyIt = patchReq.begin();
1765 propertyIt != patchReq.end(); ++propertyIt)
1766 {
1767 if (propertyIt.key() != "VLANEnable" &&
1768 propertyIt.key() != "VLANId")
1769 {
1770 auto fieldInJsonIt =
1771 res.jsonValue.find(propertyIt.key());
1772
1773 if (fieldInJsonIt == res.jsonValue.end())
1774 {
1775 // Field not in scope of defined fields
1776 messages::addMessageToJsonRoot(
1777 res.jsonValue,
1778 messages::propertyUnknown(propertyIt.key()));
1779 }
1780 else if (*fieldInJsonIt != *propertyIt)
1781 {
1782 // User attempted to modify non-writable field
1783 messages::addMessageToJsonRoot(
1784 res.jsonValue, messages::propertyNotWritable(
1785 propertyIt.key()));
1786 }
1787 }
1788 }
1789
1790 EthernetInterface::handleVlanPatch(ifaceId, patchReq, eth_data,
1791 "/", asyncResp);
1792 });
1793 }
1794
1795 void doDelete(crow::Response &res, const crow::Request &req,
1796 const std::vector<std::string> &params) override
1797 {
1798 if (params.size() != 2)
1799 {
1800 res.result(boost::beast::http::status::internal_server_error);
1801 res.end();
1802 return;
1803 }
1804
1805 const std::string &parentIfaceId = params[0];
1806 const std::string &ifaceId = params[1];
1807
1808 if (!verifyNames(res, parentIfaceId, ifaceId))
1809 {
1810 return;
1811 }
1812
1813 // Get single eth interface data, and call the below callback for JSON
1814 // preparation
1815 ethernetProvider.getEthernetIfaceData(
1816 ifaceId, [&, parentIfaceId,
1817 ifaceId](const bool &success,
1818 const EthernetInterfaceData &eth_data,
1819 const std::vector<IPv4AddressData> &ipv4_data) {
1820 if (success && eth_data.vlanId != nullptr)
1821 {
1822 res.jsonValue = parseInterfaceData(parentIfaceId, ifaceId,
1823 eth_data, ipv4_data);
1824
1825 // Disable VLAN
1826 OnDemandEthernetProvider::disableVlan(
1827 ifaceId, [&](const boost::system::error_code ec) {
1828 if (ec)
1829 {
1830 res.jsonValue = nlohmann::json::object();
1831 messages::addMessageToErrorJson(
1832 res.jsonValue, messages::internalError());
1833 res.result(boost::beast::http::status::
1834 internal_server_error);
1835 }
1836 res.end();
1837 });
1838 }
1839 else
1840 {
1841 // ... otherwise return error
1842 // TODO(Pawel)consider distinguish between non existing
1843 // object, and other errors
1844 messages::addMessageToErrorJson(
1845 res.jsonValue, messages::resourceNotFound(
1846 "VLAN Network Interface", ifaceId));
1847 res.result(boost::beast::http::status::not_found);
1848 res.end();
1849 }
1850 });
1851 }
1852
1853 /**
1854 * This allows VlanNetworkInterfaceCollection to reuse this class' doGet
1855 * method, to maintain consistency of returned data, as Collection's doPost
1856 * should return data for created member which should match member's doGet
1857 * result in 100%.
1858 */
1859 friend VlanNetworkInterfaceCollection;
1860
1861 // Ethernet Provider object
1862 // TODO(Pawel) consider move it to singleton
1863 OnDemandEthernetProvider ethernetProvider;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001864};
1865
1866/**
1867 * VlanNetworkInterfaceCollection derived class for delivering
1868 * VLANNetworkInterface Collection Schema
1869 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001870class VlanNetworkInterfaceCollection : public Node
1871{
1872 public:
1873 template <typename CrowApp>
1874 // TODO(Pawel) Remove line from below, where we assume that there is only
1875 // one manager called openbmc This shall be generic, but requires to update
1876 // GetSubroutes method
1877 VlanNetworkInterfaceCollection(CrowApp &app) :
1878 Node(app,
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001879 "/redfish/v1/Managers/openbmc/EthernetInterfaces/<str>/VLANs/",
1880 std::string()),
Ed Tanous1abe55e2018-09-05 08:30:59 -07001881 memberVlan(app)
1882 {
1883 Node::json["@odata.type"] =
1884 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1885 Node::json["@odata.context"] =
1886 "/redfish/v1/$metadata"
1887 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1888 Node::json["Name"] = "VLAN Network Interface Collection";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001889
Ed Tanous1abe55e2018-09-05 08:30:59 -07001890 entityPrivileges = {
1891 {boost::beast::http::verb::get, {{"Login"}}},
1892 {boost::beast::http::verb::head, {{"Login"}}},
1893 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1894 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1895 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1896 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001897 }
1898
Ed Tanous1abe55e2018-09-05 08:30:59 -07001899 private:
1900 /**
1901 * Functions triggers appropriate requests on DBus
1902 */
1903 void doGet(crow::Response &res, const crow::Request &req,
1904 const std::vector<std::string> &params) override
1905 {
1906 if (params.size() != 1)
1907 {
1908 // This means there is a problem with the router
1909 res.result(boost::beast::http::status::internal_server_error);
1910 res.end();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001911
Ed Tanous1abe55e2018-09-05 08:30:59 -07001912 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001913 }
1914
Ed Tanous1abe55e2018-09-05 08:30:59 -07001915 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1916 // for any Manager, not only hardcoded 'openbmc'.
1917 std::string managerId = "openbmc";
1918 std::string rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001919
Ed Tanous1abe55e2018-09-05 08:30:59 -07001920 // get eth interface list, and call the below callback for JSON
1921 // preparation
1922 ethernetProvider.getEthernetIfaceList(
1923 [&, managerId{std::move(managerId)},
1924 rootInterfaceName{std::move(rootInterfaceName)}](
1925 const bool &success,
1926 const std::vector<std::string> &iface_list) {
1927 if (success)
1928 {
1929 bool rootInterfaceFound = false;
1930 nlohmann::json ifaceArray = nlohmann::json::array();
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001931
Ed Tanous1abe55e2018-09-05 08:30:59 -07001932 for (const std::string &ifaceItem : iface_list)
1933 {
1934 if (ifaceItem == rootInterfaceName)
1935 {
1936 rootInterfaceFound = true;
1937 }
1938 else if (boost::starts_with(ifaceItem,
1939 rootInterfaceName + "_"))
1940 {
1941 ifaceArray.push_back(
1942 {{"@odata.id", "/redfish/v1/Managers/" +
1943 managerId +
1944 "/EthernetInterfaces/" +
1945 rootInterfaceName +
1946 "/VLANs/" + ifaceItem}});
1947 }
1948 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001949
Ed Tanous1abe55e2018-09-05 08:30:59 -07001950 if (rootInterfaceFound)
1951 {
1952 Node::json["Members"] = ifaceArray;
1953 Node::json["Members@odata.count"] = ifaceArray.size();
1954 Node::json["@odata.id"] = "/redfish/v1/Managers/" +
1955 managerId +
1956 "/EthernetInterfaces/" +
1957 rootInterfaceName + "/VLANs";
1958 res.jsonValue = Node::json;
1959 }
1960 else
1961 {
1962 messages::addMessageToErrorJson(
1963 res.jsonValue,
1964 messages::resourceNotFound("EthernetInterface",
1965 rootInterfaceName));
1966 res.result(boost::beast::http::status::not_found);
1967 res.end();
1968 }
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001969 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001970 else
1971 {
1972 // No success, best what we can do is return INTERNALL ERROR
1973 res.result(
1974 boost::beast::http::status::internal_server_error);
1975 }
1976 res.end();
1977 });
1978 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001979
Ed Tanous1abe55e2018-09-05 08:30:59 -07001980 void doPost(crow::Response &res, const crow::Request &req,
1981 const std::vector<std::string> &params) override
1982 {
1983 if (params.size() != 1)
1984 {
1985 // This means there is a problem with the router
1986 res.result(boost::beast::http::status::internal_server_error);
1987 res.end();
1988 return;
1989 }
1990
1991 nlohmann::json postReq;
1992
1993 if (!json_util::processJsonFromRequest(res, req, postReq))
1994 {
1995 return;
1996 }
1997
1998 // TODO(Pawel) this shall be parametrized call to get EthernetInterfaces
1999 // for any Manager, not only hardcoded 'openbmc'.
2000 std::string managerId = "openbmc";
2001 std::string rootInterfaceName = params[0];
2002 uint64_t vlanId;
2003 bool errorDetected;
2004
2005 if (json_util::getUnsigned(
2006 "VLANId", postReq, vlanId,
2007 static_cast<uint8_t>(json_util::MessageSetting::MISSING) |
2008 static_cast<uint8_t>(json_util::MessageSetting::TYPE_ERROR),
2009 res.jsonValue, "/VLANId") != json_util::Result::SUCCESS)
2010 {
2011 res.end();
2012 return;
2013 }
2014
2015 // get eth interface list, and call the below callback for JSON
2016 // preparation
2017 ethernetProvider.getEthernetIfaceList(
2018 [&, managerId{std::move(managerId)},
2019 rootInterfaceName{std::move(rootInterfaceName)}](
2020 const bool &success,
2021 const std::vector<std::string> &iface_list) {
2022 if (success)
2023 {
2024 bool rootInterfaceFound = false;
2025
2026 for (const std::string &ifaceItem : iface_list)
2027 {
2028 if (ifaceItem == rootInterfaceName)
2029 {
2030 rootInterfaceFound = true;
2031 break;
2032 }
2033 }
2034
2035 if (rootInterfaceFound)
2036 {
2037 ethernetProvider.createVlan(
2038 rootInterfaceName, vlanId,
2039 [&, vlanId, rootInterfaceName, req{std::move(req)}](
2040 const boost::system::error_code ec) {
2041 if (ec)
2042 {
2043 messages::addMessageToErrorJson(
2044 res.jsonValue,
2045 messages::internalError());
2046 res.end();
2047 }
2048 else
2049 {
2050 memberVlan.doGet(
2051 res, req,
2052 {rootInterfaceName,
2053 rootInterfaceName + "_" +
2054 std::to_string(vlanId)});
2055 }
2056 });
2057 }
2058 else
2059 {
2060 messages::addMessageToErrorJson(
2061 res.jsonValue,
2062 messages::resourceNotFound("EthernetInterface",
2063 rootInterfaceName));
2064 res.result(boost::beast::http::status::not_found);
2065 res.end();
2066 }
2067 }
2068 else
2069 {
2070 // No success, best what we can do is return INTERNALL ERROR
2071 res.result(
2072 boost::beast::http::status::internal_server_error);
2073 res.end();
2074 }
2075 });
2076 }
2077
2078 // Ethernet Provider object
2079 // TODO(Pawel) consider move it to singleton
2080 OnDemandEthernetProvider ethernetProvider;
2081 VlanNetworkInterface memberVlan;
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02002082};
2083
Ed Tanous1abe55e2018-09-05 08:30:59 -07002084} // namespace redfish