blob: b9920187b73d7af07153144686d71cf3df2a1382 [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 Tanous029573d2019-02-01 10:57:49 -080043 std::string, sdbusplus::message::variant<
44 std::string, bool, uint8_t, int16_t, uint16_t,
45 int32_t, uint32_t, int64_t, uint64_t, double,
46 std::vector<std::string>>>>>>>;
Ed Tanous4a0cb852018-10-15 07:55:04 -070047
48enum class LinkType
49{
50 Local,
51 Global
52};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010053
54/**
55 * Structure for keeping IPv4 data required by Redfish
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010056 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070057struct IPv4AddressData
58{
59 std::string id;
Ed Tanous4a0cb852018-10-15 07:55:04 -070060 std::string address;
61 std::string domain;
62 std::string gateway;
Ed Tanous1abe55e2018-09-05 08:30:59 -070063 std::string netmask;
64 std::string origin;
Ed Tanous4a0cb852018-10-15 07:55:04 -070065 LinkType linktype;
66
Ed Tanous1abe55e2018-09-05 08:30:59 -070067 bool operator<(const IPv4AddressData &obj) const
68 {
Ed Tanous4a0cb852018-10-15 07:55:04 -070069 return id < obj.id;
Ed Tanous1abe55e2018-09-05 08:30:59 -070070 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010071};
72
73/**
74 * Structure for keeping basic single Ethernet Interface information
75 * available from DBus
76 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070077struct EthernetInterfaceData
78{
Ed Tanous4a0cb852018-10-15 07:55:04 -070079 uint32_t speed;
80 bool auto_neg;
manojkiraneda2a133282019-02-19 13:09:43 +053081 bool DHCPEnabled;
Ed Tanous4a0cb852018-10-15 07:55:04 -070082 std::string hostname;
83 std::string default_gateway;
84 std::string mac_address;
Ed Tanousa24526d2018-12-10 15:17:59 -080085 std::optional<uint32_t> vlan_id;
Ed Tanous029573d2019-02-01 10:57:49 -080086 std::vector<std::string> nameservers;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010087};
88
Ed Tanous4a0cb852018-10-15 07:55:04 -070089// Helper function that changes bits netmask notation (i.e. /24)
90// into full dot notation
91inline std::string getNetmask(unsigned int bits)
Ed Tanous1abe55e2018-09-05 08:30:59 -070092{
Ed Tanous4a0cb852018-10-15 07:55:04 -070093 uint32_t value = 0xffffffff << (32 - bits);
94 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
95 std::to_string((value >> 16) & 0xff) + "." +
96 std::to_string((value >> 8) & 0xff) + "." +
97 std::to_string(value & 0xff);
98 return netmask;
99}
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100100
Ed Tanous4a0cb852018-10-15 07:55:04 -0700101inline std::string
102 translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
103 bool isIPv4)
104{
105 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700106 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700107 return "Static";
108 }
109 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
110 {
111 if (isIPv4)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700112 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700113 return "IPv4LinkLocal";
114 }
115 else
116 {
117 return "LinkLocal";
118 }
119 }
120 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
121 {
122 if (isIPv4)
123 {
124 return "DHCP";
125 }
126 else
127 {
128 return "DHCPv6";
129 }
130 }
131 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
132 {
133 return "SLAAC";
134 }
135 return "";
136}
137
138inline std::string
139 translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
140{
141 if (inputOrigin == "Static")
142 {
143 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
144 }
145 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
146 {
147 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
148 }
149 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
150 {
151 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
152 }
153 if (inputOrigin == "SLAAC")
154 {
155 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
156 }
157 return "";
158}
159
160inline void extractEthernetInterfaceData(const std::string &ethiface_id,
161 const GetManagedObjects &dbus_data,
162 EthernetInterfaceData &ethData)
163{
164 for (const auto &objpath : dbus_data)
165 {
Ed Tanous029573d2019-02-01 10:57:49 -0800166 for (const auto &ifacePair : objpath.second)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700167 {
Ed Tanous029573d2019-02-01 10:57:49 -0800168 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700169 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700170 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700171 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700172 for (const auto &propertyPair : ifacePair.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700173 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700174 if (propertyPair.first == "MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700175 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700176 const std::string *mac =
Ed Tanousabf2add2019-01-22 16:40:12 -0800177 std::get_if<std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700178 if (mac != nullptr)
179 {
180 ethData.mac_address = *mac;
181 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700182 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700183 }
184 }
185 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
186 {
187 for (const auto &propertyPair : ifacePair.second)
188 {
189 if (propertyPair.first == "Id")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700190 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800191 const uint32_t *id =
Ed Tanousabf2add2019-01-22 16:40:12 -0800192 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700193 if (id != nullptr)
194 {
195 ethData.vlan_id = *id;
196 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700197 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700198 }
199 }
200 else if (ifacePair.first ==
201 "xyz.openbmc_project.Network.EthernetInterface")
202 {
203 for (const auto &propertyPair : ifacePair.second)
204 {
205 if (propertyPair.first == "AutoNeg")
206 {
207 const bool *auto_neg =
Ed Tanousabf2add2019-01-22 16:40:12 -0800208 std::get_if<bool>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700209 if (auto_neg != nullptr)
210 {
211 ethData.auto_neg = *auto_neg;
212 }
213 }
214 else if (propertyPair.first == "Speed")
215 {
216 const uint32_t *speed =
Ed Tanousabf2add2019-01-22 16:40:12 -0800217 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700218 if (speed != nullptr)
219 {
220 ethData.speed = *speed;
221 }
222 }
Ed Tanous029573d2019-02-01 10:57:49 -0800223 else if (propertyPair.first == "NameServers")
Ed Tanous4a0cb852018-10-15 07:55:04 -0700224 {
Ed Tanous029573d2019-02-01 10:57:49 -0800225 const std::vector<std::string> *nameservers =
226 sdbusplus::message::variant_ns::get_if<
227 std::vector<std::string>>(
228 &propertyPair.second);
229 if (nameservers != nullptr)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700230 {
Ed Tanous029573d2019-02-01 10:57:49 -0800231 ethData.nameservers = std::move(*nameservers);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700232 }
233 }
manojkiraneda2a133282019-02-19 13:09:43 +0530234 else if (propertyPair.first == "DHCPEnabled")
235 {
236 const bool *DHCPEnabled =
237 std::get_if<bool>(&propertyPair.second);
238 if (DHCPEnabled != nullptr)
239 {
240 ethData.DHCPEnabled = *DHCPEnabled;
241 }
242 }
Ed Tanous029573d2019-02-01 10:57:49 -0800243 }
244 }
245 }
246 // System configuration shows up in the global namespace, so no need
247 // to check eth number
248 if (ifacePair.first ==
249 "xyz.openbmc_project.Network.SystemConfiguration")
250 {
251 for (const auto &propertyPair : ifacePair.second)
252 {
253 if (propertyPair.first == "HostName")
254 {
255 const std::string *hostname =
256 sdbusplus::message::variant_ns::get_if<std::string>(
257 &propertyPair.second);
258 if (hostname != nullptr)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700259 {
Ed Tanous029573d2019-02-01 10:57:49 -0800260 ethData.hostname = *hostname;
261 }
262 }
263 else if (propertyPair.first == "DefaultGateway")
264 {
265 const std::string *defaultGateway =
266 sdbusplus::message::variant_ns::get_if<std::string>(
267 &propertyPair.second);
268 if (defaultGateway != nullptr)
269 {
270 ethData.default_gateway = *defaultGateway;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700271 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700272 }
273 }
274 }
275 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700276 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700277}
278
279// Helper function that extracts data for single ethernet ipv4 address
280inline void
281 extractIPData(const std::string &ethiface_id,
282 const GetManagedObjects &dbus_data,
283 boost::container::flat_set<IPv4AddressData> &ipv4_config)
284{
285 const std::string ipv4PathStart =
286 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
287
288 // Since there might be several IPv4 configurations aligned with
289 // single ethernet interface, loop over all of them
290 for (const auto &objpath : dbus_data)
291 {
292 // Check if proper pattern for object path appears
293 if (boost::starts_with(objpath.first.str, ipv4PathStart))
294 {
295 for (auto &interface : objpath.second)
296 {
297 if (interface.first == "xyz.openbmc_project.Network.IP")
298 {
299 // Instance IPv4AddressData structure, and set as
300 // appropriate
301 std::pair<
302 boost::container::flat_set<IPv4AddressData>::iterator,
303 bool>
304 it = ipv4_config.insert(
Ed Tanous6ea007a2019-02-13 22:48:25 -0800305 {objpath.first.str.substr(ipv4PathStart.size()), "",
306 "", "", "", "", LinkType::Local});
Ed Tanous4a0cb852018-10-15 07:55:04 -0700307 IPv4AddressData &ipv4_address = *it.first;
308 for (auto &property : interface.second)
309 {
310 if (property.first == "Address")
311 {
312 const std::string *address =
Ed Tanousabf2add2019-01-22 16:40:12 -0800313 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700314 if (address != nullptr)
315 {
316 ipv4_address.address = *address;
317 }
318 }
319 else if (property.first == "Gateway")
320 {
321 const std::string *gateway =
Ed Tanousabf2add2019-01-22 16:40:12 -0800322 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700323 if (gateway != nullptr)
324 {
325 ipv4_address.gateway = *gateway;
326 }
327 }
328 else if (property.first == "Origin")
329 {
330 const std::string *origin =
Ed Tanousabf2add2019-01-22 16:40:12 -0800331 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700332 if (origin != nullptr)
333 {
334 ipv4_address.origin =
335 translateAddressOriginDbusToRedfish(*origin,
336 true);
337 }
338 }
339 else if (property.first == "PrefixLength")
340 {
341 const uint8_t *mask =
Ed Tanousabf2add2019-01-22 16:40:12 -0800342 std::get_if<uint8_t>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700343 if (mask != nullptr)
344 {
345 // convert it to the string
346 ipv4_address.netmask = getNetmask(*mask);
347 }
348 }
349 else
350 {
351 BMCWEB_LOG_ERROR
352 << "Got extra property: " << property.first
353 << " on the " << objpath.first.str << " object";
354 }
355 }
356 // Check if given address is local, or global
357 ipv4_address.linktype =
358 boost::starts_with(ipv4_address.address, "169.254.")
359 ? LinkType::Global
360 : LinkType::Local;
361 }
362 }
363 }
364 }
365}
366
367/**
368 * @brief Sets given Id on the given VLAN interface through D-Bus
369 *
370 * @param[in] ifaceId Id of VLAN interface that should be modified
371 * @param[in] inputVlanId New ID of the VLAN
372 * @param[in] callback Function that will be called after the operation
373 *
374 * @return None.
375 */
376template <typename CallbackFunc>
377void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
378 CallbackFunc &&callback)
379{
380 crow::connections::systemBus->async_method_call(
381 callback, "xyz.openbmc_project.Network",
382 std::string("/xyz/openbmc_project/network/") + ifaceId,
383 "org.freedesktop.DBus.Properties", "Set",
384 "xyz.openbmc_project.Network.VLAN", "Id",
Ed Tanousabf2add2019-01-22 16:40:12 -0800385 std::variant<uint32_t>(inputVlanId));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700386}
387
388/**
389 * @brief Helper function that verifies IP address to check if it is in
390 * proper format. If bits pointer is provided, also calculates active
391 * bit count for Subnet Mask.
392 *
393 * @param[in] ip IP that will be verified
394 * @param[out] bits Calculated mask in bits notation
395 *
396 * @return true in case of success, false otherwise
397 */
398inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
399 uint8_t *bits = nullptr)
400{
401 std::vector<std::string> bytesInMask;
402
403 boost::split(bytesInMask, ip, boost::is_any_of("."));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404
405 static const constexpr int ipV4AddressSectionsCount = 4;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700406 if (bytesInMask.size() != ipV4AddressSectionsCount)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700408 return false;
409 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410
Ed Tanous4a0cb852018-10-15 07:55:04 -0700411 if (bits != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700412 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700413 *bits = 0;
414 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700415
Ed Tanous4a0cb852018-10-15 07:55:04 -0700416 char *endPtr;
417 long previousValue = 255;
418 bool firstZeroInByteHit;
419 for (const std::string &byte : bytesInMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700421 if (byte.empty())
422 {
423 return false;
424 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425
Ed Tanous4a0cb852018-10-15 07:55:04 -0700426 // Use strtol instead of stroi to avoid exceptions
427 long value = std::strtol(byte.c_str(), &endPtr, 10);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428
Ed Tanous4a0cb852018-10-15 07:55:04 -0700429 // endPtr should point to the end of the string, otherwise given string
430 // is not 100% number
431 if (*endPtr != '\0')
432 {
433 return false;
434 }
435
436 // Value should be contained in byte
437 if (value < 0 || value > 255)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
439 return false;
440 }
441
442 if (bits != nullptr)
443 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700444 // Mask has to be continuous between bytes
445 if (previousValue != 255 && value != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700446 {
447 return false;
448 }
449
Ed Tanous4a0cb852018-10-15 07:55:04 -0700450 // Mask has to be continuous inside bytes
451 firstZeroInByteHit = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700452
Ed Tanous4a0cb852018-10-15 07:55:04 -0700453 // Count bits
454 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700456 if (value & (1 << bitIdx))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700457 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700458 if (firstZeroInByteHit)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700459 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700460 // Continuity not preserved
461 return false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462 }
463 else
464 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700465 (*bits)++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700466 }
467 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700468 else
469 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700470 firstZeroInByteHit = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700471 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700472 }
473 }
474
475 previousValue = value;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 }
477
Ed Tanous4a0cb852018-10-15 07:55:04 -0700478 return true;
479}
480
481/**
Ed Tanous4a0cb852018-10-15 07:55:04 -0700482 * @brief Changes IPv4 address origin property
483 *
484 * @param[in] ifaceId Id of interface whose IP should be modified
485 * @param[in] ipIdx Index of IP in input array that should be
486 * modified
487 * @param[in] ipHash DBus Hash id of modified IP
488 * @param[in] newValue New value in Redfish format
489 * @param[in] newValueDbus New value in D-Bus format
490 * @param[io] asyncResp Response object that will be returned to client
491 *
492 * @return true if give IP is valid and has been sent do D-Bus, false
493 * otherwise
494 */
Ed Tanous6ea007a2019-02-13 22:48:25 -0800495inline void changeIPv4Origin(const std::string &ifaceId, size_t ipIdx,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700496 const std::string &ipHash,
497 const std::string &newValue,
498 const std::string &newValueDbus,
499 const std::shared_ptr<AsyncResp> asyncResp)
500{
501 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
502 const boost::system::error_code ec) {
503 if (ec)
504 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800505 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700506 }
507 else
508 {
509 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
510 newValue;
511 }
512 };
513
514 crow::connections::systemBus->async_method_call(
515 std::move(callback), "xyz.openbmc_project.Network",
516 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
517 "org.freedesktop.DBus.Properties", "Set",
518 "xyz.openbmc_project.Network.IP", "Origin",
Ed Tanousabf2add2019-01-22 16:40:12 -0800519 std::variant<std::string>(newValueDbus));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700520}
521
522/**
523 * @brief Modifies SubnetMask for given IP
524 *
525 * @param[in] ifaceId Id of interface whose IP should be modified
526 * @param[in] ipIdx Index of IP in input array that should be
527 * modified
528 * @param[in] ipHash DBus Hash id of modified IP
529 * @param[in] newValueStr Mask in dot notation as string
530 * @param[in] newValue Mask as PrefixLength in bitcount
531 * @param[io] asyncResp Response object that will be returned to client
532 *
533 * @return None
534 */
Ed Tanous6ea007a2019-02-13 22:48:25 -0800535inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId,
536 size_t ipIdx,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700537 const std::string &ipHash,
538 const std::string &newValueStr,
539 uint8_t &newValue,
540 std::shared_ptr<AsyncResp> asyncResp)
541{
542 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
543 const boost::system::error_code ec) {
544 if (ec)
545 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800546 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700547 }
548 else
549 {
550 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
551 newValueStr;
552 }
553 };
554
555 crow::connections::systemBus->async_method_call(
556 std::move(callback), "xyz.openbmc_project.Network",
557 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
558 "org.freedesktop.DBus.Properties", "Set",
559 "xyz.openbmc_project.Network.IP", "PrefixLength",
Ed Tanousabf2add2019-01-22 16:40:12 -0800560 std::variant<uint8_t>(newValue));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700561}
562
563/**
Ed Tanous4a0cb852018-10-15 07:55:04 -0700564 * @brief Deletes given IPv4
565 *
566 * @param[in] ifaceId Id of interface whose IP should be deleted
567 * @param[in] ipIdx Index of IP in input array that should be deleted
568 * @param[in] ipHash DBus Hash id of IP that should be deleted
569 * @param[io] asyncResp Response object that will be returned to client
570 *
571 * @return None
572 */
573inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
574 unsigned int ipIdx,
575 const std::shared_ptr<AsyncResp> asyncResp)
576{
577 crow::connections::systemBus->async_method_call(
578 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700579 if (ec)
580 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800581 messages::internalError(asyncResp->res);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100582 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700583 else
584 {
585 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
586 }
587 },
588 "xyz.openbmc_project.Network",
589 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
590 "xyz.openbmc_project.Object.Delete", "Delete");
591}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700592
Ed Tanous4a0cb852018-10-15 07:55:04 -0700593/**
594 * @brief Creates IPv4 with given data
595 *
596 * @param[in] ifaceId Id of interface whose IP should be deleted
597 * @param[in] ipIdx Index of IP in input array that should be deleted
598 * @param[in] ipHash DBus Hash id of IP that should be deleted
599 * @param[io] asyncResp Response object that will be returned to client
600 *
601 * @return None
602 */
Ed Tanous6ea007a2019-02-13 22:48:25 -0800603inline void createIPv4(const std::string &ifaceId, uint8_t subnetMask,
604 const std::string &gateway, const std::string &address,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700605 std::shared_ptr<AsyncResp> asyncResp)
606{
Ed Tanous43b761d2019-02-13 20:10:56 -0800607 auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700608 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700609 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800610 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700611 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700612 };
613
Ed Tanous4a0cb852018-10-15 07:55:04 -0700614 crow::connections::systemBus->async_method_call(
615 std::move(createIpHandler), "xyz.openbmc_project.Network",
616 "/xyz/openbmc_project/network/" + ifaceId,
617 "xyz.openbmc_project.Network.IP.Create", "IP",
618 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
619 gateway);
620}
manojkiraneda2a133282019-02-19 13:09:43 +0530621using GetAllPropertiesType =
622 boost::container::flat_map<std::string, sdbusplus::message::variant<bool>>;
623
624inline void getDHCPConfigData(const std::shared_ptr<AsyncResp> asyncResp)
625{
626 auto getConfig = [asyncResp](const boost::system::error_code error_code,
627 const GetAllPropertiesType &dbus_data) {
628 if (error_code)
629 {
630 BMCWEB_LOG_ERROR << "D-Bus response error: " << error_code;
631 messages::internalError(asyncResp->res);
632 return;
633 }
634 nlohmann::json &DHCPConfigTypeJson =
635 asyncResp->res.jsonValue["DHCPv4Configuration"];
636 for (const auto &property : dbus_data)
637 {
638 auto value =
639 sdbusplus::message::variant_ns::get_if<bool>(&property.second);
640
641 if (value == nullptr)
642 {
643 continue;
644 }
645 if (property.first == "DNSEnabled")
646 {
647 DHCPConfigTypeJson["UseDNSServers"] = *value;
648 }
649 else if (property.first == "HostNameEnabled")
650 {
651 DHCPConfigTypeJson["UseDomainName"] = *value;
652 }
653 else if (property.first == "NTPEnabled")
654 {
655 DHCPConfigTypeJson["UseNTPServers"] = *value;
656 }
657 }
658 };
659 crow::connections::systemBus->async_method_call(
660 std::move(getConfig), "xyz.openbmc_project.Network",
661 "/xyz/openbmc_project/network/config/dhcp",
662 "org.freedesktop.DBus.Properties", "GetAll",
663 "xyz.openbmc_project.Network.DHCPConfiguration");
664}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700665
Ed Tanous4a0cb852018-10-15 07:55:04 -0700666/**
667 * Function that retrieves all properties for given Ethernet Interface
668 * Object
669 * from EntityManager Network Manager
670 * @param ethiface_id a eth interface id to query on DBus
671 * @param callback a function that shall be called to convert Dbus output
672 * into JSON
673 */
674template <typename CallbackFunc>
675void getEthernetIfaceData(const std::string &ethiface_id,
676 CallbackFunc &&callback)
677{
678 crow::connections::systemBus->async_method_call(
679 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
680 const boost::system::error_code error_code,
681 const GetManagedObjects &resp) {
682 EthernetInterfaceData ethData{};
683 boost::container::flat_set<IPv4AddressData> ipv4Data;
684
685 if (error_code)
686 {
687 callback(false, ethData, ipv4Data);
688 return;
689 }
690
691 extractEthernetInterfaceData(ethiface_id, resp, ethData);
692 extractIPData(ethiface_id, resp, ipv4Data);
693
694 // Fix global GW
695 for (IPv4AddressData &ipv4 : ipv4Data)
696 {
697 if ((ipv4.linktype == LinkType::Global) &&
698 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700699 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700700 ipv4.gateway = ethData.default_gateway;
701 }
702 }
703
704 // Finally make a callback with usefull data
705 callback(true, ethData, ipv4Data);
706 },
707 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
708 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
709};
710
711/**
712 * Function that retrieves all Ethernet Interfaces available through Network
713 * Manager
714 * @param callback a function that shall be called to convert Dbus output
715 * into JSON.
716 */
717template <typename CallbackFunc>
718void getEthernetIfaceList(CallbackFunc &&callback)
719{
720 crow::connections::systemBus->async_method_call(
721 [callback{std::move(callback)}](
722 const boost::system::error_code error_code,
723 GetManagedObjects &resp) {
724 // Callback requires vector<string> to retrieve all available
725 // ethernet interfaces
726 std::vector<std::string> iface_list;
727 iface_list.reserve(resp.size());
728 if (error_code)
729 {
730 callback(false, iface_list);
731 return;
732 }
733
734 // Iterate over all retrieved ObjectPaths.
735 for (const auto &objpath : resp)
736 {
737 // And all interfaces available for certain ObjectPath.
738 for (const auto &interface : objpath.second)
739 {
740 // If interface is
741 // xyz.openbmc_project.Network.EthernetInterface, this is
742 // what we're looking for.
743 if (interface.first ==
744 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700745 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700746 // Cut out everyting until last "/", ...
747 const std::string &iface_id = objpath.first.str;
748 std::size_t last_pos = iface_id.rfind("/");
749 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700750 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700751 // and put it into output vector.
752 iface_list.emplace_back(
753 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 }
755 }
756 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700757 }
758 // Finally make a callback with useful data
759 callback(true, iface_list);
760 },
761 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
762 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100763};
764
765/**
766 * EthernetCollection derived class for delivering Ethernet Collection Schema
767 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768class EthernetCollection : public Node
769{
770 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700771 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700773 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700774 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 entityPrivileges = {
776 {boost::beast::http::verb::get, {{"Login"}}},
777 {boost::beast::http::verb::head, {{"Login"}}},
778 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
779 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
780 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
781 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
782 }
783
784 private:
785 /**
786 * Functions triggers appropriate requests on DBus
787 */
788 void doGet(crow::Response &res, const crow::Request &req,
789 const std::vector<std::string> &params) override
790 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800791 res.jsonValue["@odata.type"] =
792 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
793 res.jsonValue["@odata.context"] =
794 "/redfish/v1/"
795 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
796 res.jsonValue["@odata.id"] =
797 "/redfish/v1/Managers/bmc/EthernetInterfaces";
798 res.jsonValue["Name"] = "Ethernet Network Interface Collection";
799 res.jsonValue["Description"] =
800 "Collection of EthernetInterfaces for this Manager";
801
Ed Tanous4a0cb852018-10-15 07:55:04 -0700802 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700803 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700804 getEthernetIfaceList(
805 [&res](const bool &success,
806 const std::vector<std::string> &iface_list) {
807 if (!success)
808 {
809 messages::internalError(res);
810 res.end();
811 return;
812 }
813
814 nlohmann::json &iface_array = res.jsonValue["Members"];
815 iface_array = nlohmann::json::array();
816 for (const std::string &iface_item : iface_list)
817 {
818 iface_array.push_back(
819 {{"@odata.id",
820 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
821 iface_item}});
822 }
823
824 res.jsonValue["Members@odata.count"] = iface_array.size();
825 res.jsonValue["@odata.id"] =
826 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700827 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700828 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700829 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100830};
831
832/**
833 * EthernetInterface derived class for delivering Ethernet Schema
834 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700835class EthernetInterface : public Node
836{
837 public:
838 /*
839 * Default Constructor
840 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700841 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700842 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700843 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700844 std::string())
845 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 entityPrivileges = {
847 {boost::beast::http::verb::get, {{"Login"}}},
848 {boost::beast::http::verb::head, {{"Login"}}},
849 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
850 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
851 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
852 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200853 }
854
Ed Tanous1abe55e2018-09-05 08:30:59 -0700855 // TODO(kkowalsk) Find a suitable class/namespace for this
Ed Tanous0627a2c2018-11-29 17:09:23 -0800856 static void handleVlanPatch(const std::string &ifaceId, bool vlanEnable,
857 uint64_t vlanId,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700858 const EthernetInterfaceData &ethData,
859 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700860 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700861 if (!ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700862 {
863 // This interface is not a VLAN. Cannot do anything with it
864 // TODO(kkowalsk) Change this message
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800865 messages::propertyNotWritable(asyncResp->res, "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 return;
868 }
869
870 // VLAN is configured on the interface
Ed Tanous0627a2c2018-11-29 17:09:23 -0800871 if (vlanEnable == true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700872 {
873 // Change VLAN Id
Ed Tanous0627a2c2018-11-29 17:09:23 -0800874 asyncResp->res.jsonValue["VLANId"] = vlanId;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700875 auto callback = [asyncResp](const boost::system::error_code ec) {
876 if (ec)
877 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700878 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700879 }
880 else
881 {
882 asyncResp->res.jsonValue["VLANEnable"] = true;
883 }
884 };
885 crow::connections::systemBus->async_method_call(
886 std::move(callback), "xyz.openbmc_project.Network",
887 "/xyz/openbmc_project/network/" + ifaceId,
888 "org.freedesktop.DBus.Properties", "Set",
889 "xyz.openbmc_project.Network.VLAN", "Id",
Ed Tanousabf2add2019-01-22 16:40:12 -0800890 std::variant<uint32_t>(vlanId));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700892 else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700893 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700894 auto callback = [asyncResp](const boost::system::error_code ec) {
895 if (ec)
896 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700897 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700898 return;
899 }
900 asyncResp->res.jsonValue["VLANEnable"] = false;
901 };
902
903 crow::connections::systemBus->async_method_call(
904 std::move(callback), "xyz.openbmc_project.Network",
905 "/xyz/openbmc_project/network/" + ifaceId,
906 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700907 }
908 }
909
910 private:
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800911 void handleHostnamePatch(const std::string &hostname,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700912 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700913 {
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800914 asyncResp->res.jsonValue["HostName"] = hostname;
915 crow::connections::systemBus->async_method_call(
916 [asyncResp](const boost::system::error_code ec) {
917 if (ec)
918 {
919 messages::internalError(asyncResp->res);
920 }
921 },
922 "xyz.openbmc_project.Network",
923 "/xyz/openbmc_project/network/config",
924 "org.freedesktop.DBus.Properties", "Set",
925 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
Ed Tanousabf2add2019-01-22 16:40:12 -0800926 std::variant<std::string>(hostname));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927 }
928
Ratan Guptad5776652019-03-03 08:47:22 +0530929 void handleMACAddressPatch(const std::string &ifaceId,
930 const std::string &macAddress,
931 const std::shared_ptr<AsyncResp> &asyncResp)
932 {
933 crow::connections::systemBus->async_method_call(
934 [asyncResp, macAddress](const boost::system::error_code ec) {
935 if (ec)
936 {
937 messages::internalError(asyncResp->res);
938 return;
939 }
940 asyncResp->res.jsonValue["MACAddress"] = std::move(macAddress);
941 },
942 "xyz.openbmc_project.Network",
943 "/xyz/openbmc_project/network/" + ifaceId,
944 "org.freedesktop.DBus.Properties", "Set",
945 "xyz.openbmc_project.Network.MACAddress", "MACAddress",
946 std::variant<std::string>(macAddress));
947 }
948
Ed Tanous4a0cb852018-10-15 07:55:04 -0700949 void handleIPv4Patch(
Ratan Guptaf476acb2019-03-02 16:46:57 +0530950 const std::string &ifaceId, nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700951 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
952 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700953 {
Ratan Guptaf476acb2019-03-02 16:46:57 +0530954 if (!input.is_array())
955 {
956 messages::propertyValueTypeError(asyncResp->res, input.dump(),
957 "IPv4Addresses");
958 return;
959 }
960
Ed Tanous4a0cb852018-10-15 07:55:04 -0700961 int entryIdx = 0;
962 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
963 ipv4Data.begin();
Ed Tanous537174c2018-12-10 15:09:31 -0800964 for (nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700965 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700966 std::string pathString =
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800967 "IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968
Ratan Guptaf476acb2019-03-02 16:46:57 +0530969 if (thisJson.is_null())
970 {
971 if (thisData != ipv4Data.end())
972 {
973 deleteIPv4(ifaceId, thisData->id, entryIdx, asyncResp);
974 thisData++;
975 }
976 else
977 {
978 messages::propertyValueFormatError(
979 asyncResp->res, input.dump(), pathString);
980 return;
981 // TODO(ratagupt) Not sure about the property where value is
982 // list and if unable to update one of the
983 // list value then should we proceed further or
984 // break there, would ask in the redfish forum
985 // till then we stop processing the next list item.
986 }
987 entryIdx++;
988 continue; // not an error as per the redfish spec.
989 }
990
Ratan Gupta9474b372019-03-01 15:13:37 +0530991 if (thisJson.empty())
992 {
993 if (thisData != ipv4Data.end())
994 {
995 thisData++;
996 }
997 else
998 {
999 messages::propertyMissing(asyncResp->res,
1000 pathString + "/Address");
1001 return;
Ratan Guptaf476acb2019-03-02 16:46:57 +05301002 // TODO(ratagupt) Not sure about the property where value is
Ratan Gupta9474b372019-03-01 15:13:37 +05301003 // list and if unable to update one of the
1004 // list value then should we proceed further or
1005 // break there, would ask in the redfish forum
1006 // till then we stop processing the next list item.
1007 }
1008 entryIdx++;
1009 continue; // not an error as per the redfish spec.
1010 }
1011
Ed Tanous537174c2018-12-10 15:09:31 -08001012 std::optional<std::string> address;
1013 std::optional<std::string> addressOrigin;
1014 std::optional<std::string> subnetMask;
1015 std::optional<std::string> gateway;
1016
1017 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1018 address, "AddressOrigin", addressOrigin,
1019 "SubnetMask", subnetMask, "Gateway",
1020 gateway))
1021 {
1022 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001023 }
1024
Ed Tanous537174c2018-12-10 15:09:31 -08001025 if (address)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 {
Ed Tanous537174c2018-12-10 15:09:31 -08001027 if (!ipv4VerifyIpAndGetBitcount(*address))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001028 {
Ed Tanous537174c2018-12-10 15:09:31 -08001029 messages::propertyValueFormatError(asyncResp->res, *address,
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001030 pathString + "/Address");
Ed Tanous537174c2018-12-10 15:09:31 -08001031 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001032 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001034
Ed Tanous537174c2018-12-10 15:09:31 -08001035 uint8_t prefixLength = 0;
1036 if (subnetMask)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001037 {
Ed Tanous537174c2018-12-10 15:09:31 -08001038 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
Ed Tanous4a0cb852018-10-15 07:55:04 -07001039 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001040 messages::propertyValueFormatError(
Ed Tanous537174c2018-12-10 15:09:31 -08001041 asyncResp->res, *subnetMask,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001042 pathString + "/SubnetMask");
Ed Tanous537174c2018-12-10 15:09:31 -08001043 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001044 }
1045 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001046 std::string addressOriginInDBusFormat;
Ed Tanous537174c2018-12-10 15:09:31 -08001047 if (addressOrigin)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001048 {
Ed Tanous537174c2018-12-10 15:09:31 -08001049 // Get Address origin in proper format
1050 addressOriginInDBusFormat =
1051 translateAddressOriginRedfishToDbus(*addressOrigin);
1052 if (addressOriginInDBusFormat.empty())
Ed Tanous4a0cb852018-10-15 07:55:04 -07001053 {
Ed Tanous537174c2018-12-10 15:09:31 -08001054 messages::propertyValueNotInList(
1055 asyncResp->res, *addressOrigin,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001056 pathString + "/AddressOrigin");
Ed Tanous537174c2018-12-10 15:09:31 -08001057 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001058 }
1059 }
1060
Ed Tanous537174c2018-12-10 15:09:31 -08001061 if (gateway)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001062 {
Ed Tanous537174c2018-12-10 15:09:31 -08001063 if (!ipv4VerifyIpAndGetBitcount(*gateway))
Ed Tanous6ea007a2019-02-13 22:48:25 -08001064
Ed Tanous4a0cb852018-10-15 07:55:04 -07001065 {
Ed Tanous537174c2018-12-10 15:09:31 -08001066 messages::propertyValueFormatError(asyncResp->res, *gateway,
1067 pathString + "/Gateway");
1068 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001069 }
1070 }
1071
Ratan Guptaf476acb2019-03-02 16:46:57 +05301072 // if IP address exist then modify it.
Ed Tanous4a0cb852018-10-15 07:55:04 -07001073 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301075 // Apply changes
1076 if (address)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001077 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301078 auto callback = [asyncResp, entryIdx,
1079 address{std::string(*address)}](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001080 const boost::system::error_code ec) {
1081 if (ec)
1082 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001083 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001084 return;
1085 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301086 asyncResp->res
1087 .jsonValue["IPv4Addresses"][entryIdx]["Address"] =
1088 std::move(address);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001089 };
Ratan Guptaf476acb2019-03-02 16:46:57 +05301090
Ed Tanous4a0cb852018-10-15 07:55:04 -07001091 crow::connections::systemBus->async_method_call(
1092 std::move(callback), "xyz.openbmc_project.Network",
1093 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1094 thisData->id,
Ratan Guptaf476acb2019-03-02 16:46:57 +05301095 "org.freedesktop.DBus.Properties", "Set",
1096 "xyz.openbmc_project.Network.IP", "Address",
1097 std::variant<std::string>(*address));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001098 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301099
1100 if (subnetMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001101 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301102 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1103 thisData->id, *subnetMask,
1104 prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001105 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301106
1107 if (addressOrigin)
1108 {
1109 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1110 *addressOrigin, addressOriginInDBusFormat,
1111 asyncResp);
1112 }
1113
1114 if (gateway)
1115 {
1116 auto callback = [asyncResp, entryIdx,
1117 gateway{std::string(*gateway)}](
1118 const boost::system::error_code ec) {
1119 if (ec)
1120 {
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
1124 asyncResp->res
1125 .jsonValue["IPv4Addresses"][entryIdx]["Gateway"] =
1126 std::move(gateway);
1127 };
1128
1129 crow::connections::systemBus->async_method_call(
1130 std::move(callback), "xyz.openbmc_project.Network",
1131 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1132 thisData->id,
1133 "org.freedesktop.DBus.Properties", "Set",
1134 "xyz.openbmc_project.Network.IP", "Gateway",
1135 std::variant<std::string>(*gateway));
1136 }
1137
Ed Tanous4a0cb852018-10-15 07:55:04 -07001138 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001139 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001140 else
1141 {
1142 // Create IPv4 with provided data
Ed Tanous537174c2018-12-10 15:09:31 -08001143 if (!gateway)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001144 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001145 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001146 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001147 continue;
1148 }
1149
Ed Tanous537174c2018-12-10 15:09:31 -08001150 if (!address)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001151 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001152 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001153 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001154 continue;
1155 }
1156
Ed Tanous537174c2018-12-10 15:09:31 -08001157 if (!subnetMask)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001158 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001159 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001160 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001161 continue;
1162 }
1163
Ed Tanous6ea007a2019-02-13 22:48:25 -08001164 createIPv4(ifaceId, entryIdx, *gateway, *address, asyncResp);
Ratan Gupta95897b22019-03-07 18:25:57 +05301165
1166 nlohmann::json &ipv4AddressJson =
1167 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx];
1168 ipv4AddressJson["Address"] = *address;
1169 ipv4AddressJson["SubnetMask"] = *subnetMask;
1170 ipv4AddressJson["Gateway"] = *gateway;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001171 }
1172 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001173 }
1174 }
1175
Ed Tanous0f74e642018-11-12 15:17:05 -08001176 void parseInterfaceData(
1177 nlohmann::json &json_response, const std::string &iface_id,
1178 const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001179 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001181 json_response["Id"] = iface_id;
1182 json_response["@odata.id"] =
1183 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous029573d2019-02-01 10:57:49 -08001184 json_response["InterfaceEnabled"] = true;
1185 if (ethData.speed == 0)
1186 {
1187 json_response["LinkStatus"] = "NoLink";
1188 json_response["Status"] = {
1189 {"Health", "OK"},
1190 {"State", "Disabled"},
1191 };
1192 }
1193 else
1194 {
1195 json_response["LinkStatus"] = "LinkUp";
1196 json_response["Status"] = {
1197 {"Health", "OK"},
1198 {"State", "Enabled"},
1199 };
1200 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001201 json_response["SpeedMbps"] = ethData.speed;
1202 json_response["MACAddress"] = ethData.mac_address;
manojkiraneda2a133282019-02-19 13:09:43 +05301203 json_response["DHCPv4Configuration"]["DHCPEnabled"] =
1204 ethData.DHCPEnabled;
1205
Ed Tanous4a0cb852018-10-15 07:55:04 -07001206 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001207 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001208 json_response["HostName"] = ethData.hostname;
1209 }
1210
1211 nlohmann::json &vlanObj = json_response["VLAN"];
1212 if (ethData.vlan_id)
1213 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001214 vlanObj["VLANEnable"] = true;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001215 vlanObj["VLANId"] = *ethData.vlan_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001216 }
1217 else
1218 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001219 vlanObj["VLANEnable"] = false;
1220 vlanObj["VLANId"] = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001221 }
Ed Tanous029573d2019-02-01 10:57:49 -08001222 json_response["NameServers"] = ethData.nameservers;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001223
Ed Tanous4a0cb852018-10-15 07:55:04 -07001224 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001225 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001226 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1227 ipv4_array = nlohmann::json::array();
1228 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229 {
Ed Tanous029573d2019-02-01 10:57:49 -08001230 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1231 {"SubnetMask", ipv4_config.netmask},
1232 {"Address", ipv4_config.address},
1233 {"Gateway", ipv4_config.gateway}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001234 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001235 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001236 }
1237
1238 /**
1239 * Functions triggers appropriate requests on DBus
1240 */
1241 void doGet(crow::Response &res, const crow::Request &req,
1242 const std::vector<std::string> &params) override
1243 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001244 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001245 if (params.size() != 1)
1246 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001247 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001248 return;
1249 }
1250
Ed Tanous4a0cb852018-10-15 07:55:04 -07001251 getEthernetIfaceData(
1252 params[0],
1253 [this, asyncResp, iface_id{std::string(params[0])}](
1254 const bool &success, const EthernetInterfaceData &ethData,
1255 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1256 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001257 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001258 // TODO(Pawel)consider distinguish between non existing
1259 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001260 messages::resourceNotFound(asyncResp->res,
1261 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001262 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001263 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001264 asyncResp->res.jsonValue["@odata.type"] =
1265 "#EthernetInterface.v1_2_0.EthernetInterface";
1266 asyncResp->res.jsonValue["@odata.context"] =
1267 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1268 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1269 asyncResp->res.jsonValue["Description"] =
1270 "Management Network Interface";
1271
1272 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1273 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274 });
manojkiraneda2a133282019-02-19 13:09:43 +05301275 getDHCPConfigData(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276 }
1277
1278 void doPatch(crow::Response &res, const crow::Request &req,
1279 const std::vector<std::string> &params) override
1280 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001281 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001282 if (params.size() != 1)
1283 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001284 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001285 return;
1286 }
1287
Ed Tanous4a0cb852018-10-15 07:55:04 -07001288 const std::string &iface_id = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001289
Ed Tanous0627a2c2018-11-29 17:09:23 -08001290 std::optional<nlohmann::json> vlan;
Ed Tanousbc0bd6e2018-12-10 14:07:55 -08001291 std::optional<std::string> hostname;
Ratan Guptad5776652019-03-03 08:47:22 +05301292 std::optional<std::string> macAddress;
Ratan Guptaf476acb2019-03-02 16:46:57 +05301293 std::optional<nlohmann::json> ipv4Addresses;
1294 std::optional<nlohmann::json> ipv6Addresses;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001295
1296 if (!json_util::readJson(req, res, "VLAN", vlan, "HostName", hostname,
1297 "IPv4Addresses", ipv4Addresses,
Ratan Guptad5776652019-03-03 08:47:22 +05301298 "IPv6Addresses", ipv6Addresses, "MACAddress",
1299 macAddress))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001300 {
1301 return;
1302 }
Ratan Guptaf15aad32019-03-01 13:41:13 +05301303
1304 std::optional<uint64_t> vlanId;
1305 std::optional<bool> vlanEnable;
1306
Ed Tanous0627a2c2018-11-29 17:09:23 -08001307 if (vlan)
1308 {
1309 if (!json_util::readJson(*vlan, res, "VLANEnable", vlanEnable,
1310 "VLANId", vlanId))
1311 {
1312 return;
1313 }
1314 // Need both vlanId and vlanEnable to service this request
1315 if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
1316 {
1317 if (vlanId)
1318 {
1319 messages::propertyMissing(asyncResp->res, "VLANEnable");
1320 }
1321 else
1322 {
1323 messages::propertyMissing(asyncResp->res, "VLANId");
1324 }
1325
1326 return;
1327 }
1328 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001329
Ed Tanous4a0cb852018-10-15 07:55:04 -07001330 // Get single eth interface data, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001331 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001332 getEthernetIfaceData(
1333 iface_id,
Ed Tanous0627a2c2018-11-29 17:09:23 -08001334 [this, asyncResp, iface_id, vlanId, vlanEnable,
Ratan Guptad5776652019-03-03 08:47:22 +05301335 hostname = std::move(hostname), macAddress = std::move(macAddress),
Ed Tanous0627a2c2018-11-29 17:09:23 -08001336 ipv4Addresses = std::move(ipv4Addresses),
1337 ipv6Addresses = std::move(ipv6Addresses)](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001338 const bool &success, const EthernetInterfaceData &ethData,
1339 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340 if (!success)
1341 {
1342 // ... otherwise return error
1343 // TODO(Pawel)consider distinguish between non existing
1344 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001345 messages::resourceNotFound(
1346 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001347 return;
1348 }
1349
Ed Tanous0f74e642018-11-12 15:17:05 -08001350 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1351 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001352
Ed Tanous0627a2c2018-11-29 17:09:23 -08001353 if (vlanId && vlanEnable)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001354 {
Ed Tanous0627a2c2018-11-29 17:09:23 -08001355 handleVlanPatch(iface_id, *vlanId, *vlanEnable, ethData,
1356 asyncResp);
1357 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001358
Ed Tanous0627a2c2018-11-29 17:09:23 -08001359 if (hostname)
1360 {
1361 handleHostnamePatch(*hostname, asyncResp);
1362 }
1363
Ratan Guptad5776652019-03-03 08:47:22 +05301364 if (macAddress)
1365 {
1366 handleMACAddressPatch(iface_id, *macAddress, asyncResp);
1367 }
1368
Ed Tanous0627a2c2018-11-29 17:09:23 -08001369 if (ipv4Addresses)
1370 {
Ed Tanous537174c2018-12-10 15:09:31 -08001371 // TODO(ed) for some reason the capture of ipv4Addresses
1372 // above is returning a const value, not a non-const value.
1373 // This doesn't really work for us, as we need to be able to
1374 // efficiently move out the intermedia nlohmann::json
1375 // objects. This makes a copy of the structure, and operates
1376 // on that, but could be done more efficiently
Ratan Guptaf476acb2019-03-02 16:46:57 +05301377 nlohmann::json ipv4 = std::move(*ipv4Addresses);
Ed Tanous537174c2018-12-10 15:09:31 -08001378 handleIPv4Patch(iface_id, ipv4, ipv4Data, asyncResp);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001379 }
1380
1381 if (ipv6Addresses)
1382 {
1383 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1384 messages::propertyNotWritable(asyncResp->res,
1385 "IPv6Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001386 }
1387 });
1388 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001389};
1390
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001391/**
Ed Tanous4a0cb852018-10-15 07:55:04 -07001392 * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1393 * Schema
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001394 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001395class VlanNetworkInterface : public Node
1396{
1397 public:
1398 /*
1399 * Default Constructor
1400 */
1401 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001402 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001403 Node(app,
Ed Tanous0f74e642018-11-12 15:17:05 -08001404 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001405 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001406 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001407 entityPrivileges = {
1408 {boost::beast::http::verb::get, {{"Login"}}},
1409 {boost::beast::http::verb::head, {{"Login"}}},
1410 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1411 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1412 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1413 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001414 }
1415
Ed Tanous1abe55e2018-09-05 08:30:59 -07001416 private:
Ed Tanous0f74e642018-11-12 15:17:05 -08001417 void parseInterfaceData(
1418 nlohmann::json &json_response, const std::string &parent_iface_id,
1419 const std::string &iface_id, const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001420 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001422 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001423 json_response["Id"] = iface_id;
1424 json_response["@odata.id"] =
1425 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1426 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001427
Ed Tanous4a0cb852018-10-15 07:55:04 -07001428 json_response["VLANEnable"] = true;
1429 if (ethData.vlan_id)
1430 {
1431 json_response["VLANId"] = *ethData.vlan_id;
1432 }
Ed Tanousa434f2b2018-07-27 13:04:22 -07001433 }
1434
Ed Tanous1abe55e2018-09-05 08:30:59 -07001435 bool verifyNames(crow::Response &res, const std::string &parent,
1436 const std::string &iface)
1437 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001438 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001439 if (!boost::starts_with(iface, parent + "_"))
1440 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001441 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1442 iface);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001443 return false;
1444 }
1445 else
1446 {
1447 return true;
1448 }
1449 }
1450
1451 /**
1452 * Functions triggers appropriate requests on DBus
1453 */
1454 void doGet(crow::Response &res, const crow::Request &req,
1455 const std::vector<std::string> &params) override
1456 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001457 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1458 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001459 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1460 // Check if there is required param, truly entering this shall be
1461 // impossible.
1462 if (params.size() != 2)
1463 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001464 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001465 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001466 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001467 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001468
Ed Tanous4a0cb852018-10-15 07:55:04 -07001469 const std::string &parent_iface_id = params[0];
1470 const std::string &iface_id = params[1];
Ed Tanous0f74e642018-11-12 15:17:05 -08001471 res.jsonValue["@odata.type"] =
1472 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1473 res.jsonValue["@odata.context"] =
1474 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1475 res.jsonValue["Name"] = "VLAN Network Interface";
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001476
Ed Tanous4a0cb852018-10-15 07:55:04 -07001477 if (!verifyNames(res, parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001478 {
1479 return;
1480 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001481
Ed Tanous1abe55e2018-09-05 08:30:59 -07001482 // Get single eth interface data, and call the below callback for JSON
1483 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001484 getEthernetIfaceData(
1485 iface_id,
1486 [this, asyncResp, parent_iface_id, iface_id](
1487 const bool &success, const EthernetInterfaceData &ethData,
1488 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1489 if (success && ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001490 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001491 parseInterfaceData(asyncResp->res.jsonValue,
1492 parent_iface_id, iface_id, ethData,
1493 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001494 }
1495 else
1496 {
1497 // ... otherwise return error
1498 // 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", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001503 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001504 }
1505
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506 void doPatch(crow::Response &res, const crow::Request &req,
1507 const std::vector<std::string> &params) override
1508 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001509 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001510 if (params.size() != 2)
1511 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001512 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001513 return;
1514 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001515
Ed Tanous1abe55e2018-09-05 08:30:59 -07001516 const std::string &parentIfaceId = params[0];
1517 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001518
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519 if (!verifyNames(res, parentIfaceId, ifaceId))
1520 {
1521 return;
1522 }
1523
Ed Tanous0627a2c2018-11-29 17:09:23 -08001524 bool vlanEnable = false;
1525 uint64_t vlanId = 0;
1526
1527 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
1528 vlanId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 {
1530 return;
1531 }
1532
1533 // Get single eth interface data, and call the below callback for JSON
1534 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001535 getEthernetIfaceData(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001536 ifaceId,
Ed Tanous0627a2c2018-11-29 17:09:23 -08001537 [this, asyncResp, parentIfaceId, ifaceId, vlanEnable, vlanId](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001538 const bool &success, const EthernetInterfaceData &ethData,
1539 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001540 if (!success)
1541 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542 // TODO(Pawel)consider distinguish between non existing
1543 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001544 messages::resourceNotFound(
1545 asyncResp->res, "VLAN Network Interface", ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001546
1547 return;
1548 }
1549
Ed Tanous0f74e642018-11-12 15:17:05 -08001550 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1551 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001552
Ed Tanous0627a2c2018-11-29 17:09:23 -08001553 EthernetInterface::handleVlanPatch(ifaceId, vlanId, vlanEnable,
1554 ethData, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001555 });
1556 }
1557
1558 void doDelete(crow::Response &res, const crow::Request &req,
1559 const std::vector<std::string> &params) override
1560 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001561 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001562 if (params.size() != 2)
1563 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001564 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565 return;
1566 }
1567
1568 const std::string &parentIfaceId = params[0];
1569 const std::string &ifaceId = params[1];
1570
Ed Tanous4a0cb852018-10-15 07:55:04 -07001571 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001572 {
1573 return;
1574 }
1575
1576 // Get single eth interface data, and call the below callback for JSON
1577 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001578 getEthernetIfaceData(
1579 ifaceId,
1580 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1581 ifaceId{std::string(ifaceId)}](
1582 const bool &success, const EthernetInterfaceData &ethData,
1583 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1584 if (success && ethData.vlan_id)
1585 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001586 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1587 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588
Jason M. Billsf12894f2018-10-09 12:45:45 -07001589 auto callback =
1590 [asyncResp](const boost::system::error_code ec) {
1591 if (ec)
1592 {
1593 messages::internalError(asyncResp->res);
1594 }
1595 };
1596 crow::connections::systemBus->async_method_call(
1597 std::move(callback), "xyz.openbmc_project.Network",
1598 std::string("/xyz/openbmc_project/network/") + ifaceId,
1599 "xyz.openbmc_project.Object.Delete", "Delete");
1600 }
1601 else
1602 {
1603 // ... otherwise return error
1604 // TODO(Pawel)consider distinguish between non existing
1605 // object, and other errors
1606 messages::resourceNotFound(
1607 asyncResp->res, "VLAN Network Interface", ifaceId);
1608 }
1609 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001610 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001611};
1612
1613/**
1614 * VlanNetworkInterfaceCollection derived class for delivering
1615 * VLANNetworkInterface Collection Schema
1616 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001617class VlanNetworkInterfaceCollection : public Node
1618{
1619 public:
1620 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001621 VlanNetworkInterfaceCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001622 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1623 std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001624 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001625 entityPrivileges = {
1626 {boost::beast::http::verb::get, {{"Login"}}},
1627 {boost::beast::http::verb::head, {{"Login"}}},
1628 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1629 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1630 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1631 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001632 }
1633
Ed Tanous1abe55e2018-09-05 08:30:59 -07001634 private:
1635 /**
1636 * Functions triggers appropriate requests on DBus
1637 */
1638 void doGet(crow::Response &res, const crow::Request &req,
1639 const std::vector<std::string> &params) override
1640 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001641 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001642 if (params.size() != 1)
1643 {
1644 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001645 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001646 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001647 }
1648
Ed Tanous4a0cb852018-10-15 07:55:04 -07001649 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001650
Ed Tanous4a0cb852018-10-15 07:55:04 -07001651 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001653 getEthernetIfaceList(
Ed Tanous43b761d2019-02-13 20:10:56 -08001654 [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
Jason M. Billsf12894f2018-10-09 12:45:45 -07001655 const bool &success,
1656 const std::vector<std::string> &iface_list) {
1657 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001658 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001659 messages::internalError(asyncResp->res);
1660 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001661 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001662 asyncResp->res.jsonValue["@odata.type"] =
1663 "#VLanNetworkInterfaceCollection."
1664 "VLanNetworkInterfaceCollection";
1665 asyncResp->res.jsonValue["@odata.context"] =
1666 "/redfish/v1/$metadata"
1667 "#VLanNetworkInterfaceCollection."
1668 "VLanNetworkInterfaceCollection";
1669 asyncResp->res.jsonValue["Name"] =
1670 "VLAN Network Interface Collection";
Ed Tanous4a0cb852018-10-15 07:55:04 -07001671
Jason M. Billsf12894f2018-10-09 12:45:45 -07001672 nlohmann::json iface_array = nlohmann::json::array();
1673
1674 for (const std::string &iface_item : iface_list)
1675 {
1676 if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1677 {
1678 iface_array.push_back(
1679 {{"@odata.id",
1680 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1681 rootInterfaceName + "/VLANs/" + iface_item}});
1682 }
1683 }
1684
1685 if (iface_array.empty())
1686 {
1687 messages::resourceNotFound(
1688 asyncResp->res, "EthernetInterface", rootInterfaceName);
1689 return;
1690 }
1691 asyncResp->res.jsonValue["Members@odata.count"] =
1692 iface_array.size();
1693 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1694 asyncResp->res.jsonValue["@odata.id"] =
1695 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1696 rootInterfaceName + "/VLANs";
1697 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001699
Ed Tanous1abe55e2018-09-05 08:30:59 -07001700 void doPost(crow::Response &res, const crow::Request &req,
1701 const std::vector<std::string> &params) override
1702 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001703 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001704 if (params.size() != 1)
1705 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001706 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001707 return;
1708 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001709
Ed Tanous0627a2c2018-11-29 17:09:23 -08001710 uint32_t vlanId = 0;
1711 if (!json_util::readJson(req, res, "VLANId", vlanId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001713 return;
1714 }
1715 const std::string &rootInterfaceName = params[0];
Ed Tanous4a0cb852018-10-15 07:55:04 -07001716 auto callback = [asyncResp](const boost::system::error_code ec) {
1717 if (ec)
1718 {
1719 // TODO(ed) make more consistent error messages based on
1720 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001721 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001722 return;
1723 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001724 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001725 };
1726 crow::connections::systemBus->async_method_call(
1727 std::move(callback), "xyz.openbmc_project.Network",
1728 "/xyz/openbmc_project/network",
1729 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
Ed Tanous0627a2c2018-11-29 17:09:23 -08001730 rootInterfaceName, vlanId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001731 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001732};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001733} // namespace redfish