blob: c44c63f53abb3e76e890a5df090238231df5537e [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>
20#include <boost/optional.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020021#include <dbus_singleton.hpp>
Kowalski, Kamil588c3f02018-04-03 14:55:27 +020022#include <error_messages.hpp>
Kowalski, Kamil179db1d2018-04-23 11:12:41 +020023#include <node.hpp>
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;
83 boost::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 =
174 mapbox::getPtr<const std::string>(
175 propertyPair.second);
176 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 Tanous4a0cb852018-10-15 07:55:04 -0700189 const uint32_t *id = mapbox::getPtr<const uint32_t>(
190 propertyPair.second);
191 if (id != nullptr)
192 {
193 ethData.vlan_id = *id;
194 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700195 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700196 }
197 }
198 else if (ifacePair.first ==
199 "xyz.openbmc_project.Network.EthernetInterface")
200 {
201 for (const auto &propertyPair : ifacePair.second)
202 {
203 if (propertyPair.first == "AutoNeg")
204 {
205 const bool *auto_neg =
206 mapbox::getPtr<const bool>(propertyPair.second);
207 if (auto_neg != nullptr)
208 {
209 ethData.auto_neg = *auto_neg;
210 }
211 }
212 else if (propertyPair.first == "Speed")
213 {
214 const uint32_t *speed =
215 mapbox::getPtr<const uint32_t>(
216 propertyPair.second);
217 if (speed != nullptr)
218 {
219 ethData.speed = *speed;
220 }
221 }
222 }
223 }
224 else if (ifacePair.first ==
225 "xyz.openbmc_project.Network.SystemConfiguration")
226 {
227 for (const auto &propertyPair : ifacePair.second)
228 {
229 if (propertyPair.first == "HostName")
230 {
231 const std::string *hostname =
232 mapbox::getPtr<const std::string>(
233 propertyPair.second);
234 if (hostname != nullptr)
235 {
236 ethData.hostname = *hostname;
237 }
238 }
239 else if (propertyPair.first == "DefaultGateway")
240 {
241 const std::string *defaultGateway =
242 mapbox::getPtr<const std::string>(
243 propertyPair.second);
244 if (defaultGateway != nullptr)
245 {
246 ethData.default_gateway = *defaultGateway;
247 }
248 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700249 }
250 }
251 }
252 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700253 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700254}
255
256// Helper function that extracts data for single ethernet ipv4 address
257inline void
258 extractIPData(const std::string &ethiface_id,
259 const GetManagedObjects &dbus_data,
260 boost::container::flat_set<IPv4AddressData> &ipv4_config)
261{
262 const std::string ipv4PathStart =
263 "/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
264
265 // Since there might be several IPv4 configurations aligned with
266 // single ethernet interface, loop over all of them
267 for (const auto &objpath : dbus_data)
268 {
269 // Check if proper pattern for object path appears
270 if (boost::starts_with(objpath.first.str, ipv4PathStart))
271 {
272 for (auto &interface : objpath.second)
273 {
274 if (interface.first == "xyz.openbmc_project.Network.IP")
275 {
276 // Instance IPv4AddressData structure, and set as
277 // appropriate
278 std::pair<
279 boost::container::flat_set<IPv4AddressData>::iterator,
280 bool>
281 it = ipv4_config.insert(
282 {objpath.first.str.substr(ipv4PathStart.size())});
283 IPv4AddressData &ipv4_address = *it.first;
284 for (auto &property : interface.second)
285 {
286 if (property.first == "Address")
287 {
288 const std::string *address =
289 mapbox::getPtr<const std::string>(
290 property.second);
291 if (address != nullptr)
292 {
293 ipv4_address.address = *address;
294 }
295 }
296 else if (property.first == "Gateway")
297 {
298 const std::string *gateway =
299 mapbox::getPtr<const std::string>(
300 property.second);
301 if (gateway != nullptr)
302 {
303 ipv4_address.gateway = *gateway;
304 }
305 }
306 else if (property.first == "Origin")
307 {
308 const std::string *origin =
309 mapbox::getPtr<const std::string>(
310 property.second);
311 if (origin != nullptr)
312 {
313 ipv4_address.origin =
314 translateAddressOriginDbusToRedfish(*origin,
315 true);
316 }
317 }
318 else if (property.first == "PrefixLength")
319 {
320 const uint8_t *mask =
321 mapbox::getPtr<uint8_t>(property.second);
322 if (mask != nullptr)
323 {
324 // convert it to the string
325 ipv4_address.netmask = getNetmask(*mask);
326 }
327 }
328 else
329 {
330 BMCWEB_LOG_ERROR
331 << "Got extra property: " << property.first
332 << " on the " << objpath.first.str << " object";
333 }
334 }
335 // Check if given address is local, or global
336 ipv4_address.linktype =
337 boost::starts_with(ipv4_address.address, "169.254.")
338 ? LinkType::Global
339 : LinkType::Local;
340 }
341 }
342 }
343 }
344}
345
346/**
347 * @brief Sets given Id on the given VLAN interface through D-Bus
348 *
349 * @param[in] ifaceId Id of VLAN interface that should be modified
350 * @param[in] inputVlanId New ID of the VLAN
351 * @param[in] callback Function that will be called after the operation
352 *
353 * @return None.
354 */
355template <typename CallbackFunc>
356void changeVlanId(const std::string &ifaceId, const uint32_t &inputVlanId,
357 CallbackFunc &&callback)
358{
359 crow::connections::systemBus->async_method_call(
360 callback, "xyz.openbmc_project.Network",
361 std::string("/xyz/openbmc_project/network/") + ifaceId,
362 "org.freedesktop.DBus.Properties", "Set",
363 "xyz.openbmc_project.Network.VLAN", "Id",
364 sdbusplus::message::variant<uint32_t>(inputVlanId));
365}
366
367/**
368 * @brief Helper function that verifies IP address to check if it is in
369 * proper format. If bits pointer is provided, also calculates active
370 * bit count for Subnet Mask.
371 *
372 * @param[in] ip IP that will be verified
373 * @param[out] bits Calculated mask in bits notation
374 *
375 * @return true in case of success, false otherwise
376 */
377inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
378 uint8_t *bits = nullptr)
379{
380 std::vector<std::string> bytesInMask;
381
382 boost::split(bytesInMask, ip, boost::is_any_of("."));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700383
384 static const constexpr int ipV4AddressSectionsCount = 4;
Ed Tanous4a0cb852018-10-15 07:55:04 -0700385 if (bytesInMask.size() != ipV4AddressSectionsCount)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700386 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700387 return false;
388 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700389
Ed Tanous4a0cb852018-10-15 07:55:04 -0700390 if (bits != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700391 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700392 *bits = 0;
393 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700394
Ed Tanous4a0cb852018-10-15 07:55:04 -0700395 char *endPtr;
396 long previousValue = 255;
397 bool firstZeroInByteHit;
398 for (const std::string &byte : bytesInMask)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700399 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700400 if (byte.empty())
401 {
402 return false;
403 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700404
Ed Tanous4a0cb852018-10-15 07:55:04 -0700405 // Use strtol instead of stroi to avoid exceptions
406 long value = std::strtol(byte.c_str(), &endPtr, 10);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700407
Ed Tanous4a0cb852018-10-15 07:55:04 -0700408 // endPtr should point to the end of the string, otherwise given string
409 // is not 100% number
410 if (*endPtr != '\0')
411 {
412 return false;
413 }
414
415 // Value should be contained in byte
416 if (value < 0 || value > 255)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700417 {
418 return false;
419 }
420
421 if (bits != nullptr)
422 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700423 // Mask has to be continuous between bytes
424 if (previousValue != 255 && value != 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700425 {
426 return false;
427 }
428
Ed Tanous4a0cb852018-10-15 07:55:04 -0700429 // Mask has to be continuous inside bytes
430 firstZeroInByteHit = false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700431
Ed Tanous4a0cb852018-10-15 07:55:04 -0700432 // Count bits
433 for (int bitIdx = 7; bitIdx >= 0; bitIdx--)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700434 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700435 if (value & (1 << bitIdx))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700436 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700437 if (firstZeroInByteHit)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700438 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700439 // Continuity not preserved
440 return false;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700441 }
442 else
443 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700444 (*bits)++;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700445 }
446 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700447 else
448 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700449 firstZeroInByteHit = true;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700450 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700451 }
452 }
453
454 previousValue = value;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700455 }
456
Ed Tanous4a0cb852018-10-15 07:55:04 -0700457 return true;
458}
459
460/**
461 * @brief Changes IPv4 address type property (Address, Gateway)
462 *
463 * @param[in] ifaceId Id of interface whose IP should be modified
464 * @param[in] ipIdx Index of IP in input array that should be modified
465 * @param[in] ipHash DBus Hash id of modified IP
466 * @param[in] name Name of field in JSON representation
467 * @param[in] newValue New value that should be written
468 * @param[io] asyncResp Response object that will be returned to client
469 *
470 * @return true if give IP is valid and has been sent do D-Bus, false
471 * otherwise
472 */
473inline void changeIPv4AddressProperty(
474 const std::string &ifaceId, int ipIdx, const std::string &ipHash,
475 const std::string &name, const std::string &newValue,
476 const std::shared_ptr<AsyncResp> asyncResp)
477{
478 auto callback = [asyncResp, ipIdx, name{std::string(name)},
479 newValue{std::move(newValue)}](
480 const boost::system::error_code ec) {
481 if (ec)
482 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800483 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700484 }
485 else
486 {
487 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
488 }
489 };
490
491 crow::connections::systemBus->async_method_call(
492 std::move(callback), "xyz.openbmc_project.Network",
493 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
494 "org.freedesktop.DBus.Properties", "Set",
495 "xyz.openbmc_project.Network.IP", name,
496 sdbusplus::message::variant<std::string>(newValue));
497}
498
499/**
500 * @brief Changes IPv4 address origin property
501 *
502 * @param[in] ifaceId Id of interface whose IP should be modified
503 * @param[in] ipIdx Index of IP in input array that should be
504 * modified
505 * @param[in] ipHash DBus Hash id of modified IP
506 * @param[in] newValue New value in Redfish format
507 * @param[in] newValueDbus New value in D-Bus format
508 * @param[io] asyncResp Response object that will be returned to client
509 *
510 * @return true if give IP is valid and has been sent do D-Bus, false
511 * otherwise
512 */
513inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
514 const std::string &ipHash,
515 const std::string &newValue,
516 const std::string &newValueDbus,
517 const std::shared_ptr<AsyncResp> asyncResp)
518{
519 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
520 const boost::system::error_code ec) {
521 if (ec)
522 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800523 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700524 }
525 else
526 {
527 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
528 newValue;
529 }
530 };
531
532 crow::connections::systemBus->async_method_call(
533 std::move(callback), "xyz.openbmc_project.Network",
534 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
535 "org.freedesktop.DBus.Properties", "Set",
536 "xyz.openbmc_project.Network.IP", "Origin",
537 sdbusplus::message::variant<std::string>(newValueDbus));
538}
539
540/**
541 * @brief Modifies SubnetMask for given IP
542 *
543 * @param[in] ifaceId Id of interface whose IP should be modified
544 * @param[in] ipIdx Index of IP in input array that should be
545 * modified
546 * @param[in] ipHash DBus Hash id of modified IP
547 * @param[in] newValueStr Mask in dot notation as string
548 * @param[in] newValue Mask as PrefixLength in bitcount
549 * @param[io] asyncResp Response object that will be returned to client
550 *
551 * @return None
552 */
553inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
554 const std::string &ipHash,
555 const std::string &newValueStr,
556 uint8_t &newValue,
557 std::shared_ptr<AsyncResp> asyncResp)
558{
559 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
560 const boost::system::error_code ec) {
561 if (ec)
562 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800563 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700564 }
565 else
566 {
567 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
568 newValueStr;
569 }
570 };
571
572 crow::connections::systemBus->async_method_call(
573 std::move(callback), "xyz.openbmc_project.Network",
574 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
575 "org.freedesktop.DBus.Properties", "Set",
576 "xyz.openbmc_project.Network.IP", "PrefixLength",
577 sdbusplus::message::variant<uint8_t>(newValue));
578}
579
580/**
581 * @brief Sets given HostName of the machine through D-Bus
582 *
583 * @param[in] newHostname New name that HostName will be changed to
584 * @param[in] callback Function that will be called after the operation
585 *
586 * @return None.
587 */
588template <typename CallbackFunc>
589void setHostName(const std::string &newHostname, CallbackFunc &&callback)
590{
591 crow::connections::systemBus->async_method_call(
592 callback, "xyz.openbmc_project.Network",
593 "/xyz/openbmc_project/network/config",
594 "org.freedesktop.DBus.Properties", "Set",
595 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
596 sdbusplus::message::variant<std::string>(newHostname));
597}
598
599/**
600 * @brief Deletes given IPv4
601 *
602 * @param[in] ifaceId Id of interface whose IP should be deleted
603 * @param[in] ipIdx Index of IP in input array that should be deleted
604 * @param[in] ipHash DBus Hash id of IP that should be deleted
605 * @param[io] asyncResp Response object that will be returned to client
606 *
607 * @return None
608 */
609inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
610 unsigned int ipIdx,
611 const std::shared_ptr<AsyncResp> asyncResp)
612{
613 crow::connections::systemBus->async_method_call(
614 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700615 if (ec)
616 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800617 messages::internalError(asyncResp->res);
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100618 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700619 else
620 {
621 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
622 }
623 },
624 "xyz.openbmc_project.Network",
625 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
626 "xyz.openbmc_project.Object.Delete", "Delete");
627}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700628
Ed Tanous4a0cb852018-10-15 07:55:04 -0700629/**
630 * @brief Creates IPv4 with given data
631 *
632 * @param[in] ifaceId Id of interface whose IP should be deleted
633 * @param[in] ipIdx Index of IP in input array that should be deleted
634 * @param[in] ipHash DBus Hash id of IP that should be deleted
635 * @param[io] asyncResp Response object that will be returned to client
636 *
637 * @return None
638 */
639inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
640 uint8_t subnetMask, const std::string &gateway,
641 const std::string &address,
642 std::shared_ptr<AsyncResp> asyncResp)
643{
644 auto createIpHandler = [ipIdx,
645 asyncResp](const boost::system::error_code ec) {
646 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700647 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800648 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -0700649 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700650 };
651
Ed Tanous4a0cb852018-10-15 07:55:04 -0700652 crow::connections::systemBus->async_method_call(
653 std::move(createIpHandler), "xyz.openbmc_project.Network",
654 "/xyz/openbmc_project/network/" + ifaceId,
655 "xyz.openbmc_project.Network.IP.Create", "IP",
656 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
657 gateway);
658}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659
Ed Tanous4a0cb852018-10-15 07:55:04 -0700660/**
661 * Function that retrieves all properties for given Ethernet Interface
662 * Object
663 * from EntityManager Network Manager
664 * @param ethiface_id a eth interface id to query on DBus
665 * @param callback a function that shall be called to convert Dbus output
666 * into JSON
667 */
668template <typename CallbackFunc>
669void getEthernetIfaceData(const std::string &ethiface_id,
670 CallbackFunc &&callback)
671{
672 crow::connections::systemBus->async_method_call(
673 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
674 const boost::system::error_code error_code,
675 const GetManagedObjects &resp) {
676 EthernetInterfaceData ethData{};
677 boost::container::flat_set<IPv4AddressData> ipv4Data;
678
679 if (error_code)
680 {
681 callback(false, ethData, ipv4Data);
682 return;
683 }
684
685 extractEthernetInterfaceData(ethiface_id, resp, ethData);
686 extractIPData(ethiface_id, resp, ipv4Data);
687
688 // Fix global GW
689 for (IPv4AddressData &ipv4 : ipv4Data)
690 {
691 if ((ipv4.linktype == LinkType::Global) &&
692 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700693 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700694 ipv4.gateway = ethData.default_gateway;
695 }
696 }
697
698 // Finally make a callback with usefull data
699 callback(true, ethData, ipv4Data);
700 },
701 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
702 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
703};
704
705/**
706 * Function that retrieves all Ethernet Interfaces available through Network
707 * Manager
708 * @param callback a function that shall be called to convert Dbus output
709 * into JSON.
710 */
711template <typename CallbackFunc>
712void getEthernetIfaceList(CallbackFunc &&callback)
713{
714 crow::connections::systemBus->async_method_call(
715 [callback{std::move(callback)}](
716 const boost::system::error_code error_code,
717 GetManagedObjects &resp) {
718 // Callback requires vector<string> to retrieve all available
719 // ethernet interfaces
720 std::vector<std::string> iface_list;
721 iface_list.reserve(resp.size());
722 if (error_code)
723 {
724 callback(false, iface_list);
725 return;
726 }
727
728 // Iterate over all retrieved ObjectPaths.
729 for (const auto &objpath : resp)
730 {
731 // And all interfaces available for certain ObjectPath.
732 for (const auto &interface : objpath.second)
733 {
734 // If interface is
735 // xyz.openbmc_project.Network.EthernetInterface, this is
736 // what we're looking for.
737 if (interface.first ==
738 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700739 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700740 // Cut out everyting until last "/", ...
741 const std::string &iface_id = objpath.first.str;
742 std::size_t last_pos = iface_id.rfind("/");
743 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700744 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700745 // and put it into output vector.
746 iface_list.emplace_back(
747 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700748 }
749 }
750 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700751 }
752 // Finally make a callback with useful data
753 callback(true, iface_list);
754 },
755 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
756 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100757};
758
759/**
760 * EthernetCollection derived class for delivering Ethernet Collection Schema
761 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700762class EthernetCollection : public Node
763{
764 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700765 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700766 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700767 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700768 {
769 Node::json["@odata.type"] =
770 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
771 Node::json["@odata.context"] =
772 "/redfish/v1/"
773 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
Ed Tanous4a0cb852018-10-15 07:55:04 -0700774 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700775 Node::json["Name"] = "Ethernet Network Interface Collection";
776 Node::json["Description"] =
777 "Collection of EthernetInterfaces for this Manager";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100778
Ed Tanous1abe55e2018-09-05 08:30:59 -0700779 entityPrivileges = {
780 {boost::beast::http::verb::get, {{"Login"}}},
781 {boost::beast::http::verb::head, {{"Login"}}},
782 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
783 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
784 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
785 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
786 }
787
788 private:
789 /**
790 * Functions triggers appropriate requests on DBus
791 */
792 void doGet(crow::Response &res, const crow::Request &req,
793 const std::vector<std::string> &params) override
794 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700795 res.jsonValue = Node::json;
796 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700797 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700798 getEthernetIfaceList(
799 [&res](const bool &success,
800 const std::vector<std::string> &iface_list) {
801 if (!success)
802 {
803 messages::internalError(res);
804 res.end();
805 return;
806 }
807
808 nlohmann::json &iface_array = res.jsonValue["Members"];
809 iface_array = nlohmann::json::array();
810 for (const std::string &iface_item : iface_list)
811 {
812 iface_array.push_back(
813 {{"@odata.id",
814 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
815 iface_item}});
816 }
817
818 res.jsonValue["Members@odata.count"] = iface_array.size();
819 res.jsonValue["@odata.id"] =
820 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700821 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700822 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700823 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100824};
825
826/**
827 * EthernetInterface derived class for delivering Ethernet Schema
828 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700829class EthernetInterface : public Node
830{
831 public:
832 /*
833 * Default Constructor
834 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700835 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700836 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700837 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700838 std::string())
839 {
840 Node::json["@odata.type"] =
841 "#EthernetInterface.v1_2_0.EthernetInterface";
842 Node::json["@odata.context"] =
843 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
844 Node::json["Name"] = "Manager Ethernet Interface";
845 Node::json["Description"] = "Management Network Interface";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100846
Ed Tanous1abe55e2018-09-05 08:30:59 -0700847 entityPrivileges = {
848 {boost::beast::http::verb::get, {{"Login"}}},
849 {boost::beast::http::verb::head, {{"Login"}}},
850 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
851 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
852 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
853 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200854 }
855
Ed Tanous1abe55e2018-09-05 08:30:59 -0700856 // TODO(kkowalsk) Find a suitable class/namespace for this
857 static void handleVlanPatch(const std::string &ifaceId,
858 const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700859 const EthernetInterfaceData &ethData,
860 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700861 {
862 if (!input.is_object())
863 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700864 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800865 "VLAN");
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200866 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700867 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200868
Ed Tanous4a0cb852018-10-15 07:55:04 -0700869 nlohmann::json::const_iterator vlanEnable = input.find("VLANEnable");
870 if (vlanEnable == input.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800872 messages::propertyMissing(asyncResp->res, "VLANEnable");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700873 return;
874 }
875 const bool *vlanEnableBool = vlanEnable->get_ptr<const bool *>();
876 if (vlanEnableBool == nullptr)
877 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700878 messages::propertyValueTypeError(asyncResp->res, vlanEnable->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800879 "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700880 return;
881 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200882
Ed Tanous4a0cb852018-10-15 07:55:04 -0700883 nlohmann::json::const_iterator vlanId = input.find("VLANId");
884 if (vlanId == input.end())
885 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800886 messages::propertyMissing(asyncResp->res, "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700887 return;
888 }
889 const uint64_t *vlanIdUint = vlanId->get_ptr<const uint64_t *>();
890 if (vlanIdUint == nullptr)
891 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700892 messages::propertyValueTypeError(asyncResp->res, vlanId->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800893 "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700894 return;
895 }
896
897 if (!ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700898 {
899 // This interface is not a VLAN. Cannot do anything with it
900 // TODO(kkowalsk) Change this message
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800901 messages::propertyNotWritable(asyncResp->res, "VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700902
Ed Tanous1abe55e2018-09-05 08:30:59 -0700903 return;
904 }
905
906 // VLAN is configured on the interface
Ed Tanous4a0cb852018-10-15 07:55:04 -0700907 if (*vlanEnableBool == true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700908 {
909 // Change VLAN Id
Ed Tanous4a0cb852018-10-15 07:55:04 -0700910 asyncResp->res.jsonValue["VLANId"] = *vlanIdUint;
911 auto callback = [asyncResp](const boost::system::error_code ec) {
912 if (ec)
913 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700914 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700915 }
916 else
917 {
918 asyncResp->res.jsonValue["VLANEnable"] = true;
919 }
920 };
921 crow::connections::systemBus->async_method_call(
922 std::move(callback), "xyz.openbmc_project.Network",
923 "/xyz/openbmc_project/network/" + ifaceId,
924 "org.freedesktop.DBus.Properties", "Set",
925 "xyz.openbmc_project.Network.VLAN", "Id",
926 sdbusplus::message::variant<uint32_t>(*vlanIdUint));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700927 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700928 else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700929 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700930 auto callback = [asyncResp](const boost::system::error_code ec) {
931 if (ec)
932 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700933 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700934 return;
935 }
936 asyncResp->res.jsonValue["VLANEnable"] = false;
937 };
938
939 crow::connections::systemBus->async_method_call(
940 std::move(callback), "xyz.openbmc_project.Network",
941 "/xyz/openbmc_project/network/" + ifaceId,
942 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700943 }
944 }
945
946 private:
947 void handleHostnamePatch(const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700948 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700949 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700950 const std::string *newHostname = input.get_ptr<const std::string *>();
951 if (newHostname == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700952 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700953 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800954 "HostName");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700955 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700956 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700957
958 // Change hostname
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800959 setHostName(*newHostname,
960 [asyncResp, newHostname{std::string(*newHostname)}](
961 const boost::system::error_code ec) {
962 if (ec)
963 {
964 messages::internalError(asyncResp->res);
965 }
966 else
967 {
968 asyncResp->res.jsonValue["HostName"] = newHostname;
969 }
970 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700971 }
972
Ed Tanous4a0cb852018-10-15 07:55:04 -0700973 void handleIPv4Patch(
974 const std::string &ifaceId, const nlohmann::json &input,
975 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
976 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700977 {
978 if (!input.is_array())
979 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700980 messages::propertyValueTypeError(asyncResp->res, input.dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800981 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700982 return;
983 }
984
985 // According to Redfish PATCH definition, size must be at least equal
Ed Tanous4a0cb852018-10-15 07:55:04 -0700986 if (input.size() < ipv4Data.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700987 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800988 messages::propertyValueFormatError(asyncResp->res, input.dump(),
989 "IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700990 return;
991 }
992
Ed Tanous4a0cb852018-10-15 07:55:04 -0700993 int entryIdx = 0;
994 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
995 ipv4Data.begin();
996 for (const nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700997 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700998 std::string pathString =
Jason M. Billsa08b46c2018-11-06 15:01:08 -0800999 "IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001000 // Check that entry is not of some unexpected type
Ed Tanous4a0cb852018-10-15 07:55:04 -07001001 if (!thisJson.is_object() && !thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001002 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001003 messages::propertyValueTypeError(asyncResp->res,
1004 thisJson.dump(),
1005 pathString + "/IPv4Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001006
1007 continue;
1008 }
1009
Ed Tanous4a0cb852018-10-15 07:55:04 -07001010 nlohmann::json::const_iterator addressFieldIt =
1011 thisJson.find("Address");
1012 const std::string *addressField = nullptr;
1013 if (addressFieldIt != thisJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001014 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001015 addressField = addressFieldIt->get_ptr<const std::string *>();
1016 if (addressField == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001017 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001018 messages::propertyValueFormatError(asyncResp->res,
1019 addressFieldIt->dump(),
1020 pathString + "/Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001021 continue;
1022 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001023 else
1024 {
1025 if (!ipv4VerifyIpAndGetBitcount(*addressField))
1026 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001027 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001028 asyncResp->res, *addressField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001029 pathString + "/Address");
1030 continue;
1031 }
1032 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001034
1035 boost::optional<uint8_t> prefixLength;
1036 const std::string *subnetField = nullptr;
1037 nlohmann::json::const_iterator subnetFieldIt =
1038 thisJson.find("SubnetMask");
1039 if (subnetFieldIt != thisJson.end())
1040 {
1041 subnetField = subnetFieldIt->get_ptr<const std::string *>();
1042 if (subnetField == nullptr)
1043 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001044 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001045 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001046 pathString + "/SubnetMask");
1047 continue;
1048 }
1049 else
1050 {
1051 prefixLength = 0;
1052 if (!ipv4VerifyIpAndGetBitcount(*subnetField,
1053 &*prefixLength))
1054 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001055 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001056 asyncResp->res, *subnetField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001057 pathString + "/SubnetMask");
1058 continue;
1059 }
1060 }
1061 }
1062
1063 std::string addressOriginInDBusFormat;
1064 const std::string *addressOriginField = nullptr;
1065 nlohmann::json::const_iterator addressOriginFieldIt =
1066 thisJson.find("AddressOrigin");
1067 if (addressOriginFieldIt != thisJson.end())
1068 {
1069 const std::string *addressOriginField =
1070 addressOriginFieldIt->get_ptr<const std::string *>();
1071 if (addressOriginField == nullptr)
1072 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001073 messages::propertyValueFormatError(
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001074 asyncResp->res, *addressOriginField,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001075 pathString + "/AddressOrigin");
1076 continue;
1077 }
1078 else
1079 {
1080 // Get Address origin in proper format
1081 addressOriginInDBusFormat =
1082 translateAddressOriginRedfishToDbus(
1083 *addressOriginField);
1084 if (addressOriginInDBusFormat.empty())
1085 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001086 messages::propertyValueNotInList(
1087 asyncResp->res, *addressOriginField,
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001088 pathString + "/AddressOrigin");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001089 continue;
1090 }
1091 }
1092 }
1093
1094 nlohmann::json::const_iterator gatewayFieldIt =
1095 thisJson.find("Gateway");
1096 const std::string *gatewayField = nullptr;
1097 if (gatewayFieldIt != thisJson.end())
1098 {
1099 const std::string *gatewayField =
1100 gatewayFieldIt->get_ptr<const std::string *>();
1101 if (gatewayField == nullptr ||
1102 !ipv4VerifyIpAndGetBitcount(*gatewayField))
1103 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001104 messages::propertyValueFormatError(
1105 asyncResp->res, *gatewayField, pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001106 continue;
1107 }
1108 }
1109
1110 // if a vlan already exists, modify the existing
1111 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001112 {
1113 // Existing object that should be modified/deleted/remain
1114 // unchanged
Ed Tanous4a0cb852018-10-15 07:55:04 -07001115 if (thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001116 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001117 auto callback = [entryIdx{std::to_string(entryIdx)},
1118 asyncResp](
1119 const boost::system::error_code ec) {
1120 if (ec)
1121 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001122 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001123 return;
1124 }
1125 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1126 nullptr;
1127 };
1128 crow::connections::systemBus->async_method_call(
1129 std::move(callback), "xyz.openbmc_project.Network",
1130 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1131 thisData->id,
1132 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001133 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001134 else if (thisJson.is_object())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001135 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001136 // Apply changes
Ed Tanous4a0cb852018-10-15 07:55:04 -07001137 if (addressField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001138 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001139 auto callback =
1140 [asyncResp, entryIdx,
1141 addressField{std::string(*addressField)}](
1142 const boost::system::error_code ec) {
1143 if (ec)
1144 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001145 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001146 return;
1147 }
1148 asyncResp->res
1149 .jsonValue["IPv4Addresses"][std::to_string(
1150 entryIdx)]["Address"] = addressField;
1151 };
1152
1153 crow::connections::systemBus->async_method_call(
1154 std::move(callback), "xyz.openbmc_project.Network",
1155 "/xyz/openbmc_project/network/" + ifaceId +
1156 "/ipv4/" + thisData->id,
1157 "org.freedesktop.DBus.Properties", "Set",
1158 "xyz.openbmc_project.Network.IP", "Address",
1159 sdbusplus::message::variant<std::string>(
1160 *addressField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001161 }
1162
Ed Tanous4a0cb852018-10-15 07:55:04 -07001163 if (prefixLength && subnetField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001164 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001165 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1166 thisData->id, *subnetField,
1167 *prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001168 }
1169
Ed Tanous4a0cb852018-10-15 07:55:04 -07001170 if (!addressOriginInDBusFormat.empty() &&
1171 addressOriginField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001172 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001173 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1174 *addressOriginField,
1175 addressOriginInDBusFormat, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001176 }
1177
Ed Tanous4a0cb852018-10-15 07:55:04 -07001178 if (gatewayField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001179 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001180 auto callback =
1181 [asyncResp, entryIdx,
1182 gatewayField{std::string(*gatewayField)}](
1183 const boost::system::error_code ec) {
1184 if (ec)
1185 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001186 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001187 return;
1188 }
1189 asyncResp->res
1190 .jsonValue["IPv4Addresses"][std::to_string(
1191 entryIdx)]["Gateway"] =
1192 std::move(gatewayField);
1193 };
1194
1195 crow::connections::systemBus->async_method_call(
1196 std::move(callback), "xyz.openbmc_project.Network",
1197 "/xyz/openbmc_project/network/" + ifaceId +
1198 "/ipv4/" + thisData->id,
1199 "org.freedesktop.DBus.Properties", "Set",
1200 "xyz.openbmc_project.Network.IP", "Gateway",
1201 sdbusplus::message::variant<std::string>(
1202 *gatewayField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001203 }
1204 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001205 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001206 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001207 else
1208 {
1209 // Create IPv4 with provided data
1210 if (gatewayField == nullptr)
1211 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001212 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001213 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001214 continue;
1215 }
1216
1217 if (addressField == nullptr)
1218 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001219 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001220 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001221 continue;
1222 }
1223
1224 if (!prefixLength)
1225 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001226 messages::propertyMissing(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001227 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001228 continue;
1229 }
1230
1231 createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField,
1232 *addressField, asyncResp);
1233 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
1234 }
1235 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001236 }
1237 }
1238
Ed Tanous4a0cb852018-10-15 07:55:04 -07001239 nlohmann::json parseInterfaceData(
1240 const std::string &iface_id, const EthernetInterfaceData &ethData,
1241 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001242 {
1243 // Copy JSON object to avoid race condition
Ed Tanous4a0cb852018-10-15 07:55:04 -07001244 nlohmann::json json_response(Node::json);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001245
Ed Tanous4a0cb852018-10-15 07:55:04 -07001246 json_response["Id"] = iface_id;
1247 json_response["@odata.id"] =
1248 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001249
Ed Tanous4a0cb852018-10-15 07:55:04 -07001250 json_response["SpeedMbps"] = ethData.speed;
1251 json_response["MACAddress"] = ethData.mac_address;
1252 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001253 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001254 json_response["HostName"] = ethData.hostname;
1255 }
1256
1257 nlohmann::json &vlanObj = json_response["VLAN"];
1258 if (ethData.vlan_id)
1259 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001260 vlanObj["VLANEnable"] = true;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001261 vlanObj["VLANId"] = *ethData.vlan_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001262 }
1263 else
1264 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001265 vlanObj["VLANEnable"] = false;
1266 vlanObj["VLANId"] = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001267 }
1268
Ed Tanous4a0cb852018-10-15 07:55:04 -07001269 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001270 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001271 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1272 ipv4_array = nlohmann::json::array();
1273 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001274 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001275 if (!ipv4_config.address.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001277 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1278 {"SubnetMask", ipv4_config.netmask},
1279 {"Address", ipv4_config.address}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001280
Ed Tanous4a0cb852018-10-15 07:55:04 -07001281 if (!ipv4_config.gateway.empty())
1282 {
1283 ipv4_array.back()["Gateway"] = ipv4_config.gateway;
1284 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001285 }
1286 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001287 }
1288
Ed Tanous4a0cb852018-10-15 07:55:04 -07001289 return json_response;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 }
1291
1292 /**
1293 * Functions triggers appropriate requests on DBus
1294 */
1295 void doGet(crow::Response &res, const crow::Request &req,
1296 const std::vector<std::string> &params) override
1297 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001298 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001299 if (params.size() != 1)
1300 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001301 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001302 return;
1303 }
1304
Ed Tanous4a0cb852018-10-15 07:55:04 -07001305 getEthernetIfaceData(
1306 params[0],
1307 [this, asyncResp, iface_id{std::string(params[0])}](
1308 const bool &success, const EthernetInterfaceData &ethData,
1309 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1310 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001311 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001312 // TODO(Pawel)consider distinguish between non existing
1313 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001314 messages::resourceNotFound(asyncResp->res,
1315 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001316 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001317 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001318 asyncResp->res.jsonValue =
1319 parseInterfaceData(iface_id, ethData, 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 Tanous4a0cb852018-10-15 07:55:04 -07001358 asyncResp->res.jsonValue =
1359 parseInterfaceData(iface_id, ethData, 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>
1417 // TODO(Pawel) Remove line from below, where we assume that there is only
Ed Tanous4a0cb852018-10-15 07:55:04 -07001418 // one manager called openbmc. This shall be generic, but requires to
1419 // update GetSubroutes method
Ed Tanous1abe55e2018-09-05 08:30:59 -07001420 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001421 Node(app,
1422 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>>/VLANs/"
1423 "<str>",
1424 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001425 {
1426 Node::json["@odata.type"] =
1427 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1428 Node::json["@odata.context"] =
1429 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1430 Node::json["Name"] = "VLAN Network Interface";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001431
Ed Tanous1abe55e2018-09-05 08:30:59 -07001432 entityPrivileges = {
1433 {boost::beast::http::verb::get, {{"Login"}}},
1434 {boost::beast::http::verb::head, {{"Login"}}},
1435 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1436 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1437 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1438 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001439 }
1440
Ed Tanous1abe55e2018-09-05 08:30:59 -07001441 private:
Ed Tanous4a0cb852018-10-15 07:55:04 -07001442 nlohmann::json parseInterfaceData(
1443 const std::string &parent_iface_id, const std::string &iface_id,
1444 const EthernetInterfaceData &ethData,
1445 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001446 {
1447 // Copy JSON object to avoid race condition
Ed Tanous4a0cb852018-10-15 07:55:04 -07001448 nlohmann::json json_response(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001449
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001451 json_response["Id"] = iface_id;
1452 json_response["@odata.id"] =
1453 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1454 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001455
Ed Tanous4a0cb852018-10-15 07:55:04 -07001456 json_response["VLANEnable"] = true;
1457 if (ethData.vlan_id)
1458 {
1459 json_response["VLANId"] = *ethData.vlan_id;
1460 }
1461 return json_response;
Ed Tanousa434f2b2018-07-27 13:04:22 -07001462 }
1463
Ed Tanous1abe55e2018-09-05 08:30:59 -07001464 bool verifyNames(crow::Response &res, const std::string &parent,
1465 const std::string &iface)
1466 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001467 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001468 if (!boost::starts_with(iface, parent + "_"))
1469 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001470 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1471 iface);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001472 return false;
1473 }
1474 else
1475 {
1476 return true;
1477 }
1478 }
1479
1480 /**
1481 * Functions triggers appropriate requests on DBus
1482 */
1483 void doGet(crow::Response &res, const crow::Request &req,
1484 const std::vector<std::string> &params) override
1485 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001486 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1487 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001488 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1489 // Check if there is required param, truly entering this shall be
1490 // impossible.
1491 if (params.size() != 2)
1492 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001493 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001494 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001495 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001496 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001497
Ed Tanous4a0cb852018-10-15 07:55:04 -07001498 const std::string &parent_iface_id = params[0];
1499 const std::string &iface_id = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001500
Ed Tanous4a0cb852018-10-15 07:55:04 -07001501 if (!verifyNames(res, parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001502 {
1503 return;
1504 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001505
Ed Tanous1abe55e2018-09-05 08:30:59 -07001506 // Get single eth interface data, and call the below callback for JSON
1507 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001508 getEthernetIfaceData(
1509 iface_id,
1510 [this, asyncResp, parent_iface_id, iface_id](
1511 const bool &success, const EthernetInterfaceData &ethData,
1512 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1513 if (success && ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001514 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001515 asyncResp->res.jsonValue = parseInterfaceData(
1516 parent_iface_id, iface_id, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001517 }
1518 else
1519 {
1520 // ... otherwise return error
1521 // TODO(Pawel)consider distinguish between non existing
1522 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001523 messages::resourceNotFound(
1524 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001525 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001526 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001527 }
1528
Ed Tanous1abe55e2018-09-05 08:30:59 -07001529 void doPatch(crow::Response &res, const crow::Request &req,
1530 const std::vector<std::string> &params) override
1531 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001532 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001533 if (params.size() != 2)
1534 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001535 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001536 return;
1537 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001538
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539 const std::string &parentIfaceId = params[0];
1540 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001541
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542 if (!verifyNames(res, parentIfaceId, ifaceId))
1543 {
1544 return;
1545 }
1546
1547 nlohmann::json patchReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001548 if (!json_util::processJsonFromRequest(res, req, patchReq))
1549 {
1550 return;
1551 }
1552
1553 // Get single eth interface data, and call the below callback for JSON
1554 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001555 getEthernetIfaceData(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001556 ifaceId,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001557 [this, asyncResp, parentIfaceId, ifaceId,
1558 patchReq{std::move(patchReq)}](
1559 const bool &success, const EthernetInterfaceData &ethData,
1560 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001561 if (!success)
1562 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001563 // TODO(Pawel)consider distinguish between non existing
1564 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001565 messages::resourceNotFound(
1566 asyncResp->res, "VLAN Network Interface", ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001567
1568 return;
1569 }
1570
Ed Tanous4a0cb852018-10-15 07:55:04 -07001571 asyncResp->res.jsonValue = parseInterfaceData(
1572 parentIfaceId, ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001573
Ed Tanous4a0cb852018-10-15 07:55:04 -07001574 for (auto propertyIt : patchReq.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001575 {
1576 if (propertyIt.key() != "VLANEnable" &&
1577 propertyIt.key() != "VLANId")
1578 {
1579 auto fieldInJsonIt =
Ed Tanous4a0cb852018-10-15 07:55:04 -07001580 asyncResp->res.jsonValue.find(propertyIt.key());
1581 if (fieldInJsonIt == asyncResp->res.jsonValue.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001582 {
1583 // Field not in scope of defined fields
Jason M. Billsf12894f2018-10-09 12:45:45 -07001584 messages::propertyUnknown(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001585 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001587 else
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588 {
1589 // User attempted to modify non-writable field
Jason M. Billsf12894f2018-10-09 12:45:45 -07001590 messages::propertyNotWritable(asyncResp->res,
Jason M. Billsf12894f2018-10-09 12:45:45 -07001591 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001592 }
1593 }
1594 }
1595
Ed Tanous4a0cb852018-10-15 07:55:04 -07001596 EthernetInterface::handleVlanPatch(ifaceId, patchReq, ethData,
1597 asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598 });
1599 }
1600
1601 void doDelete(crow::Response &res, const crow::Request &req,
1602 const std::vector<std::string> &params) override
1603 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001604 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001605 if (params.size() != 2)
1606 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001607 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001608 return;
1609 }
1610
1611 const std::string &parentIfaceId = params[0];
1612 const std::string &ifaceId = params[1];
1613
Ed Tanous4a0cb852018-10-15 07:55:04 -07001614 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001615 {
1616 return;
1617 }
1618
1619 // Get single eth interface data, and call the below callback for JSON
1620 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001621 getEthernetIfaceData(
1622 ifaceId,
1623 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1624 ifaceId{std::string(ifaceId)}](
1625 const bool &success, const EthernetInterfaceData &ethData,
1626 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1627 if (success && ethData.vlan_id)
1628 {
1629 asyncResp->res.jsonValue = parseInterfaceData(
1630 parentIfaceId, ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001631
Jason M. Billsf12894f2018-10-09 12:45:45 -07001632 auto callback =
1633 [asyncResp](const boost::system::error_code ec) {
1634 if (ec)
1635 {
1636 messages::internalError(asyncResp->res);
1637 }
1638 };
1639 crow::connections::systemBus->async_method_call(
1640 std::move(callback), "xyz.openbmc_project.Network",
1641 std::string("/xyz/openbmc_project/network/") + ifaceId,
1642 "xyz.openbmc_project.Object.Delete", "Delete");
1643 }
1644 else
1645 {
1646 // ... otherwise return error
1647 // TODO(Pawel)consider distinguish between non existing
1648 // object, and other errors
1649 messages::resourceNotFound(
1650 asyncResp->res, "VLAN Network Interface", ifaceId);
1651 }
1652 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001653 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001654};
1655
1656/**
1657 * VlanNetworkInterfaceCollection derived class for delivering
1658 * VLANNetworkInterface Collection Schema
1659 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001660class VlanNetworkInterfaceCollection : public Node
1661{
1662 public:
1663 template <typename CrowApp>
1664 // TODO(Pawel) Remove line from below, where we assume that there is only
Ed Tanous4a0cb852018-10-15 07:55:04 -07001665 // one manager called openbmc. This shall be generic, but requires to
1666 // update GetSubroutes method
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 {
1671 Node::json["@odata.type"] =
1672 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1673 Node::json["@odata.context"] =
1674 "/redfish/v1/$metadata"
1675 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1676 Node::json["Name"] = "VLAN Network Interface Collection";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001677
Ed Tanous1abe55e2018-09-05 08:30:59 -07001678 entityPrivileges = {
1679 {boost::beast::http::verb::get, {{"Login"}}},
1680 {boost::beast::http::verb::head, {{"Login"}}},
1681 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1682 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1683 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1684 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001685 }
1686
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687 private:
1688 /**
1689 * Functions triggers appropriate requests on DBus
1690 */
1691 void doGet(crow::Response &res, const crow::Request &req,
1692 const std::vector<std::string> &params) override
1693 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001694 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001695 if (params.size() != 1)
1696 {
1697 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001698 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001699 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001700 }
1701
Ed Tanous4a0cb852018-10-15 07:55:04 -07001702 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001703
Ed Tanous4a0cb852018-10-15 07:55:04 -07001704 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001706 getEthernetIfaceList(
1707 [this, asyncResp,
1708 rootInterfaceName{std::string(rootInterfaceName)}](
1709 const bool &success,
1710 const std::vector<std::string> &iface_list) {
1711 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001712 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001713 messages::internalError(asyncResp->res);
1714 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001715 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001716 asyncResp->res.jsonValue = Node::json;
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
1731 if (iface_array.empty())
1732 {
1733 messages::resourceNotFound(
1734 asyncResp->res, "EthernetInterface", rootInterfaceName);
1735 return;
1736 }
1737 asyncResp->res.jsonValue["Members@odata.count"] =
1738 iface_array.size();
1739 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1740 asyncResp->res.jsonValue["@odata.id"] =
1741 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1742 rootInterfaceName + "/VLANs";
1743 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001744 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001745
Ed Tanous1abe55e2018-09-05 08:30:59 -07001746 void doPost(crow::Response &res, const crow::Request &req,
1747 const std::vector<std::string> &params) override
1748 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001749 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001750 if (params.size() != 1)
1751 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001752 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001753 return;
1754 }
1755
1756 nlohmann::json postReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001757 if (!json_util::processJsonFromRequest(res, req, postReq))
1758 {
1759 return;
1760 }
1761
Ed Tanous4a0cb852018-10-15 07:55:04 -07001762 auto vlanIdJson = json.find("VLANId");
1763 if (vlanIdJson == json.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001764 {
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001765 messages::propertyMissing(asyncResp->res, "VLANId");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001766 return;
1767 }
1768
Ed Tanous4a0cb852018-10-15 07:55:04 -07001769 const uint64_t *vlanId = vlanIdJson->get_ptr<const uint64_t *>();
1770 if (vlanId == nullptr)
1771 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001772 messages::propertyValueTypeError(asyncResp->res, vlanIdJson->dump(),
Jason M. Billsa08b46c2018-11-06 15:01:08 -08001773 "VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001774 return;
1775 }
1776 const std::string &rootInterfaceName = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001777
Ed Tanous4a0cb852018-10-15 07:55:04 -07001778 auto callback = [asyncResp](const boost::system::error_code ec) {
1779 if (ec)
1780 {
1781 // TODO(ed) make more consistent error messages based on
1782 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001783 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001784 return;
1785 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001786 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001787 };
1788 crow::connections::systemBus->async_method_call(
1789 std::move(callback), "xyz.openbmc_project.Network",
1790 "/xyz/openbmc_project/network",
1791 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
1792 rootInterfaceName, static_cast<uint32_t>(*vlanId));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001794};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001795} // namespace redfish