blob: 7da7d0a112d297572a3c84d91dfede9b1bb67797 [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. Billsf12894f2018-10-09 12:45:45 -0700483 messages::internalError(asyncResp->res, "/IPv4Addresses/" +
484 std::to_string(ipIdx) +
485 "/" + name);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700486 }
487 else
488 {
489 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx][name] = newValue;
490 }
491 };
492
493 crow::connections::systemBus->async_method_call(
494 std::move(callback), "xyz.openbmc_project.Network",
495 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
496 "org.freedesktop.DBus.Properties", "Set",
497 "xyz.openbmc_project.Network.IP", name,
498 sdbusplus::message::variant<std::string>(newValue));
499}
500
501/**
502 * @brief Changes IPv4 address origin property
503 *
504 * @param[in] ifaceId Id of interface whose IP should be modified
505 * @param[in] ipIdx Index of IP in input array that should be
506 * modified
507 * @param[in] ipHash DBus Hash id of modified IP
508 * @param[in] newValue New value in Redfish format
509 * @param[in] newValueDbus New value in D-Bus format
510 * @param[io] asyncResp Response object that will be returned to client
511 *
512 * @return true if give IP is valid and has been sent do D-Bus, false
513 * otherwise
514 */
515inline void changeIPv4Origin(const std::string &ifaceId, int ipIdx,
516 const std::string &ipHash,
517 const std::string &newValue,
518 const std::string &newValueDbus,
519 const std::shared_ptr<AsyncResp> asyncResp)
520{
521 auto callback = [asyncResp, ipIdx, newValue{std::move(newValue)}](
522 const boost::system::error_code ec) {
523 if (ec)
524 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700525 messages::internalError(asyncResp->res, "/IPv4Addresses/" +
526 std::to_string(ipIdx) +
527 "/AddressOrigin");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700528 }
529 else
530 {
531 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["AddressOrigin"] =
532 newValue;
533 }
534 };
535
536 crow::connections::systemBus->async_method_call(
537 std::move(callback), "xyz.openbmc_project.Network",
538 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
539 "org.freedesktop.DBus.Properties", "Set",
540 "xyz.openbmc_project.Network.IP", "Origin",
541 sdbusplus::message::variant<std::string>(newValueDbus));
542}
543
544/**
545 * @brief Modifies SubnetMask for given IP
546 *
547 * @param[in] ifaceId Id of interface whose IP should be modified
548 * @param[in] ipIdx Index of IP in input array that should be
549 * modified
550 * @param[in] ipHash DBus Hash id of modified IP
551 * @param[in] newValueStr Mask in dot notation as string
552 * @param[in] newValue Mask as PrefixLength in bitcount
553 * @param[io] asyncResp Response object that will be returned to client
554 *
555 * @return None
556 */
557inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
558 const std::string &ipHash,
559 const std::string &newValueStr,
560 uint8_t &newValue,
561 std::shared_ptr<AsyncResp> asyncResp)
562{
563 auto callback = [asyncResp, ipIdx, newValueStr{std::move(newValueStr)}](
564 const boost::system::error_code ec) {
565 if (ec)
566 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700567 messages::internalError(asyncResp->res, "/IPv4Addresses/" +
568 std::to_string(ipIdx) +
569 "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700570 }
571 else
572 {
573 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx]["SubnetMask"] =
574 newValueStr;
575 }
576 };
577
578 crow::connections::systemBus->async_method_call(
579 std::move(callback), "xyz.openbmc_project.Network",
580 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
581 "org.freedesktop.DBus.Properties", "Set",
582 "xyz.openbmc_project.Network.IP", "PrefixLength",
583 sdbusplus::message::variant<uint8_t>(newValue));
584}
585
586/**
587 * @brief Sets given HostName of the machine through D-Bus
588 *
589 * @param[in] newHostname New name that HostName will be changed to
590 * @param[in] callback Function that will be called after the operation
591 *
592 * @return None.
593 */
594template <typename CallbackFunc>
595void setHostName(const std::string &newHostname, CallbackFunc &&callback)
596{
597 crow::connections::systemBus->async_method_call(
598 callback, "xyz.openbmc_project.Network",
599 "/xyz/openbmc_project/network/config",
600 "org.freedesktop.DBus.Properties", "Set",
601 "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
602 sdbusplus::message::variant<std::string>(newHostname));
603}
604
605/**
606 * @brief Deletes given IPv4
607 *
608 * @param[in] ifaceId Id of interface whose IP should be deleted
609 * @param[in] ipIdx Index of IP in input array that should be deleted
610 * @param[in] ipHash DBus Hash id of IP that should be deleted
611 * @param[io] asyncResp Response object that will be returned to client
612 *
613 * @return None
614 */
615inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
616 unsigned int ipIdx,
617 const std::shared_ptr<AsyncResp> asyncResp)
618{
619 crow::connections::systemBus->async_method_call(
620 [ipIdx, asyncResp](const boost::system::error_code ec) {
Ed Tanous1abe55e2018-09-05 08:30:59 -0700621 if (ec)
622 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700623 messages::internalError(asyncResp->res,
624 "/IPv4Addresses/" +
625 std::to_string(ipIdx) + "/");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100626 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700627 else
628 {
629 asyncResp->res.jsonValue["IPv4Addresses"][ipIdx] = nullptr;
630 }
631 },
632 "xyz.openbmc_project.Network",
633 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
634 "xyz.openbmc_project.Object.Delete", "Delete");
635}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700636
Ed Tanous4a0cb852018-10-15 07:55:04 -0700637/**
638 * @brief Creates IPv4 with given data
639 *
640 * @param[in] ifaceId Id of interface whose IP should be deleted
641 * @param[in] ipIdx Index of IP in input array that should be deleted
642 * @param[in] ipHash DBus Hash id of IP that should be deleted
643 * @param[io] asyncResp Response object that will be returned to client
644 *
645 * @return None
646 */
647inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
648 uint8_t subnetMask, const std::string &gateway,
649 const std::string &address,
650 std::shared_ptr<AsyncResp> asyncResp)
651{
652 auto createIpHandler = [ipIdx,
653 asyncResp](const boost::system::error_code ec) {
654 if (ec)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700655 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700656 messages::internalError(asyncResp->res, "/IPv4Addresses/" +
657 std::to_string(ipIdx) +
658 "/");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700659 }
Ed Tanous1abe55e2018-09-05 08:30:59 -0700660 };
661
Ed Tanous4a0cb852018-10-15 07:55:04 -0700662 crow::connections::systemBus->async_method_call(
663 std::move(createIpHandler), "xyz.openbmc_project.Network",
664 "/xyz/openbmc_project/network/" + ifaceId,
665 "xyz.openbmc_project.Network.IP.Create", "IP",
666 "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
667 gateway);
668}
Ed Tanous1abe55e2018-09-05 08:30:59 -0700669
Ed Tanous4a0cb852018-10-15 07:55:04 -0700670/**
671 * Function that retrieves all properties for given Ethernet Interface
672 * Object
673 * from EntityManager Network Manager
674 * @param ethiface_id a eth interface id to query on DBus
675 * @param callback a function that shall be called to convert Dbus output
676 * into JSON
677 */
678template <typename CallbackFunc>
679void getEthernetIfaceData(const std::string &ethiface_id,
680 CallbackFunc &&callback)
681{
682 crow::connections::systemBus->async_method_call(
683 [ethiface_id{std::string{ethiface_id}}, callback{std::move(callback)}](
684 const boost::system::error_code error_code,
685 const GetManagedObjects &resp) {
686 EthernetInterfaceData ethData{};
687 boost::container::flat_set<IPv4AddressData> ipv4Data;
688
689 if (error_code)
690 {
691 callback(false, ethData, ipv4Data);
692 return;
693 }
694
695 extractEthernetInterfaceData(ethiface_id, resp, ethData);
696 extractIPData(ethiface_id, resp, ipv4Data);
697
698 // Fix global GW
699 for (IPv4AddressData &ipv4 : ipv4Data)
700 {
701 if ((ipv4.linktype == LinkType::Global) &&
702 (ipv4.gateway == "0.0.0.0"))
Ed Tanous1abe55e2018-09-05 08:30:59 -0700703 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700704 ipv4.gateway = ethData.default_gateway;
705 }
706 }
707
708 // Finally make a callback with usefull data
709 callback(true, ethData, ipv4Data);
710 },
711 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
712 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
713};
714
715/**
716 * Function that retrieves all Ethernet Interfaces available through Network
717 * Manager
718 * @param callback a function that shall be called to convert Dbus output
719 * into JSON.
720 */
721template <typename CallbackFunc>
722void getEthernetIfaceList(CallbackFunc &&callback)
723{
724 crow::connections::systemBus->async_method_call(
725 [callback{std::move(callback)}](
726 const boost::system::error_code error_code,
727 GetManagedObjects &resp) {
728 // Callback requires vector<string> to retrieve all available
729 // ethernet interfaces
730 std::vector<std::string> iface_list;
731 iface_list.reserve(resp.size());
732 if (error_code)
733 {
734 callback(false, iface_list);
735 return;
736 }
737
738 // Iterate over all retrieved ObjectPaths.
739 for (const auto &objpath : resp)
740 {
741 // And all interfaces available for certain ObjectPath.
742 for (const auto &interface : objpath.second)
743 {
744 // If interface is
745 // xyz.openbmc_project.Network.EthernetInterface, this is
746 // what we're looking for.
747 if (interface.first ==
748 "xyz.openbmc_project.Network.EthernetInterface")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700749 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700750 // Cut out everyting until last "/", ...
751 const std::string &iface_id = objpath.first.str;
752 std::size_t last_pos = iface_id.rfind("/");
753 if (last_pos != std::string::npos)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700754 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700755 // and put it into output vector.
756 iface_list.emplace_back(
757 iface_id.substr(last_pos + 1));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700758 }
759 }
760 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700761 }
762 // Finally make a callback with useful data
763 callback(true, iface_list);
764 },
765 "xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
766 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100767};
768
769/**
770 * EthernetCollection derived class for delivering Ethernet Collection Schema
771 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700772class EthernetCollection : public Node
773{
774 public:
Ed Tanous4a0cb852018-10-15 07:55:04 -0700775 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700776 EthernetCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700777 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/")
Ed Tanous1abe55e2018-09-05 08:30:59 -0700778 {
779 Node::json["@odata.type"] =
780 "#EthernetInterfaceCollection.EthernetInterfaceCollection";
781 Node::json["@odata.context"] =
782 "/redfish/v1/"
783 "$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection";
Ed Tanous4a0cb852018-10-15 07:55:04 -0700784 Node::json["@odata.id"] = "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700785 Node::json["Name"] = "Ethernet Network Interface Collection";
786 Node::json["Description"] =
787 "Collection of EthernetInterfaces for this Manager";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100788
Ed Tanous1abe55e2018-09-05 08:30:59 -0700789 entityPrivileges = {
790 {boost::beast::http::verb::get, {{"Login"}}},
791 {boost::beast::http::verb::head, {{"Login"}}},
792 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
793 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
794 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
795 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
796 }
797
798 private:
799 /**
800 * Functions triggers appropriate requests on DBus
801 */
802 void doGet(crow::Response &res, const crow::Request &req,
803 const std::vector<std::string> &params) override
804 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700805 res.jsonValue = Node::json;
806 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -0700807 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -0700808 getEthernetIfaceList(
809 [&res](const bool &success,
810 const std::vector<std::string> &iface_list) {
811 if (!success)
812 {
813 messages::internalError(res);
814 res.end();
815 return;
816 }
817
818 nlohmann::json &iface_array = res.jsonValue["Members"];
819 iface_array = nlohmann::json::array();
820 for (const std::string &iface_item : iface_list)
821 {
822 iface_array.push_back(
823 {{"@odata.id",
824 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
825 iface_item}});
826 }
827
828 res.jsonValue["Members@odata.count"] = iface_array.size();
829 res.jsonValue["@odata.id"] =
830 "/redfish/v1/Managers/bmc/EthernetInterfaces";
Ed Tanous1abe55e2018-09-05 08:30:59 -0700831 res.end();
Jason M. Billsf12894f2018-10-09 12:45:45 -0700832 });
Ed Tanous4a0cb852018-10-15 07:55:04 -0700833 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100834};
835
836/**
837 * EthernetInterface derived class for delivering Ethernet Schema
838 */
Ed Tanous1abe55e2018-09-05 08:30:59 -0700839class EthernetInterface : public Node
840{
841 public:
842 /*
843 * Default Constructor
844 */
Ed Tanous4a0cb852018-10-15 07:55:04 -0700845 template <typename CrowApp>
Ed Tanous1abe55e2018-09-05 08:30:59 -0700846 EthernetInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -0700847 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/",
Ed Tanous1abe55e2018-09-05 08:30:59 -0700848 std::string())
849 {
850 Node::json["@odata.type"] =
851 "#EthernetInterface.v1_2_0.EthernetInterface";
852 Node::json["@odata.context"] =
853 "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
854 Node::json["Name"] = "Manager Ethernet Interface";
855 Node::json["Description"] = "Management Network Interface";
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +0100856
Ed Tanous1abe55e2018-09-05 08:30:59 -0700857 entityPrivileges = {
858 {boost::beast::http::verb::get, {{"Login"}}},
859 {boost::beast::http::verb::head, {{"Login"}}},
860 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
861 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
862 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
863 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200864 }
865
Ed Tanous1abe55e2018-09-05 08:30:59 -0700866 // TODO(kkowalsk) Find a suitable class/namespace for this
867 static void handleVlanPatch(const std::string &ifaceId,
868 const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700869 const EthernetInterfaceData &ethData,
870 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700871 {
872 if (!input.is_object())
873 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700874 messages::propertyValueTypeError(asyncResp->res, input.dump(),
875 "VLAN", "/");
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200876 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700877 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200878
Ed Tanous4a0cb852018-10-15 07:55:04 -0700879 nlohmann::json::const_iterator vlanEnable = input.find("VLANEnable");
880 if (vlanEnable == input.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700881 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700882 messages::propertyMissing(asyncResp->res, "VLANEnable",
883 "/VLANEnable");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700884 return;
885 }
886 const bool *vlanEnableBool = vlanEnable->get_ptr<const bool *>();
887 if (vlanEnableBool == nullptr)
888 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700889 messages::propertyValueTypeError(asyncResp->res, vlanEnable->dump(),
890 "VLANEnable", "/VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700891 return;
892 }
Kowalski, Kamil588c3f02018-04-03 14:55:27 +0200893
Ed Tanous4a0cb852018-10-15 07:55:04 -0700894 nlohmann::json::const_iterator vlanId = input.find("VLANId");
895 if (vlanId == input.end())
896 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700897 messages::propertyMissing(asyncResp->res, "VLANId", "/VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700898 return;
899 }
900 const uint64_t *vlanIdUint = vlanId->get_ptr<const uint64_t *>();
901 if (vlanIdUint == nullptr)
902 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700903 messages::propertyValueTypeError(asyncResp->res, vlanId->dump(),
904 "VLANId", "/VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700905 return;
906 }
907
908 if (!ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700909 {
910 // This interface is not a VLAN. Cannot do anything with it
911 // TODO(kkowalsk) Change this message
Jason M. Billsf12894f2018-10-09 12:45:45 -0700912 messages::propertyNotWritable(asyncResp->res, "VLANEnable",
913 "/VLANEnable");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700914
Ed Tanous1abe55e2018-09-05 08:30:59 -0700915 return;
916 }
917
918 // VLAN is configured on the interface
Ed Tanous4a0cb852018-10-15 07:55:04 -0700919 if (*vlanEnableBool == true)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700920 {
921 // Change VLAN Id
Ed Tanous4a0cb852018-10-15 07:55:04 -0700922 asyncResp->res.jsonValue["VLANId"] = *vlanIdUint;
923 auto callback = [asyncResp](const boost::system::error_code ec) {
924 if (ec)
925 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700926 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700927 }
928 else
929 {
930 asyncResp->res.jsonValue["VLANEnable"] = true;
931 }
932 };
933 crow::connections::systemBus->async_method_call(
934 std::move(callback), "xyz.openbmc_project.Network",
935 "/xyz/openbmc_project/network/" + ifaceId,
936 "org.freedesktop.DBus.Properties", "Set",
937 "xyz.openbmc_project.Network.VLAN", "Id",
938 sdbusplus::message::variant<uint32_t>(*vlanIdUint));
Ed Tanous1abe55e2018-09-05 08:30:59 -0700939 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700940 else
Ed Tanous1abe55e2018-09-05 08:30:59 -0700941 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700942 auto callback = [asyncResp](const boost::system::error_code ec) {
943 if (ec)
944 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700945 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -0700946 return;
947 }
948 asyncResp->res.jsonValue["VLANEnable"] = false;
949 };
950
951 crow::connections::systemBus->async_method_call(
952 std::move(callback), "xyz.openbmc_project.Network",
953 "/xyz/openbmc_project/network/" + ifaceId,
954 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700955 }
956 }
957
958 private:
959 void handleHostnamePatch(const nlohmann::json &input,
Ed Tanous4a0cb852018-10-15 07:55:04 -0700960 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700961 {
Ed Tanous4a0cb852018-10-15 07:55:04 -0700962 const std::string *newHostname = input.get_ptr<const std::string *>();
963 if (newHostname == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700964 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700965 messages::propertyValueTypeError(asyncResp->res, input.dump(),
966 "HostName", "/HostName");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700967 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -0700968 }
Ed Tanous4a0cb852018-10-15 07:55:04 -0700969
970 // Change hostname
971 setHostName(
972 *newHostname, [asyncResp, newHostname{std::string(*newHostname)}](
973 const boost::system::error_code ec) {
974 if (ec)
975 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700976 messages::internalError(asyncResp->res, "/HostName");
Ed Tanous4a0cb852018-10-15 07:55:04 -0700977 }
978 else
979 {
980 asyncResp->res.jsonValue["HostName"] = newHostname;
981 }
982 });
Ed Tanous1abe55e2018-09-05 08:30:59 -0700983 }
984
Ed Tanous4a0cb852018-10-15 07:55:04 -0700985 void handleIPv4Patch(
986 const std::string &ifaceId, const nlohmann::json &input,
987 const boost::container::flat_set<IPv4AddressData> &ipv4Data,
988 const std::shared_ptr<AsyncResp> asyncResp)
Ed Tanous1abe55e2018-09-05 08:30:59 -0700989 {
990 if (!input.is_array())
991 {
Jason M. Billsf12894f2018-10-09 12:45:45 -0700992 messages::propertyValueTypeError(asyncResp->res, input.dump(),
993 "IPv4Addresses", "/IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -0700994 return;
995 }
996
997 // According to Redfish PATCH definition, size must be at least equal
Ed Tanous4a0cb852018-10-15 07:55:04 -0700998 if (input.size() < ipv4Data.size())
Ed Tanous1abe55e2018-09-05 08:30:59 -0700999 {
1000 // TODO(kkowalsk) This should be a message indicating that not
1001 // enough data has been provided
Jason M. Billsf12894f2018-10-09 12:45:45 -07001002 messages::internalError(asyncResp->res, "/IPv4Addresses");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001003 return;
1004 }
1005
Ed Tanous4a0cb852018-10-15 07:55:04 -07001006 int entryIdx = 0;
1007 boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
1008 ipv4Data.begin();
1009 for (const nlohmann::json &thisJson : input)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001010 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001011 std::string pathString =
1012 "/IPv4Addresses/" + std::to_string(entryIdx);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001013 // Check that entry is not of some unexpected type
Ed Tanous4a0cb852018-10-15 07:55:04 -07001014 if (!thisJson.is_object() && !thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001015 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001016 messages::propertyValueTypeError(
1017 asyncResp->res, thisJson.dump(), "IPv4Address", pathString);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001018
1019 continue;
1020 }
1021
Ed Tanous4a0cb852018-10-15 07:55:04 -07001022 nlohmann::json::const_iterator addressFieldIt =
1023 thisJson.find("Address");
1024 const std::string *addressField = nullptr;
1025 if (addressFieldIt != thisJson.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001026 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001027 addressField = addressFieldIt->get_ptr<const std::string *>();
1028 if (addressField == nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001029 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001030 messages::propertyValueFormatError(
1031 asyncResp->res, addressFieldIt->dump(), "Address",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001032 pathString + "/Address");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001033 continue;
1034 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001035 else
1036 {
1037 if (!ipv4VerifyIpAndGetBitcount(*addressField))
1038 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001039 messages::propertyValueFormatError(
1040 asyncResp->res, *addressField, "Address",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001041 pathString + "/Address");
1042 continue;
1043 }
1044 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001045 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001046
1047 boost::optional<uint8_t> prefixLength;
1048 const std::string *subnetField = nullptr;
1049 nlohmann::json::const_iterator subnetFieldIt =
1050 thisJson.find("SubnetMask");
1051 if (subnetFieldIt != thisJson.end())
1052 {
1053 subnetField = subnetFieldIt->get_ptr<const std::string *>();
1054 if (subnetField == nullptr)
1055 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001056 messages::propertyValueFormatError(
1057 asyncResp->res, *subnetField, "SubnetMask",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001058 pathString + "/SubnetMask");
1059 continue;
1060 }
1061 else
1062 {
1063 prefixLength = 0;
1064 if (!ipv4VerifyIpAndGetBitcount(*subnetField,
1065 &*prefixLength))
1066 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001067 messages::propertyValueFormatError(
1068 asyncResp->res, *subnetField, "SubnetMask",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001069 pathString + "/SubnetMask");
1070 continue;
1071 }
1072 }
1073 }
1074
1075 std::string addressOriginInDBusFormat;
1076 const std::string *addressOriginField = nullptr;
1077 nlohmann::json::const_iterator addressOriginFieldIt =
1078 thisJson.find("AddressOrigin");
1079 if (addressOriginFieldIt != thisJson.end())
1080 {
1081 const std::string *addressOriginField =
1082 addressOriginFieldIt->get_ptr<const std::string *>();
1083 if (addressOriginField == nullptr)
1084 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001085 messages::propertyValueFormatError(
1086 asyncResp->res, *addressOriginField, "AddressOrigin",
Ed Tanous4a0cb852018-10-15 07:55:04 -07001087 pathString + "/AddressOrigin");
1088 continue;
1089 }
1090 else
1091 {
1092 // Get Address origin in proper format
1093 addressOriginInDBusFormat =
1094 translateAddressOriginRedfishToDbus(
1095 *addressOriginField);
1096 if (addressOriginInDBusFormat.empty())
1097 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001098 messages::propertyValueNotInList(
1099 asyncResp->res, *addressOriginField,
1100 "AddressOrigin", pathString + "/AddressOrigin");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001101 continue;
1102 }
1103 }
1104 }
1105
1106 nlohmann::json::const_iterator gatewayFieldIt =
1107 thisJson.find("Gateway");
1108 const std::string *gatewayField = nullptr;
1109 if (gatewayFieldIt != thisJson.end())
1110 {
1111 const std::string *gatewayField =
1112 gatewayFieldIt->get_ptr<const std::string *>();
1113 if (gatewayField == nullptr ||
1114 !ipv4VerifyIpAndGetBitcount(*gatewayField))
1115 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001116 messages::propertyValueFormatError(asyncResp->res,
1117 *gatewayField, "Gateway",
1118 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001119 continue;
1120 }
1121 }
1122
1123 // if a vlan already exists, modify the existing
1124 if (thisData != ipv4Data.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001125 {
1126 // Existing object that should be modified/deleted/remain
1127 // unchanged
Ed Tanous4a0cb852018-10-15 07:55:04 -07001128 if (thisJson.is_null())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001129 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001130 auto callback = [entryIdx{std::to_string(entryIdx)},
1131 asyncResp](
1132 const boost::system::error_code ec) {
1133 if (ec)
1134 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001135 messages::internalError(asyncResp->res,
1136 "/IPv4Addresses/" +
1137 entryIdx + "/");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001138 return;
1139 }
1140 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] =
1141 nullptr;
1142 };
1143 crow::connections::systemBus->async_method_call(
1144 std::move(callback), "xyz.openbmc_project.Network",
1145 "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
1146 thisData->id,
1147 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001148 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001149 else if (thisJson.is_object())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001150 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001151 // Apply changes
Ed Tanous4a0cb852018-10-15 07:55:04 -07001152 if (addressField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001153 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001154 auto callback =
1155 [asyncResp, entryIdx,
1156 addressField{std::string(*addressField)}](
1157 const boost::system::error_code ec) {
1158 if (ec)
1159 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001160 messages::internalError(
1161 asyncResp->res,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001162 "/IPv4Addresses/" +
1163 std::to_string(entryIdx) +
1164 "/Address");
1165 return;
1166 }
1167 asyncResp->res
1168 .jsonValue["IPv4Addresses"][std::to_string(
1169 entryIdx)]["Address"] = addressField;
1170 };
1171
1172 crow::connections::systemBus->async_method_call(
1173 std::move(callback), "xyz.openbmc_project.Network",
1174 "/xyz/openbmc_project/network/" + ifaceId +
1175 "/ipv4/" + thisData->id,
1176 "org.freedesktop.DBus.Properties", "Set",
1177 "xyz.openbmc_project.Network.IP", "Address",
1178 sdbusplus::message::variant<std::string>(
1179 *addressField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001180 }
1181
Ed Tanous4a0cb852018-10-15 07:55:04 -07001182 if (prefixLength && subnetField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001183 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001184 changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
1185 thisData->id, *subnetField,
1186 *prefixLength, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001187 }
1188
Ed Tanous4a0cb852018-10-15 07:55:04 -07001189 if (!addressOriginInDBusFormat.empty() &&
1190 addressOriginField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001191 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001192 changeIPv4Origin(ifaceId, entryIdx, thisData->id,
1193 *addressOriginField,
1194 addressOriginInDBusFormat, asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001195 }
1196
Ed Tanous4a0cb852018-10-15 07:55:04 -07001197 if (gatewayField != nullptr)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001198 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001199 auto callback =
1200 [asyncResp, entryIdx,
1201 gatewayField{std::string(*gatewayField)}](
1202 const boost::system::error_code ec) {
1203 if (ec)
1204 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001205 messages::internalError(
1206 asyncResp->res,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001207 "/IPv4Addresses/" +
1208 std::to_string(entryIdx) +
1209 "/Gateway");
1210 return;
1211 }
1212 asyncResp->res
1213 .jsonValue["IPv4Addresses"][std::to_string(
1214 entryIdx)]["Gateway"] =
1215 std::move(gatewayField);
1216 };
1217
1218 crow::connections::systemBus->async_method_call(
1219 std::move(callback), "xyz.openbmc_project.Network",
1220 "/xyz/openbmc_project/network/" + ifaceId +
1221 "/ipv4/" + thisData->id,
1222 "org.freedesktop.DBus.Properties", "Set",
1223 "xyz.openbmc_project.Network.IP", "Gateway",
1224 sdbusplus::message::variant<std::string>(
1225 *gatewayField));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001226 }
1227 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001228 thisData++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001229 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001230 else
1231 {
1232 // Create IPv4 with provided data
1233 if (gatewayField == nullptr)
1234 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001235 messages::propertyMissing(asyncResp->res, "Gateway",
1236 pathString + "/Gateway");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001237 continue;
1238 }
1239
1240 if (addressField == nullptr)
1241 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001242 messages::propertyMissing(asyncResp->res, "Address",
1243 pathString + "/Address");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001244 continue;
1245 }
1246
1247 if (!prefixLength)
1248 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001249 messages::propertyMissing(asyncResp->res, "SubnetMask",
1250 pathString + "/SubnetMask");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001251 continue;
1252 }
1253
1254 createIPv4(ifaceId, entryIdx, *prefixLength, *gatewayField,
1255 *addressField, asyncResp);
1256 asyncResp->res.jsonValue["IPv4Addresses"][entryIdx] = thisJson;
1257 }
1258 entryIdx++;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001259 }
1260 }
1261
Ed Tanous4a0cb852018-10-15 07:55:04 -07001262 nlohmann::json parseInterfaceData(
1263 const std::string &iface_id, const EthernetInterfaceData &ethData,
1264 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001265 {
1266 // Copy JSON object to avoid race condition
Ed Tanous4a0cb852018-10-15 07:55:04 -07001267 nlohmann::json json_response(Node::json);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001268
Ed Tanous4a0cb852018-10-15 07:55:04 -07001269 json_response["Id"] = iface_id;
1270 json_response["@odata.id"] =
1271 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001272
Ed Tanous4a0cb852018-10-15 07:55:04 -07001273 json_response["SpeedMbps"] = ethData.speed;
1274 json_response["MACAddress"] = ethData.mac_address;
1275 if (!ethData.hostname.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001276 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001277 json_response["HostName"] = ethData.hostname;
1278 }
1279
1280 nlohmann::json &vlanObj = json_response["VLAN"];
1281 if (ethData.vlan_id)
1282 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001283 vlanObj["VLANEnable"] = true;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001284 vlanObj["VLANId"] = *ethData.vlan_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001285 }
1286 else
1287 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001288 vlanObj["VLANEnable"] = false;
1289 vlanObj["VLANId"] = 0;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001290 }
1291
Ed Tanous4a0cb852018-10-15 07:55:04 -07001292 if (ipv4Data.size() > 0)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001293 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001294 nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
1295 ipv4_array = nlohmann::json::array();
1296 for (auto &ipv4_config : ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001297 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001298 if (!ipv4_config.address.empty())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001299 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001300 ipv4_array.push_back({{"AddressOrigin", ipv4_config.origin},
1301 {"SubnetMask", ipv4_config.netmask},
1302 {"Address", ipv4_config.address}});
Ed Tanous1abe55e2018-09-05 08:30:59 -07001303
Ed Tanous4a0cb852018-10-15 07:55:04 -07001304 if (!ipv4_config.gateway.empty())
1305 {
1306 ipv4_array.back()["Gateway"] = ipv4_config.gateway;
1307 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001308 }
1309 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001310 }
1311
Ed Tanous4a0cb852018-10-15 07:55:04 -07001312 return json_response;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001313 }
1314
1315 /**
1316 * Functions triggers appropriate requests on DBus
1317 */
1318 void doGet(crow::Response &res, const crow::Request &req,
1319 const std::vector<std::string> &params) override
1320 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001321 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001322 if (params.size() != 1)
1323 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001324 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001325 return;
1326 }
1327
Ed Tanous4a0cb852018-10-15 07:55:04 -07001328 getEthernetIfaceData(
1329 params[0],
1330 [this, asyncResp, iface_id{std::string(params[0])}](
1331 const bool &success, const EthernetInterfaceData &ethData,
1332 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1333 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001334 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001335 // TODO(Pawel)consider distinguish between non existing
1336 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001337 messages::resourceNotFound(asyncResp->res,
1338 "EthernetInterface", iface_id);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001339 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001340 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001341 asyncResp->res.jsonValue =
1342 parseInterfaceData(iface_id, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001343 });
1344 }
1345
1346 void doPatch(crow::Response &res, const crow::Request &req,
1347 const std::vector<std::string> &params) override
1348 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001349 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001350 if (params.size() != 1)
1351 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001352 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001353 return;
1354 }
1355
Ed Tanous4a0cb852018-10-15 07:55:04 -07001356 const std::string &iface_id = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001357
1358 nlohmann::json patchReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001359 if (!json_util::processJsonFromRequest(res, req, patchReq))
1360 {
1361 return;
1362 }
1363
Ed Tanous4a0cb852018-10-15 07:55:04 -07001364 // Get single eth interface data, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001365 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001366 getEthernetIfaceData(
1367 iface_id,
1368 [this, asyncResp, iface_id, patchReq = std::move(patchReq)](
1369 const bool &success, const EthernetInterfaceData &ethData,
1370 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001371 if (!success)
1372 {
1373 // ... otherwise return error
1374 // TODO(Pawel)consider distinguish between non existing
1375 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001376 messages::resourceNotFound(
1377 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001378 return;
1379 }
1380
Ed Tanous4a0cb852018-10-15 07:55:04 -07001381 asyncResp->res.jsonValue =
1382 parseInterfaceData(iface_id, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001383
Ed Tanous4a0cb852018-10-15 07:55:04 -07001384 for (auto propertyIt : patchReq.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001385 {
1386 if (propertyIt.key() == "VLAN")
1387 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001388 handleVlanPatch(iface_id, propertyIt.value(), ethData,
1389 asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001390 }
1391 else if (propertyIt.key() == "HostName")
1392 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001393 handleHostnamePatch(propertyIt.value(), asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001394 }
1395 else if (propertyIt.key() == "IPv4Addresses")
1396 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001397 handleIPv4Patch(iface_id, propertyIt.value(), ipv4Data,
Ed Tanous1abe55e2018-09-05 08:30:59 -07001398 asyncResp);
1399 }
1400 else if (propertyIt.key() == "IPv6Addresses")
1401 {
1402 // TODO(kkowalsk) IPv6 Not supported on D-Bus yet
Jason M. Billsf12894f2018-10-09 12:45:45 -07001403 messages::propertyNotWritable(
1404 asyncResp->res, propertyIt.key(), propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001405 }
1406 else
1407 {
1408 auto fieldInJsonIt =
Ed Tanous4a0cb852018-10-15 07:55:04 -07001409 asyncResp->res.jsonValue.find(propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001410
Ed Tanous4a0cb852018-10-15 07:55:04 -07001411 if (fieldInJsonIt == asyncResp->res.jsonValue.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001412 {
1413 // Field not in scope of defined fields
Jason M. Billsf12894f2018-10-09 12:45:45 -07001414 messages::propertyUnknown(asyncResp->res,
1415 propertyIt.key(),
1416 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001417 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001418 else
Ed Tanous1abe55e2018-09-05 08:30:59 -07001419 {
1420 // User attempted to modify non-writable field
Jason M. Billsf12894f2018-10-09 12:45:45 -07001421 messages::propertyNotWritable(asyncResp->res,
1422 propertyIt.key(),
1423 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001424 }
1425 }
1426 }
1427 });
1428 }
Rapkiewicz, Pawel9391bb92018-03-20 03:12:18 +01001429};
1430
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001431/**
Ed Tanous4a0cb852018-10-15 07:55:04 -07001432 * VlanNetworkInterface derived class for delivering VLANNetworkInterface
1433 * Schema
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001434 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001435class VlanNetworkInterface : public Node
1436{
1437 public:
1438 /*
1439 * Default Constructor
1440 */
1441 template <typename CrowApp>
1442 // TODO(Pawel) Remove line from below, where we assume that there is only
Ed Tanous4a0cb852018-10-15 07:55:04 -07001443 // one manager called openbmc. This shall be generic, but requires to
1444 // update GetSubroutes method
Ed Tanous1abe55e2018-09-05 08:30:59 -07001445 VlanNetworkInterface(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001446 Node(app,
1447 "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>>/VLANs/"
1448 "<str>",
1449 std::string(), std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001450 {
1451 Node::json["@odata.type"] =
1452 "#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
1453 Node::json["@odata.context"] =
1454 "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
1455 Node::json["Name"] = "VLAN Network Interface";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001456
Ed Tanous1abe55e2018-09-05 08:30:59 -07001457 entityPrivileges = {
1458 {boost::beast::http::verb::get, {{"Login"}}},
1459 {boost::beast::http::verb::head, {{"Login"}}},
1460 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1461 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1462 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1463 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001464 }
1465
Ed Tanous1abe55e2018-09-05 08:30:59 -07001466 private:
Ed Tanous4a0cb852018-10-15 07:55:04 -07001467 nlohmann::json parseInterfaceData(
1468 const std::string &parent_iface_id, const std::string &iface_id,
1469 const EthernetInterfaceData &ethData,
1470 const boost::container::flat_set<IPv4AddressData> &ipv4Data)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001471 {
1472 // Copy JSON object to avoid race condition
Ed Tanous4a0cb852018-10-15 07:55:04 -07001473 nlohmann::json json_response(Node::json);
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001474
Ed Tanous1abe55e2018-09-05 08:30:59 -07001475 // Fill out obvious data...
Ed Tanous4a0cb852018-10-15 07:55:04 -07001476 json_response["Id"] = iface_id;
1477 json_response["@odata.id"] =
1478 "/redfish/v1/Managers/bmc/EthernetInterfaces/" + parent_iface_id +
1479 "/VLANs/" + iface_id;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001480
Ed Tanous4a0cb852018-10-15 07:55:04 -07001481 json_response["VLANEnable"] = true;
1482 if (ethData.vlan_id)
1483 {
1484 json_response["VLANId"] = *ethData.vlan_id;
1485 }
1486 return json_response;
Ed Tanousa434f2b2018-07-27 13:04:22 -07001487 }
1488
Ed Tanous1abe55e2018-09-05 08:30:59 -07001489 bool verifyNames(crow::Response &res, const std::string &parent,
1490 const std::string &iface)
1491 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001492 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001493 if (!boost::starts_with(iface, parent + "_"))
1494 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001495 messages::resourceNotFound(asyncResp->res, "VLAN Network Interface",
1496 iface);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001497 return false;
1498 }
1499 else
1500 {
1501 return true;
1502 }
1503 }
1504
1505 /**
1506 * Functions triggers appropriate requests on DBus
1507 */
1508 void doGet(crow::Response &res, const crow::Request &req,
1509 const std::vector<std::string> &params) override
1510 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001511 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1512 // TODO(Pawel) this shall be parameterized call (two params) to get
Ed Tanous1abe55e2018-09-05 08:30:59 -07001513 // EthernetInterfaces for any Manager, not only hardcoded 'openbmc'.
1514 // Check if there is required param, truly entering this shall be
1515 // impossible.
1516 if (params.size() != 2)
1517 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001518 messages::internalError(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001519 res.end();
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001520 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001521 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001522
Ed Tanous4a0cb852018-10-15 07:55:04 -07001523 const std::string &parent_iface_id = params[0];
1524 const std::string &iface_id = params[1];
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001525
Ed Tanous4a0cb852018-10-15 07:55:04 -07001526 if (!verifyNames(res, parent_iface_id, iface_id))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001527 {
1528 return;
1529 }
Kowalski, Kamil927a5052018-07-03 14:16:46 +02001530
Ed Tanous1abe55e2018-09-05 08:30:59 -07001531 // Get single eth interface data, and call the below callback for JSON
1532 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001533 getEthernetIfaceData(
1534 iface_id,
1535 [this, asyncResp, parent_iface_id, iface_id](
1536 const bool &success, const EthernetInterfaceData &ethData,
1537 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1538 if (success && ethData.vlan_id)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001539 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001540 asyncResp->res.jsonValue = parseInterfaceData(
1541 parent_iface_id, iface_id, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001542 }
1543 else
1544 {
1545 // ... otherwise return error
1546 // TODO(Pawel)consider distinguish between non existing
1547 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001548 messages::resourceNotFound(
1549 asyncResp->res, "VLAN Network Interface", iface_id);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001550 }
Ed Tanous1abe55e2018-09-05 08:30:59 -07001551 });
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001552 }
1553
Ed Tanous1abe55e2018-09-05 08:30:59 -07001554 void doPatch(crow::Response &res, const crow::Request &req,
1555 const std::vector<std::string> &params) override
1556 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001557 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001558 if (params.size() != 2)
1559 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001560 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001561 return;
1562 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001563
Ed Tanous1abe55e2018-09-05 08:30:59 -07001564 const std::string &parentIfaceId = params[0];
1565 const std::string &ifaceId = params[1];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001566
Ed Tanous1abe55e2018-09-05 08:30:59 -07001567 if (!verifyNames(res, parentIfaceId, ifaceId))
1568 {
1569 return;
1570 }
1571
1572 nlohmann::json patchReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001573 if (!json_util::processJsonFromRequest(res, req, patchReq))
1574 {
1575 return;
1576 }
1577
1578 // Get single eth interface data, and call the below callback for JSON
1579 // preparation
Ed Tanous4a0cb852018-10-15 07:55:04 -07001580 getEthernetIfaceData(
Ed Tanous1abe55e2018-09-05 08:30:59 -07001581 ifaceId,
Ed Tanous4a0cb852018-10-15 07:55:04 -07001582 [this, asyncResp, parentIfaceId, ifaceId,
1583 patchReq{std::move(patchReq)}](
1584 const bool &success, const EthernetInterfaceData &ethData,
1585 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001586 if (!success)
1587 {
Ed Tanous1abe55e2018-09-05 08:30:59 -07001588 // TODO(Pawel)consider distinguish between non existing
1589 // object, and other errors
Jason M. Billsf12894f2018-10-09 12:45:45 -07001590 messages::resourceNotFound(
1591 asyncResp->res, "VLAN Network Interface", ifaceId);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001592
1593 return;
1594 }
1595
Ed Tanous4a0cb852018-10-15 07:55:04 -07001596 asyncResp->res.jsonValue = parseInterfaceData(
1597 parentIfaceId, ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001598
Ed Tanous4a0cb852018-10-15 07:55:04 -07001599 for (auto propertyIt : patchReq.items())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001600 {
1601 if (propertyIt.key() != "VLANEnable" &&
1602 propertyIt.key() != "VLANId")
1603 {
1604 auto fieldInJsonIt =
Ed Tanous4a0cb852018-10-15 07:55:04 -07001605 asyncResp->res.jsonValue.find(propertyIt.key());
1606 if (fieldInJsonIt == asyncResp->res.jsonValue.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001607 {
1608 // Field not in scope of defined fields
Jason M. Billsf12894f2018-10-09 12:45:45 -07001609 messages::propertyUnknown(asyncResp->res,
1610 propertyIt.key(),
1611 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001612 }
Ed Tanous4a0cb852018-10-15 07:55:04 -07001613 else
Ed Tanous1abe55e2018-09-05 08:30:59 -07001614 {
1615 // User attempted to modify non-writable field
Jason M. Billsf12894f2018-10-09 12:45:45 -07001616 messages::propertyNotWritable(asyncResp->res,
1617 propertyIt.key(),
1618 propertyIt.key());
Ed Tanous1abe55e2018-09-05 08:30:59 -07001619 }
1620 }
1621 }
1622
Ed Tanous4a0cb852018-10-15 07:55:04 -07001623 EthernetInterface::handleVlanPatch(ifaceId, patchReq, ethData,
1624 asyncResp);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001625 });
1626 }
1627
1628 void doDelete(crow::Response &res, const crow::Request &req,
1629 const std::vector<std::string> &params) override
1630 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001631 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001632 if (params.size() != 2)
1633 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001634 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001635 return;
1636 }
1637
1638 const std::string &parentIfaceId = params[0];
1639 const std::string &ifaceId = params[1];
1640
Ed Tanous4a0cb852018-10-15 07:55:04 -07001641 if (!verifyNames(asyncResp->res, parentIfaceId, ifaceId))
Ed Tanous1abe55e2018-09-05 08:30:59 -07001642 {
1643 return;
1644 }
1645
1646 // Get single eth interface data, and call the below callback for JSON
1647 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001648 getEthernetIfaceData(
1649 ifaceId,
1650 [this, asyncResp, parentIfaceId{std::string(parentIfaceId)},
1651 ifaceId{std::string(ifaceId)}](
1652 const bool &success, const EthernetInterfaceData &ethData,
1653 const boost::container::flat_set<IPv4AddressData> &ipv4Data) {
1654 if (success && ethData.vlan_id)
1655 {
1656 asyncResp->res.jsonValue = parseInterfaceData(
1657 parentIfaceId, ifaceId, ethData, ipv4Data);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001658
Jason M. Billsf12894f2018-10-09 12:45:45 -07001659 auto callback =
1660 [asyncResp](const boost::system::error_code ec) {
1661 if (ec)
1662 {
1663 messages::internalError(asyncResp->res);
1664 }
1665 };
1666 crow::connections::systemBus->async_method_call(
1667 std::move(callback), "xyz.openbmc_project.Network",
1668 std::string("/xyz/openbmc_project/network/") + ifaceId,
1669 "xyz.openbmc_project.Object.Delete", "Delete");
1670 }
1671 else
1672 {
1673 // ... otherwise return error
1674 // TODO(Pawel)consider distinguish between non existing
1675 // object, and other errors
1676 messages::resourceNotFound(
1677 asyncResp->res, "VLAN Network Interface", ifaceId);
1678 }
1679 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001680 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001681};
1682
1683/**
1684 * VlanNetworkInterfaceCollection derived class for delivering
1685 * VLANNetworkInterface Collection Schema
1686 */
Ed Tanous1abe55e2018-09-05 08:30:59 -07001687class VlanNetworkInterfaceCollection : public Node
1688{
1689 public:
1690 template <typename CrowApp>
1691 // TODO(Pawel) Remove line from below, where we assume that there is only
Ed Tanous4a0cb852018-10-15 07:55:04 -07001692 // one manager called openbmc. This shall be generic, but requires to
1693 // update GetSubroutes method
Ed Tanous1abe55e2018-09-05 08:30:59 -07001694 VlanNetworkInterfaceCollection(CrowApp &app) :
Ed Tanous4a0cb852018-10-15 07:55:04 -07001695 Node(app, "/redfish/v1/Managers/bmc/EthernetInterfaces/<str>/VLANs/",
1696 std::string())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001697 {
1698 Node::json["@odata.type"] =
1699 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1700 Node::json["@odata.context"] =
1701 "/redfish/v1/$metadata"
1702 "#VLanNetworkInterfaceCollection.VLanNetworkInterfaceCollection";
1703 Node::json["Name"] = "VLAN Network Interface Collection";
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001704
Ed Tanous1abe55e2018-09-05 08:30:59 -07001705 entityPrivileges = {
1706 {boost::beast::http::verb::get, {{"Login"}}},
1707 {boost::beast::http::verb::head, {{"Login"}}},
1708 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1709 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1710 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1711 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001712 }
1713
Ed Tanous1abe55e2018-09-05 08:30:59 -07001714 private:
1715 /**
1716 * Functions triggers appropriate requests on DBus
1717 */
1718 void doGet(crow::Response &res, const crow::Request &req,
1719 const std::vector<std::string> &params) override
1720 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001721 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001722 if (params.size() != 1)
1723 {
1724 // This means there is a problem with the router
Jason M. Billsf12894f2018-10-09 12:45:45 -07001725 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001726 return;
Ed Tanous8ceb2ec2018-08-13 11:11:56 -07001727 }
1728
Ed Tanous4a0cb852018-10-15 07:55:04 -07001729 const std::string &rootInterfaceName = params[0];
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001730
Ed Tanous4a0cb852018-10-15 07:55:04 -07001731 // Get eth interface list, and call the below callback for JSON
Ed Tanous1abe55e2018-09-05 08:30:59 -07001732 // preparation
Jason M. Billsf12894f2018-10-09 12:45:45 -07001733 getEthernetIfaceList(
1734 [this, asyncResp,
1735 rootInterfaceName{std::string(rootInterfaceName)}](
1736 const bool &success,
1737 const std::vector<std::string> &iface_list) {
1738 if (!success)
Ed Tanous1abe55e2018-09-05 08:30:59 -07001739 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001740 messages::internalError(asyncResp->res);
1741 return;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001742 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001743 asyncResp->res.jsonValue = Node::json;
Ed Tanous4a0cb852018-10-15 07:55:04 -07001744
Jason M. Billsf12894f2018-10-09 12:45:45 -07001745 nlohmann::json iface_array = nlohmann::json::array();
1746
1747 for (const std::string &iface_item : iface_list)
1748 {
1749 if (boost::starts_with(iface_item, rootInterfaceName + "_"))
1750 {
1751 iface_array.push_back(
1752 {{"@odata.id",
1753 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1754 rootInterfaceName + "/VLANs/" + iface_item}});
1755 }
1756 }
1757
1758 if (iface_array.empty())
1759 {
1760 messages::resourceNotFound(
1761 asyncResp->res, "EthernetInterface", rootInterfaceName);
1762 return;
1763 }
1764 asyncResp->res.jsonValue["Members@odata.count"] =
1765 iface_array.size();
1766 asyncResp->res.jsonValue["Members"] = std::move(iface_array);
1767 asyncResp->res.jsonValue["@odata.id"] =
1768 "/redfish/v1/Managers/bmc/EthernetInterfaces/" +
1769 rootInterfaceName + "/VLANs";
1770 });
Ed Tanous1abe55e2018-09-05 08:30:59 -07001771 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001772
Ed Tanous1abe55e2018-09-05 08:30:59 -07001773 void doPost(crow::Response &res, const crow::Request &req,
1774 const std::vector<std::string> &params) override
1775 {
Ed Tanous4a0cb852018-10-15 07:55:04 -07001776 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001777 if (params.size() != 1)
1778 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001779 messages::internalError(asyncResp->res);
Ed Tanous1abe55e2018-09-05 08:30:59 -07001780 return;
1781 }
1782
1783 nlohmann::json postReq;
Ed Tanous1abe55e2018-09-05 08:30:59 -07001784 if (!json_util::processJsonFromRequest(res, req, postReq))
1785 {
1786 return;
1787 }
1788
Ed Tanous4a0cb852018-10-15 07:55:04 -07001789 auto vlanIdJson = json.find("VLANId");
1790 if (vlanIdJson == json.end())
Ed Tanous1abe55e2018-09-05 08:30:59 -07001791 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001792 messages::propertyMissing(asyncResp->res, "VLANId", "/VLANId");
Ed Tanous1abe55e2018-09-05 08:30:59 -07001793 return;
1794 }
1795
Ed Tanous4a0cb852018-10-15 07:55:04 -07001796 const uint64_t *vlanId = vlanIdJson->get_ptr<const uint64_t *>();
1797 if (vlanId == nullptr)
1798 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001799 messages::propertyValueTypeError(asyncResp->res, vlanIdJson->dump(),
1800 "VLANId", "/VLANId");
Ed Tanous4a0cb852018-10-15 07:55:04 -07001801 return;
1802 }
1803 const std::string &rootInterfaceName = params[0];
Ed Tanous1abe55e2018-09-05 08:30:59 -07001804
Ed Tanous4a0cb852018-10-15 07:55:04 -07001805 auto callback = [asyncResp](const boost::system::error_code ec) {
1806 if (ec)
1807 {
1808 // TODO(ed) make more consistent error messages based on
1809 // phosphor-network responses
Jason M. Billsf12894f2018-10-09 12:45:45 -07001810 messages::internalError(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001811 return;
1812 }
Jason M. Billsf12894f2018-10-09 12:45:45 -07001813 messages::created(asyncResp->res);
Ed Tanous4a0cb852018-10-15 07:55:04 -07001814 };
1815 crow::connections::systemBus->async_method_call(
1816 std::move(callback), "xyz.openbmc_project.Network",
1817 "/xyz/openbmc_project/network",
1818 "xyz.openbmc_project.Network.VLAN.Create", "VLAN",
1819 rootInterfaceName, static_cast<uint32_t>(*vlanId));
Ed Tanous1abe55e2018-09-05 08:30:59 -07001820 }
Kowalski, Kamile439f0f2018-05-21 08:13:57 +02001821};
Ed Tanous1abe55e2018-09-05 08:30:59 -07001822} // namespace redfish