blob: d3fe1fe38fff7e0d6dc72c5899146894e29e3cd2 [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>
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010025
Ed Tanous1abe55e2018-09-05 08:30:59 -070026namespace redfish
27{
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010028
29/**
30 * DBus types primitives for several generic DBus interfaces
31 * TODO(Pawel) consider move this to separate file into boost::dbus
32 */
Ed Tanousaa2e59c2018-04-12 12:17:20 -070033using PropertiesMapType = boost::container::flat_map<
34 std::string,
35 sdbusplus::message::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<
43 std::string, sdbusplus::message::variant<
44 std::string, bool, uint8_t, int16_t, uint16_t,
Ed Tanous4a0cb852018-10-15 07:55:04 -070045 int32_t, uint32_t, int64_t, uint64_t, double>>>>>>;
46
47enum class LinkType
48{
49 Local,
50 Global
51};
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010052
53/**
54 * Structure for keeping IPv4 data required by Redfish
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010055 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070056struct IPv4AddressData
57{
58 std::string id;
Ed Tanous4a0cb852018-10-15 07:55:04 -070059 std::string address;
60 std::string domain;
61 std::string gateway;
Ed Tanous1abe55e2018-09-05 08:30:59 -070062 std::string netmask;
63 std::string origin;
Ed Tanous4a0cb852018-10-15 07:55:04 -070064 LinkType linktype;
65
Ed Tanous1abe55e2018-09-05 08:30:59 -070066 bool operator<(const IPv4AddressData &obj) const
67 {
Ed Tanous4a0cb852018-10-15 07:55:04 -070068 return id < obj.id;
Ed Tanous1abe55e2018-09-05 08:30:59 -070069 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010070};
71
72/**
73 * Structure for keeping basic single Ethernet Interface information
74 * available from DBus
75 */
Ed Tanous1abe55e2018-09-05 08:30:59 -070076struct EthernetInterfaceData
77{
Ed Tanous4a0cb852018-10-15 07:55:04 -070078 uint32_t speed;
79 bool auto_neg;
80 std::string hostname;
81 std::string default_gateway;
82 std::string mac_address;
Ed Tanousa24526d2018-12-10 15:17:59 -080083 std::optional<uint32_t> vlan_id;
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010084};
85
Ed Tanous4a0cb852018-10-15 07:55:04 -070086// Helper function that changes bits netmask notation (i.e. /24)
87// into full dot notation
88inline std::string getNetmask(unsigned int bits)
Ed Tanous1abe55e2018-09-05 08:30:59 -070089{
Ed Tanous4a0cb852018-10-15 07:55:04 -070090 uint32_t value = 0xffffffff << (32 - bits);
91 std::string netmask = std::to_string((value >> 24) & 0xff) + "." +
92 std::to_string((value >> 16) & 0xff) + "." +
93 std::to_string((value >> 8) & 0xff) + "." +
94 std::to_string(value & 0xff);
95 return netmask;
96}
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +010097
Ed Tanous4a0cb852018-10-15 07:55:04 -070098inline std::string
99 translateAddressOriginDbusToRedfish(const std::string &inputOrigin,
100 bool isIPv4)
101{
102 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.Static")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700103 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700104 return "Static";
105 }
106 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal")
107 {
108 if (isIPv4)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700109 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700110 return "IPv4LinkLocal";
111 }
112 else
113 {
114 return "LinkLocal";
115 }
116 }
117 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP")
118 {
119 if (isIPv4)
120 {
121 return "DHCP";
122 }
123 else
124 {
125 return "DHCPv6";
126 }
127 }
128 if (inputOrigin == "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC")
129 {
130 return "SLAAC";
131 }
132 return "";
133}
134
135inline std::string
136 translateAddressOriginRedfishToDbus(const std::string &inputOrigin)
137{
138 if (inputOrigin == "Static")
139 {
140 return "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
141 }
142 if (inputOrigin == "DHCP" || inputOrigin == "DHCPv6")
143 {
144 return "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
145 }
146 if (inputOrigin == "IPv4LinkLocal" || inputOrigin == "LinkLocal")
147 {
148 return "xyz.openbmc_project.Network.IP.AddressOrigin.LinkLocal";
149 }
150 if (inputOrigin == "SLAAC")
151 {
152 return "xyz.openbmc_project.Network.IP.AddressOrigin.SLAAC";
153 }
154 return "";
155}
156
157inline void extractEthernetInterfaceData(const std::string &ethiface_id,
158 const GetManagedObjects &dbus_data,
159 EthernetInterfaceData &ethData)
160{
161 for (const auto &objpath : dbus_data)
162 {
163 if (objpath.first == "/xyz/openbmc_project/network/" + ethiface_id)
164 {
165 for (const auto &ifacePair : objpath.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700166 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700167 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700168 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700169 for (const auto &propertyPair : ifacePair.second)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700170 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700171 if (propertyPair.first == "MACAddress")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700172 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700173 const std::string *mac =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800174 sdbusplus::message::variant_ns::get_if<
175 std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700176 if (mac != nullptr)
177 {
178 ethData.mac_address = *mac;
179 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700180 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700181 }
182 }
183 else if (ifacePair.first == "xyz.openbmc_project.Network.VLAN")
184 {
185 for (const auto &propertyPair : ifacePair.second)
186 {
187 if (propertyPair.first == "Id")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700188 {
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800189 const uint32_t *id =
190 sdbusplus::message::variant_ns::get_if<
191 uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700192 if (id != nullptr)
193 {
194 ethData.vlan_id = *id;
195 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700196 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700197 }
198 }
199 else if (ifacePair.first ==
200 "xyz.openbmc_project.Network.EthernetInterface")
201 {
202 for (const auto &propertyPair : ifacePair.second)
203 {
204 if (propertyPair.first == "AutoNeg")
205 {
206 const bool *auto_neg =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800207 sdbusplus::message::variant_ns::get_if<bool>(
208 &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 Tanous1b6b96c2018-11-30 11:35:41 -0800217 sdbusplus::message::variant_ns::get_if<
218 uint32_t>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700219 if (speed != nullptr)
220 {
221 ethData.speed = *speed;
222 }
223 }
224 }
225 }
226 else if (ifacePair.first ==
227 "xyz.openbmc_project.Network.SystemConfiguration")
228 {
229 for (const auto &propertyPair : ifacePair.second)
230 {
231 if (propertyPair.first == "HostName")
232 {
233 const std::string *hostname =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800234 sdbusplus::message::variant_ns::get_if<
235 std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700236 if (hostname != nullptr)
237 {
238 ethData.hostname = *hostname;
239 }
240 }
241 else if (propertyPair.first == "DefaultGateway")
242 {
243 const std::string *defaultGateway =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800244 sdbusplus::message::variant_ns::get_if<
245 std::string>(&propertyPair.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700246 if (defaultGateway != nullptr)
247 {
248 ethData.default_gateway = *defaultGateway;
249 }
250 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700251 }
252 }
253 }
254 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700255 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700256}
257
258// Helper function that extracts data for single ethernet ipv4 address
259inline void
260 extractIPData(const std::string &ethiface_id,
261 const GetManagedObjects &dbus_data,
262 boost::container::flat_set<IPv4AddressData> &ipv4_config)
263{
264 const std::string ipv4PathStart =
265 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
266
267 // Since there might be several IPv4 configurations aligned with
268 // single ethernet interface, loop over all of them
269 for (const auto &objpath : dbus_data)
270 {
271 // Check if proper pattern for object path appears
272 if (boost::starts_with(objpath.first.str, ipv4PathStart))
273 {
274 for (auto &interface : objpath.second)
275 {
276 if (interface.first == "xyz.openbmc_project.Network.IP")
277 {
278 // Instance IPv4AddressData structure, and set as
279 // appropriate
280 std::pair<
281 boost::container::flat_set<IPv4AddressData>::iterator,
282 bool>
283 it = ipv4_config.insert(
284 {objpath.first.str.substr(ipv4PathStart.size())});
285 IPv4AddressData &ipv4_address = *it.first;
286 for (auto &property : interface.second)
287 {
288 if (property.first == "Address")
289 {
290 const std::string *address =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800291 sdbusplus::message::variant_ns::get_if<
292 std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700293 if (address != nullptr)
294 {
295 ipv4_address.address = *address;
296 }
297 }
298 else if (property.first == "Gateway")
299 {
300 const std::string *gateway =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800301 sdbusplus::message::variant_ns::get_if<
302 std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700303 if (gateway != nullptr)
304 {
305 ipv4_address.gateway = *gateway;
306 }
307 }
308 else if (property.first == "Origin")
309 {
310 const std::string *origin =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800311 sdbusplus::message::variant_ns::get_if<
312 std::string>(&property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700313 if (origin != nullptr)
314 {
315 ipv4_address.origin =
316 translateAddressOriginDbusToRedfish(*origin,
317 true);
318 }
319 }
320 else if (property.first == "PrefixLength")
321 {
322 const uint8_t *mask =
Ed Tanous1b6b96c2018-11-30 11:35:41 -0800323 sdbusplus::message::variant_ns::get_if<uint8_t>(
324 &property.second);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700325 if (mask != nullptr)
326 {
327 // convert it to the string
328 ipv4_address.netmask = getNetmask(*mask);
329 }
330 }
331 else
332 {
333 BMCWEB_LOG_ERROR
334 << "Got extra property: " << property.first
335 << " on the " << objpath.first.str << " object";
336 }
337 }
338 // Check if given address is local, or global
339 ipv4_address.linktype =
340 boost::starts_with(ipv4_address.address, "169.254.")
341 ? LinkType::Global
342 : LinkType::Local;
343 }
344 }
345 }
346 }
347}
348
349/**
350 * @brief Sets given Id on the given VLAN interface through D-Bus
351 *
352 * @param[in] ifaceId Id of VLAN interface that should be modified
353 * @param[in] inputVlanId New ID of the VLAN
354 * @param[in] callback Function that will be called after the operation
355 *
356 * @return None.
357 */
358template <typename CallbackFunc>
359void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
360 CallbackFunc &&callback)
361{
362 crow::connections::systemBus->async_method_call(
363 callback, "xyz.openbmc_project.Network",
364 std::string("/xyz/openbmc_project/network/") + ifaceId,
365 "org.freedesktop.DBus.Properties", "Set",
366 "xyz.openbmc_project.Network.VLAN", "Id",
367 sdbusplus::message::variant<uint32_t>(inputVlanId));
368}
369
370/**
371 * @brief Helper function that verifies IP address to check if it is in
372 * proper format. If bits pointer is provided, also calculates active
373 * bit count for Subnet Mask.
374 *
375 * @param[in] ip IP that will be verified
376 * @param[out] bits Calculated mask in bits notation
377 *
378 * @return true in case of success, false otherwise
379 */
380inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
381 uint8_t *bits = nullptr)
382{
383 std::vector<std::string> bytesInMask;
384
385 boost::split(bytesInMask, ip, boost::is_any_of("."));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700386
387 static const constexpr int ipV4AddressSectionsCount = 4;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700388 if (bytesInMask.size() != ipV4AddressSectionsCount)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700390 return false;
391 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700392
Ed Tanous4a0cb852018-10-15 07:55:04 -0700393 if (bits != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700395 *bits = 0;
396 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700397
Ed Tanous4a0cb852018-10-15 07:55:04 -0700398 char *endPtr;
399 long previousValue = 255;
400 bool firstZeroInByteHit;
401 for (const std::string &byte : bytesInMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700402 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700403 if (byte.empty())
404 {
405 return false;
406 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407
Ed Tanous4a0cb852018-10-15 07:55:04 -0700408 // Use strtol instead of stroi to avoid exceptions
409 long value = std::strtol(byte.c_str(), &endPtr, 10);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700410
Ed Tanous4a0cb852018-10-15 07:55:04 -0700411 // endPtr should point to the end of the string, otherwise given string
412 // is not 100% number
413 if (*endPtr != '\0')
414 {
415 return false;
416 }
417
418 // Value should be contained in byte
419 if (value < 0 || value > 255)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700420 {
421 return false;
422 }
423
424 if (bits != nullptr)
425 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700426 // Mask has to be continuous between bytes
427 if (previousValue != 255 && value != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700428 {
429 return false;
430 }
431
Ed Tanous4a0cb852018-10-15 07:55:04 -0700432 // Mask has to be continuous inside bytes
433 firstZeroInByteHit = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434
Ed Tanous4a0cb852018-10-15 07:55:04 -0700435 // Count bits
436 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700437 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700438 if (value & (1 << bitIdx))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700439 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700440 if (firstZeroInByteHit)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700442 // Continuity not preserved
443 return false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700444 }
445 else
446 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700447 (*bits)++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700448 }
449 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 else
451 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700452 firstZeroInByteHit = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700453 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700454 }
455 }
456
457 previousValue = value;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700458 }
459
Ed Tanous4a0cb852018-10-15 07:55:04 -0700460 return true;
461}
462
463/**
464 * @brief Changes IPv4 address type property (Address, Gateway)
465 *
466 * @param[in] ifaceId Id of interface whose IP should be modified
467 * @param[in] ipIdx Index of IP in input array that should be modified
468 * @param[in] ipHash DBus Hash id of modified IP
469 * @param[in] name Name of field in JSON representation
470 * @param[in] newValue New value that should be written
471 * @param[io] asyncResp Response object that will be returned to client
472 *
473 * @return true if give IP is valid and has been sent do D-Bus, false
474 * otherwise
475 */
476inline void changeIPv4AddressProperty(
477 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
478 const std::string &name, const std::string &newValue,
479 const std::shared_ptr<AsyncResp> asyncResp)
480{
481 auto callback = [asyncResp, ipIdx, name{std::string(name)},
482 newValue{std::move(newValue)}](
483 const boost::system::error_code ec) {
484 if (ec)
485 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800486 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700487 }
488 else
489 {
490 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
491 }
492 };
493
494 crow::connections::systemBus->async_method_call(
495 std::move(callback), "xyz.openbmc_project.Network",
496 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
497 "org.freedesktop.DBus.Properties", "Set",
498 "xyz.openbmc_project.Network.IP", name,
499 sdbusplus::message::variant<std::string>(newValue));
500}
501
502/**
503 * @brief Changes IPv4 address origin property
504 *
505 * @param[in] ifaceId Id of interface whose IP should be modified
506 * @param[in] ipIdx Index of IP in input array that should be
507 * modified
508 * @param[in] ipHash DBus Hash id of modified IP
509 * @param[in] newValue New value in Redfish format
510 * @param[in] newValueDbus New value in D-Bus format
511 * @param[io] asyncResp Response object that will be returned to client
512 *
513 * @return true if give IP is valid and has been sent do D-Bus, false
514 * otherwise
515 */
516inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
517 const std::string &ipHash,
518 const std::string &newValue,
519 const std::string &newValueDbus,
520 const std::shared_ptr<AsyncResp> asyncResp)
521{
522 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
523 const boost::system::error_code ec) {
524 if (ec)
525 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800526 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700527 }
528 else
529 {
530 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
531 newValue;
532 }
533 };
534
535 crow::connections::systemBus->async_method_call(
536 std::move(callback), "xyz.openbmc_project.Network",
537 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
538 "org.freedesktop.DBus.Properties", "Set",
539 "xyz.openbmc_project.Network.IP", "Origin",
540 sdbusplus::message::variant<std::string>(newValueDbus));
541}
542
543/**
544 * @brief Modifies SubnetMask for given IP
545 *
546 * @param[in] ifaceId Id of interface whose IP should be modified
547 * @param[in] ipIdx Index of IP in input array that should be
548 * modified
549 * @param[in] ipHash DBus Hash id of modified IP
550 * @param[in] newValueStr Mask in dot notation as string
551 * @param[in] newValue Mask as PrefixLength in bitcount
552 * @param[io] asyncResp Response object that will be returned to client
553 *
554 * @return None
555 */
556inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
557 const std::string &ipHash,
558 const std::string &newValueStr,
559 uint8_t &newValue,
560 std::shared_ptr<AsyncResp> asyncResp)
561{
562 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
563 const boost::system::error_code ec) {
564 if (ec)
565 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800566 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700567 }
568 else
569 {
570 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
571 newValueStr;
572 }
573 };
574
575 crow::connections::systemBus->async_method_call(
576 std::move(callback), "xyz.openbmc_project.Network",
577 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
578 "org.freedesktop.DBus.Properties", "Set",
579 "xyz.openbmc_project.Network.IP", "PrefixLength",
580 sdbusplus::message::variant<uint8_t>(newValue));
581}
582
583/**
584 * @brief Sets given HostName of the machine through D-Bus
585 *
586 * @param[in] newHostname New name that HostName will be changed to
587 * @param[in] callback Function that will be called after the operation
588 *
589 * @return None.
590 */
591template <typename CallbackFunc>
592void setHostName(const std::string &newHostname, CallbackFunc &&callback)
593{
594 crow::connections::systemBus->async_method_call(
595 callback, "xyz.openbmc_project.Network",
596 "/xyz/openbmc_project/network/config",
597 "org.freedesktop.DBus.Properties", "Set",
598 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
599 sdbusplus::message::variant<std::string>(newHostname));
600}
601
602/**
603 * @brief Deletes given IPv4
604 *
605 * @param[in] ifaceId Id of interface whose IP should be deleted
606 * @param[in] ipIdx Index of IP in input array that should be deleted
607 * @param[in] ipHash DBus Hash id of IP that should be deleted
608 * @param[io] asyncResp Response object that will be returned to client
609 *
610 * @return None
611 */
612inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
613 unsigned int ipIdx,
614 const std::shared_ptr<AsyncResp> asyncResp)
615{
616 crow::connections::systemBus->async_method_call(
617 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700618 if (ec)
619 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800620 messages::internalError(asyncResp->res);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100621 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700622 else
623 {
624 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
625 }
626 },
627 "xyz.openbmc_project.Network",
628 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
629 "xyz.openbmc_project.Object.Delete", "Delete");
630}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700631
Ed Tanous4a0cb852018-10-15 07:55:04 -0700632/**
633 * @brief Creates IPv4 with given data
634 *
635 * @param[in] ifaceId Id of interface whose IP should be deleted
636 * @param[in] ipIdx Index of IP in input array that should be deleted
637 * @param[in] ipHash DBus Hash id of IP that should be deleted
638 * @param[io] asyncResp Response object that will be returned to client
639 *
640 * @return None
641 */
642inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
643 uint8_t subnetMask, const std::string &gateway,
644 const std::string &address,
645 std::shared_ptr<AsyncResp> asyncResp)
646{
647 auto createIpHandler = [ipIdx,
648 asyncResp](const boost::system::error_code ec) {
649 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800651 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700652 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700653 };
654
Ed Tanous4a0cb852018-10-15 07:55:04 -0700655 crow::connections::systemBus->async_method_call(
656 std::move(createIpHandler), "xyz.openbmc_project.Network",
657 "/xyz/openbmc_project/network/" + ifaceId,
658 "xyz.openbmc_project.Network.IP.Create", "IP",
659 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
660 gateway);
661}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700662
Ed Tanous4a0cb852018-10-15 07:55:04 -0700663/**
664 * Function that retrieves all properties for given Ethernet Interface
665 * Object
666 * from EntityManager Network Manager
667 * @param ethiface_id a eth interface id to query on DBus
668 * @param callback a function that shall be called to convert Dbus output
669 * into JSON
670 */
671template <typename CallbackFunc>
672void getEthernetIfaceData(const std::string &ethiface_id,
673 CallbackFunc &&callback)
674{
675 crow::connections::systemBus->async_method_call(
676 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
677 const boost::system::error_code error_code,
678 const GetManagedObjects &resp) {
679 EthernetInterfaceData ethData{};
680 boost::container::flat_set<IPv4AddressData> ipv4Data;
681
682 if (error_code)
683 {
684 callback(false, ethData, ipv4Data);
685 return;
686 }
687
688 extractEthernetInterfaceData(ethiface_id, resp, ethData);
689 extractIPData(ethiface_id, resp, ipv4Data);
690
691 // Fix global GW
692 for (IPv4AddressData &ipv4 : ipv4Data)
693 {
694 if ((ipv4.linktype == LinkType::Global) &&
695 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700696 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700697 ipv4.gateway = ethData.default_gateway;
698 }
699 }
700
701 // Finally make a callback with usefull data
702 callback(true, ethData, ipv4Data);
703 },
704 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
705 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
706};
707
708/**
709 * Function that retrieves all Ethernet Interfaces available through Network
710 * Manager
711 * @param callback a function that shall be called to convert Dbus output
712 * into JSON.
713 */
714template <typename CallbackFunc>
715void getEthernetIfaceList(CallbackFunc &&callback)
716{
717 crow::connections::systemBus->async_method_call(
718 [callback{std::move(callback)}](
719 const boost::system::error_code error_code,
720 GetManagedObjects &resp) {
721 // Callback requires vector<string> to retrieve all available
722 // ethernet interfaces
723 std::vector<std::string> iface_list;
724 iface_list.reserve(resp.size());
725 if (error_code)
726 {
727 callback(false, iface_list);
728 return;
729 }
730
731 // Iterate over all retrieved ObjectPaths.
732 for (const auto &objpath : resp)
733 {
734 // And all interfaces available for certain ObjectPath.
735 for (const auto &interface : objpath.second)
736 {
737 // If interface is
738 // xyz.openbmc_project.Network.EthernetInterface, this is
739 // what we're looking for.
740 if (interface.first ==
741 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700742 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700743 // Cut out everyting until last "/", ...
744 const std::string &iface_id = objpath.first.str;
745 std::size_t last_pos = iface_id.rfind("/");
746 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700747 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700748 // and put it into output vector.
749 iface_list.emplace_back(
750 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700751 }
752 }
753 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700754 }
755 // Finally make a callback with useful data
756 callback(true, iface_list);
757 },
758 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
759 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100760};
761
762/**
763 * EthernetCollection derived class for delivering Ethernet Collection Schema
764 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700765class EthernetCollection : public Node
766{
767 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700768 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700769 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700770 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700771 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772 entityPrivileges = {
773 {boost::beast::http::verb::get, {{"Login"}}},
774 {boost::beast::http::verb::head, {{"Login"}}},
775 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
776 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
777 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
778 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
779 }
780
781 private:
782 /**
783 * Functions triggers appropriate requests on DBus
784 */
785 void doGet(crow::Response &res, const crow::Request &req,
786 const std::vector<std::string> &params) override
787 {
Ed Tanous0f74e642018-11-12 15:17:05 -0800788 res.jsonValue["@odata.type"] =
789 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
790 res.jsonValue["@odata.context"] =
791 "/redfish/v1/"
792 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
793 res.jsonValue["@odata.id"] =
794 "/redfish/v1/Managers/bmc/EthernetInterfaces";
795 res.jsonValue["Name"] = "Ethernet Network Interface Collection";
796 res.jsonValue["Description"] =
797 "Collection of EthernetInterfaces for this Manager";
798
Ed Tanous4a0cb852018-10-15 07:55:04 -0700799 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700800 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700801 getEthernetIfaceList(
802 [&res](const bool &success,
803 const std::vector<std::string> &iface_list) {
804 if (!success)
805 {
806 messages::internalError(res);
807 res.end();
808 return;
809 }
810
811 nlohmann::json &iface_array = res.jsonValue["Members"];
812 iface_array = nlohmann::json::array();
813 for (const std::string &iface_item : iface_list)
814 {
815 iface_array.push_back(
816 {{"@odata.id",
817 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
818 iface_item}});
819 }
820
821 res.jsonValue["Members@odata.count"] = iface_array.size();
822 res.jsonValue["@odata.id"] =
823 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700824 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700825 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700826 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100827};
828
829/**
830 * EthernetInterface derived class for delivering Ethernet Schema
831 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700832class EthernetInterface : public Node
833{
834 public:
835 /*
836 * Default Constructor
837 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700838 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700840 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700841 std::string())
842 {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700843 entityPrivileges = {
844 {boost::beast::http::verb::get, {{"Login"}}},
845 {boost::beast::http::verb::head, {{"Login"}}},
846 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
847 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
848 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
849 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200850 }
851
Ed Tanous1abe55e2018-09-05 08:30:59 -0700852 // TODO(kkowalsk) Find a suitable class/namespace for this
853 static void handleVlanPatch(const std::string &ifaceId,
854 const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700855 const EthernetInterfaceData &ethData,
856 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 {
858 if (!input.is_object())
859 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700860 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800861 "VLAN");
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200862 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700863 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200864
Ed Tanous4a0cb852018-10-15 07:55:04 -0700865 nlohmann::json::const_iterator vlanEnable = input.find("VLANEnable");
866 if (vlanEnable == input.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800868 messages::propertyMissing(asyncResp->res, "VLANEnable");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700869 return;
870 }
871 const bool *vlanEnableBool = vlanEnable->get_ptr<const bool *>();
872 if (vlanEnableBool == nullptr)
873 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700874 messages::propertyValueTypeError(asyncResp->res, vlanEnable->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800875 "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700876 return;
877 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200878
Ed Tanous4a0cb852018-10-15 07:55:04 -0700879 nlohmann::json::const_iterator vlanId = input.find("VLANId");
880 if (vlanId == input.end())
881 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800882 messages::propertyMissing(asyncResp->res, "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700883 return;
884 }
885 const uint64_t *vlanIdUint = vlanId->get_ptr<const uint64_t *>();
886 if (vlanIdUint == nullptr)
887 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700888 messages::propertyValueTypeError(asyncResp->res, vlanId->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800889 "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700890 return;
891 }
892
893 if (!ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700894 {
895 // This interface is not a VLAN. Cannot do anything with it
896 // TODO(kkowalsk) Change this message
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800897 messages::propertyNotWritable(asyncResp->res, "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898
Ed Tanous1abe55e2018-09-05 08:30:59 -0700899 return;
900 }
901
902 // VLAN is configured on the interface
Ed Tanous4a0cb852018-10-15 07:55:04 -0700903 if (*vlanEnableBool == true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700904 {
905 // Change VLAN Id
Ed Tanous4a0cb852018-10-15 07:55:04 -0700906 asyncResp->res.jsonValue["VLANId"] = *vlanIdUint;
907 auto callback = [asyncResp](const boost::system::error_code ec) {
908 if (ec)
909 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700910 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700911 }
912 else
913 {
914 asyncResp->res.jsonValue["VLANEnable"] = true;
915 }
916 };
917 crow::connections::systemBus->async_method_call(
918 std::move(callback), "xyz.openbmc_project.Network",
919 "/xyz/openbmc_project/network/" + ifaceId,
920 "org.freedesktop.DBus.Properties", "Set",
921 "xyz.openbmc_project.Network.VLAN", "Id",
922 sdbusplus::message::variant<uint32_t>(*vlanIdUint));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700923 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700924 else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700925 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700926 auto callback = [asyncResp](const boost::system::error_code ec) {
927 if (ec)
928 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700929 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700930 return;
931 }
932 asyncResp->res.jsonValue["VLANEnable"] = false;
933 };
934
935 crow::connections::systemBus->async_method_call(
936 std::move(callback), "xyz.openbmc_project.Network",
937 "/xyz/openbmc_project/network/" + ifaceId,
938 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 }
940 }
941
942 private:
943 void handleHostnamePatch(const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700944 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700945 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700946 const std::string *newHostname = input.get_ptr<const std::string *>();
947 if (newHostname == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700948 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700949 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800950 "HostName");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700951 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700953
954 // Change hostname
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800955 setHostName(*newHostname,
956 [asyncResp, newHostname{std::string(*newHostname)}](
957 const boost::system::error_code ec) {
958 if (ec)
959 {
960 messages::internalError(asyncResp->res);
961 }
962 else
963 {
964 asyncResp->res.jsonValue["HostName"] = newHostname;
965 }
966 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700967 }
968
Ed Tanous4a0cb852018-10-15 07:55:04 -0700969 void handleIPv4Patch(
970 const std::string &ifaceId, const nlohmann::json &input,
971 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
972 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700973 {
974 if (!input.is_array())
975 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700976 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800977 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700978 return;
979 }
980
981 // According to Redfish PATCH definition, size must be at least equal
Ed Tanous4a0cb852018-10-15 07:55:04 -0700982 if (input.size() < ipv4Data.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700983 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800984 messages::propertyValueFormatError(asyncResp->res, input.dump(),
985 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700986 return;
987 }
988
Ed Tanous4a0cb852018-10-15 07:55:04 -0700989 int entryIdx = 0;
990 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
991 ipv4Data.begin();
992 for (const nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700993 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700994 std::string pathString =
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800995 "IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700996 // Check that entry is not of some unexpected type
Ed Tanous4a0cb852018-10-15 07:55:04 -0700997 if (!thisJson.is_object() && !thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700998 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800999 messages::propertyValueTypeError(asyncResp->res,
1000 thisJson.dump(),
1001 pathString + "/IPv4Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002
1003 continue;
1004 }
1005
Ed Tanous4a0cb852018-10-15 07:55:04 -07001006 nlohmann::json::const_iterator addressFieldIt =
1007 thisJson.find("Address");
1008 const std::string *addressField = nullptr;
1009 if (addressFieldIt != thisJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001010 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001011 addressField = addressFieldIt->get_ptr<const std::string *>();
1012 if (addressField == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001013 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001014 messages::propertyValueFormatError(asyncResp->res,
1015 addressFieldIt->dump(),
1016 pathString + "/Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 continue;
1018 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001019 else
1020 {
1021 if (!ipv4VerifyIpAndGetBitcount(*addressField))
1022 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001023 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001024 asyncResp->res, *addressField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001025 pathString + "/Address");
1026 continue;
1027 }
1028 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001030
Ed Tanousa24526d2018-12-10 15:17:59 -08001031 std::optional<uint8_t> prefixLength;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001032 const std::string *subnetField = nullptr;
1033 nlohmann::json::const_iterator subnetFieldIt =
1034 thisJson.find("SubnetMask");
1035 if (subnetFieldIt != thisJson.end())
1036 {
1037 subnetField = subnetFieldIt->get_ptr<const std::string *>();
1038 if (subnetField == nullptr)
1039 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001040 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001041 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001042 pathString + "/SubnetMask");
1043 continue;
1044 }
1045 else
1046 {
1047 prefixLength = 0;
1048 if (!ipv4VerifyIpAndGetBitcount(*subnetField,
1049 &*prefixLength))
1050 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001051 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001052 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001053 pathString + "/SubnetMask");
1054 continue;
1055 }
1056 }
1057 }
1058
1059 std::string addressOriginInDBusFormat;
1060 const std::string *addressOriginField = nullptr;
1061 nlohmann::json::const_iterator addressOriginFieldIt =
1062 thisJson.find("AddressOrigin");
1063 if (addressOriginFieldIt != thisJson.end())
1064 {
1065 const std::string *addressOriginField =
1066 addressOriginFieldIt->get_ptr<const std::string *>();
1067 if (addressOriginField == nullptr)
1068 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001069 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001070 asyncResp->res, *addressOriginField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001071 pathString + "/AddressOrigin");
1072 continue;
1073 }
1074 else
1075 {
1076 // Get Address origin in proper format
1077 addressOriginInDBusFormat =
1078 translateAddressOriginRedfishToDbus(
1079 *addressOriginField);
1080 if (addressOriginInDBusFormat.empty())
1081 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001082 messages::propertyValueNotInList(
1083 asyncResp->res, *addressOriginField,
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001084 pathString + "/AddressOrigin");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001085 continue;
1086 }
1087 }
1088 }
1089
1090 nlohmann::json::const_iterator gatewayFieldIt =
1091 thisJson.find("Gateway");
1092 const std::string *gatewayField = nullptr;
1093 if (gatewayFieldIt != thisJson.end())
1094 {
1095 const std::string *gatewayField =
1096 gatewayFieldIt->get_ptr<const std::string *>();
1097 if (gatewayField == nullptr ||
1098 !ipv4VerifyIpAndGetBitcount(*gatewayField))
1099 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001100 messages::propertyValueFormatError(
1101 asyncResp->res, *gatewayField, pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001102 continue;
1103 }
1104 }
1105
1106 // if a vlan already exists, modify the existing
1107 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001108 {
1109 // Existing object that should be modified/deleted/remain
1110 // unchanged
Ed Tanous4a0cb852018-10-15 07:55:04 -07001111 if (thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001113 auto callback = [entryIdx{std::to_string(entryIdx)},
1114 asyncResp](
1115 const boost::system::error_code ec) {
1116 if (ec)
1117 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001118 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001119 return;
1120 }
1121 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1122 nullptr;
1123 };
1124 crow::connections::systemBus->async_method_call(
1125 std::move(callback), "xyz.openbmc_project.Network",
1126 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1127 thisData->id,
1128 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001130 else if (thisJson.is_object())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001131 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001132 // Apply changes
Ed Tanous4a0cb852018-10-15 07:55:04 -07001133 if (addressField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001134 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001135 auto callback =
1136 [asyncResp, entryIdx,
1137 addressField{std::string(*addressField)}](
1138 const boost::system::error_code ec) {
1139 if (ec)
1140 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001141 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001142 return;
1143 }
1144 asyncResp->res
1145 .jsonValue["IPv4Addresses"][std::to_string(
1146 entryIdx)]["Address"] = addressField;
1147 };
1148
1149 crow::connections::systemBus->async_method_call(
1150 std::move(callback), "xyz.openbmc_project.Network",
1151 "/xyz/openbmc_project/network/" + ifaceId +
1152 "/ipv4/" + thisData->id,
1153 "org.freedesktop.DBus.Properties", "Set",
1154 "xyz.openbmc_project.Network.IP", "Address",
1155 sdbusplus::message::variant<std::string>(
1156 *addressField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001157 }
1158
Ed Tanous4a0cb852018-10-15 07:55:04 -07001159 if (prefixLength && subnetField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001160 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001161 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1162 thisData->id, *subnetField,
1163 *prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 }
1165
Ed Tanous4a0cb852018-10-15 07:55:04 -07001166 if (!addressOriginInDBusFormat.empty() &&
1167 addressOriginField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001168 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001169 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1170 *addressOriginField,
1171 addressOriginInDBusFormat, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001172 }
1173
Ed Tanous4a0cb852018-10-15 07:55:04 -07001174 if (gatewayField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001175 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001176 auto callback =
1177 [asyncResp, entryIdx,
1178 gatewayField{std::string(*gatewayField)}](
1179 const boost::system::error_code ec) {
1180 if (ec)
1181 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001182 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001183 return;
1184 }
1185 asyncResp->res
1186 .jsonValue["IPv4Addresses"][std::to_string(
1187 entryIdx)]["Gateway"] =
1188 std::move(gatewayField);
1189 };
1190
1191 crow::connections::systemBus->async_method_call(
1192 std::move(callback), "xyz.openbmc_project.Network",
1193 "/xyz/openbmc_project/network/" + ifaceId +
1194 "/ipv4/" + thisData->id,
1195 "org.freedesktop.DBus.Properties", "Set",
1196 "xyz.openbmc_project.Network.IP", "Gateway",
1197 sdbusplus::message::variant<std::string>(
1198 *gatewayField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001199 }
1200 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001201 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001202 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001203 else
1204 {
1205 // Create IPv4 with provided data
1206 if (gatewayField == nullptr)
1207 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001208 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001209 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001210 continue;
1211 }
1212
1213 if (addressField == nullptr)
1214 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001215 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001216 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001217 continue;
1218 }
1219
1220 if (!prefixLength)
1221 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001222 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001223 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001224 continue;
1225 }
1226
1227 createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField,
1228 *addressField, asyncResp);
1229 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
1230 }
1231 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001232 }
1233 }
1234
Ed Tanous0f74e642018-11-12 15:17:05 -08001235 void parseInterfaceData(
1236 nlohmann::json &json_response, const std::string &iface_id,
1237 const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001238 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001239 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001240 json_response["Id"] = iface_id;
1241 json_response["@odata.id"] =
1242 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001243
Ed Tanous4a0cb852018-10-15 07:55:04 -07001244 json_response["SpeedMbps"] = ethData.speed;
1245 json_response["MACAddress"] = ethData.mac_address;
1246 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001247 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001248 json_response["HostName"] = ethData.hostname;
1249 }
1250
1251 nlohmann::json &vlanObj = json_response["VLAN"];
1252 if (ethData.vlan_id)
1253 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001254 vlanObj["VLANEnable"] = true;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001255 vlanObj["VLANId"] = *ethData.vlan_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001256 }
1257 else
1258 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001259 vlanObj["VLANEnable"] = false;
1260 vlanObj["VLANId"] = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001261 }
1262
Ed Tanous4a0cb852018-10-15 07:55:04 -07001263 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001264 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001265 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1266 ipv4_array = nlohmann::json::array();
1267 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001269 if (!ipv4_config.address.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001270 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001271 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1272 {"SubnetMask", ipv4_config.netmask},
1273 {"Address", ipv4_config.address}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274
Ed Tanous4a0cb852018-10-15 07:55:04 -07001275 if (!ipv4_config.gateway.empty())
1276 {
1277 ipv4_array.back()["Gateway"] = ipv4_config.gateway;
1278 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001279 }
1280 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001281 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001282 }
1283
1284 /**
1285 * Functions triggers appropriate requests on DBus
1286 */
1287 void doGet(crow::Response &res, const crow::Request &req,
1288 const std::vector<std::string> &params) override
1289 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001290 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001291 if (params.size() != 1)
1292 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001293 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001294 return;
1295 }
1296
Ed Tanous4a0cb852018-10-15 07:55:04 -07001297 getEthernetIfaceData(
1298 params[0],
1299 [this, asyncResp, iface_id{std::string(params[0])}](
1300 const bool &success, const EthernetInterfaceData &ethData,
1301 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1302 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001303 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001304 // TODO(Pawel)consider distinguish between non existing
1305 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001306 messages::resourceNotFound(asyncResp->res,
1307 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001308 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001309 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001310 asyncResp->res.jsonValue["@odata.type"] =
1311 "#EthernetInterface.v1_2_0.EthernetInterface";
1312 asyncResp->res.jsonValue["@odata.context"] =
1313 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
1314 asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
1315 asyncResp->res.jsonValue["Description"] =
1316 "Management Network Interface";
1317
1318 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1319 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001320 });
1321 }
1322
1323 void doPatch(crow::Response &res, const crow::Request &req,
1324 const std::vector<std::string> &params) override
1325 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001326 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001327 if (params.size() != 1)
1328 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001329 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001330 return;
1331 }
1332
Ed Tanous4a0cb852018-10-15 07:55:04 -07001333 const std::string &iface_id = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001334
1335 nlohmann::json patchReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001336 if (!json_util::processJsonFromRequest(res, req, patchReq))
1337 {
1338 return;
1339 }
1340
Ed Tanous4a0cb852018-10-15 07:55:04 -07001341 // Get single eth interface data, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001342 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001343 getEthernetIfaceData(
1344 iface_id,
1345 [this, asyncResp, iface_id, patchReq = std::move(patchReq)](
1346 const bool &success, const EthernetInterfaceData &ethData,
1347 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001348 if (!success)
1349 {
1350 // ... otherwise return error
1351 // TODO(Pawel)consider distinguish between non existing
1352 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001353 messages::resourceNotFound(
1354 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001355 return;
1356 }
1357
Ed Tanous0f74e642018-11-12 15:17:05 -08001358 parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
1359 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001360
Ed Tanous4a0cb852018-10-15 07:55:04 -07001361 for (auto propertyIt : patchReq.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001362 {
1363 if (propertyIt.key() == "VLAN")
1364 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001365 handleVlanPatch(iface_id, propertyIt.value(), ethData,
1366 asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001367 }
1368 else if (propertyIt.key() == "HostName")
1369 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001370 handleHostnamePatch(propertyIt.value(), asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001371 }
1372 else if (propertyIt.key() == "IPv4Addresses")
1373 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001374 handleIPv4Patch(iface_id, propertyIt.value(), ipv4Data,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001375 asyncResp);
1376 }
1377 else if (propertyIt.key() == "IPv6Addresses")
1378 {
1379 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001380 messages::propertyNotWritable(asyncResp->res,
1381 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001382 }
1383 else
1384 {
1385 auto fieldInJsonIt =
Ed Tanous4a0cb852018-10-15 07:55:04 -07001386 asyncResp->res.jsonValue.find(propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001387
Ed Tanous4a0cb852018-10-15 07:55:04 -07001388 if (fieldInJsonIt == asyncResp->res.jsonValue.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001389 {
1390 // Field not in scope of defined fields
Jason M. Billsf12894f2018-10-09 12:45:45 -07001391 messages::propertyUnknown(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001392 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001393 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001394 else
Ed Tanous1abe55e2018-09-05 08:30:59 -07001395 {
1396 // User attempted to modify non-writable field
Jason M. Billsf12894f2018-10-09 12:45:45 -07001397 messages::propertyNotWritable(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001398 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001399 }
1400 }
1401 }
1402 });
1403 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001404};
1405
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001406/**
Ed Tanous4a0cb852018-10-15 07:55:04 -07001407 * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1408 * Schema
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001409 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001410class VlanNetworkInterface : public Node
1411{
1412 public:
1413 /*
1414 * Default Constructor
1415 */
1416 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001417 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001418 Node(app,
Ed Tanous0f74e642018-11-12 15:17:05 -08001419 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/<str>",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001420 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001421 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001422 entityPrivileges = {
1423 {boost::beast::http::verb::get, {{"Login"}}},
1424 {boost::beast::http::verb::head, {{"Login"}}},
1425 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1426 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1427 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1428 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001429 }
1430
Ed Tanous1abe55e2018-09-05 08:30:59 -07001431 private:
Ed Tanous0f74e642018-11-12 15:17:05 -08001432 void parseInterfaceData(
1433 nlohmann::json &json_response, const std::string &parent_iface_id,
1434 const std::string &iface_id, const EthernetInterfaceData &ethData,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001435 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001436 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001437 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001438 json_response["Id"] = iface_id;
1439 json_response["@odata.id"] =
1440 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1441 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001442
Ed Tanous4a0cb852018-10-15 07:55:04 -07001443 json_response["VLANEnable"] = true;
1444 if (ethData.vlan_id)
1445 {
1446 json_response["VLANId"] = *ethData.vlan_id;
1447 }
Ed Tanousa434f2b2018-07-27 13:04:22 -07001448 }
1449
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 bool verifyNames(crow::Response &res, const std::string &parent,
1451 const std::string &iface)
1452 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001453 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001454 if (!boost::starts_with(iface, parent + "_"))
1455 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001456 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1457 iface);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001458 return false;
1459 }
1460 else
1461 {
1462 return true;
1463 }
1464 }
1465
1466 /**
1467 * Functions triggers appropriate requests on DBus
1468 */
1469 void doGet(crow::Response &res, const crow::Request &req,
1470 const std::vector<std::string> &params) override
1471 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001472 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1473 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001474 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1475 // Check if there is required param, truly entering this shall be
1476 // impossible.
1477 if (params.size() != 2)
1478 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001479 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001480 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001481 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001482 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001483
Ed Tanous4a0cb852018-10-15 07:55:04 -07001484 const std::string &parent_iface_id = params[0];
1485 const std::string &iface_id = params[1];
Ed Tanous0f74e642018-11-12 15:17:05 -08001486 res.jsonValue["@odata.type"] =
1487 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1488 res.jsonValue["@odata.context"] =
1489 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1490 res.jsonValue["Name"] = "VLAN Network Interface";
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001491
Ed Tanous4a0cb852018-10-15 07:55:04 -07001492 if (!verifyNames(res, parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001493 {
1494 return;
1495 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001496
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497 // Get single eth interface data, and call the below callback for JSON
1498 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001499 getEthernetIfaceData(
1500 iface_id,
1501 [this, asyncResp, parent_iface_id, iface_id](
1502 const bool &success, const EthernetInterfaceData &ethData,
1503 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1504 if (success && ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001505 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001506 parseInterfaceData(asyncResp->res.jsonValue,
1507 parent_iface_id, iface_id, ethData,
1508 ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001509 }
1510 else
1511 {
1512 // ... otherwise return error
1513 // TODO(Pawel)consider distinguish between non existing
1514 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001515 messages::resourceNotFound(
1516 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001518 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001519 }
1520
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 void doPatch(crow::Response &res, const crow::Request &req,
1522 const std::vector<std::string> &params) override
1523 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001524 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001525 if (params.size() != 2)
1526 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001527 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001528 return;
1529 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001530
Ed Tanous1abe55e2018-09-05 08:30:59 -07001531 const std::string &parentIfaceId = params[0];
1532 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001533
Ed Tanous1abe55e2018-09-05 08:30:59 -07001534 if (!verifyNames(res, parentIfaceId, ifaceId))
1535 {
1536 return;
1537 }
1538
1539 nlohmann::json patchReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001540 if (!json_util::processJsonFromRequest(res, req, patchReq))
1541 {
1542 return;
1543 }
1544
1545 // Get single eth interface data, and call the below callback for JSON
1546 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001547 getEthernetIfaceData(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548 ifaceId,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001549 [this, asyncResp, parentIfaceId, ifaceId,
1550 patchReq{std::move(patchReq)}](
1551 const bool &success, const EthernetInterfaceData &ethData,
1552 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001553 if (!success)
1554 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001555 // TODO(Pawel)consider distinguish between non existing
1556 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001557 messages::resourceNotFound(
1558 asyncResp->res, "VLAN Network Interface", ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001559
1560 return;
1561 }
1562
Ed Tanous0f74e642018-11-12 15:17:05 -08001563 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1564 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001565
Ed Tanous4a0cb852018-10-15 07:55:04 -07001566 for (auto propertyIt : patchReq.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001567 {
1568 if (propertyIt.key() != "VLANEnable" &&
1569 propertyIt.key() != "VLANId")
1570 {
1571 auto fieldInJsonIt =
Ed Tanous4a0cb852018-10-15 07:55:04 -07001572 asyncResp->res.jsonValue.find(propertyIt.key());
1573 if (fieldInJsonIt == asyncResp->res.jsonValue.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001574 {
1575 // Field not in scope of defined fields
Jason M. Billsf12894f2018-10-09 12:45:45 -07001576 messages::propertyUnknown(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001577 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001578 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001579 else
Ed Tanous1abe55e2018-09-05 08:30:59 -07001580 {
1581 // User attempted to modify non-writable field
Jason M. Billsf12894f2018-10-09 12:45:45 -07001582 messages::propertyNotWritable(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001583 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001584 }
1585 }
1586 }
1587
Ed Tanous4a0cb852018-10-15 07:55:04 -07001588 EthernetInterface::handleVlanPatch(ifaceId, patchReq, ethData,
1589 asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001590 });
1591 }
1592
1593 void doDelete(crow::Response &res, const crow::Request &req,
1594 const std::vector<std::string> &params) override
1595 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001596 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001597 if (params.size() != 2)
1598 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001599 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 return;
1601 }
1602
1603 const std::string &parentIfaceId = params[0];
1604 const std::string &ifaceId = params[1];
1605
Ed Tanous4a0cb852018-10-15 07:55:04 -07001606 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 {
1608 return;
1609 }
1610
1611 // Get single eth interface data, and call the below callback for JSON
1612 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001613 getEthernetIfaceData(
1614 ifaceId,
1615 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1616 ifaceId{std::string(ifaceId)}](
1617 const bool &success, const EthernetInterfaceData &ethData,
1618 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1619 if (success && ethData.vlan_id)
1620 {
Ed Tanous0f74e642018-11-12 15:17:05 -08001621 parseInterfaceData(asyncResp->res.jsonValue, parentIfaceId,
1622 ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001623
Jason M. Billsf12894f2018-10-09 12:45:45 -07001624 auto callback =
1625 [asyncResp](const boost::system::error_code ec) {
1626 if (ec)
1627 {
1628 messages::internalError(asyncResp->res);
1629 }
1630 };
1631 crow::connections::systemBus->async_method_call(
1632 std::move(callback), "xyz.openbmc_project.Network",
1633 std::string("/xyz/openbmc_project/network/") + ifaceId,
1634 "xyz.openbmc_project.Object.Delete", "Delete");
1635 }
1636 else
1637 {
1638 // ... otherwise return error
1639 // TODO(Pawel)consider distinguish between non existing
1640 // object, and other errors
1641 messages::resourceNotFound(
1642 asyncResp->res, "VLAN Network Interface", ifaceId);
1643 }
1644 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001645 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001646};
1647
1648/**
1649 * VlanNetworkInterfaceCollection derived class for delivering
1650 * VLANNetworkInterface Collection Schema
1651 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001652class VlanNetworkInterfaceCollection : public Node
1653{
1654 public:
1655 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -07001656 VlanNetworkInterfaceCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001657 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1658 std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001659 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660 entityPrivileges = {
1661 {boost::beast::http::verb::get, {{"Login"}}},
1662 {boost::beast::http::verb::head, {{"Login"}}},
1663 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1664 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1665 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1666 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001667 }
1668
Ed Tanous1abe55e2018-09-05 08:30:59 -07001669 private:
1670 /**
1671 * Functions triggers appropriate requests on DBus
1672 */
1673 void doGet(crow::Response &res, const crow::Request &req,
1674 const std::vector<std::string> &params) override
1675 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001676 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001677 if (params.size() != 1)
1678 {
1679 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001680 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001681 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001682 }
1683
Ed Tanous4a0cb852018-10-15 07:55:04 -07001684 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001685
Ed Tanous4a0cb852018-10-15 07:55:04 -07001686 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001688 getEthernetIfaceList(
1689 [this, asyncResp,
1690 rootInterfaceName{std::string(rootInterfaceName)}](
1691 const bool &success,
1692 const std::vector<std::string> &iface_list) {
1693 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001694 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001695 messages::internalError(asyncResp->res);
1696 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001697 }
Ed Tanous0f74e642018-11-12 15:17:05 -08001698 asyncResp->res.jsonValue["@odata.type"] =
1699 "#VLanNetworkInterfaceCollection."
1700 "VLanNetworkInterfaceCollection";
1701 asyncResp->res.jsonValue["@odata.context"] =
1702 "/redfish/v1/$metadata"
1703 "#VLanNetworkInterfaceCollection."
1704 "VLanNetworkInterfaceCollection";
1705 asyncResp->res.jsonValue["Name"] =
1706 "VLAN Network Interface Collection";
Ed Tanous4a0cb852018-10-15 07:55:04 -07001707
Jason M. Billsf12894f2018-10-09 12:45:45 -07001708 nlohmann::json iface_array = nlohmann::json::array();
1709
1710 for (const std::string &iface_item : iface_list)
1711 {
1712 if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1713 {
1714 iface_array.push_back(
1715 {{"@odata.id",
1716 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1717 rootInterfaceName + "/VLANs/" + iface_item}});
1718 }
1719 }
1720
1721 if (iface_array.empty())
1722 {
1723 messages::resourceNotFound(
1724 asyncResp->res, "EthernetInterface", rootInterfaceName);
1725 return;
1726 }
1727 asyncResp->res.jsonValue["Members@odata.count"] =
1728 iface_array.size();
1729 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1730 asyncResp->res.jsonValue["@odata.id"] =
1731 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1732 rootInterfaceName + "/VLANs";
1733 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001734 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001735
Ed Tanous1abe55e2018-09-05 08:30:59 -07001736 void doPost(crow::Response &res, const crow::Request &req,
1737 const std::vector<std::string> &params) override
1738 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001739 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001740 if (params.size() != 1)
1741 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001742 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001743 return;
1744 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001745 nlohmann::json postReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 if (!json_util::processJsonFromRequest(res, req, postReq))
1747 {
1748 return;
1749 }
1750
Ed Tanous0f74e642018-11-12 15:17:05 -08001751 auto vlanIdJson = postReq.find("VLANId");
Ed Tanous1b6b96c2018-11-30 11:35:41 -08001752
Ed Tanous0f74e642018-11-12 15:17:05 -08001753 if (vlanIdJson == postReq.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001754 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001755 messages::propertyMissing(asyncResp->res, "VLANId");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001756 return;
1757 }
1758
Ed Tanous4a0cb852018-10-15 07:55:04 -07001759 const uint64_t *vlanId = vlanIdJson->get_ptr<const uint64_t *>();
1760 if (vlanId == nullptr)
1761 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001762 messages::propertyValueTypeError(asyncResp->res, vlanIdJson->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001763 "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001764 return;
1765 }
1766 const std::string &rootInterfaceName = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001767
Ed Tanous4a0cb852018-10-15 07:55:04 -07001768 auto callback = [asyncResp](const boost::system::error_code ec) {
1769 if (ec)
1770 {
1771 // TODO(ed) make more consistent error messages based on
1772 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001773 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001774 return;
1775 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001776 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001777 };
1778 crow::connections::systemBus->async_method_call(
1779 std::move(callback), "xyz.openbmc_project.Network",
1780 "/xyz/openbmc_project/network",
1781 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
1782 rootInterfaceName, static_cast<uint32_t>(*vlanId));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001783 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001784};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001785} // namespace redfish