blob: 914758538a859c5b8881e63ea77a2b8ea1f68e5a [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>
Ed Tanous4a0cb852018-10-15 07:55:04 -070019#include <boost/container/flat_set.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020020#include <dbus_singleton.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020021#include <error_messages.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020022#include <node.hpp>
Ed Tanousa24526d2018-12-10 15:17:59 -080023#include <optional>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020024#include <utils/json_utils.hpp>
Ed Tanousabf2add2019-01-22 16:40:12 -080025#include <variant>
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010026
Ed Tanous1abe55e2018-09-05 08:30:59 -070027namespace redfish
28{
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010029
30/**
31 * DBus types primitives for several generic DBus interfaces
32 * TODO(Pawel) consider move this to separate file into boost::dbus
33 */
Ed Tanousaa2e59c2018-04-12 12:17:20 -070034using PropertiesMapType = boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -080035 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
36 int32_t, uint32_t, int64_t, uint64_t, double>>;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010037
Ed Tanous4a0cb852018-10-15 07:55:04 -070038using GetManagedObjects = std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070039 sdbusplus::message::object_path,
Ed Tanous4a0cb852018-10-15 07:55:04 -070040 std::vector<std::pair<
Ed Tanousaa2e59c2018-04-12 12:17:20 -070041 std::string,
42 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -080043 std::string,
44 std::variant<std::string, bool, uint8_t, int16_t, uint16_t, int32_t,
45 uint32_t, int64_t, uint64_t, double>>>>>>;
Ed Tanous4a0cb852018-10-15 07:55:04 -070046
47enum class LinkType
48{
49 Local,
50 Global
51};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010052
53/**
54 * Structure for keeping IPv4 data required by Redfish
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010055 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070056struct IPv4AddressData
57{
58 std::string id;
Ed Tanous4a0cb852018-10-15 07:55:04 -070059 std::string address;
60 std::string domain;
61 std::string gateway;
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 std::string netmask;
63 std::string origin;
Ed Tanous4a0cb852018-10-15 07:55:04 -070064 LinkType linktype;
65
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 bool operator<(const IPv4AddressData &obj) const
67 {
Ed Tanous4a0cb852018-10-15 07:55:04 -070068 return id < obj.id;
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010070};
71
72/**
73 * Structure for keeping basic single Ethernet Interface information
74 * available from DBus
75 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070076struct EthernetInterfaceData
77{
Ed Tanous4a0cb852018-10-15 07:55:04 -070078 uint32_t speed;
79 bool auto_neg;
80 std::string hostname;
81 std::string default_gateway;
82 std::string mac_address;
Ed Tanousa24526d2018-12-10 15:17:59 -080083 std::optional<uint32_t> vlan_id;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010084};
85
Ed Tanous4a0cb852018-10-15 07:55:04 -070086// Helper function that changes bits netmask notation (i.e. /24)
87// into full dot notation
88inline std::string getNetmask(unsigned int bits)
Ed Tanous1abe55e2018-09-05 08:30:59 -070089{
Ed Tanous4a0cb852018-10-15 07:55:04 -070090 uint32_t value = 0xffffffff << (32 - bits);
91 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
92 std::to_string((value >> 16) & 0xff) + "." +
93 std::to_string((value >> 8) & 0xff) + "." +
94 std::to_string(value & 0xff);
95 return netmask;
96}
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010097
Ed Tanous4a0cb852018-10-15 07:55:04 -070098inline std::string
99 translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
100 bool isIPv4)
101{
102 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700104 return "Static";
105 }
106 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
107 {
108 if (isIPv4)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700110 return "IPv4LinkLocal";
111 }
112 else
113 {
114 return "LinkLocal";
115 }
116 }
117 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
118 {
119 if (isIPv4)
120 {
121 return "DHCP";
122 }
123 else
124 {
125 return "DHCPv6";
126 }
127 }
128 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
129 {
130 return "SLAAC";
131 }
132 return "";
133}
134
135inline std::string
136 translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
137{
138 if (inputOrigin == "Static")
139 {
140 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
141 }
142 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
143 {
144 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
145 }
146 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
147 {
148 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
149 }
150 if (inputOrigin == "SLAAC")
151 {
152 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
153 }
154 return "";
155}
156
157inline void extractEthernetInterfaceData(const std::string &ethiface_id,
158 const GetManagedObjects &dbus_data,
159 EthernetInterfaceData &ethData)
160{
161 for (const auto &objpath : dbus_data)
162 {
163 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
164 {
165 for (const auto &ifacePair : objpath.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700167 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700168 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700169 for (const auto &propertyPair : ifacePair.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700171 if (propertyPair.first == "MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700173 const std::string *mac =
Ed Tanousabf2add2019-01-22 16:40:12 -0800174 std::get_if<std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700175 if (mac != nullptr)
176 {
177 ethData.mac_address = *mac;
178 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700179 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700180 }
181 }
182 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
183 {
184 for (const auto &propertyPair : ifacePair.second)
185 {
186 if (propertyPair.first == "Id")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700187 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800188 const uint32_t *id =
Ed Tanousabf2add2019-01-22 16:40:12 -0800189 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700190 if (id != nullptr)
191 {
192 ethData.vlan_id = *id;
193 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700194 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700195 }
196 }
197 else if (ifacePair.first ==
198 "xyz.openbmc_project.Network.EthernetInterface")
199 {
200 for (const auto &propertyPair : ifacePair.second)
201 {
202 if (propertyPair.first == "AutoNeg")
203 {
204 const bool *auto_neg =
Ed Tanousabf2add2019-01-22 16:40:12 -0800205 std::get_if<bool>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700206 if (auto_neg != nullptr)
207 {
208 ethData.auto_neg = *auto_neg;
209 }
210 }
211 else if (propertyPair.first == "Speed")
212 {
213 const uint32_t *speed =
Ed Tanousabf2add2019-01-22 16:40:12 -0800214 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700215 if (speed != nullptr)
216 {
217 ethData.speed = *speed;
218 }
219 }
220 }
221 }
222 else if (ifacePair.first ==
223 "xyz.openbmc_project.Network.SystemConfiguration")
224 {
225 for (const auto &propertyPair : ifacePair.second)
226 {
227 if (propertyPair.first == "HostName")
228 {
229 const std::string *hostname =
Ed Tanousabf2add2019-01-22 16:40:12 -0800230 std::get_if<std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700231 if (hostname != nullptr)
232 {
233 ethData.hostname = *hostname;
234 }
235 }
236 else if (propertyPair.first == "DefaultGateway")
237 {
238 const std::string *defaultGateway =
Ed Tanousabf2add2019-01-22 16:40:12 -0800239 std::get_if<std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700240 if (defaultGateway != nullptr)
241 {
242 ethData.default_gateway = *defaultGateway;
243 }
244 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700245 }
246 }
247 }
248 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700250}
251
252// Helper function that extracts data for single ethernet ipv4 address
253inline void
254 extractIPData(const std::string &ethiface_id,
255 const GetManagedObjects &dbus_data,
256 boost::container::flat_set<IPv4AddressData> &ipv4_config)
257{
258 const std::string ipv4PathStart =
259 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
260
261 // Since there might be several IPv4 configurations aligned with
262 // single ethernet interface, loop over all of them
263 for (const auto &objpath : dbus_data)
264 {
265 // Check if proper pattern for object path appears
266 if (boost::starts_with(objpath.first.str, ipv4PathStart))
267 {
268 for (auto &interface : objpath.second)
269 {
270 if (interface.first == "xyz.openbmc_project.Network.IP")
271 {
272 // Instance IPv4AddressData structure, and set as
273 // appropriate
274 std::pair<
275 boost::container::flat_set<IPv4AddressData>::iterator,
276 bool>
277 it = ipv4_config.insert(
278 {objpath.first.str.substr(ipv4PathStart.size())});
279 IPv4AddressData &ipv4_address = *it.first;
280 for (auto &property : interface.second)
281 {
282 if (property.first == "Address")
283 {
284 const std::string *address =
Ed Tanousabf2add2019-01-22 16:40:12 -0800285 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700286 if (address != nullptr)
287 {
288 ipv4_address.address = *address;
289 }
290 }
291 else if (property.first == "Gateway")
292 {
293 const std::string *gateway =
Ed Tanousabf2add2019-01-22 16:40:12 -0800294 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700295 if (gateway != nullptr)
296 {
297 ipv4_address.gateway = *gateway;
298 }
299 }
300 else if (property.first == "Origin")
301 {
302 const std::string *origin =
Ed Tanousabf2add2019-01-22 16:40:12 -0800303 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700304 if (origin != nullptr)
305 {
306 ipv4_address.origin =
307 translateAddressOriginDbusToRedfish(*origin,
308 true);
309 }
310 }
311 else if (property.first == "PrefixLength")
312 {
313 const uint8_t *mask =
Ed Tanousabf2add2019-01-22 16:40:12 -0800314 std::get_if<uint8_t>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700315 if (mask != nullptr)
316 {
317 // convert it to the string
318 ipv4_address.netmask = getNetmask(*mask);
319 }
320 }
321 else
322 {
323 BMCWEB_LOG_ERROR
324 << "Got extra property: " << property.first
325 << " on the " << objpath.first.str << " object";
326 }
327 }
328 // Check if given address is local, or global
329 ipv4_address.linktype =
330 boost::starts_with(ipv4_address.address, "169.254.")
331 ? LinkType::Global
332 : LinkType::Local;
333 }
334 }
335 }
336 }
337}
338
339/**
340 * @brief Sets given Id on the given VLAN interface through D-Bus
341 *
342 * @param[in] ifaceId Id of VLAN interface that should be modified
343 * @param[in] inputVlanId New ID of the VLAN
344 * @param[in] callback Function that will be called after the operation
345 *
346 * @return None.
347 */
348template <typename CallbackFunc>
349void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
350 CallbackFunc &&callback)
351{
352 crow::connections::systemBus->async_method_call(
353 callback, "xyz.openbmc_project.Network",
354 std::string("/xyz/openbmc_project/network/") + ifaceId,
355 "org.freedesktop.DBus.Properties", "Set",
356 "xyz.openbmc_project.Network.VLAN", "Id",
Ed Tanousabf2add2019-01-22 16:40:12 -0800357 std::variant<uint32_t>(inputVlanId));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700358}
359
360/**
361 * @brief Helper function that verifies IP address to check if it is in
362 * proper format. If bits pointer is provided, also calculates active
363 * bit count for Subnet Mask.
364 *
365 * @param[in] ip IP that will be verified
366 * @param[out] bits Calculated mask in bits notation
367 *
368 * @return true in case of success, false otherwise
369 */
370inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
371 uint8_t *bits = nullptr)
372{
373 std::vector<std::string> bytesInMask;
374
375 boost::split(bytesInMask, ip, boost::is_any_of("."));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700376
377 static const constexpr int ipV4AddressSectionsCount = 4;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700378 if (bytesInMask.size() != ipV4AddressSectionsCount)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700379 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700380 return false;
381 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700382
Ed Tanous4a0cb852018-10-15 07:55:04 -0700383 if (bits != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700384 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700385 *bits = 0;
386 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700387
Ed Tanous4a0cb852018-10-15 07:55:04 -0700388 char *endPtr;
389 long previousValue = 255;
390 bool firstZeroInByteHit;
391 for (const std::string &byte : bytesInMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700393 if (byte.empty())
394 {
395 return false;
396 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397
Ed Tanous4a0cb852018-10-15 07:55:04 -0700398 // Use strtol instead of stroi to avoid exceptions
399 long value = std::strtol(byte.c_str(), &endPtr, 10);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700400
Ed Tanous4a0cb852018-10-15 07:55:04 -0700401 // endPtr should point to the end of the string, otherwise given string
402 // is not 100% number
403 if (*endPtr != '\0')
404 {
405 return false;
406 }
407
408 // Value should be contained in byte
409 if (value < 0 || value > 255)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410 {
411 return false;
412 }
413
414 if (bits != nullptr)
415 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700416 // Mask has to be continuous between bytes
417 if (previousValue != 255 && value != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700418 {
419 return false;
420 }
421
Ed Tanous4a0cb852018-10-15 07:55:04 -0700422 // Mask has to be continuous inside bytes
423 firstZeroInByteHit = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700424
Ed Tanous4a0cb852018-10-15 07:55:04 -0700425 // Count bits
426 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700427 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700428 if (value & (1 << bitIdx))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700429 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700430 if (firstZeroInByteHit)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700432 // Continuity not preserved
433 return false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 }
435 else
436 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700437 (*bits)++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 }
439 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700440 else
441 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700442 firstZeroInByteHit = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700443 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700444 }
445 }
446
447 previousValue = value;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
449
Ed Tanous4a0cb852018-10-15 07:55:04 -0700450 return true;
451}
452
453/**
454 * @brief Changes IPv4 address type property (Address, Gateway)
455 *
456 * @param[in] ifaceId Id of interface whose IP should be modified
457 * @param[in] ipIdx Index of IP in input array that should be modified
458 * @param[in] ipHash DBus Hash id of modified IP
459 * @param[in] name Name of field in JSON representation
460 * @param[in] newValue New value that should be written
461 * @param[io] asyncResp Response object that will be returned to client
462 *
463 * @return true if give IP is valid and has been sent do D-Bus, false
464 * otherwise
465 */
466inline void changeIPv4AddressProperty(
467 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
468 const std::string &name, const std::string &newValue,
469 const std::shared_ptr<AsyncResp> asyncResp)
470{
471 auto callback = [asyncResp, ipIdx, name{std::string(name)},
472 newValue{std::move(newValue)}](
473 const boost::system::error_code ec) {
474 if (ec)
475 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800476 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700477 }
478 else
479 {
480 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
481 }
482 };
483
484 crow::connections::systemBus->async_method_call(
485 std::move(callback), "xyz.openbmc_project.Network",
486 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
487 "org.freedesktop.DBus.Properties", "Set",
488 "xyz.openbmc_project.Network.IP", name,
Ed Tanousabf2add2019-01-22 16:40:12 -0800489 std::variant<std::string>(newValue));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700490}
491
492/**
493 * @brief Changes IPv4 address origin property
494 *
495 * @param[in] ifaceId Id of interface whose IP should be modified
496 * @param[in] ipIdx Index of IP in input array that should be
497 * modified
498 * @param[in] ipHash DBus Hash id of modified IP
499 * @param[in] newValue New value in Redfish format
500 * @param[in] newValueDbus New value in D-Bus format
501 * @param[io] asyncResp Response object that will be returned to client
502 *
503 * @return true if give IP is valid and has been sent do D-Bus, false
504 * otherwise
505 */
506inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
507 const std::string &ipHash,
508 const std::string &newValue,
509 const std::string &newValueDbus,
510 const std::shared_ptr<AsyncResp> asyncResp)
511{
512 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
513 const boost::system::error_code ec) {
514 if (ec)
515 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800516 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700517 }
518 else
519 {
520 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
521 newValue;
522 }
523 };
524
525 crow::connections::systemBus->async_method_call(
526 std::move(callback), "xyz.openbmc_project.Network",
527 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
528 "org.freedesktop.DBus.Properties", "Set",
529 "xyz.openbmc_project.Network.IP", "Origin",
Ed Tanousabf2add2019-01-22 16:40:12 -0800530 std::variant<std::string>(newValueDbus));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700531}
532
533/**
534 * @brief Modifies SubnetMask for given IP
535 *
536 * @param[in] ifaceId Id of interface whose IP should be modified
537 * @param[in] ipIdx Index of IP in input array that should be
538 * modified
539 * @param[in] ipHash DBus Hash id of modified IP
540 * @param[in] newValueStr Mask in dot notation as string
541 * @param[in] newValue Mask as PrefixLength in bitcount
542 * @param[io] asyncResp Response object that will be returned to client
543 *
544 * @return None
545 */
546inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
547 const std::string &ipHash,
548 const std::string &newValueStr,
549 uint8_t &newValue,
550 std::shared_ptr<AsyncResp> asyncResp)
551{
552 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
553 const boost::system::error_code ec) {
554 if (ec)
555 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800556 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700557 }
558 else
559 {
560 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
561 newValueStr;
562 }
563 };
564
565 crow::connections::systemBus->async_method_call(
566 std::move(callback), "xyz.openbmc_project.Network",
567 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
568 "org.freedesktop.DBus.Properties", "Set",
569 "xyz.openbmc_project.Network.IP", "PrefixLength",
Ed Tanousabf2add2019-01-22 16:40:12 -0800570 std::variant<uint8_t>(newValue));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700571}
572
573/**
Ed Tanous4a0cb852018-10-15 07:55:04 -0700574 * @brief Deletes given IPv4
575 *
576 * @param[in] ifaceId Id of interface whose IP should be deleted
577 * @param[in] ipIdx Index of IP in input array that should be deleted
578 * @param[in] ipHash DBus Hash id of IP that should be deleted
579 * @param[io] asyncResp Response object that will be returned to client
580 *
581 * @return None
582 */
583inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
584 unsigned int ipIdx,
585 const std::shared_ptr<AsyncResp> asyncResp)
586{
587 crow::connections::systemBus->async_method_call(
588 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700589 if (ec)
590 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800591 messages::internalError(asyncResp->res);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100592 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700593 else
594 {
595 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
596 }
597 },
598 "xyz.openbmc_project.Network",
599 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
600 "xyz.openbmc_project.Object.Delete", "Delete");
601}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700602
Ed Tanous4a0cb852018-10-15 07:55:04 -0700603/**
604 * @brief Creates IPv4 with given data
605 *
606 * @param[in] ifaceId Id of interface whose IP should be deleted
607 * @param[in] ipIdx Index of IP in input array that should be deleted
608 * @param[in] ipHash DBus Hash id of IP that should be deleted
609 * @param[io] asyncResp Response object that will be returned to client
610 *
611 * @return None
612 */
613inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
614 uint8_t subnetMask, const std::string &gateway,
615 const std::string &address,
616 std::shared_ptr<AsyncResp> asyncResp)
617{
618 auto createIpHandler = [ipIdx,
619 asyncResp](const boost::system::error_code ec) {
620 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800622 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700623 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700624 };
625
Ed Tanous4a0cb852018-10-15 07:55:04 -0700626 crow::connections::systemBus->async_method_call(
627 std::move(createIpHandler), "xyz.openbmc_project.Network",
628 "/xyz/openbmc_project/network/" + ifaceId,
629 "xyz.openbmc_project.Network.IP.Create", "IP",
630 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
631 gateway);
632}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700633
Ed Tanous4a0cb852018-10-15 07:55:04 -0700634/**
635 * Function that retrieves all properties for given Ethernet Interface
636 * Object
637 * from EntityManager Network Manager
638 * @param ethiface_id a eth interface id to query on DBus
639 * @param callback a function that shall be called to convert Dbus output
640 * into JSON
641 */
642template <typename CallbackFunc>
643void getEthernetIfaceData(const std::string &ethiface_id,
644 CallbackFunc &&callback)
645{
646 crow::connections::systemBus->async_method_call(
647 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
648 const boost::system::error_code error_code,
649 const GetManagedObjects &resp) {
650 EthernetInterfaceData ethData{};
651 boost::container::flat_set<IPv4AddressData> ipv4Data;
652
653 if (error_code)
654 {
655 callback(false, ethData, ipv4Data);
656 return;
657 }
658
659 extractEthernetInterfaceData(ethiface_id, resp, ethData);
660 extractIPData(ethiface_id, resp, ipv4Data);
661
662 // Fix global GW
663 for (IPv4AddressData &ipv4 : ipv4Data)
664 {
665 if ((ipv4.linktype == LinkType::Global) &&
666 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700667 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700668 ipv4.gateway = ethData.default_gateway;
669 }
670 }
671
672 // Finally make a callback with usefull data
673 callback(true, ethData, ipv4Data);
674 },
675 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
676 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
677};
678
679/**
680 * Function that retrieves all Ethernet Interfaces available through Network
681 * Manager
682 * @param callback a function that shall be called to convert Dbus output
683 * into JSON.
684 */
685template <typename CallbackFunc>
686void getEthernetIfaceList(CallbackFunc &&callback)
687{
688 crow::connections::systemBus->async_method_call(
689 [callback{std::move(callback)}](
690 const boost::system::error_code error_code,
691 GetManagedObjects &resp) {
692 // Callback requires vector<string> to retrieve all available
693 // ethernet interfaces
694 std::vector<std::string> iface_list;
695 iface_list.reserve(resp.size());
696 if (error_code)
697 {
698 callback(false, iface_list);
699 return;
700 }
701
702 // Iterate over all retrieved ObjectPaths.
703 for (const auto &objpath : resp)
704 {
705 // And all interfaces available for certain ObjectPath.
706 for (const auto &interface : objpath.second)
707 {
708 // If interface is
709 // xyz.openbmc_project.Network.EthernetInterface, this is
710 // what we're looking for.
711 if (interface.first ==
712 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700714 // Cut out everyting until last "/", ...
715 const std::string &iface_id = objpath.first.str;
716 std::size_t last_pos = iface_id.rfind("/");
717 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700718 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700719 // and put it into output vector.
720 iface_list.emplace_back(
721 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700722 }
723 }
724 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700725 }
726 // Finally make a callback with useful data
727 callback(true, iface_list);
728 },
729 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
730 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100731};
732
733/**
734 * EthernetCollection derived class for delivering Ethernet Collection Schema
735 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700736class EthernetCollection : public Node
737{
738 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700739 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700740 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700741 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700743 entityPrivileges = {
744 {boost::beast::http::verb::get, {{"Login"}}},
745 {boost::beast::http::verb::head, {{"Login"}}},
746 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
747 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
748 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
749 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
750 }
751
752 private:
753 /**
754 * Functions triggers appropriate requests on DBus
755 */
756 void doGet(crow::Response &res, const crow::Request &req,
757 const std::vector<std::string> &params) override
758 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800759 res.jsonValue["@odata.type"] =
760 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
761 res.jsonValue["@odata.context"] =
762 "/redfish/v1/"
763 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
764 res.jsonValue["@odata.id"] =
765 "/redfish/v1/Managers/bmc/EthernetInterfaces";
766 res.jsonValue["Name"] = "Ethernet Network Interface Collection";
767 res.jsonValue["Description"] =
768 "Collection of EthernetInterfaces for this Manager";
769
Ed Tanous4a0cb852018-10-15 07:55:04 -0700770 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700772 getEthernetIfaceList(
773 [&res](const bool &success,
774 const std::vector<std::string> &iface_list) {
775 if (!success)
776 {
777 messages::internalError(res);
778 res.end();
779 return;
780 }
781
782 nlohmann::json &iface_array = res.jsonValue["Members"];
783 iface_array = nlohmann::json::array();
784 for (const std::string &iface_item : iface_list)
785 {
786 iface_array.push_back(
787 {{"@odata.id",
788 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
789 iface_item}});
790 }
791
792 res.jsonValue["Members@odata.count"] = iface_array.size();
793 res.jsonValue["@odata.id"] =
794 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700795 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700796 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700797 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100798};
799
800/**
801 * EthernetInterface derived class for delivering Ethernet Schema
802 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803class EthernetInterface : public Node
804{
805 public:
806 /*
807 * Default Constructor
808 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700809 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700810 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700811 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700812 std::string())
813 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700814 entityPrivileges = {
815 {boost::beast::http::verb::get, {{"Login"}}},
816 {boost::beast::http::verb::head, {{"Login"}}},
817 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
818 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
819 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
820 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200821 }
822
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 // TODO(kkowalsk) Find a suitable class/namespace for this
Ed Tanous0627a2c2018-11-29 17:09:23 -0800824 static void handleVlanPatch(const std::string &ifaceId, bool vlanEnable,
825 uint64_t vlanId,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700826 const EthernetInterfaceData &ethData,
827 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700828 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700829 if (!ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700830 {
831 // This interface is not a VLAN. Cannot do anything with it
832 // TODO(kkowalsk) Change this message
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800833 messages::propertyNotWritable(asyncResp->res, "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700834
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835 return;
836 }
837
838 // VLAN is configured on the interface
Ed Tanous0627a2c2018-11-29 17:09:23 -0800839 if (vlanEnable == true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700840 {
841 // Change VLAN Id
Ed Tanous0627a2c2018-11-29 17:09:23 -0800842 asyncResp->res.jsonValue["VLANId"] = vlanId;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700843 auto callback = [asyncResp](const boost::system::error_code ec) {
844 if (ec)
845 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700846 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700847 }
848 else
849 {
850 asyncResp->res.jsonValue["VLANEnable"] = true;
851 }
852 };
853 crow::connections::systemBus->async_method_call(
854 std::move(callback), "xyz.openbmc_project.Network",
855 "/xyz/openbmc_project/network/" + ifaceId,
856 "org.freedesktop.DBus.Properties", "Set",
857 "xyz.openbmc_project.Network.VLAN", "Id",
Ed Tanousabf2add2019-01-22 16:40:12 -0800858 std::variant<uint32_t>(vlanId));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700859 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700860 else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700862 auto callback = [asyncResp](const boost::system::error_code ec) {
863 if (ec)
864 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700865 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700866 return;
867 }
868 asyncResp->res.jsonValue["VLANEnable"] = false;
869 };
870
871 crow::connections::systemBus->async_method_call(
872 std::move(callback), "xyz.openbmc_project.Network",
873 "/xyz/openbmc_project/network/" + ifaceId,
874 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700875 }
876 }
877
878 private:
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800879 void handleHostnamePatch(const std::string &hostname,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700880 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 {
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800882 asyncResp->res.jsonValue["HostName"] = hostname;
883 crow::connections::systemBus->async_method_call(
884 [asyncResp](const boost::system::error_code ec) {
885 if (ec)
886 {
887 messages::internalError(asyncResp->res);
888 }
889 },
890 "xyz.openbmc_project.Network",
891 "/xyz/openbmc_project/network/config",
892 "org.freedesktop.DBus.Properties", "Set",
893 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
Ed Tanousabf2add2019-01-22 16:40:12 -0800894 std::variant<std::string>(hostname));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 }
896
Ed Tanous4a0cb852018-10-15 07:55:04 -0700897 void handleIPv4Patch(
898 const std::string &ifaceId, const nlohmann::json &input,
899 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
900 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700901 {
902 if (!input.is_array())
903 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700904 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800905 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700906 return;
907 }
908
909 // According to Redfish PATCH definition, size must be at least equal
Ed Tanous4a0cb852018-10-15 07:55:04 -0700910 if (input.size() < ipv4Data.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700911 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800912 messages::propertyValueFormatError(asyncResp->res, input.dump(),
913 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914 return;
915 }
916
Ed Tanous4a0cb852018-10-15 07:55:04 -0700917 int entryIdx = 0;
918 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
919 ipv4Data.begin();
920 for (const nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700921 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700922 std::string pathString =
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800923 "IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700924 // Check that entry is not of some unexpected type
Ed Tanous4a0cb852018-10-15 07:55:04 -0700925 if (!thisJson.is_object() && !thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700926 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800927 messages::propertyValueTypeError(asyncResp->res,
928 thisJson.dump(),
929 pathString + "/IPv4Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700930
931 continue;
932 }
933
Ed Tanous4a0cb852018-10-15 07:55:04 -0700934 nlohmann::json::const_iterator addressFieldIt =
935 thisJson.find("Address");
936 const std::string *addressField = nullptr;
937 if (addressFieldIt != thisJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700938 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700939 addressField = addressFieldIt->get_ptr<const std::string *>();
940 if (addressField == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800942 messages::propertyValueFormatError(asyncResp->res,
943 addressFieldIt->dump(),
944 pathString + "/Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700945 continue;
946 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700947 else
948 {
949 if (!ipv4VerifyIpAndGetBitcount(*addressField))
950 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700951 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800952 asyncResp->res, *addressField,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700953 pathString + "/Address");
954 continue;
955 }
956 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700957 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700958
Ed Tanousa24526d2018-12-10 15:17:59 -0800959 std::optional<uint8_t> prefixLength;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700960 const std::string *subnetField = nullptr;
961 nlohmann::json::const_iterator subnetFieldIt =
962 thisJson.find("SubnetMask");
963 if (subnetFieldIt != thisJson.end())
964 {
965 subnetField = subnetFieldIt->get_ptr<const std::string *>();
966 if (subnetField == nullptr)
967 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700968 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800969 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700970 pathString + "/SubnetMask");
971 continue;
972 }
973 else
974 {
975 prefixLength = 0;
976 if (!ipv4VerifyIpAndGetBitcount(*subnetField,
977 &*prefixLength))
978 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700979 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800980 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700981 pathString + "/SubnetMask");
982 continue;
983 }
984 }
985 }
986
987 std::string addressOriginInDBusFormat;
988 const std::string *addressOriginField = nullptr;
989 nlohmann::json::const_iterator addressOriginFieldIt =
990 thisJson.find("AddressOrigin");
991 if (addressOriginFieldIt != thisJson.end())
992 {
993 const std::string *addressOriginField =
994 addressOriginFieldIt->get_ptr<const std::string *>();
995 if (addressOriginField == nullptr)
996 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700997 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800998 asyncResp->res, *addressOriginField,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700999 pathString + "/AddressOrigin");
1000 continue;
1001 }
1002 else
1003 {
1004 // Get Address origin in proper format
1005 addressOriginInDBusFormat =
1006 translateAddressOriginRedfishToDbus(
1007 *addressOriginField);
1008 if (addressOriginInDBusFormat.empty())
1009 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001010 messages::propertyValueNotInList(
1011 asyncResp->res, *addressOriginField,
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001012 pathString + "/AddressOrigin");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001013 continue;
1014 }
1015 }
1016 }
1017
1018 nlohmann::json::const_iterator gatewayFieldIt =
1019 thisJson.find("Gateway");
1020 const std::string *gatewayField = nullptr;
1021 if (gatewayFieldIt != thisJson.end())
1022 {
1023 const std::string *gatewayField =
1024 gatewayFieldIt->get_ptr<const std::string *>();
1025 if (gatewayField == nullptr ||
1026 !ipv4VerifyIpAndGetBitcount(*gatewayField))
1027 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001028 messages::propertyValueFormatError(
1029 asyncResp->res, *gatewayField, pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001030 continue;
1031 }
1032 }
1033
1034 // if a vlan already exists, modify the existing
1035 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001036 {
1037 // Existing object that should be modified/deleted/remain
1038 // unchanged
Ed Tanous4a0cb852018-10-15 07:55:04 -07001039 if (thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001040 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001041 auto callback = [entryIdx{std::to_string(entryIdx)},
1042 asyncResp](
1043 const boost::system::error_code ec) {
1044 if (ec)
1045 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001046 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001047 return;
1048 }
1049 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1050 nullptr;
1051 };
1052 crow::connections::systemBus->async_method_call(
1053 std::move(callback), "xyz.openbmc_project.Network",
1054 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1055 thisData->id,
1056 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001057 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001058 else if (thisJson.is_object())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001059 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001060 // Apply changes
Ed Tanous4a0cb852018-10-15 07:55:04 -07001061 if (addressField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001062 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001063 auto callback =
1064 [asyncResp, entryIdx,
1065 addressField{std::string(*addressField)}](
1066 const boost::system::error_code ec) {
1067 if (ec)
1068 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001069 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001070 return;
1071 }
1072 asyncResp->res
1073 .jsonValue["IPv4Addresses"][std::to_string(
1074 entryIdx)]["Address"] = addressField;
1075 };
1076
1077 crow::connections::systemBus->async_method_call(
1078 std::move(callback), "xyz.openbmc_project.Network",
1079 "/xyz/openbmc_project/network/" + ifaceId +
1080 "/ipv4/" + thisData->id,
1081 "org.freedesktop.DBus.Properties", "Set",
1082 "xyz.openbmc_project.Network.IP", "Address",
Ed Tanousabf2add2019-01-22 16:40:12 -08001083 std::variant<std::string>(*addressField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001084 }
1085
Ed Tanous4a0cb852018-10-15 07:55:04 -07001086 if (prefixLength && subnetField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001087 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001088 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1089 thisData->id, *subnetField,
1090 *prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001091 }
1092
Ed Tanous4a0cb852018-10-15 07:55:04 -07001093 if (!addressOriginInDBusFormat.empty() &&
1094 addressOriginField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001096 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1097 *addressOriginField,
1098 addressOriginInDBusFormat, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001099 }
1100
Ed Tanous4a0cb852018-10-15 07:55:04 -07001101 if (gatewayField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001103 auto callback =
1104 [asyncResp, entryIdx,
1105 gatewayField{std::string(*gatewayField)}](
1106 const boost::system::error_code ec) {
1107 if (ec)
1108 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001109 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001110 return;
1111 }
1112 asyncResp->res
1113 .jsonValue["IPv4Addresses"][std::to_string(
1114 entryIdx)]["Gateway"] =
1115 std::move(gatewayField);
1116 };
1117
1118 crow::connections::systemBus->async_method_call(
1119 std::move(callback), "xyz.openbmc_project.Network",
1120 "/xyz/openbmc_project/network/" + ifaceId +
1121 "/ipv4/" + thisData->id,
1122 "org.freedesktop.DBus.Properties", "Set",
1123 "xyz.openbmc_project.Network.IP", "Gateway",
Ed Tanousabf2add2019-01-22 16:40:12 -08001124 std::variant<std::string>(*gatewayField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001125 }
1126 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001127 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001128 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001129 else
1130 {
1131 // Create IPv4 with provided data
1132 if (gatewayField == nullptr)
1133 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001134 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001135 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001136 continue;
1137 }
1138
1139 if (addressField == nullptr)
1140 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001141 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001142 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001143 continue;
1144 }
1145
1146 if (!prefixLength)
1147 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001148 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001149 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001150 continue;
1151 }
1152
1153 createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField,
1154 *addressField, asyncResp);
1155 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
1156 }
1157 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001158 }
1159 }
1160
Ed Tanous0f74e642018-11-12 15:17:05 -08001161 void parseInterfaceData(
1162 nlohmann::json &json_response, const std::string &iface_id,
1163 const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001164 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001165 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001166 json_response["Id"] = iface_id;
1167 json_response["@odata.id"] =
1168 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001169
Ed Tanous4a0cb852018-10-15 07:55:04 -07001170 json_response["SpeedMbps"] = ethData.speed;
1171 json_response["MACAddress"] = ethData.mac_address;
1172 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001173 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001174 json_response["HostName"] = ethData.hostname;
1175 }
1176
1177 nlohmann::json &vlanObj = json_response["VLAN"];
1178 if (ethData.vlan_id)
1179 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 vlanObj["VLANEnable"] = true;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001181 vlanObj["VLANId"] = *ethData.vlan_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001182 }
1183 else
1184 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001185 vlanObj["VLANEnable"] = false;
1186 vlanObj["VLANId"] = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001187 }
1188
Ed Tanous4a0cb852018-10-15 07:55:04 -07001189 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001190 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001191 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1192 ipv4_array = nlohmann::json::array();
1193 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001194 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001195 if (!ipv4_config.address.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001196 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001197 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1198 {"SubnetMask", ipv4_config.netmask},
1199 {"Address", ipv4_config.address}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001200
Ed Tanous4a0cb852018-10-15 07:55:04 -07001201 if (!ipv4_config.gateway.empty())
1202 {
1203 ipv4_array.back()["Gateway"] = ipv4_config.gateway;
1204 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001205 }
1206 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001207 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001208 }
1209
1210 /**
1211 * Functions triggers appropriate requests on DBus
1212 */
1213 void doGet(crow::Response &res, const crow::Request &req,
1214 const std::vector<std::string> &params) override
1215 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001216 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001217 if (params.size() != 1)
1218 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001219 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001220 return;
1221 }
1222
Ed Tanous4a0cb852018-10-15 07:55:04 -07001223 getEthernetIfaceData(
1224 params[0],
1225 [this, asyncResp, iface_id{std::string(params[0])}](
1226 const bool &success, const EthernetInterfaceData &ethData,
1227 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1228 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001230 // TODO(Pawel)consider distinguish between non existing
1231 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001232 messages::resourceNotFound(asyncResp->res,
1233 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001234 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001235 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001236 asyncResp->res.jsonValue["@odata.type"] =
1237 "#EthernetInterface.v1_2_0.EthernetInterface";
1238 asyncResp->res.jsonValue["@odata.context"] =
1239 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1240 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1241 asyncResp->res.jsonValue["Description"] =
1242 "Management Network Interface";
1243
1244 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1245 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001246 });
1247 }
1248
1249 void doPatch(crow::Response &res, const crow::Request &req,
1250 const std::vector<std::string> &params) override
1251 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001252 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001253 if (params.size() != 1)
1254 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001255 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 return;
1257 }
1258
Ed Tanous4a0cb852018-10-15 07:55:04 -07001259 const std::string &iface_id = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260
Ed Tanous0627a2c2018-11-29 17:09:23 -08001261 std::optional<nlohmann::json> vlan;
Ed Tanousbc0bd6e2018-12-10 14:07:55 -08001262 std::optional<std::string> hostname;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001263 std::optional<nlohmann::json> ipv4Addresses;
1264 std::optional<nlohmann::json> ipv6Addresses;
1265
1266 if (!json_util::readJson(req, res, "VLAN", vlan, "HostName", hostname,
1267 "IPv4Addresses", ipv4Addresses,
1268 "IPv6Addresses", ipv6Addresses))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001269 {
1270 return;
1271 }
Ed Tanous0627a2c2018-11-29 17:09:23 -08001272 std::optional<uint64_t> vlanId = 0;
1273 std::optional<bool> vlanEnable = false;
1274 if (vlan)
1275 {
1276 if (!json_util::readJson(*vlan, res, "VLANEnable", vlanEnable,
1277 "VLANId", vlanId))
1278 {
1279 return;
1280 }
1281 // Need both vlanId and vlanEnable to service this request
1282 if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
1283 {
1284 if (vlanId)
1285 {
1286 messages::propertyMissing(asyncResp->res, "VLANEnable");
1287 }
1288 else
1289 {
1290 messages::propertyMissing(asyncResp->res, "VLANId");
1291 }
1292
1293 return;
1294 }
1295 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001296
Ed Tanous4a0cb852018-10-15 07:55:04 -07001297 // Get single eth interface data, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001299 getEthernetIfaceData(
1300 iface_id,
Ed Tanous0627a2c2018-11-29 17:09:23 -08001301 [this, asyncResp, iface_id, vlanId, vlanEnable,
1302 hostname = std::move(hostname),
1303 ipv4Addresses = std::move(ipv4Addresses),
1304 ipv6Addresses = std::move(ipv6Addresses)](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001305 const bool &success, const EthernetInterfaceData &ethData,
1306 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001307 if (!success)
1308 {
1309 // ... otherwise return error
1310 // TODO(Pawel)consider distinguish between non existing
1311 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001312 messages::resourceNotFound(
1313 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001314 return;
1315 }
1316
Ed Tanous0f74e642018-11-12 15:17:05 -08001317 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1318 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001319
Ed Tanous0627a2c2018-11-29 17:09:23 -08001320 if (vlanId && vlanEnable)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001321 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001322 handleVlanPatch(iface_id, *vlanId, *vlanEnable, ethData,
1323 asyncResp);
1324 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325
Ed Tanous0627a2c2018-11-29 17:09:23 -08001326 if (hostname)
1327 {
1328 handleHostnamePatch(*hostname, asyncResp);
1329 }
1330
1331 if (ipv4Addresses)
1332 {
1333 handleIPv4Patch(iface_id, *ipv4Addresses, ipv4Data,
1334 asyncResp);
1335 }
1336
1337 if (ipv6Addresses)
1338 {
1339 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1340 messages::propertyNotWritable(asyncResp->res,
1341 "IPv6Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001342 }
1343 });
1344 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001345};
1346
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001347/**
Ed Tanous4a0cb852018-10-15 07:55:04 -07001348 * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1349 * Schema
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001350 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001351class VlanNetworkInterface : public Node
1352{
1353 public:
1354 /*
1355 * Default Constructor
1356 */
1357 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001358 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001359 Node(app,
Ed Tanous0f74e642018-11-12 15:17:05 -08001360 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001361 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001362 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001363 entityPrivileges = {
1364 {boost::beast::http::verb::get, {{"Login"}}},
1365 {boost::beast::http::verb::head, {{"Login"}}},
1366 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1367 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1368 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1369 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001370 }
1371
Ed Tanous1abe55e2018-09-05 08:30:59 -07001372 private:
Ed Tanous0f74e642018-11-12 15:17:05 -08001373 void parseInterfaceData(
1374 nlohmann::json &json_response, const std::string &parent_iface_id,
1375 const std::string &iface_id, const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001376 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001377 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001378 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001379 json_response["Id"] = iface_id;
1380 json_response["@odata.id"] =
1381 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1382 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001383
Ed Tanous4a0cb852018-10-15 07:55:04 -07001384 json_response["VLANEnable"] = true;
1385 if (ethData.vlan_id)
1386 {
1387 json_response["VLANId"] = *ethData.vlan_id;
1388 }
Ed Tanousa434f2b2018-07-27 13:04:22 -07001389 }
1390
Ed Tanous1abe55e2018-09-05 08:30:59 -07001391 bool verifyNames(crow::Response &res, const std::string &parent,
1392 const std::string &iface)
1393 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001394 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001395 if (!boost::starts_with(iface, parent + "_"))
1396 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001397 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1398 iface);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001399 return false;
1400 }
1401 else
1402 {
1403 return true;
1404 }
1405 }
1406
1407 /**
1408 * Functions triggers appropriate requests on DBus
1409 */
1410 void doGet(crow::Response &res, const crow::Request &req,
1411 const std::vector<std::string> &params) override
1412 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001413 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1414 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001415 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1416 // Check if there is required param, truly entering this shall be
1417 // impossible.
1418 if (params.size() != 2)
1419 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001420 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001422 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001423 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001424
Ed Tanous4a0cb852018-10-15 07:55:04 -07001425 const std::string &parent_iface_id = params[0];
1426 const std::string &iface_id = params[1];
Ed Tanous0f74e642018-11-12 15:17:05 -08001427 res.jsonValue["@odata.type"] =
1428 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1429 res.jsonValue["@odata.context"] =
1430 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1431 res.jsonValue["Name"] = "VLAN Network Interface";
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001432
Ed Tanous4a0cb852018-10-15 07:55:04 -07001433 if (!verifyNames(res, parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001434 {
1435 return;
1436 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001437
Ed Tanous1abe55e2018-09-05 08:30:59 -07001438 // Get single eth interface data, and call the below callback for JSON
1439 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001440 getEthernetIfaceData(
1441 iface_id,
1442 [this, asyncResp, parent_iface_id, iface_id](
1443 const bool &success, const EthernetInterfaceData &ethData,
1444 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1445 if (success && ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001446 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001447 parseInterfaceData(asyncResp->res.jsonValue,
1448 parent_iface_id, iface_id, ethData,
1449 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 }
1451 else
1452 {
1453 // ... otherwise return error
1454 // TODO(Pawel)consider distinguish between non existing
1455 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001456 messages::resourceNotFound(
1457 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001458 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001459 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001460 }
1461
Ed Tanous1abe55e2018-09-05 08:30:59 -07001462 void doPatch(crow::Response &res, const crow::Request &req,
1463 const std::vector<std::string> &params) override
1464 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001465 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466 if (params.size() != 2)
1467 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001468 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001469 return;
1470 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001471
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 const std::string &parentIfaceId = params[0];
1473 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001474
Ed Tanous1abe55e2018-09-05 08:30:59 -07001475 if (!verifyNames(res, parentIfaceId, ifaceId))
1476 {
1477 return;
1478 }
1479
Ed Tanous0627a2c2018-11-29 17:09:23 -08001480 bool vlanEnable = false;
1481 uint64_t vlanId = 0;
1482
1483 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
1484 vlanId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001485 {
1486 return;
1487 }
1488
1489 // Get single eth interface data, and call the below callback for JSON
1490 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001491 getEthernetIfaceData(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001492 ifaceId,
Ed Tanous0627a2c2018-11-29 17:09:23 -08001493 [this, asyncResp, parentIfaceId, ifaceId, vlanEnable, vlanId](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001494 const bool &success, const EthernetInterfaceData &ethData,
1495 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001496 if (!success)
1497 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001498 // TODO(Pawel)consider distinguish between non existing
1499 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001500 messages::resourceNotFound(
1501 asyncResp->res, "VLAN Network Interface", ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502
1503 return;
1504 }
1505
Ed Tanous0f74e642018-11-12 15:17:05 -08001506 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1507 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508
Ed Tanous0627a2c2018-11-29 17:09:23 -08001509 EthernetInterface::handleVlanPatch(ifaceId, vlanId, vlanEnable,
1510 ethData, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001511 });
1512 }
1513
1514 void doDelete(crow::Response &res, const crow::Request &req,
1515 const std::vector<std::string> &params) override
1516 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001517 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518 if (params.size() != 2)
1519 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001520 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 return;
1522 }
1523
1524 const std::string &parentIfaceId = params[0];
1525 const std::string &ifaceId = params[1];
1526
Ed Tanous4a0cb852018-10-15 07:55:04 -07001527 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001528 {
1529 return;
1530 }
1531
1532 // Get single eth interface data, and call the below callback for JSON
1533 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001534 getEthernetIfaceData(
1535 ifaceId,
1536 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1537 ifaceId{std::string(ifaceId)}](
1538 const bool &success, const EthernetInterfaceData &ethData,
1539 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1540 if (success && ethData.vlan_id)
1541 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001542 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1543 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001544
Jason M. Billsf12894f2018-10-09 12:45:45 -07001545 auto callback =
1546 [asyncResp](const boost::system::error_code ec) {
1547 if (ec)
1548 {
1549 messages::internalError(asyncResp->res);
1550 }
1551 };
1552 crow::connections::systemBus->async_method_call(
1553 std::move(callback), "xyz.openbmc_project.Network",
1554 std::string("/xyz/openbmc_project/network/") + ifaceId,
1555 "xyz.openbmc_project.Object.Delete", "Delete");
1556 }
1557 else
1558 {
1559 // ... otherwise return error
1560 // TODO(Pawel)consider distinguish between non existing
1561 // object, and other errors
1562 messages::resourceNotFound(
1563 asyncResp->res, "VLAN Network Interface", ifaceId);
1564 }
1565 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001566 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001567};
1568
1569/**
1570 * VlanNetworkInterfaceCollection derived class for delivering
1571 * VLANNetworkInterface Collection Schema
1572 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001573class VlanNetworkInterfaceCollection : public Node
1574{
1575 public:
1576 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001577 VlanNetworkInterfaceCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001578 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1579 std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001580 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001581 entityPrivileges = {
1582 {boost::beast::http::verb::get, {{"Login"}}},
1583 {boost::beast::http::verb::head, {{"Login"}}},
1584 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1585 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1586 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1587 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001588 }
1589
Ed Tanous1abe55e2018-09-05 08:30:59 -07001590 private:
1591 /**
1592 * Functions triggers appropriate requests on DBus
1593 */
1594 void doGet(crow::Response &res, const crow::Request &req,
1595 const std::vector<std::string> &params) override
1596 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001597 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598 if (params.size() != 1)
1599 {
1600 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001601 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001602 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001603 }
1604
Ed Tanous4a0cb852018-10-15 07:55:04 -07001605 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001606
Ed Tanous4a0cb852018-10-15 07:55:04 -07001607 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001608 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001609 getEthernetIfaceList(
1610 [this, asyncResp,
1611 rootInterfaceName{std::string(rootInterfaceName)}](
1612 const bool &success,
1613 const std::vector<std::string> &iface_list) {
1614 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001615 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001616 messages::internalError(asyncResp->res);
1617 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001618 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001619 asyncResp->res.jsonValue["@odata.type"] =
1620 "#VLanNetworkInterfaceCollection."
1621 "VLanNetworkInterfaceCollection";
1622 asyncResp->res.jsonValue["@odata.context"] =
1623 "/redfish/v1/$metadata"
1624 "#VLanNetworkInterfaceCollection."
1625 "VLanNetworkInterfaceCollection";
1626 asyncResp->res.jsonValue["Name"] =
1627 "VLAN Network Interface Collection";
Ed Tanous4a0cb852018-10-15 07:55:04 -07001628
Jason M. Billsf12894f2018-10-09 12:45:45 -07001629 nlohmann::json iface_array = nlohmann::json::array();
1630
1631 for (const std::string &iface_item : iface_list)
1632 {
1633 if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1634 {
1635 iface_array.push_back(
1636 {{"@odata.id",
1637 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1638 rootInterfaceName + "/VLANs/" + iface_item}});
1639 }
1640 }
1641
1642 if (iface_array.empty())
1643 {
1644 messages::resourceNotFound(
1645 asyncResp->res, "EthernetInterface", rootInterfaceName);
1646 return;
1647 }
1648 asyncResp->res.jsonValue["Members@odata.count"] =
1649 iface_array.size();
1650 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1651 asyncResp->res.jsonValue["@odata.id"] =
1652 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1653 rootInterfaceName + "/VLANs";
1654 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001655 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001656
Ed Tanous1abe55e2018-09-05 08:30:59 -07001657 void doPost(crow::Response &res, const crow::Request &req,
1658 const std::vector<std::string> &params) override
1659 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001660 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001661 if (params.size() != 1)
1662 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001663 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001664 return;
1665 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001666
Ed Tanous0627a2c2018-11-29 17:09:23 -08001667 uint32_t vlanId = 0;
1668 if (!json_util::readJson(req, res, "VLANId", vlanId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001670 return;
1671 }
1672 const std::string &rootInterfaceName = params[0];
Ed Tanous4a0cb852018-10-15 07:55:04 -07001673 auto callback = [asyncResp](const boost::system::error_code ec) {
1674 if (ec)
1675 {
1676 // TODO(ed) make more consistent error messages based on
1677 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001678 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001679 return;
1680 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001681 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001682 };
1683 crow::connections::systemBus->async_method_call(
1684 std::move(callback), "xyz.openbmc_project.Network",
1685 "/xyz/openbmc_project/network",
1686 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
Ed Tanous0627a2c2018-11-29 17:09:23 -08001687 rootInterfaceName, vlanId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001688 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001689};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001690} // namespace redfish