summaryrefslogtreecommitdiff
path: root/redfish-core/lib/ethernet.hpp
diff options
context:
space:
mode:
authorJohnathan Mantey <johnathanx.mantey@intel.com>2019-06-18 22:44:21 +0300
committerEd Tanous <ed.tanous@intel.com>2019-09-14 00:31:32 +0300
commit017848263e97e6eb9f7d486071a616626fec0591 (patch)
tree03b73816878a4499df62e29a8478c38634ff43b2 /redfish-core/lib/ethernet.hpp
parentdac62eefae32e5a2be5fc918af2ec39c7ca39ea6 (diff)
downloadbmcweb-017848263e97e6eb9f7d486071a616626fec0591.tar.xz
Redfish: Make NIC HW reflect PATCH changes
PATCH commands only changed the state of the DBus database. The PATCH did not actually change the state of the NIC settings. IPMITOOL and "ip addr" both delete the specific hash entry, and then create a new entry using the requested state. This change aligns Redfish with that behavior. The requested element is deleted, and subsequently recreated with the new values. This guarantees the NIC HW is actually updated, and in sync with the DBus database. Tested by: From eth1 modify eth0 state: IPv4 tests: Delete all addresses Create five new addresses Add a new entry to the end of the collection Delete the new entry just added Send a "keep" command that does not modify any state, and "keeps" more entries than are actually in the IPv4Addresses collection Keep all entries, and delete entries that do not exist Delete all entries except self assigned Send a PATCH with an empty IPv4StaticAddresses array IPv6 tests: Delete all entries except SLAAC Create five static entries Modify only the prefix for one entry Modify only the address for one entry Add an additional entry Delete the new entry Keep all entries, plus a few that don't exist Keep all entries, delete a few that don't exist Delete all entries except SLAAC Send a PATCH with an empty IPv6StaticAddresses array Change-Id: Id5f733f795588ba36b5d3ab3b0017a01ee3f2da7 Signed-off-by: Johnathan Mantey <johnathanx.mantey@intel.com>
Diffstat (limited to 'redfish-core/lib/ethernet.hpp')
-rw-r--r--redfish-core/lib/ethernet.hpp847
1 files changed, 388 insertions, 459 deletions
diff --git a/redfish-core/lib/ethernet.hpp b/redfish-core/lib/ethernet.hpp
index 09074fda57..ff37330c4b 100644
--- a/redfish-core/lib/ethernet.hpp
+++ b/redfish-core/lib/ethernet.hpp
@@ -296,10 +296,10 @@ inline bool extractEthernetInterfaceData(const std::string &ethiface_id,
}
// Helper function that extracts data for single ethernet ipv6 address
-inline void extractIPV6Data(
- const std::string &ethiface_id, const GetManagedObjects &dbus_data,
- boost::container::flat_set<IPv6AddressData> &ipv6_config,
- boost::container::flat_set<IPv6AddressData> &ipv6_static_config)
+inline void
+ extractIPV6Data(const std::string &ethiface_id,
+ const GetManagedObjects &dbus_data,
+ boost::container::flat_set<IPv6AddressData> &ipv6_config)
{
const std::string ipv6PathStart =
"/xyz/openbmc_project/network/" + ethiface_id + "/ipv6/";
@@ -361,20 +361,6 @@ inline void extractIPV6Data(
<< " on the " << objpath.first.str << " object";
}
}
- if (ipv6_address.origin == "Static")
- {
- std::pair<boost::container::flat_set<
- IPv6AddressData>::iterator,
- bool>
- iter = ipv6_static_config.insert(
- {objpath.first.str.substr(
- ipv6PathStart.size())});
- IPv6AddressData &ipv6_static_address = *iter.first;
-
- ipv6_static_address.address = ipv6_address.address;
- ipv6_static_address.prefixLength =
- ipv6_address.prefixLength;
- }
}
}
}
@@ -382,10 +368,10 @@ inline void extractIPV6Data(
}
// Helper function that extracts data for single ethernet ipv4 address
-inline void extractIPData(
- const std::string &ethiface_id, const GetManagedObjects &dbus_data,
- boost::container::flat_set<IPv4AddressData> &ipv4_config,
- boost::container::flat_set<IPv4AddressData> &ipv4_static_config)
+inline void
+ extractIPData(const std::string &ethiface_id,
+ const GetManagedObjects &dbus_data,
+ boost::container::flat_set<IPv4AddressData> &ipv4_config)
{
const std::string ipv4PathStart =
"/xyz/openbmc_project/network/" + ethiface_id + "/ipv4/";
@@ -457,17 +443,6 @@ inline void extractIPData(
<< " on the " << objpath.first.str << " object";
}
}
-
- if (ipv4_address.origin == "Static")
- {
- IPv4AddressData ipv4_static_address = {
- objpath.first.str.substr(ipv4PathStart.size())};
- ipv4_static_address.address = ipv4_address.address;
- ipv4_static_address.gateway = ipv4_address.gateway;
- ipv4_static_address.netmask = ipv4_address.netmask;
- ipv4_static_config.emplace(ipv4_static_address);
- }
-
// Check if given address is local, or global
ipv4_address.linktype =
boost::starts_with(ipv4_address.address, "169.254.")
@@ -594,147 +569,166 @@ inline bool ipv4VerifyIpAndGetBitcount(const std::string &ip,
}
/**
- * @brief Changes IPv4 address type property (Address, Gateway)
+ * @brief Deletes given IPv4 interface
*
- * @param[in] ifaceId Id of interface whose IP should be modified
- * @param[in] ipIdx Index of IP in input array that should be modified
- * @param[in] ipHash DBus Hash id of modified IP
- * @param[in] name Name of field in JSON representation
- * @param[in] newValue New value that should be written
+ * @param[in] ifaceId Id of interface whose IP should be deleted
+ * @param[in] ipHash DBus Hash id of IP that should be deleted
* @param[io] asyncResp Response object that will be returned to client
*
- * @return true if give IP is valid and has been sent do D-Bus, false
- * otherwise
+ * @return None
*/
-inline void changeIPv4AddressProperty(
- const std::string &ifaceId, int ipIdx, const std::string &ipHash,
- const std::string &name, const std::string &newValue,
- const std::shared_ptr<AsyncResp> asyncResp)
+inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
+ const std::shared_ptr<AsyncResp> asyncResp)
{
- auto callback =
- [asyncResp, ipIdx, name{std::string(name)},
- newValue{std::move(newValue)}](const boost::system::error_code ec) {
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
if (ec)
{
messages::internalError(asyncResp->res);
}
- };
-
- crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
+ },
+ "xyz.openbmc_project.Network",
"/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", name,
- std::variant<std::string>(newValue));
+ "xyz.openbmc_project.Object.Delete", "Delete");
}
/**
- * @brief Modifies SubnetMask for given IP
+ * @brief Creates a static IPv4 entry
*
- * @param[in] ifaceId Id of interface whose IP should be modified
- * @param[in] ipIdx Index of IP in input array that should be
- * modified
- * @param[in] ipHash DBus Hash id of modified IP
- * @param[in] newValue Mask as PrefixLength in bitcount
- * @param[io] asyncResp Response object that will be returned to client
+ * @param[in] ifaceId Id of interface upon which to create the IPv4 entry
+ * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
+ * @param[in] gateway IPv4 address of this interfaces gateway
+ * @param[in] address IPv4 address to assign to this interface
+ * @param[io] asyncResp Response object that will be returned to client
*
* @return None
*/
-inline void changeIPv4SubnetMaskProperty(const std::string &ifaceId, int ipIdx,
- const std::string &ipHash,
- uint8_t &newValue,
- std::shared_ptr<AsyncResp> asyncResp)
+inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
+ uint8_t prefixLength, const std::string &gateway,
+ const std::string &address,
+ std::shared_ptr<AsyncResp> asyncResp)
{
- auto callback = [asyncResp, ipIdx](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- }
- };
-
crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", "PrefixLength",
- std::variant<uint8_t>(newValue));
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ }
+ },
+ "xyz.openbmc_project.Network",
+ "/xyz/openbmc_project/network/" + ifaceId,
+ "xyz.openbmc_project.Network.IP.Create", "IP",
+ "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, prefixLength,
+ gateway);
}
/**
- * @brief Deletes given IPv4
+ * @brief Deletes the IPv4 entry for this interface and creates a replacement
+ * static IPv4 entry
*
- * @param[in] ifaceId Id of interface whose IP should be deleted
- * @param[in] ipHash DBus Hash id of IP that should be deleted
- * @param[io] asyncResp Response object that will be returned to client
+ * @param[in] ifaceId Id of interface upon which to create the IPv4 entry
+ * @param[in] id The unique hash entry identifying the DBus entry
+ * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
+ * @param[in] gateway IPv4 address of this interfaces gateway
+ * @param[in] address IPv4 address to assign to this interface
+ * @param[io] asyncResp Response object that will be returned to client
*
* @return None
*/
-inline void deleteIPv4(const std::string &ifaceId, const std::string &ipHash,
- const std::shared_ptr<AsyncResp> asyncResp)
+inline void deleteAndCreateIPv4(const std::string &ifaceId,
+ const std::string &id, uint8_t prefixLength,
+ const std::string &gateway,
+ const std::string &address,
+ std::shared_ptr<AsyncResp> asyncResp)
{
crow::connections::systemBus->async_method_call(
- [asyncResp](const boost::system::error_code ec) {
+ [asyncResp, ifaceId, address, prefixLength,
+ gateway](const boost::system::error_code ec) {
if (ec)
{
messages::internalError(asyncResp->res);
}
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ }
+ },
+ "xyz.openbmc_project.Network",
+ "/xyz/openbmc_project/network/" + ifaceId,
+ "xyz.openbmc_project.Network.IP.Create", "IP",
+ "xyz.openbmc_project.Network.IP.Protocol.IPv4", address,
+ prefixLength, gateway);
},
"xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + ipHash,
+ +"/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" + id,
"xyz.openbmc_project.Object.Delete", "Delete");
}
/**
- * @brief Creates IPv4 with given data
+ * @brief Deletes given IPv6
*
* @param[in] ifaceId Id of interface whose IP should be deleted
- * @param[in] ipIdx Index of IP in input array that should be deleted
* @param[in] ipHash DBus Hash id of IP that should be deleted
* @param[io] asyncResp Response object that will be returned to client
*
* @return None
*/
-inline void createIPv4(const std::string &ifaceId, unsigned int ipIdx,
- uint8_t subnetMask, const std::string &gateway,
- const std::string &address,
- std::shared_ptr<AsyncResp> asyncResp)
+inline void deleteIPv6(const std::string &ifaceId, const std::string &ipHash,
+ const std::shared_ptr<AsyncResp> asyncResp)
{
- auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- }
- };
-
crow::connections::systemBus->async_method_call(
- std::move(createIpHandler), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId,
- "xyz.openbmc_project.Network.IP.Create", "IP",
- "xyz.openbmc_project.Network.IP.Protocol.IPv4", address, subnetMask,
- gateway);
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ }
+ },
+ "xyz.openbmc_project.Network",
+ "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + ipHash,
+ "xyz.openbmc_project.Object.Delete", "Delete");
}
/**
- * @brief Deletes given IPv6
+ * @brief Deletes the IPv6 entry for this interface and creates a replacement
+ * static IPv6 entry
*
- * @param[in] ifaceId Id of interface whose IP should be deleted
- * @param[in] ipHash DBus Hash id of IP that should be deleted
- * @param[io] asyncResp Response object that will be returned to client
+ * @param[in] ifaceId Id of interface upon which to create the IPv6 entry
+ * @param[in] id The unique hash entry identifying the DBus entry
+ * @param[in] prefixLength IPv6 prefix syntax for the subnet mask
+ * @param[in] address IPv6 address to assign to this interface
+ * @param[io] asyncResp Response object that will be returned to client
*
* @return None
*/
-inline void deleteIPv6(const std::string &ifaceId, const std::string &ipHash,
- const std::shared_ptr<AsyncResp> asyncResp)
+inline void deleteAndCreateIPv6(const std::string &ifaceId,
+ const std::string &id, uint8_t prefixLength,
+ const std::string &address,
+ std::shared_ptr<AsyncResp> asyncResp)
{
crow::connections::systemBus->async_method_call(
- [asyncResp](const boost::system::error_code ec) {
+ [asyncResp, ifaceId, address,
+ prefixLength](const boost::system::error_code ec) {
if (ec)
{
messages::internalError(asyncResp->res);
}
+ crow::connections::systemBus->async_method_call(
+ [asyncResp](const boost::system::error_code ec) {
+ if (ec)
+ {
+ messages::internalError(asyncResp->res);
+ }
+ },
+ "xyz.openbmc_project.Network",
+ "/xyz/openbmc_project/network/" + ifaceId,
+ "xyz.openbmc_project.Network.IP.Create", "IP",
+ "xyz.openbmc_project.Network.IP.Protocol.IPv6", address,
+ prefixLength, "");
},
"xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + ipHash,
+ +"/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" + id,
"xyz.openbmc_project.Object.Delete", "Delete");
}
@@ -742,15 +736,14 @@ inline void deleteIPv6(const std::string &ifaceId, const std::string &ipHash,
* @brief Creates IPv6 with given data
*
* @param[in] ifaceId Id of interface whose IP should be added
- * @param[in] ipIdx Index of IP in input array that should be added
* @param[in] prefixLength Prefix length that needs to be added
* @param[in] address IP address that needs to be added
* @param[io] asyncResp Response object that will be returned to client
*
* @return None
*/
-inline void createIPv6(const std::string &ifaceId, unsigned int ipIdx,
- uint8_t prefixLength, const std::string &address,
+inline void createIPv6(const std::string &ifaceId, uint8_t prefixLength,
+ const std::string &address,
std::shared_ptr<AsyncResp> asyncResp)
{
auto createIpHandler = [asyncResp](const boost::system::error_code ec) {
@@ -831,14 +824,11 @@ void getEthernetIfaceData(const std::string &ethiface_id,
const GetManagedObjects &resp) {
EthernetInterfaceData ethData{};
boost::container::flat_set<IPv4AddressData> ipv4Data;
- boost::container::flat_set<IPv4AddressData> ipv4StaticData;
boost::container::flat_set<IPv6AddressData> ipv6Data;
- boost::container::flat_set<IPv6AddressData> ipv6StaticData;
if (error_code)
{
- callback(false, ethData, ipv4Data, ipv4StaticData, ipv6Data,
- ipv6StaticData);
+ callback(false, ethData, ipv4Data, ipv6Data);
return;
}
@@ -846,12 +836,11 @@ void getEthernetIfaceData(const std::string &ethiface_id,
extractEthernetInterfaceData(ethiface_id, resp, ethData);
if (!found)
{
- callback(false, ethData, ipv4Data, ipv4StaticData, ipv6Data,
- ipv6StaticData);
+ callback(false, ethData, ipv4Data, ipv6Data);
return;
}
- extractIPData(ethiface_id, resp, ipv4Data, ipv4StaticData);
+ extractIPData(ethiface_id, resp, ipv4Data);
// Fix global GW
for (IPv4AddressData &ipv4 : ipv4Data)
{
@@ -863,10 +852,9 @@ void getEthernetIfaceData(const std::string &ethiface_id,
}
}
- extractIPV6Data(ethiface_id, resp, ipv6Data, ipv6StaticData);
+ extractIPV6Data(ethiface_id, resp, ipv6Data);
// Finally make a callback with usefull data
- callback(true, ethData, ipv4Data, ipv4StaticData, ipv6Data,
- ipv6StaticData);
+ callback(true, ethData, ipv4Data, ipv6Data);
},
"xyz.openbmc_project.Network", "/xyz/openbmc_project/network",
"org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
@@ -1138,199 +1126,211 @@ class EthernetInterface : public Node
setDHCPv4Config("NTPEnabled", *useNTPServers, asyncResp);
}
}
+
+ boost::container::flat_set<IPv4AddressData>::const_iterator
+ GetNextStaticIPEntry(
+ boost::container::flat_set<IPv4AddressData>::const_iterator head,
+ boost::container::flat_set<IPv4AddressData>::const_iterator end)
+ {
+ for (; head != end; head++)
+ {
+ if (head->origin == "Static")
+ {
+ return head;
+ }
+ }
+ return end;
+ }
+
+ boost::container::flat_set<IPv6AddressData>::const_iterator
+ GetNextStaticIPEntry(
+ boost::container::flat_set<IPv6AddressData>::const_iterator head,
+ boost::container::flat_set<IPv6AddressData>::const_iterator end)
+ {
+ for (; head != end; head++)
+ {
+ if (head->origin == "Static")
+ {
+ return head;
+ }
+ }
+ return end;
+ }
+
void handleIPv4StaticPatch(
const std::string &ifaceId, nlohmann::json &input,
- const boost::container::flat_set<IPv4AddressData> &ipv4StaticData,
+ const boost::container::flat_set<IPv4AddressData> &ipv4Data,
const std::shared_ptr<AsyncResp> asyncResp)
{
- if (!input.is_array())
+ if ((!input.is_array()) || input.empty())
{
messages::propertyValueTypeError(asyncResp->res, input.dump(),
"IPv4StaticAddresses");
return;
}
- int entryIdx = 0;
- boost::container::flat_set<IPv4AddressData>::const_iterator thisData =
- ipv4StaticData.begin();
+ int entryIdx = 1;
+ // Find the first static IP address currently active on the NIC and
+ // match it to the first JSON element in the IPv4StaticAddresses array.
+ // Match each subsequent JSON element to the next static IP programmed
+ // into the NIC.
+ boost::container::flat_set<IPv4AddressData>::const_iterator NICIPentry =
+ GetNextStaticIPEntry(ipv4Data.cbegin(), ipv4Data.cend());
+
for (nlohmann::json &thisJson : input)
{
std::string pathString =
"IPv4StaticAddresses/" + std::to_string(entryIdx);
- if (thisJson.is_null())
+ if (!thisJson.is_null() && !thisJson.empty())
{
- if (thisData != ipv4StaticData.end())
- {
- deleteIPv4(ifaceId, thisData->id, asyncResp);
- thisData++;
- }
- else
+ std::optional<std::string> address;
+ std::optional<std::string> subnetMask;
+ std::optional<std::string> gateway;
+
+ if (!json_util::readJson(thisJson, asyncResp->res, "Address",
+ address, "SubnetMask", subnetMask,
+ "Gateway", gateway))
{
messages::propertyValueFormatError(
- asyncResp->res, input.dump(), pathString);
+ asyncResp->res, thisJson.dump(), pathString);
return;
- // TODO(ratagupt) Not sure about the property where value is
- // list and if unable to update one of the
- // list value then should we proceed further or
- // break there, would ask in the redfish forum
- // till then we stop processing the next list item.
}
- entryIdx++;
- continue; // not an error as per the redfish spec.
- }
- if (thisJson.empty())
- {
- if (thisData != ipv4StaticData.end())
+ // Find the address/subnet/gateway values. Any values that are
+ // not explicitly provided are assumed to be unmodified from the
+ // current state of the interface. Merge existing state into the
+ // current request.
+ const std::string *addr;
+ const std::string *gw;
+ uint8_t prefixLength = 0;
+ bool errorInEntry = false;
+ if (address)
{
- thisData++;
+ if (ipv4VerifyIpAndGetBitcount(*address))
+ {
+ addr = &(*address);
+ }
+ else
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, *address, pathString + "/Address");
+ errorInEntry = true;
+ }
+ }
+ else if (NICIPentry != ipv4Data.cend())
+ {
+ addr = &(NICIPentry->address);
}
else
{
messages::propertyMissing(asyncResp->res,
pathString + "/Address");
- return;
- // TODO(ratagupt) Not sure about the property where value is
- // list and if unable to update one of the
- // list value then should we proceed further or
- // break there, would ask in the redfish forum
- // till then we stop processing the next list item.
+ errorInEntry = true;
}
- entryIdx++;
- continue; // not an error as per the redfish spec.
- }
-
- std::optional<std::string> address;
- std::optional<std::string> subnetMask;
- std::optional<std::string> gateway;
- if (!json_util::readJson(thisJson, asyncResp->res, "Address",
- address, "SubnetMask", subnetMask,
- "Gateway", gateway))
- {
- return;
- }
-
- if (address)
- {
- if (!ipv4VerifyIpAndGetBitcount(*address))
+ if (subnetMask)
{
- messages::propertyValueFormatError(asyncResp->res, *address,
- pathString + "/Address");
- return;
+ if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, *subnetMask,
+ pathString + "/SubnetMask");
+ errorInEntry = true;
+ }
}
- }
-
- uint8_t prefixLength = 0;
- if (subnetMask)
- {
- if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
+ else if (NICIPentry != ipv4Data.cend())
{
- messages::propertyValueFormatError(
- asyncResp->res, *subnetMask,
- pathString + "/SubnetMask");
- return;
+ if (!ipv4VerifyIpAndGetBitcount(NICIPentry->netmask,
+ &prefixLength))
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, NICIPentry->netmask,
+ pathString + "/SubnetMask");
+ errorInEntry = true;
+ }
}
- }
-
- if (gateway)
- {
- if (!ipv4VerifyIpAndGetBitcount(*gateway))
+ else
{
- messages::propertyValueFormatError(asyncResp->res, *gateway,
- pathString + "/Gateway");
- return;
+ messages::propertyMissing(asyncResp->res,
+ pathString + "/SubnetMask");
+ errorInEntry = true;
}
- }
- // if IP address exist then modify it.
- if (thisData != ipv4StaticData.end())
- {
- // Apply changes
- if (address)
+ if (gateway)
{
- auto callback =
- [asyncResp](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- };
-
- crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
- thisData->id,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", "Address",
- std::variant<std::string>(*address));
+ if (ipv4VerifyIpAndGetBitcount(*gateway))
+ {
+ gw = &(*gateway);
+ }
+ else
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, *gateway, pathString + "/Gateway");
+ errorInEntry = true;
+ }
}
-
- if (subnetMask)
+ else if (NICIPentry != ipv4Data.cend())
{
- changeIPv4SubnetMaskProperty(ifaceId, entryIdx,
- thisData->id, prefixLength,
- asyncResp);
+ gw = &NICIPentry->gateway;
}
-
- if (gateway)
+ else
{
- auto callback =
- [asyncResp](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- };
+ messages::propertyMissing(asyncResp->res,
+ pathString + "/Gateway");
+ errorInEntry = true;
+ }
- crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv4/" +
- thisData->id,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", "Gateway",
- std::variant<std::string>(*gateway));
+ if (errorInEntry)
+ {
+ return;
}
- thisData++;
+ if (NICIPentry != ipv4Data.cend())
+ {
+ deleteAndCreateIPv4(ifaceId, NICIPentry->id, prefixLength,
+ *gw, *addr, asyncResp);
+ NICIPentry =
+ GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
+ }
+ else
+ {
+ createIPv4(ifaceId, entryIdx, prefixLength, *gateway,
+ *address, asyncResp);
+ }
+ entryIdx++;
}
else
{
- // Create IPv4 with provided data
- if (!gateway)
+ if (NICIPentry == ipv4Data.cend())
{
- messages::propertyMissing(asyncResp->res,
- pathString + "/Gateway");
- continue;
+ // Requesting a DELETE/DO NOT MODIFY action for an item
+ // that isn't present on the eth(n) interface. Input JSON is
+ // in error, so bail out.
+ if (thisJson.is_null())
+ {
+ messages::resourceCannotBeDeleted(asyncResp->res);
+ return;
+ }
+ else
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, thisJson.dump(), pathString);
+ return;
+ }
}
- if (!address)
+ if (thisJson.is_null())
{
- messages::propertyMissing(asyncResp->res,
- pathString + "/Address");
- continue;
+ deleteIPv4(ifaceId, NICIPentry->id, asyncResp);
}
-
- if (!subnetMask)
+ if (NICIPentry != ipv4Data.cend())
{
- messages::propertyMissing(asyncResp->res,
- pathString + "/SubnetMask");
- continue;
+ NICIPentry =
+ GetNextStaticIPEntry(++NICIPentry, ipv4Data.cend());
}
-
- createIPv4(ifaceId, entryIdx, prefixLength, *gateway, *address,
- asyncResp);
-
- nlohmann::json &ipv4StaticAddressJson =
- asyncResp->res.jsonValue["IPv4StaticAddresses"][entryIdx];
- ipv4StaticAddressJson["Address"] = *address;
- ipv4StaticAddressJson["SubnetMask"] = *subnetMask;
- ipv4StaticAddressJson["Gateway"] = *gateway;
+ entryIdx++;
}
- entryIdx++;
}
}
@@ -1356,46 +1356,50 @@ class EthernetInterface : public Node
void handleIPv6StaticAddressesPatch(
const std::string &ifaceId, nlohmann::json &input,
- const boost::container::flat_set<IPv6AddressData> &ipv6StaticData,
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data,
const std::shared_ptr<AsyncResp> asyncResp)
{
- if (!input.is_array())
+ if (!input.is_array() || input.empty())
{
messages::propertyValueTypeError(asyncResp->res, input.dump(),
"IPv6StaticAddresses");
return;
}
-
- int entryIdx = 0;
- boost::container::flat_set<IPv6AddressData>::const_iterator thisData =
- ipv6StaticData.begin();
+ int entryIdx = 1;
+ boost::container::flat_set<IPv6AddressData>::const_iterator NICIPentry =
+ GetNextStaticIPEntry(ipv6Data.cbegin(), ipv6Data.cend());
for (nlohmann::json &thisJson : input)
{
std::string pathString =
"IPv6StaticAddresses/" + std::to_string(entryIdx);
- if (thisJson.is_null())
+ if (!thisJson.is_null() && !thisJson.empty())
{
- if (thisData != ipv6StaticData.end())
- {
- deleteIPv6(ifaceId, thisData->id, asyncResp);
- thisData++;
- }
- else
+ std::optional<std::string> address;
+ std::optional<uint8_t> prefixLength;
+
+ if (!json_util::readJson(thisJson, asyncResp->res, "Address",
+ address, "PrefixLength", prefixLength))
{
messages::propertyValueFormatError(
- asyncResp->res, input.dump(), pathString);
+ asyncResp->res, thisJson.dump(), pathString);
return;
}
- entryIdx++;
- continue;
- }
- if (thisJson.empty())
- {
- if (thisData != ipv6StaticData.end())
+ const std::string *addr;
+ uint8_t prefix;
+
+ // Find the address and prefixLength values. Any values that are
+ // not explicitly provided are assumed to be unmodified from the
+ // current state of the interface. Merge existing state into the
+ // current request.
+ if (address)
+ {
+ addr = &(*address);
+ }
+ else if (NICIPentry != ipv6Data.end())
{
- thisData++;
+ addr = &(NICIPentry->address);
}
else
{
@@ -1403,92 +1407,66 @@ class EthernetInterface : public Node
pathString + "/Address");
return;
}
- entryIdx++;
- continue;
- }
-
- std::optional<std::string> address;
- std::optional<uint8_t> prefixLength;
- if (!json_util::readJson(thisJson, asyncResp->res, "Address",
- address, "PrefixLength", prefixLength))
- {
- return;
- }
-
- // if IP address exist then modify it.
- if (thisData != ipv6StaticData.end())
- {
- // Apply changes
- if (address)
+ if (prefixLength)
{
- auto callback =
- [asyncResp](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- };
-
- crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" +
- thisData->id,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", "Address",
- std::variant<std::string>(*address));
+ prefix = *prefixLength;
}
-
- if (prefixLength)
+ else if (NICIPentry != ipv6Data.end())
{
- auto callback =
- [asyncResp](const boost::system::error_code ec) {
- if (ec)
- {
- messages::internalError(asyncResp->res);
- return;
- }
- };
-
- crow::connections::systemBus->async_method_call(
- std::move(callback), "xyz.openbmc_project.Network",
- "/xyz/openbmc_project/network/" + ifaceId + "/ipv6/" +
- thisData->id,
- "org.freedesktop.DBus.Properties", "Set",
- "xyz.openbmc_project.Network.IP", "PrefixLength",
- std::variant<uint8_t>(*prefixLength));
+ prefix = NICIPentry->prefixLength;
+ }
+ else
+ {
+ messages::propertyMissing(asyncResp->res,
+ pathString + "/PrefixLength");
+ return;
}
- thisData++;
+ if (NICIPentry != ipv6Data.end())
+ {
+ deleteAndCreateIPv6(ifaceId, NICIPentry->id, prefix, *addr,
+ asyncResp);
+ NICIPentry =
+ GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
+ }
+ else
+ {
+ createIPv6(ifaceId, *prefixLength, *addr, asyncResp);
+ }
+ entryIdx++;
}
else
{
- // Create IPv6 with provided data
-
- if (!prefixLength)
+ if (NICIPentry == ipv6Data.end())
{
- messages::propertyMissing(asyncResp->res,
- pathString + "/PrefixLength");
- continue;
+ // Requesting a DELETE/DO NOT MODIFY action for an item
+ // that isn't present on the eth(n) interface. Input JSON is
+ // in error, so bail out.
+ if (thisJson.is_null())
+ {
+ messages::resourceCannotBeDeleted(asyncResp->res);
+ return;
+ }
+ else
+ {
+ messages::propertyValueFormatError(
+ asyncResp->res, thisJson.dump(), pathString);
+ return;
+ }
}
- if (!address)
+ if (thisJson.is_null())
{
- messages::propertyMissing(asyncResp->res,
- pathString + "/Address");
- continue;
+ deleteIPv6(ifaceId, NICIPentry->id, asyncResp);
}
-
- createIPv6(ifaceId, entryIdx, *prefixLength, *address,
- asyncResp);
-
- nlohmann::json &ipv6StaticAddressJson =
- asyncResp->res.jsonValue["IPv6StaticAddresses"][entryIdx];
- ipv6StaticAddressJson["Address"] = *address;
- ipv6StaticAddressJson["PrefixLength"] = *prefixLength;
+ if (NICIPentry != ipv6Data.cend())
+ {
+ NICIPentry =
+ GetNextStaticIPEntry(++NICIPentry, ipv6Data.cend());
+ }
+ entryIdx++;
}
- entryIdx++;
}
}
@@ -1496,9 +1474,7 @@ class EthernetInterface : public Node
nlohmann::json &json_response, const std::string &iface_id,
const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData> &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData> &ipv6StaticData)
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data)
{
json_response["Id"] = iface_id;
json_response["@odata.id"] =
@@ -1550,7 +1526,10 @@ class EthernetInterface : public Node
}
nlohmann::json &ipv4_array = json_response["IPv4Addresses"];
+ nlohmann::json &ipv4_static_array =
+ json_response["IPv4StaticAddresses"];
ipv4_array = nlohmann::json::array();
+ ipv4_static_array = nlohmann::json::array();
for (auto &ipv4_config : ipv4Data)
{
@@ -1564,45 +1543,35 @@ class EthernetInterface : public Node
{"SubnetMask", ipv4_config.netmask},
{"Address", ipv4_config.address},
{"Gateway", gatewayStr}});
- }
-
- nlohmann::json &ipv4_static_array =
- json_response["IPv4StaticAddresses"];
- ipv4_static_array = nlohmann::json::array();
- for (auto &ipv4_static_config : ipv4StaticData)
- {
-
- std::string gatewayStr = ipv4_static_config.gateway;
- if (gatewayStr.empty())
+ if (ipv4_config.origin == "Static")
{
- gatewayStr = "0.0.0.0";
+ ipv4_static_array.push_back(
+ {{"AddressOrigin", ipv4_config.origin},
+ {"SubnetMask", ipv4_config.netmask},
+ {"Address", ipv4_config.address},
+ {"Gateway", gatewayStr}});
}
-
- ipv4_static_array.push_back(
- {{"SubnetMask", ipv4_static_config.netmask},
- {"Address", ipv4_static_config.address},
- {"Gateway", gatewayStr}});
}
json_response["IPv6DefaultGateway"] = ethData.ipv6_default_gateway;
nlohmann::json &ipv6_array = json_response["IPv6Addresses"];
+ nlohmann::json &ipv6_static_array =
+ json_response["IPv6StaticAddresses"];
ipv6_array = nlohmann::json::array();
+ ipv6_static_array = nlohmann::json::array();
for (auto &ipv6_config : ipv6Data)
{
ipv6_array.push_back({{"Address", ipv6_config.address},
{"PrefixLength", ipv6_config.prefixLength},
{"AddressOrigin", ipv6_config.origin}});
- }
-
- nlohmann::json &ipv6_static_array =
- json_response["IPv6StaticAddresses"];
- ipv6_static_array = nlohmann::json::array();
- for (auto &ipv6_static_config : ipv6StaticData)
- {
- ipv6_static_array.push_back(
- {{"Address", ipv6_static_config.address},
- {"PrefixLength", ipv6_static_config.prefixLength}});
+ if (ipv6_config.origin == "Static")
+ {
+ ipv6_static_array.push_back(
+ {{"Address", ipv6_config.address},
+ {"PrefixLength", ipv6_config.prefixLength},
+ {"AddressOrigin", ipv6_config.origin}});
+ }
}
}
@@ -1624,11 +1593,7 @@ class EthernetInterface : public Node
[this, asyncResp, iface_id{std::string(params[0])}](
const bool &success, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData>
- &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData>
- &ipv6StaticData) {
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
if (!success)
{
// TODO(Pawel)consider distinguish between non existing
@@ -1646,14 +1611,14 @@ class EthernetInterface : public Node
asyncResp->res.jsonValue["@odata.type"] =
"#EthernetInterface.v1_4_1.EthernetInterface";
asyncResp->res.jsonValue["@odata.context"] =
- "/redfish/v1/$metadata#EthernetInterface.EthernetInterface";
+ "/redfish/v1/"
+ "$metadata#EthernetInterface.EthernetInterface";
asyncResp->res.jsonValue["Name"] = "Manager Ethernet Interface";
asyncResp->res.jsonValue["Description"] =
"Management Network Interface";
parseInterfaceData(asyncResp->res.jsonValue, iface_id, ethData,
- ipv4Data, ipv4StaticData, ipv6Data,
- ipv6StaticData);
+ ipv4Data, ipv6Data);
});
}
@@ -1672,21 +1637,17 @@ class EthernetInterface : public Node
std::optional<std::string> hostname;
std::optional<std::string> macAddress;
std::optional<std::string> ipv6DefaultGateway;
- std::optional<nlohmann::json> ipv4Addresses;
std::optional<nlohmann::json> ipv4StaticAddresses;
- std::optional<nlohmann::json> ipv6Addresses;
std::optional<nlohmann::json> ipv6StaticAddresses;
std::optional<std::vector<std::string>> staticNameServers;
- std::optional<std::vector<std::string>> nameServers;
std::optional<nlohmann::json> dhcpv4;
- if (!json_util::readJson(
- req, res, "HostName", hostname, "IPv4Addresses", ipv4Addresses,
- "IPv4StaticAddresses", ipv4StaticAddresses, "MACAddress",
- macAddress, "StaticNameServers", staticNameServers,
- "IPv6DefaultGateway", ipv6DefaultGateway, "IPv6StaticAddresses",
- ipv6StaticAddresses, "NameServers", nameServers, "DHCPv4",
- dhcpv4))
+ if (!json_util::readJson(req, res, "HostName", hostname,
+ "IPv4StaticAddresses", ipv4StaticAddresses,
+ "MACAddress", macAddress, "StaticNameServers",
+ staticNameServers, "IPv6DefaultGateway",
+ ipv6DefaultGateway, "IPv6StaticAddresses",
+ ipv6StaticAddresses, "DHCPv4", dhcpv4))
{
return;
}
@@ -1696,25 +1657,19 @@ class EthernetInterface : public Node
handleDHCPv4Patch(iface_id, *dhcpv4, asyncResp);
}
- // Get single eth interface data, and call the below callback for JSON
- // preparation
+ // Get single eth interface data, and call the below callback for
+ // JSON preparation
getEthernetIfaceData(
iface_id,
[this, asyncResp, iface_id, hostname = std::move(hostname),
macAddress = std::move(macAddress),
- ipv4Addresses = std::move(ipv4Addresses),
ipv4StaticAddresses = std::move(ipv4StaticAddresses),
ipv6DefaultGateway = std::move(ipv6DefaultGateway),
ipv6StaticAddresses = std::move(ipv6StaticAddresses),
- staticNameServers = std::move(staticNameServers),
- nameServers = std::move(nameServers)](
+ staticNameServers = std::move(staticNameServers)](
const bool &success, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData>
- &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData>
- &ipv6StaticData) {
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
if (!success)
{
// ... otherwise return error
@@ -1735,32 +1690,20 @@ class EthernetInterface : public Node
handleMACAddressPatch(iface_id, *macAddress, asyncResp);
}
- if (ipv4Addresses)
- {
- messages::propertyNotWritable(asyncResp->res,
- "IPv4Addresses");
- }
-
if (ipv4StaticAddresses)
{
// TODO(ed) for some reason the capture of ipv4Addresses
- // above is returning a const value, not a non-const value.
- // This doesn't really work for us, as we need to be able to
- // efficiently move out the intermedia nlohmann::json
- // objects. This makes a copy of the structure, and operates
- // on that, but could be done more efficiently
+ // above is returning a const value, not a non-const
+ // value. This doesn't really work for us, as we need to
+ // be able to efficiently move out the intermedia
+ // nlohmann::json objects. This makes a copy of the
+ // structure, and operates on that, but could be done
+ // more efficiently
nlohmann::json ipv4Static = std::move(*ipv4StaticAddresses);
- handleIPv4StaticPatch(iface_id, ipv4Static, ipv4StaticData,
+ handleIPv4StaticPatch(iface_id, ipv4Static, ipv4Data,
asyncResp);
}
- if (nameServers)
- {
- // Data.Permissions is read-only
- messages::propertyNotWritable(asyncResp->res,
- "NameServers");
- }
-
if (staticNameServers)
{
handleStaticNameServersPatch(iface_id, *staticNameServers,
@@ -1777,7 +1720,7 @@ class EthernetInterface : public Node
{
nlohmann::json ipv6Static = std::move(*ipv6StaticAddresses);
handleIPv6StaticAddressesPatch(iface_id, ipv6Static,
- ipv6StaticData, asyncResp);
+ ipv6Data, asyncResp);
}
});
}
@@ -1813,9 +1756,7 @@ class VlanNetworkInterface : public Node
nlohmann::json &json_response, const std::string &parent_iface_id,
const std::string &iface_id, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData> &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData> &ipv6StaticData)
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data)
{
// Fill out obvious data...
json_response["Id"] = iface_id;
@@ -1865,7 +1806,8 @@ class VlanNetworkInterface : public Node
res.jsonValue["@odata.type"] =
"#VLanNetworkInterface.v1_1_0.VLanNetworkInterface";
res.jsonValue["@odata.context"] =
- "/redfish/v1/$metadata#VLanNetworkInterface.VLanNetworkInterface";
+ "/redfish/v1/"
+ "$metadata#VLanNetworkInterface.VLanNetworkInterface";
res.jsonValue["Name"] = "VLAN Network Interface";
if (!verifyNames(parent_iface_id, iface_id))
@@ -1873,25 +1815,20 @@ class VlanNetworkInterface : public Node
return;
}
- // Get single eth interface data, and call the below callback for JSON
- // preparation
+ // Get single eth interface data, and call the below callback for
+ // JSON preparation
getEthernetIfaceData(
params[1],
[this, asyncResp, parent_iface_id{std::string(params[0])},
iface_id{std::string(params[1])}](
const bool &success, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData>
- &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData>
- &ipv6StaticData) {
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
if (success && ethData.vlan_id.size() != 0)
{
parseInterfaceData(asyncResp->res.jsonValue,
parent_iface_id, iface_id, ethData,
- ipv4Data, ipv4StaticData, ipv6Data,
- ipv6StaticData);
+ ipv4Data, ipv6Data);
}
else
{
@@ -1933,19 +1870,15 @@ class VlanNetworkInterface : public Node
return;
}
- // Get single eth interface data, and call the below callback for JSON
- // preparation
+ // Get single eth interface data, and call the below callback for
+ // JSON preparation
getEthernetIfaceData(
params[1],
[this, asyncResp, parentIfaceId{std::string(params[0])},
ifaceId{std::string(params[1])}, &vlanEnable, &vlanId](
const bool &success, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData>
- &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData>
- &ipv6StaticData) {
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
if (success && !ethData.vlan_id.empty())
{
auto callback =
@@ -2007,19 +1940,15 @@ class VlanNetworkInterface : public Node
return;
}
- // Get single eth interface data, and call the below callback for JSON
- // preparation
+ // Get single eth interface data, and call the below callback for
+ // JSON preparation
getEthernetIfaceData(
params[1],
[this, asyncResp, parentIfaceId{std::string(params[0])},
ifaceId{std::string(params[1])}](
const bool &success, const EthernetInterfaceData &ethData,
const boost::container::flat_set<IPv4AddressData> &ipv4Data,
- const boost::container::flat_set<IPv4AddressData>
- &ipv4StaticData,
- const boost::container::flat_set<IPv6AddressData> &ipv6Data,
- const boost::container::flat_set<IPv6AddressData>
- &ipv6StaticData) {
+ const boost::container::flat_set<IPv6AddressData> &ipv6Data) {
if (success && !ethData.vlan_id.empty())
{
auto callback =