From 34b689a86c7cd4bcf5ab5cf7d903acba7d60f4be Mon Sep 17 00:00:00 2001
From: Gris Ge <fge@redhat.com>
Date: Wed, 10 Feb 2021 23:28:37 +0800
Subject: [PATCH] bridge/bond: New property `Interface.COPY_MAC_FROM`
Introducing `Interface.COPY_MAC_FROM` allowing bridge or bond
to use MAC address from specified port.
Example:
```yaml
---
interfaces:
- name: bond99
type: bond
state: up
copy-mac-from: eth1
link-aggregation:
mode: balance-rr
port:
- eth2
- eth1
```
Integration test case included.
Signed-off-by: Gris Ge <fge@redhat.com>
Signed-off-by: Fernando Fernandez Mancera <ffmancera@riseup.net>
---
libnmstate/ifaces/base_iface.py | 14 +++++++++++
libnmstate/ifaces/ifaces.py | 30 +++++++++++++++++++++++
libnmstate/schema.py | 1 +
libnmstate/schemas/operational-state.yaml | 4 +++
tests/integration/bond_test.py | 13 ++++++++++
tests/integration/linux_bridge_test.py | 14 +++++++++++
6 files changed, 76 insertions(+)
diff --git a/libnmstate/ifaces/base_iface.py b/libnmstate/ifaces/base_iface.py
index b4ade867..3dbcfe52 100644
--- a/libnmstate/ifaces/base_iface.py
+++ b/libnmstate/ifaces/base_iface.py
@@ -391,6 +391,20 @@ class BaseIface:
for family, rules in route_rule_metadata.items():
self.raw[family][BaseIface.ROUTE_RULES_METADATA] = rules
+ @property
+ def copy_mac_from(self):
+ return self._info.get(Interface.COPY_MAC_FROM)
+
+ def apply_copy_mac_from(self, mac):
+ """
+ * Add MAC to original desire.
+ * Remove Interface.COPY_MAC_FROM from original desire.
+ * Update MAC of merge iface
+ """
+ self.raw[Interface.MAC] = mac
+ self._origin_info[Interface.MAC] = mac
+ self._origin_info.pop(Interface.COPY_MAC_FROM, None)
+
def _remove_empty_description(state):
if state.get(Interface.DESCRIPTION) == "":
diff --git a/libnmstate/ifaces/ifaces.py b/libnmstate/ifaces/ifaces.py
index cfc306c6..1e4d2231 100644
--- a/libnmstate/ifaces/ifaces.py
+++ b/libnmstate/ifaces/ifaces.py
@@ -101,12 +101,42 @@ class Ifaces:
self._validate_unknown_slaves()
self._mark_vf_interface_as_absent_when_sriov_vf_decrease()
self._validate_unknown_parent()
+ self._apply_copy_mac_from()
self._gen_metadata()
for iface in self._ifaces.values():
iface.pre_edit_validation_and_cleanup()
self._pre_edit_validation_and_cleanup()
+ def _apply_copy_mac_from(self):
+ for iface in self._ifaces.values():
+ if iface.type not in (
+ InterfaceType.LINUX_BRIDGE,
+ InterfaceType.BOND,
+ ):
+ continue
+ if not iface.copy_mac_from:
+ continue
+
+ if iface.copy_mac_from not in iface.slaves:
+ raise NmstateValueError(
+ f"The interface {iface.name} is holding invalid "
+ f"{Interface.COPY_MAC_FROM} property "
+ f"as {iface.copy_mac_from} is not in the port "
+ f"list: {iface.port}"
+ )
+ port_iface = self._ifaces.get(iface.copy_mac_from)
+ # TODO: bridge/bond might refering the mac from new veth in the
+ # same desire state, it too complex to support that.
+ if not port_iface:
+ raise NmstateValueError(
+ f"The interface {iface.name} is holding invalid "
+ f"{Interface.COPY_MAC_FROM} property "
+ f"as the port {iface.copy_mac_from} does not exists yet"
+ )
+
+ iface.apply_copy_mac_from(port_iface.mac)
+
def _create_virtual_slaves(self):
"""
Certain master interface could have virtual slaves which does not
diff --git a/libnmstate/schema.py b/libnmstate/schema.py
index 8a86c48d..455dbf8f 100644
--- a/libnmstate/schema.py
+++ b/libnmstate/schema.py
@@ -44,6 +44,7 @@ class Interface:
MAC = "mac-address"
MTU = "mtu"
+ COPY_MAC_FROM = "copy-mac-from"
class Route:
diff --git a/libnmstate/schemas/operational-state.yaml b/libnmstate/schemas/operational-state.yaml
index d856aa5c..8fdeaeec 100644
--- a/libnmstate/schemas/operational-state.yaml
+++ b/libnmstate/schemas/operational-state.yaml
@@ -249,6 +249,8 @@ definitions:
type: string
enum:
- bond
+ copy-mac-from:
+ type: string
link-aggregation:
type: object
properties:
@@ -267,6 +269,8 @@ definitions:
- $ref: "#/definitions/interface-linux-bridge/ro"
ro:
properties:
+ copy-mac-from:
+ type: string
bridge:
type: object
properties:
--
2.29.2