blob: f1ef23f525c8caf4c2e53997701b3120282e91a0 [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;
Ravi Teja9a6fc6f2019-04-16 02:43:13 -050084 std::string ipv6_default_gateway;
Ed Tanous4a0cb852018-10-15 07:55:04 -070085 std::string mac_address;
Sunitha Harishfda13ad2019-03-21 11:01:24 -050086 std::vector<std::uint32_t> vlan_id;
Ed Tanous029573d2019-02-01 10:57:49 -080087 std::vector<std::string> nameservers;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010088};
89
Ed Tanous4a0cb852018-10-15 07:55:04 -070090// Helper function that changes bits netmask notation (i.e. /24)
91// into full dot notation
92inline std::string getNetmask(unsigned int bits)
Ed Tanous1abe55e2018-09-05 08:30:59 -070093{
Ed Tanous4a0cb852018-10-15 07:55:04 -070094 uint32_t value = 0xffffffff << (32 - bits);
95 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
96 std::to_string((value >> 16) & 0xff) + "." +
97 std::to_string((value >> 8) & 0xff) + "." +
98 std::to_string(value & 0xff);
99 return netmask;
100}
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100101
Ed Tanous4a0cb852018-10-15 07:55:04 -0700102inline std::string
103 translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
104 bool isIPv4)
105{
106 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700107 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700108 return "Static";
109 }
110 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
111 {
112 if (isIPv4)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700113 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700114 return "IPv4LinkLocal";
115 }
116 else
117 {
118 return "LinkLocal";
119 }
120 }
121 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
122 {
123 if (isIPv4)
124 {
125 return "DHCP";
126 }
127 else
128 {
129 return "DHCPv6";
130 }
131 }
132 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
133 {
134 return "SLAAC";
135 }
136 return "";
137}
138
139inline std::string
140 translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
141{
142 if (inputOrigin == "Static")
143 {
144 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
145 }
146 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
147 {
148 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
149 }
150 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
151 {
152 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
153 }
154 if (inputOrigin == "SLAAC")
155 {
156 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
157 }
158 return "";
159}
160
161inline void extractEthernetInterfaceData(const std::string &ethiface_id,
162 const GetManagedObjects &dbus_data,
163 EthernetInterfaceData &ethData)
164{
165 for (const auto &objpath : dbus_data)
166 {
Ed Tanous029573d2019-02-01 10:57:49 -0800167 for (const auto &ifacePair : objpath.second)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700168 {
Ed Tanous029573d2019-02-01 10:57:49 -0800169 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700171 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700173 for (const auto &propertyPair : ifacePair.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700174 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700175 if (propertyPair.first == "MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700176 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700177 const std::string *mac =
Ed Tanousabf2add2019-01-22 16:40:12 -0800178 std::get_if<std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700179 if (mac != nullptr)
180 {
181 ethData.mac_address = *mac;
182 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700183 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700184 }
185 }
186 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
187 {
188 for (const auto &propertyPair : ifacePair.second)
189 {
190 if (propertyPair.first == "Id")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700191 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800192 const uint32_t *id =
Ed Tanousabf2add2019-01-22 16:40:12 -0800193 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700194 if (id != nullptr)
195 {
Sunitha Harishfda13ad2019-03-21 11:01:24 -0500196 ethData.vlan_id.push_back(*id);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700197 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700198 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700199 }
200 }
201 else if (ifacePair.first ==
202 "xyz.openbmc_project.Network.EthernetInterface")
203 {
204 for (const auto &propertyPair : ifacePair.second)
205 {
206 if (propertyPair.first == "AutoNeg")
207 {
208 const bool *auto_neg =
Ed Tanousabf2add2019-01-22 16:40:12 -0800209 std::get_if<bool>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700210 if (auto_neg != nullptr)
211 {
212 ethData.auto_neg = *auto_neg;
213 }
214 }
215 else if (propertyPair.first == "Speed")
216 {
217 const uint32_t *speed =
Ed Tanousabf2add2019-01-22 16:40:12 -0800218 std::get_if<uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700219 if (speed != nullptr)
220 {
221 ethData.speed = *speed;
222 }
223 }
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -0500224 else if (propertyPair.first == "Nameservers")
Ed Tanous4a0cb852018-10-15 07:55:04 -0700225 {
Ed Tanous029573d2019-02-01 10:57:49 -0800226 const std::vector<std::string> *nameservers =
227 sdbusplus::message::variant_ns::get_if<
228 std::vector<std::string>>(
229 &propertyPair.second);
230 if (nameservers != nullptr)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700231 {
Ed Tanous029573d2019-02-01 10:57:49 -0800232 ethData.nameservers = std::move(*nameservers);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700233 }
234 }
manojkiraneda2a133282019-02-19 13:09:43 +0530235 else if (propertyPair.first == "DHCPEnabled")
236 {
237 const bool *DHCPEnabled =
238 std::get_if<bool>(&propertyPair.second);
239 if (DHCPEnabled != nullptr)
240 {
241 ethData.DHCPEnabled = *DHCPEnabled;
242 }
243 }
Ed Tanous029573d2019-02-01 10:57:49 -0800244 }
245 }
246 }
247 // System configuration shows up in the global namespace, so no need
248 // to check eth number
249 if (ifacePair.first ==
250 "xyz.openbmc_project.Network.SystemConfiguration")
251 {
252 for (const auto &propertyPair : ifacePair.second)
253 {
254 if (propertyPair.first == "HostName")
255 {
256 const std::string *hostname =
257 sdbusplus::message::variant_ns::get_if<std::string>(
258 &propertyPair.second);
259 if (hostname != nullptr)
Ed Tanous4a0cb852018-10-15 07:55:04 -0700260 {
Ed Tanous029573d2019-02-01 10:57:49 -0800261 ethData.hostname = *hostname;
262 }
263 }
264 else if (propertyPair.first == "DefaultGateway")
265 {
266 const std::string *defaultGateway =
267 sdbusplus::message::variant_ns::get_if<std::string>(
268 &propertyPair.second);
269 if (defaultGateway != nullptr)
270 {
271 ethData.default_gateway = *defaultGateway;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700272 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700273 }
Ravi Teja9a6fc6f2019-04-16 02:43:13 -0500274 else if (propertyPair.first == "DefaultGateway6")
275 {
276 const std::string *defaultGateway6 =
277 sdbusplus::message::variant_ns::get_if<std::string>(
278 &propertyPair.second);
279 if (defaultGateway6 != nullptr)
280 {
281 ethData.ipv6_default_gateway = *defaultGateway6;
282 }
283 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700284 }
285 }
286 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700287 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700288}
289
290// Helper function that extracts data for single ethernet ipv4 address
291inline void
292 extractIPData(const std::string &ethiface_id,
293 const GetManagedObjects &dbus_data,
294 boost::container::flat_set<IPv4AddressData> &ipv4_config)
295{
296 const std::string ipv4PathStart =
297 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
298
299 // Since there might be several IPv4 configurations aligned with
300 // single ethernet interface, loop over all of them
301 for (const auto &objpath : dbus_data)
302 {
303 // Check if proper pattern for object path appears
304 if (boost::starts_with(objpath.first.str, ipv4PathStart))
305 {
306 for (auto &interface : objpath.second)
307 {
308 if (interface.first == "xyz.openbmc_project.Network.IP")
309 {
310 // Instance IPv4AddressData structure, and set as
311 // appropriate
312 std::pair<
313 boost::container::flat_set<IPv4AddressData>::iterator,
314 bool>
315 it = ipv4_config.insert(
Ed Tanousb01bf292019-03-25 19:25:26 +0000316 {objpath.first.str.substr(ipv4PathStart.size())});
Ed Tanous4a0cb852018-10-15 07:55:04 -0700317 IPv4AddressData &ipv4_address = *it.first;
318 for (auto &property : interface.second)
319 {
320 if (property.first == "Address")
321 {
322 const std::string *address =
Ed Tanousabf2add2019-01-22 16:40:12 -0800323 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700324 if (address != nullptr)
325 {
326 ipv4_address.address = *address;
327 }
328 }
329 else if (property.first == "Gateway")
330 {
331 const std::string *gateway =
Ed Tanousabf2add2019-01-22 16:40:12 -0800332 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700333 if (gateway != nullptr)
334 {
335 ipv4_address.gateway = *gateway;
336 }
337 }
338 else if (property.first == "Origin")
339 {
340 const std::string *origin =
Ed Tanousabf2add2019-01-22 16:40:12 -0800341 std::get_if<std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700342 if (origin != nullptr)
343 {
344 ipv4_address.origin =
345 translateAddressOriginDbusToRedfish(*origin,
346 true);
347 }
348 }
349 else if (property.first == "PrefixLength")
350 {
351 const uint8_t *mask =
Ed Tanousabf2add2019-01-22 16:40:12 -0800352 std::get_if<uint8_t>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700353 if (mask != nullptr)
354 {
355 // convert it to the string
356 ipv4_address.netmask = getNetmask(*mask);
357 }
358 }
359 else
360 {
361 BMCWEB_LOG_ERROR
362 << "Got extra property: " << property.first
363 << " on the " << objpath.first.str << " object";
364 }
365 }
366 // Check if given address is local, or global
367 ipv4_address.linktype =
368 boost::starts_with(ipv4_address.address, "169.254.")
369 ? LinkType::Global
370 : LinkType::Local;
371 }
372 }
373 }
374 }
375}
376
377/**
378 * @brief Sets given Id on the given VLAN interface through D-Bus
379 *
380 * @param[in] ifaceId Id of VLAN interface that should be modified
381 * @param[in] inputVlanId New ID of the VLAN
382 * @param[in] callback Function that will be called after the operation
383 *
384 * @return None.
385 */
386template <typename CallbackFunc>
387void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
388 CallbackFunc &&callback)
389{
390 crow::connections::systemBus->async_method_call(
391 callback, "xyz.openbmc_project.Network",
392 std::string("/xyz/openbmc_project/network/") + ifaceId,
393 "org.freedesktop.DBus.Properties", "Set",
394 "xyz.openbmc_project.Network.VLAN", "Id",
Ed Tanousabf2add2019-01-22 16:40:12 -0800395 std::variant<uint32_t>(inputVlanId));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700396}
397
398/**
399 * @brief Helper function that verifies IP address to check if it is in
400 * proper format. If bits pointer is provided, also calculates active
401 * bit count for Subnet Mask.
402 *
403 * @param[in] ip IP that will be verified
404 * @param[out] bits Calculated mask in bits notation
405 *
406 * @return true in case of success, false otherwise
407 */
408inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
409 uint8_t *bits = nullptr)
410{
411 std::vector<std::string> bytesInMask;
412
413 boost::split(bytesInMask, ip, boost::is_any_of("."));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700414
415 static const constexpr int ipV4AddressSectionsCount = 4;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700416 if (bytesInMask.size() != ipV4AddressSectionsCount)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700418 return false;
419 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420
Ed Tanous4a0cb852018-10-15 07:55:04 -0700421 if (bits != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700422 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700423 *bits = 0;
424 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425
Ed Tanous4a0cb852018-10-15 07:55:04 -0700426 char *endPtr;
427 long previousValue = 255;
428 bool firstZeroInByteHit;
429 for (const std::string &byte : bytesInMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700430 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700431 if (byte.empty())
432 {
433 return false;
434 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700435
Ed Tanous4a0cb852018-10-15 07:55:04 -0700436 // Use strtol instead of stroi to avoid exceptions
437 long value = std::strtol(byte.c_str(), &endPtr, 10);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438
Ed Tanous4a0cb852018-10-15 07:55:04 -0700439 // endPtr should point to the end of the string, otherwise given string
440 // is not 100% number
441 if (*endPtr != '\0')
442 {
443 return false;
444 }
445
446 // Value should be contained in byte
447 if (value < 0 || value > 255)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 {
449 return false;
450 }
451
452 if (bits != nullptr)
453 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700454 // Mask has to be continuous between bytes
455 if (previousValue != 255 && value != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700456 {
457 return false;
458 }
459
Ed Tanous4a0cb852018-10-15 07:55:04 -0700460 // Mask has to be continuous inside bytes
461 firstZeroInByteHit = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700462
Ed Tanous4a0cb852018-10-15 07:55:04 -0700463 // Count bits
464 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700465 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700466 if (value & (1 << bitIdx))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700467 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700468 if (firstZeroInByteHit)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700469 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700470 // Continuity not preserved
471 return false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700472 }
473 else
474 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700475 (*bits)++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700476 }
477 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700478 else
479 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700480 firstZeroInByteHit = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700481 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700482 }
483 }
484
485 previousValue = value;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700486 }
487
Ed Tanous4a0cb852018-10-15 07:55:04 -0700488 return true;
489}
490
491/**
Ed Tanousb01bf292019-03-25 19:25:26 +0000492 * @brief Changes IPv4 address type property (Address, Gateway)
493 *
494 * @param[in] ifaceId Id of interface whose IP should be modified
495 * @param[in] ipIdx Index of IP in input array that should be modified
496 * @param[in] ipHash DBus Hash id of modified IP
497 * @param[in] name Name of field in JSON representation
498 * @param[in] newValue New value that should be written
499 * @param[io] asyncResp Response object that will be returned to client
500 *
501 * @return true if give IP is valid and has been sent do D-Bus, false
502 * otherwise
503 */
504inline void changeIPv4AddressProperty(
505 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
506 const std::string &name, const std::string &newValue,
507 const std::shared_ptr<AsyncResp> asyncResp)
508{
509 auto callback = [asyncResp, ipIdx, name{std::string(name)},
510 newValue{std::move(newValue)}](
511 const boost::system::error_code ec) {
512 if (ec)
513 {
514 messages::internalError(asyncResp->res);
515 }
516 else
517 {
518 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
519 }
520 };
521
522 crow::connections::systemBus->async_method_call(
523 std::move(callback), "xyz.openbmc_project.Network",
524 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
525 "org.freedesktop.DBus.Properties", "Set",
526 "xyz.openbmc_project.Network.IP", name,
527 std::variant<std::string>(newValue));
528}
529
530/**
Ed Tanous4a0cb852018-10-15 07:55:04 -0700531 * @brief Changes IPv4 address origin property
532 *
533 * @param[in] ifaceId Id of interface whose IP should be modified
534 * @param[in] ipIdx Index of IP in input array that should be
535 * modified
536 * @param[in] ipHash DBus Hash id of modified IP
537 * @param[in] newValue New value in Redfish format
538 * @param[in] newValueDbus New value in D-Bus format
539 * @param[io] asyncResp Response object that will be returned to client
540 *
541 * @return true if give IP is valid and has been sent do D-Bus, false
542 * otherwise
543 */
Ed Tanousb01bf292019-03-25 19:25:26 +0000544inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700545 const std::string &ipHash,
546 const std::string &newValue,
547 const std::string &newValueDbus,
548 const std::shared_ptr<AsyncResp> asyncResp)
549{
550 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
551 const boost::system::error_code ec) {
552 if (ec)
553 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800554 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700555 }
556 else
557 {
558 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
559 newValue;
560 }
561 };
562
563 crow::connections::systemBus->async_method_call(
564 std::move(callback), "xyz.openbmc_project.Network",
565 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
566 "org.freedesktop.DBus.Properties", "Set",
567 "xyz.openbmc_project.Network.IP", "Origin",
Ed Tanousabf2add2019-01-22 16:40:12 -0800568 std::variant<std::string>(newValueDbus));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700569}
570
571/**
572 * @brief Modifies SubnetMask for given IP
573 *
574 * @param[in] ifaceId Id of interface whose IP should be modified
575 * @param[in] ipIdx Index of IP in input array that should be
576 * modified
577 * @param[in] ipHash DBus Hash id of modified IP
578 * @param[in] newValueStr Mask in dot notation as string
579 * @param[in] newValue Mask as PrefixLength in bitcount
580 * @param[io] asyncResp Response object that will be returned to client
581 *
582 * @return None
583 */
Ed Tanousb01bf292019-03-25 19:25:26 +0000584inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700585 const std::string &ipHash,
586 const std::string &newValueStr,
587 uint8_t &newValue,
588 std::shared_ptr<AsyncResp> asyncResp)
589{
590 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
591 const boost::system::error_code ec) {
592 if (ec)
593 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800594 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700595 }
596 else
597 {
598 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
599 newValueStr;
600 }
601 };
602
603 crow::connections::systemBus->async_method_call(
604 std::move(callback), "xyz.openbmc_project.Network",
605 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
606 "org.freedesktop.DBus.Properties", "Set",
607 "xyz.openbmc_project.Network.IP", "PrefixLength",
Ed Tanousabf2add2019-01-22 16:40:12 -0800608 std::variant<uint8_t>(newValue));
Ed Tanous4a0cb852018-10-15 07:55:04 -0700609}
610
611/**
Ed Tanous4a0cb852018-10-15 07:55:04 -0700612 * @brief Deletes given IPv4
613 *
614 * @param[in] ifaceId Id of interface whose IP should be deleted
615 * @param[in] ipIdx Index of IP in input array that should be deleted
616 * @param[in] ipHash DBus Hash id of IP that should be deleted
617 * @param[io] asyncResp Response object that will be returned to client
618 *
619 * @return None
620 */
621inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
622 unsigned int ipIdx,
623 const std::shared_ptr<AsyncResp> asyncResp)
624{
625 crow::connections::systemBus->async_method_call(
626 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700627 if (ec)
628 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800629 messages::internalError(asyncResp->res);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100630 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700631 else
632 {
633 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
634 }
635 },
636 "xyz.openbmc_project.Network",
637 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
638 "xyz.openbmc_project.Object.Delete", "Delete");
639}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700640
Ed Tanous4a0cb852018-10-15 07:55:04 -0700641/**
642 * @brief Creates IPv4 with given data
643 *
644 * @param[in] ifaceId Id of interface whose IP should be deleted
645 * @param[in] ipIdx Index of IP in input array that should be deleted
646 * @param[in] ipHash DBus Hash id of IP that should be deleted
647 * @param[io] asyncResp Response object that will be returned to client
648 *
649 * @return None
650 */
Ed Tanousb01bf292019-03-25 19:25:26 +0000651inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
652 uint8_t subnetMask, const std::string &gateway,
653 const std::string &address,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700654 std::shared_ptr<AsyncResp> asyncResp)
655{
Ed Tanous43b761d2019-02-13 20:10:56 -0800656 auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700657 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700658 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800659 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700661 };
662
Ed Tanous4a0cb852018-10-15 07:55:04 -0700663 crow::connections::systemBus->async_method_call(
664 std::move(createIpHandler), "xyz.openbmc_project.Network",
665 "/xyz/openbmc_project/network/" + ifaceId,
666 "xyz.openbmc_project.Network.IP.Create", "IP",
667 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
668 gateway);
669}
manojkiraneda2a133282019-02-19 13:09:43 +0530670using GetAllPropertiesType =
671 boost::container::flat_map<std::string, sdbusplus::message::variant<bool>>;
672
673inline void getDHCPConfigData(const std::shared_ptr<AsyncResp> asyncResp)
674{
675 auto getConfig = [asyncResp](const boost::system::error_code error_code,
676 const GetAllPropertiesType &dbus_data) {
677 if (error_code)
678 {
679 BMCWEB_LOG_ERROR << "D-Bus response error: " << error_code;
680 messages::internalError(asyncResp->res);
681 return;
682 }
Sunitha Harishfda13ad2019-03-21 11:01:24 -0500683 nlohmann::json &DHCPConfigTypeJson = asyncResp->res.jsonValue["DHCPv4"];
manojkiraneda2a133282019-02-19 13:09:43 +0530684 for (const auto &property : dbus_data)
685 {
686 auto value =
687 sdbusplus::message::variant_ns::get_if<bool>(&property.second);
688
689 if (value == nullptr)
690 {
691 continue;
692 }
693 if (property.first == "DNSEnabled")
694 {
695 DHCPConfigTypeJson["UseDNSServers"] = *value;
696 }
697 else if (property.first == "HostNameEnabled")
698 {
699 DHCPConfigTypeJson["UseDomainName"] = *value;
700 }
701 else if (property.first == "NTPEnabled")
702 {
703 DHCPConfigTypeJson["UseNTPServers"] = *value;
704 }
705 }
706 };
707 crow::connections::systemBus->async_method_call(
708 std::move(getConfig), "xyz.openbmc_project.Network",
709 "/xyz/openbmc_project/network/config/dhcp",
710 "org.freedesktop.DBus.Properties", "GetAll",
711 "xyz.openbmc_project.Network.DHCPConfiguration");
712}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700713
Ed Tanous4a0cb852018-10-15 07:55:04 -0700714/**
715 * Function that retrieves all properties for given Ethernet Interface
716 * Object
717 * from EntityManager Network Manager
718 * @param ethiface_id a eth interface id to query on DBus
719 * @param callback a function that shall be called to convert Dbus output
720 * into JSON
721 */
722template <typename CallbackFunc>
723void getEthernetIfaceData(const std::string &ethiface_id,
724 CallbackFunc &&callback)
725{
726 crow::connections::systemBus->async_method_call(
727 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
728 const boost::system::error_code error_code,
729 const GetManagedObjects &resp) {
730 EthernetInterfaceData ethData{};
731 boost::container::flat_set<IPv4AddressData> ipv4Data;
732
733 if (error_code)
734 {
735 callback(false, ethData, ipv4Data);
736 return;
737 }
738
739 extractEthernetInterfaceData(ethiface_id, resp, ethData);
740 extractIPData(ethiface_id, resp, ipv4Data);
741
742 // Fix global GW
743 for (IPv4AddressData &ipv4 : ipv4Data)
744 {
745 if ((ipv4.linktype == LinkType::Global) &&
746 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700748 ipv4.gateway = ethData.default_gateway;
749 }
750 }
751
752 // Finally make a callback with usefull data
753 callback(true, ethData, ipv4Data);
754 },
755 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
756 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
757};
758
759/**
760 * Function that retrieves all Ethernet Interfaces available through Network
761 * Manager
762 * @param callback a function that shall be called to convert Dbus output
763 * into JSON.
764 */
765template <typename CallbackFunc>
766void getEthernetIfaceList(CallbackFunc &&callback)
767{
768 crow::connections::systemBus->async_method_call(
769 [callback{std::move(callback)}](
770 const boost::system::error_code error_code,
771 GetManagedObjects &resp) {
772 // Callback requires vector<string> to retrieve all available
773 // ethernet interfaces
774 std::vector<std::string> iface_list;
775 iface_list.reserve(resp.size());
776 if (error_code)
777 {
778 callback(false, iface_list);
779 return;
780 }
781
782 // Iterate over all retrieved ObjectPaths.
783 for (const auto &objpath : resp)
784 {
785 // And all interfaces available for certain ObjectPath.
786 for (const auto &interface : objpath.second)
787 {
788 // If interface is
789 // xyz.openbmc_project.Network.EthernetInterface, this is
790 // what we're looking for.
791 if (interface.first ==
792 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700793 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700794 // Cut out everyting until last "/", ...
795 const std::string &iface_id = objpath.first.str;
796 std::size_t last_pos = iface_id.rfind("/");
797 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700798 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700799 // and put it into output vector.
800 iface_list.emplace_back(
801 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700802 }
803 }
804 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700805 }
806 // Finally make a callback with useful data
807 callback(true, iface_list);
808 },
809 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
810 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100811};
812
813/**
814 * EthernetCollection derived class for delivering Ethernet Collection Schema
815 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700816class EthernetCollection : public Node
817{
818 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700819 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700820 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700821 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700822 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700823 entityPrivileges = {
824 {boost::beast::http::verb::get, {{"Login"}}},
825 {boost::beast::http::verb::head, {{"Login"}}},
826 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
827 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
828 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
829 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
830 }
831
832 private:
833 /**
834 * Functions triggers appropriate requests on DBus
835 */
836 void doGet(crow::Response &res, const crow::Request &req,
837 const std::vector<std::string> &params) override
838 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800839 res.jsonValue["@odata.type"] =
840 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
841 res.jsonValue["@odata.context"] =
842 "/redfish/v1/"
843 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
844 res.jsonValue["@odata.id"] =
845 "/redfish/v1/Managers/bmc/EthernetInterfaces";
846 res.jsonValue["Name"] = "Ethernet Network Interface Collection";
847 res.jsonValue["Description"] =
848 "Collection of EthernetInterfaces for this Manager";
849
Ed Tanous4a0cb852018-10-15 07:55:04 -0700850 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700851 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700852 getEthernetIfaceList(
853 [&res](const bool &success,
854 const std::vector<std::string> &iface_list) {
855 if (!success)
856 {
857 messages::internalError(res);
858 res.end();
859 return;
860 }
861
862 nlohmann::json &iface_array = res.jsonValue["Members"];
863 iface_array = nlohmann::json::array();
Sunitha Harishfda13ad2019-03-21 11:01:24 -0500864 std::string tag = "_";
Jason M. Billsf12894f2018-10-09 12:45:45 -0700865 for (const std::string &iface_item : iface_list)
866 {
Sunitha Harishfda13ad2019-03-21 11:01:24 -0500867 std::size_t found = iface_item.find(tag);
868 if (found == std::string::npos)
869 {
870 iface_array.push_back(
871 {{"@odata.id",
872 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
873 iface_item}});
874 }
Jason M. Billsf12894f2018-10-09 12:45:45 -0700875 }
876
877 res.jsonValue["Members@odata.count"] = iface_array.size();
878 res.jsonValue["@odata.id"] =
879 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700881 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700882 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100883};
884
885/**
886 * EthernetInterface derived class for delivering Ethernet Schema
887 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700888class EthernetInterface : public Node
889{
890 public:
891 /*
892 * Default Constructor
893 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700894 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700895 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700896 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700897 std::string())
898 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 entityPrivileges = {
900 {boost::beast::http::verb::get, {{"Login"}}},
901 {boost::beast::http::verb::head, {{"Login"}}},
902 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
903 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
904 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
905 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200906 }
907
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 private:
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800909 void handleHostnamePatch(const std::string &hostname,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700910 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700911 {
Ed Tanousbc0bd6e2018-12-10 14:07:55 -0800912 asyncResp->res.jsonValue["HostName"] = hostname;
913 crow::connections::systemBus->async_method_call(
914 [asyncResp](const boost::system::error_code ec) {
915 if (ec)
916 {
917 messages::internalError(asyncResp->res);
918 }
919 },
920 "xyz.openbmc_project.Network",
921 "/xyz/openbmc_project/network/config",
922 "org.freedesktop.DBus.Properties", "Set",
923 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
Ed Tanousabf2add2019-01-22 16:40:12 -0800924 std::variant<std::string>(hostname));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 }
926
Ratan Guptad5776652019-03-03 08:47:22 +0530927 void handleMACAddressPatch(const std::string &ifaceId,
928 const std::string &macAddress,
929 const std::shared_ptr<AsyncResp> &asyncResp)
930 {
931 crow::connections::systemBus->async_method_call(
932 [asyncResp, macAddress](const boost::system::error_code ec) {
933 if (ec)
934 {
935 messages::internalError(asyncResp->res);
936 return;
937 }
938 asyncResp->res.jsonValue["MACAddress"] = std::move(macAddress);
939 },
940 "xyz.openbmc_project.Network",
941 "/xyz/openbmc_project/network/" + ifaceId,
942 "org.freedesktop.DBus.Properties", "Set",
943 "xyz.openbmc_project.Network.MACAddress", "MACAddress",
944 std::variant<std::string>(macAddress));
945 }
946
Ed Tanous4a0cb852018-10-15 07:55:04 -0700947 void handleIPv4Patch(
Ratan Guptaf476acb2019-03-02 16:46:57 +0530948 const std::string &ifaceId, nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700949 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
950 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700951 {
Ratan Guptaf476acb2019-03-02 16:46:57 +0530952 if (!input.is_array())
953 {
954 messages::propertyValueTypeError(asyncResp->res, input.dump(),
955 "IPv4Addresses");
956 return;
957 }
958
Ed Tanous4a0cb852018-10-15 07:55:04 -0700959 int entryIdx = 0;
960 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
961 ipv4Data.begin();
Ed Tanous537174c2018-12-10 15:09:31 -0800962 for (nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700963 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700964 std::string pathString =
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800965 "IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700966
Ratan Guptaf476acb2019-03-02 16:46:57 +0530967 if (thisJson.is_null())
968 {
969 if (thisData != ipv4Data.end())
970 {
971 deleteIPv4(ifaceId, thisData->id, entryIdx, asyncResp);
972 thisData++;
973 }
974 else
975 {
976 messages::propertyValueFormatError(
977 asyncResp->res, input.dump(), pathString);
978 return;
979 // TODO(ratagupt) Not sure about the property where value is
980 // list and if unable to update one of the
981 // list value then should we proceed further or
982 // break there, would ask in the redfish forum
983 // till then we stop processing the next list item.
984 }
985 entryIdx++;
986 continue; // not an error as per the redfish spec.
987 }
988
Ratan Gupta9474b372019-03-01 15:13:37 +0530989 if (thisJson.empty())
990 {
991 if (thisData != ipv4Data.end())
992 {
993 thisData++;
994 }
995 else
996 {
997 messages::propertyMissing(asyncResp->res,
998 pathString + "/Address");
999 return;
Ratan Guptaf476acb2019-03-02 16:46:57 +05301000 // TODO(ratagupt) Not sure about the property where value is
Ratan Gupta9474b372019-03-01 15:13:37 +05301001 // list and if unable to update one of the
1002 // list value then should we proceed further or
1003 // break there, would ask in the redfish forum
1004 // till then we stop processing the next list item.
1005 }
1006 entryIdx++;
1007 continue; // not an error as per the redfish spec.
1008 }
1009
Ed Tanous537174c2018-12-10 15:09:31 -08001010 std::optional<std::string> address;
1011 std::optional<std::string> addressOrigin;
1012 std::optional<std::string> subnetMask;
1013 std::optional<std::string> gateway;
1014
1015 if (!json_util::readJson(thisJson, asyncResp->res, "Address",
1016 address, "AddressOrigin", addressOrigin,
1017 "SubnetMask", subnetMask, "Gateway",
1018 gateway))
1019 {
1020 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001021 }
1022
Ed Tanous537174c2018-12-10 15:09:31 -08001023 if (address)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001024 {
Ed Tanous537174c2018-12-10 15:09:31 -08001025 if (!ipv4VerifyIpAndGetBitcount(*address))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 {
Ed Tanous537174c2018-12-10 15:09:31 -08001027 messages::propertyValueFormatError(asyncResp->res, *address,
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001028 pathString + "/Address");
Ed Tanous537174c2018-12-10 15:09:31 -08001029 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001030 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001031 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001032
Ed Tanous537174c2018-12-10 15:09:31 -08001033 uint8_t prefixLength = 0;
1034 if (subnetMask)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001035 {
Ed Tanous537174c2018-12-10 15:09:31 -08001036 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
Ed Tanous4a0cb852018-10-15 07:55:04 -07001037 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001038 messages::propertyValueFormatError(
Ed Tanous537174c2018-12-10 15:09:31 -08001039 asyncResp->res, *subnetMask,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001040 pathString + "/SubnetMask");
Ed Tanous537174c2018-12-10 15:09:31 -08001041 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001042 }
1043 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001044 std::string addressOriginInDBusFormat;
Ed Tanous537174c2018-12-10 15:09:31 -08001045 if (addressOrigin)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001046 {
Ed Tanous537174c2018-12-10 15:09:31 -08001047 // Get Address origin in proper format
1048 addressOriginInDBusFormat =
1049 translateAddressOriginRedfishToDbus(*addressOrigin);
1050 if (addressOriginInDBusFormat.empty())
Ed Tanous4a0cb852018-10-15 07:55:04 -07001051 {
Ed Tanous537174c2018-12-10 15:09:31 -08001052 messages::propertyValueNotInList(
1053 asyncResp->res, *addressOrigin,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001054 pathString + "/AddressOrigin");
Ed Tanous537174c2018-12-10 15:09:31 -08001055 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001056 }
1057 }
1058
Ed Tanous537174c2018-12-10 15:09:31 -08001059 if (gateway)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001060 {
Ed Tanous537174c2018-12-10 15:09:31 -08001061 if (!ipv4VerifyIpAndGetBitcount(*gateway))
Ed Tanous4a0cb852018-10-15 07:55:04 -07001062 {
Ed Tanous537174c2018-12-10 15:09:31 -08001063 messages::propertyValueFormatError(asyncResp->res, *gateway,
1064 pathString + "/Gateway");
1065 return;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001066 }
1067 }
1068
Ratan Guptaf476acb2019-03-02 16:46:57 +05301069 // if IP address exist then modify it.
Ed Tanous4a0cb852018-10-15 07:55:04 -07001070 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001071 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301072 // Apply changes
1073 if (address)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001074 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301075 auto callback = [asyncResp, entryIdx,
1076 address{std::string(*address)}](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001077 const boost::system::error_code ec) {
1078 if (ec)
1079 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001080 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001081 return;
1082 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301083 asyncResp->res
1084 .jsonValue["IPv4Addresses"][entryIdx]["Address"] =
1085 std::move(address);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001086 };
Ratan Guptaf476acb2019-03-02 16:46:57 +05301087
Ed Tanous4a0cb852018-10-15 07:55:04 -07001088 crow::connections::systemBus->async_method_call(
1089 std::move(callback), "xyz.openbmc_project.Network",
1090 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1091 thisData->id,
Ratan Guptaf476acb2019-03-02 16:46:57 +05301092 "org.freedesktop.DBus.Properties", "Set",
1093 "xyz.openbmc_project.Network.IP", "Address",
1094 std::variant<std::string>(*address));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001095 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301096
1097 if (subnetMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001098 {
Ratan Guptaf476acb2019-03-02 16:46:57 +05301099 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1100 thisData->id, *subnetMask,
1101 prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001102 }
Ratan Guptaf476acb2019-03-02 16:46:57 +05301103
1104 if (addressOrigin)
1105 {
1106 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1107 *addressOrigin, addressOriginInDBusFormat,
1108 asyncResp);
1109 }
1110
1111 if (gateway)
1112 {
1113 auto callback = [asyncResp, entryIdx,
1114 gateway{std::string(*gateway)}](
1115 const boost::system::error_code ec) {
1116 if (ec)
1117 {
1118 messages::internalError(asyncResp->res);
1119 return;
1120 }
1121 asyncResp->res
1122 .jsonValue["IPv4Addresses"][entryIdx]["Gateway"] =
1123 std::move(gateway);
1124 };
1125
1126 crow::connections::systemBus->async_method_call(
1127 std::move(callback), "xyz.openbmc_project.Network",
1128 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1129 thisData->id,
1130 "org.freedesktop.DBus.Properties", "Set",
1131 "xyz.openbmc_project.Network.IP", "Gateway",
1132 std::variant<std::string>(*gateway));
1133 }
1134
Ed Tanous4a0cb852018-10-15 07:55:04 -07001135 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001136 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001137 else
1138 {
1139 // Create IPv4 with provided data
Ed Tanous537174c2018-12-10 15:09:31 -08001140 if (!gateway)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001141 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001142 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001143 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001144 continue;
1145 }
1146
Ed Tanous537174c2018-12-10 15:09:31 -08001147 if (!address)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001148 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001149 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001150 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001151 continue;
1152 }
1153
Ed Tanous537174c2018-12-10 15:09:31 -08001154 if (!subnetMask)
Ed Tanous4a0cb852018-10-15 07:55:04 -07001155 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001156 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001157 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001158 continue;
1159 }
1160
Ed Tanousb01bf292019-03-25 19:25:26 +00001161 createIPv4(ifaceId, entryIdx, prefixLength, *gateway, *address,
1162 asyncResp);
Ratan Gupta95897b22019-03-07 18:25:57 +05301163
1164 nlohmann::json &ipv4AddressJson =
1165 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx];
1166 ipv4AddressJson["Address"] = *address;
1167 ipv4AddressJson["SubnetMask"] = *subnetMask;
1168 ipv4AddressJson["Gateway"] = *gateway;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001169 }
1170 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001171 }
1172 }
1173
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001174 void handleStaticNameServersPatch(
1175 const std::string &ifaceId,
1176 const std::vector<std::string> &updatedStaticNameServers,
1177 const std::shared_ptr<AsyncResp> &asyncResp)
1178 {
1179 crow::connections::systemBus->async_method_call(
1180 [asyncResp,
1181 updatedStaticNameServers](const boost::system::error_code ec) {
1182 if (ec)
1183 {
1184 messages::internalError(asyncResp->res);
1185 return;
1186 }
1187 asyncResp->res.jsonValue["NameServers"] =
1188 updatedStaticNameServers;
1189 asyncResp->res.jsonValue["StaticNameServers"] =
1190 updatedStaticNameServers;
1191 },
1192 "xyz.openbmc_project.Network",
1193 "/xyz/openbmc_project/network/" + ifaceId,
1194 "org.freedesktop.DBus.Properties", "Set",
1195 "xyz.openbmc_project.Network.EthernetInterface", "Nameservers",
1196 std::variant<std::vector<std::string>>{updatedStaticNameServers});
1197 }
1198
Ed Tanous0f74e642018-11-12 15:17:05 -08001199 void parseInterfaceData(
1200 nlohmann::json &json_response, const std::string &iface_id,
1201 const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001202 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001203 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001204 json_response["Id"] = iface_id;
1205 json_response["@odata.id"] =
1206 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous029573d2019-02-01 10:57:49 -08001207 json_response["InterfaceEnabled"] = true;
1208 if (ethData.speed == 0)
1209 {
1210 json_response["LinkStatus"] = "NoLink";
1211 json_response["Status"] = {
1212 {"Health", "OK"},
1213 {"State", "Disabled"},
1214 };
1215 }
1216 else
1217 {
1218 json_response["LinkStatus"] = "LinkUp";
1219 json_response["Status"] = {
1220 {"Health", "OK"},
1221 {"State", "Enabled"},
1222 };
1223 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001224 json_response["SpeedMbps"] = ethData.speed;
1225 json_response["MACAddress"] = ethData.mac_address;
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001226 json_response["DHCPv4"]["DHCPEnabled"] = ethData.DHCPEnabled;
manojkiraneda2a133282019-02-19 13:09:43 +05301227
Ed Tanous4a0cb852018-10-15 07:55:04 -07001228 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001230 json_response["HostName"] = ethData.hostname;
1231 }
1232
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001233 json_response["VLANs"] = {
1234 {"@odata.id", "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1235 iface_id + "/VLANs"}};
1236
Ed Tanous029573d2019-02-01 10:57:49 -08001237 json_response["NameServers"] = ethData.nameservers;
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001238 json_response["StaticNameServers"] = ethData.nameservers;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001239
Ed Tanous4a0cb852018-10-15 07:55:04 -07001240 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001241 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001242 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1243 ipv4_array = nlohmann::json::array();
1244 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001245 {
Gunnar Millsfa5053a2019-04-03 16:53:44 -05001246
1247 std::string gatewayStr = ipv4_config.gateway;
1248 if (gatewayStr.empty())
1249 {
1250 gatewayStr = "0.0.0.0";
1251 }
1252
Ed Tanous029573d2019-02-01 10:57:49 -08001253 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1254 {"SubnetMask", ipv4_config.netmask},
1255 {"Address", ipv4_config.address},
Gunnar Millsfa5053a2019-04-03 16:53:44 -05001256 {"Gateway", gatewayStr}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001257 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001258 }
Ravi Teja9a6fc6f2019-04-16 02:43:13 -05001259 json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 }
1261
1262 /**
1263 * Functions triggers appropriate requests on DBus
1264 */
1265 void doGet(crow::Response &res, const crow::Request &req,
1266 const std::vector<std::string> &params) override
1267 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001268 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001269 if (params.size() != 1)
1270 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001271 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001272 return;
1273 }
1274
Ed Tanous4a0cb852018-10-15 07:55:04 -07001275 getEthernetIfaceData(
1276 params[0],
1277 [this, asyncResp, iface_id{std::string(params[0])}](
1278 const bool &success, const EthernetInterfaceData &ethData,
1279 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1280 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001281 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001282 // TODO(Pawel)consider distinguish between non existing
1283 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001284 messages::resourceNotFound(asyncResp->res,
1285 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001286 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001287 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001288 asyncResp->res.jsonValue["@odata.type"] =
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001289 "#EthernetInterface.v1_4_1.EthernetInterface";
Ed Tanous0f74e642018-11-12 15:17:05 -08001290 asyncResp->res.jsonValue["@odata.context"] =
1291 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1292 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1293 asyncResp->res.jsonValue["Description"] =
1294 "Management Network Interface";
1295
1296 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1297 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001298 });
manojkiraneda2a133282019-02-19 13:09:43 +05301299 getDHCPConfigData(asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001300 }
1301
1302 void doPatch(crow::Response &res, const crow::Request &req,
1303 const std::vector<std::string> &params) override
1304 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001305 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001306 if (params.size() != 1)
1307 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001308 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001309 return;
1310 }
1311
Ed Tanous4a0cb852018-10-15 07:55:04 -07001312 const std::string &iface_id = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001313
Ed Tanousbc0bd6e2018-12-10 14:07:55 -08001314 std::optional<std::string> hostname;
Ratan Guptad5776652019-03-03 08:47:22 +05301315 std::optional<std::string> macAddress;
Ravi Teja9a6fc6f2019-04-16 02:43:13 -05001316 std::optional<std::string> ipv6DefaultGateway;
Ratan Guptaf476acb2019-03-02 16:46:57 +05301317 std::optional<nlohmann::json> ipv4Addresses;
1318 std::optional<nlohmann::json> ipv6Addresses;
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001319 std::optional<std::vector<std::string>> staticNameServers;
RAJESWARAN THILLAIGOVINDAN5112e9b2019-03-06 03:10:24 -06001320 std::optional<std::vector<std::string>> nameServers;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001321
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001322 if (!json_util::readJson(
1323 req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses,
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001324 "IPv6Addresses", ipv6Addresses, "MACAddress", macAddress,
Ravi Teja9a6fc6f2019-04-16 02:43:13 -05001325 "StaticNameServers", staticNameServers, "IPv6DefaultGateway",
RAJESWARAN THILLAIGOVINDAN5112e9b2019-03-06 03:10:24 -06001326 ipv6DefaultGateway, "NameServers", nameServers))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001327 {
1328 return;
1329 }
Ratan Guptaf15aad32019-03-01 13:41:13 +05301330
Ed Tanous4a0cb852018-10-15 07:55:04 -07001331 // Get single eth interface data, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001332 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001333 getEthernetIfaceData(
1334 iface_id,
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001335 [this, asyncResp, iface_id, hostname = std::move(hostname),
1336 macAddress = std::move(macAddress),
Ed Tanous0627a2c2018-11-29 17:09:23 -08001337 ipv4Addresses = std::move(ipv4Addresses),
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001338 ipv6Addresses = std::move(ipv6Addresses),
Ravi Teja9a6fc6f2019-04-16 02:43:13 -05001339 ipv6DefaultGateway = std::move(ipv6DefaultGateway),
RAJESWARAN THILLAIGOVINDAN5112e9b2019-03-06 03:10:24 -06001340 staticNameServers = std::move(staticNameServers),
1341 nameServers = std::move(nameServers)](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001342 const bool &success, const EthernetInterfaceData &ethData,
1343 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001344 if (!success)
1345 {
1346 // ... otherwise return error
1347 // TODO(Pawel)consider distinguish between non existing
1348 // object, and other errors
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001349 messages::resourceNotFound(asyncResp->res,
1350 "Ethernet Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001351 return;
1352 }
1353
Ed Tanous0f74e642018-11-12 15:17:05 -08001354 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1355 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001356
Ed Tanous0627a2c2018-11-29 17:09:23 -08001357 if (hostname)
1358 {
1359 handleHostnamePatch(*hostname, asyncResp);
1360 }
1361
Ratan Guptad5776652019-03-03 08:47:22 +05301362 if (macAddress)
1363 {
1364 handleMACAddressPatch(iface_id, *macAddress, asyncResp);
1365 }
1366
Ed Tanous0627a2c2018-11-29 17:09:23 -08001367 if (ipv4Addresses)
1368 {
Ed Tanous537174c2018-12-10 15:09:31 -08001369 // TODO(ed) for some reason the capture of ipv4Addresses
1370 // above is returning a const value, not a non-const value.
1371 // This doesn't really work for us, as we need to be able to
1372 // efficiently move out the intermedia nlohmann::json
1373 // objects. This makes a copy of the structure, and operates
1374 // on that, but could be done more efficiently
Ratan Guptaf476acb2019-03-02 16:46:57 +05301375 nlohmann::json ipv4 = std::move(*ipv4Addresses);
Ed Tanous537174c2018-12-10 15:09:31 -08001376 handleIPv4Patch(iface_id, ipv4, ipv4Data, asyncResp);
Ed Tanous0627a2c2018-11-29 17:09:23 -08001377 }
1378
RAJESWARAN THILLAIGOVINDAN5112e9b2019-03-06 03:10:24 -06001379 if (nameServers)
1380 {
1381 // Data.Permissions is read-only
1382 messages::propertyNotWritable(asyncResp->res,
1383 "NameServers");
1384 }
1385
Ed Tanous0627a2c2018-11-29 17:09:23 -08001386 if (ipv6Addresses)
1387 {
1388 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
1389 messages::propertyNotWritable(asyncResp->res,
1390 "IPv6Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001391 }
RAJESWARAN THILLAIGOVINDANf85837b2019-04-04 05:18:53 -05001392
1393 if (staticNameServers)
1394 {
1395 handleStaticNameServersPatch(iface_id, *staticNameServers,
1396 asyncResp);
1397 }
Ravi Teja9a6fc6f2019-04-16 02:43:13 -05001398
1399 if (ipv6DefaultGateway)
1400 {
1401 messages::propertyNotWritable(asyncResp->res,
1402 "IPv6DefaultGateway");
1403 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001404 });
1405 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001406};
1407
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001408/**
Ed Tanous4a0cb852018-10-15 07:55:04 -07001409 * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1410 * Schema
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001411 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412class VlanNetworkInterface : public Node
1413{
1414 public:
1415 /*
1416 * Default Constructor
1417 */
1418 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001419 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001420 Node(app,
Ed Tanous0f74e642018-11-12 15:17:05 -08001421 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001422 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001423 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001424 entityPrivileges = {
1425 {boost::beast::http::verb::get, {{"Login"}}},
1426 {boost::beast::http::verb::head, {{"Login"}}},
1427 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1428 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1429 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1430 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001431 }
1432
Ed Tanous1abe55e2018-09-05 08:30:59 -07001433 private:
Ed Tanous0f74e642018-11-12 15:17:05 -08001434 void parseInterfaceData(
1435 nlohmann::json &json_response, const std::string &parent_iface_id,
1436 const std::string &iface_id, const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001437 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001438 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001439 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001440 json_response["Id"] = iface_id;
1441 json_response["@odata.id"] =
1442 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1443 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001444
Ed Tanous4a0cb852018-10-15 07:55:04 -07001445 json_response["VLANEnable"] = true;
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001446 if (!ethData.vlan_id.empty())
Ed Tanous4a0cb852018-10-15 07:55:04 -07001447 {
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001448 json_response["VLANId"] = ethData.vlan_id.back();
Ed Tanous4a0cb852018-10-15 07:55:04 -07001449 }
Ed Tanousa434f2b2018-07-27 13:04:22 -07001450 }
1451
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001452 bool verifyNames(const std::string &parent, const std::string &iface)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001453 {
1454 if (!boost::starts_with(iface, parent + "_"))
1455 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001456 return false;
1457 }
1458 else
1459 {
1460 return true;
1461 }
1462 }
1463
1464 /**
1465 * Functions triggers appropriate requests on DBus
1466 */
1467 void doGet(crow::Response &res, const crow::Request &req,
1468 const std::vector<std::string> &params) override
1469 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001470 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1471 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1473 // Check if there is required param, truly entering this shall be
1474 // impossible.
1475 if (params.size() != 2)
1476 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001477 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001478 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001479 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001480 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001481
Ed Tanous4a0cb852018-10-15 07:55:04 -07001482 const std::string &parent_iface_id = params[0];
1483 const std::string &iface_id = params[1];
Ed Tanous0f74e642018-11-12 15:17:05 -08001484 res.jsonValue["@odata.type"] =
1485 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1486 res.jsonValue["@odata.context"] =
1487 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1488 res.jsonValue["Name"] = "VLAN Network Interface";
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001489
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001490 if (!verifyNames(parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001491 {
1492 return;
1493 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001494
Ed Tanous1abe55e2018-09-05 08:30:59 -07001495 // Get single eth interface data, and call the below callback for JSON
1496 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001497 getEthernetIfaceData(
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001498 params[1],
1499 [this, asyncResp, parent_iface_id{std::string(params[0])},
1500 iface_id{std::string(params[1])}](
Ed Tanous4a0cb852018-10-15 07:55:04 -07001501 const bool &success, const EthernetInterfaceData &ethData,
1502 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001503 if (success && ethData.vlan_id.size() != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001504 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001505 parseInterfaceData(asyncResp->res.jsonValue,
1506 parent_iface_id, iface_id, ethData,
1507 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001508 }
1509 else
1510 {
1511 // ... otherwise return error
1512 // TODO(Pawel)consider distinguish between non existing
1513 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001514 messages::resourceNotFound(
1515 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001516 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001518 }
1519
Ed Tanous1abe55e2018-09-05 08:30:59 -07001520 void doPatch(crow::Response &res, const crow::Request &req,
1521 const std::vector<std::string> &params) override
1522 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001523 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001524 if (params.size() != 2)
1525 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001526 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001527 return;
1528 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001529
Ed Tanous1abe55e2018-09-05 08:30:59 -07001530 const std::string &parentIfaceId = params[0];
1531 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001532
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001533 if (!verifyNames(parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 {
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001535 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1536 ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001537 return;
1538 }
1539
Ed Tanous0627a2c2018-11-29 17:09:23 -08001540 bool vlanEnable = false;
1541 uint64_t vlanId = 0;
1542
1543 if (!json_util::readJson(req, res, "VLANEnable", vlanEnable, "VLANId",
1544 vlanId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001545 {
1546 return;
1547 }
1548
1549 // Get single eth interface data, and call the below callback for JSON
1550 // preparation
Sunitha Harish08244d02019-04-01 03:57:25 -05001551 getEthernetIfaceData(params[1], [this, asyncResp,
1552 parentIfaceId{std::string(params[0])},
1553 ifaceId{std::string(params[1])},
1554 &vlanEnable, &vlanId](
1555 const bool &success,
1556 const EthernetInterfaceData
1557 &ethData,
1558 const boost::container::flat_set<
1559 IPv4AddressData> &ipv4Data) {
1560 if (success && !ethData.vlan_id.empty())
1561 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001562 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1563 ifaceId, ethData, ipv4Data);
Sunitha Harish08244d02019-04-01 03:57:25 -05001564 auto callback =
1565 [asyncResp](const boost::system::error_code ec) {
1566 if (ec)
1567 {
1568 messages::internalError(asyncResp->res);
1569 }
1570 };
Ed Tanous1abe55e2018-09-05 08:30:59 -07001571
Sunitha Harish08244d02019-04-01 03:57:25 -05001572 if (vlanEnable == true)
1573 {
1574 crow::connections::systemBus->async_method_call(
1575 std::move(callback), "xyz.openbmc_project.Network",
1576 "/xyz/openbmc_project/network/" + ifaceId,
1577 "org.freedesktop.DBus.Properties", "Set",
1578 "xyz.openbmc_project.Network.VLAN", "Id",
1579 std::variant<uint32_t>(vlanId));
1580 }
1581 else
1582 {
1583 BMCWEB_LOG_DEBUG
1584 << "vlanEnable is false. Deleting the vlan interface";
1585 crow::connections::systemBus->async_method_call(
1586 std::move(callback), "xyz.openbmc_project.Network",
1587 std::string("/xyz/openbmc_project/network/") + ifaceId,
1588 "xyz.openbmc_project.Object.Delete", "Delete");
1589 }
1590 }
1591 else
1592 {
1593 // TODO(Pawel)consider distinguish between non existing
1594 // object, and other errors
1595 messages::resourceNotFound(asyncResp->res,
1596 "VLAN Network Interface", ifaceId);
1597 return;
1598 }
1599 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 }
1601
1602 void doDelete(crow::Response &res, const crow::Request &req,
1603 const std::vector<std::string> &params) override
1604 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001606 if (params.size() != 2)
1607 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001608 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001609 return;
1610 }
1611
1612 const std::string &parentIfaceId = params[0];
1613 const std::string &ifaceId = params[1];
1614
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001615 if (!verifyNames(parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001616 {
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001617 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1618 ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001619 return;
1620 }
1621
1622 // Get single eth interface data, and call the below callback for JSON
1623 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001624 getEthernetIfaceData(
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001625 params[1],
1626 [this, asyncResp, parentIfaceId{std::string(params[0])},
1627 ifaceId{std::string(params[1])}](
Jason M. Billsf12894f2018-10-09 12:45:45 -07001628 const bool &success, const EthernetInterfaceData &ethData,
1629 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001630 if (success && !ethData.vlan_id.empty())
Jason M. Billsf12894f2018-10-09 12:45:45 -07001631 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001632 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1633 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001634
Jason M. Billsf12894f2018-10-09 12:45:45 -07001635 auto callback =
1636 [asyncResp](const boost::system::error_code ec) {
1637 if (ec)
1638 {
1639 messages::internalError(asyncResp->res);
1640 }
1641 };
1642 crow::connections::systemBus->async_method_call(
1643 std::move(callback), "xyz.openbmc_project.Network",
1644 std::string("/xyz/openbmc_project/network/") + ifaceId,
1645 "xyz.openbmc_project.Object.Delete", "Delete");
1646 }
1647 else
1648 {
1649 // ... otherwise return error
1650 // TODO(Pawel)consider distinguish between non existing
1651 // object, and other errors
1652 messages::resourceNotFound(
1653 asyncResp->res, "VLAN Network Interface", ifaceId);
1654 }
1655 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001656 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001657};
1658
1659/**
1660 * VlanNetworkInterfaceCollection derived class for delivering
1661 * VLANNetworkInterface Collection Schema
1662 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001663class VlanNetworkInterfaceCollection : public Node
1664{
1665 public:
1666 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001667 VlanNetworkInterfaceCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001668 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1669 std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001670 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001671 entityPrivileges = {
1672 {boost::beast::http::verb::get, {{"Login"}}},
1673 {boost::beast::http::verb::head, {{"Login"}}},
1674 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1675 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1676 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1677 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001678 }
1679
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 private:
1681 /**
1682 * Functions triggers appropriate requests on DBus
1683 */
1684 void doGet(crow::Response &res, const crow::Request &req,
1685 const std::vector<std::string> &params) override
1686 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001687 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001688 if (params.size() != 1)
1689 {
1690 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001691 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001692 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001693 }
1694
Ed Tanous4a0cb852018-10-15 07:55:04 -07001695 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001696
Ed Tanous4a0cb852018-10-15 07:55:04 -07001697 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001698 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001699 getEthernetIfaceList(
Ed Tanous43b761d2019-02-13 20:10:56 -08001700 [asyncResp, rootInterfaceName{std::string(rootInterfaceName)}](
Jason M. Billsf12894f2018-10-09 12:45:45 -07001701 const bool &success,
1702 const std::vector<std::string> &iface_list) {
1703 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001704 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001705 messages::internalError(asyncResp->res);
1706 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001707 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001708 asyncResp->res.jsonValue["@odata.type"] =
1709 "#VLanNetworkInterfaceCollection."
1710 "VLanNetworkInterfaceCollection";
1711 asyncResp->res.jsonValue["@odata.context"] =
1712 "/redfish/v1/$metadata"
1713 "#VLanNetworkInterfaceCollection."
1714 "VLanNetworkInterfaceCollection";
1715 asyncResp->res.jsonValue["Name"] =
1716 "VLAN Network Interface Collection";
Ed Tanous4a0cb852018-10-15 07:55:04 -07001717
Jason M. Billsf12894f2018-10-09 12:45:45 -07001718 nlohmann::json iface_array = nlohmann::json::array();
1719
1720 for (const std::string &iface_item : iface_list)
1721 {
1722 if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1723 {
1724 iface_array.push_back(
1725 {{"@odata.id",
1726 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1727 rootInterfaceName + "/VLANs/" + iface_item}});
1728 }
1729 }
1730
Jason M. Billsf12894f2018-10-09 12:45:45 -07001731 asyncResp->res.jsonValue["Members@odata.count"] =
1732 iface_array.size();
1733 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1734 asyncResp->res.jsonValue["@odata.id"] =
1735 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1736 rootInterfaceName + "/VLANs";
1737 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001738 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001739
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 void doPost(crow::Response &res, const crow::Request &req,
1741 const std::vector<std::string> &params) override
1742 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001743 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001744 if (params.size() != 1)
1745 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001746 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001747 return;
1748 }
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001749 bool vlanEnable = false;
Ed Tanous0627a2c2018-11-29 17:09:23 -08001750 uint32_t vlanId = 0;
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001751 if (!json_util::readJson(req, res, "VLANId", vlanId, "VLANEnable",
1752 vlanEnable))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001753 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001754 return;
1755 }
Sunitha Harishfda13ad2019-03-21 11:01:24 -05001756 // Need both vlanId and vlanEnable to service this request
1757 if (!vlanId)
1758 {
1759 messages::propertyMissing(asyncResp->res, "VLANId");
1760 }
1761 if (!vlanEnable)
1762 {
1763 messages::propertyMissing(asyncResp->res, "VLANEnable");
1764 }
1765 if (static_cast<bool>(vlanId) ^ static_cast<bool>(vlanEnable))
1766 {
1767 return;
1768 }
1769
Ed Tanous4a0cb852018-10-15 07:55:04 -07001770 const std::string &rootInterfaceName = params[0];
Ed Tanous4a0cb852018-10-15 07:55:04 -07001771 auto callback = [asyncResp](const boost::system::error_code ec) {
1772 if (ec)
1773 {
1774 // TODO(ed) make more consistent error messages based on
1775 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001776 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001777 return;
1778 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001779 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001780 };
1781 crow::connections::systemBus->async_method_call(
1782 std::move(callback), "xyz.openbmc_project.Network",
1783 "/xyz/openbmc_project/network",
1784 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
Ed Tanous0627a2c2018-11-29 17:09:23 -08001785 rootInterfaceName, vlanId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001786 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001787};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001788} // namespace redfish