Blob Blame History Raw
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