From a3aa111076a9ab5bbda87547e939951ddeda3895 Mon Sep 17 00:00:00 2001
From: Vjaceslavs Klimovs <vklimovs@gmail.com>
Date: Sat, 10 Oct 2026 09:28:22 -0700
Subject: [PATCH] link: add clear_altnames defaults option to keep unconfigured
 altnames

---
 docs/docs/defaults.md             |  1 +
 docs/docs/interfaces.md           |  2 +-
 schema/2/ifstate.conf.schema.json |  5 +++++
 src/libifstate/__init__.py        |  9 ++++++++-
 src/libifstate/link/base.py       | 10 ++++++----
 src/libifstate/parser/base.py     |  1 +
 6 files changed, 22 insertions(+), 6 deletions(-)

diff --git a/docs/docs/defaults.md b/docs/docs/defaults.md
index 91abc64..60772fa 100644
--- a/docs/docs/defaults.md
+++ b/docs/docs/defaults.md
@@ -25,6 +25,7 @@ parameters:
   - match:
     - ifname: ''
     clear_addresses: true
+    clear_altnames: true
     clear_fdb: true
     clear_neighbours: true
     clear_tc: true
diff --git a/docs/docs/interfaces.md b/docs/docs/interfaces.md
index a5edbe2..953a398 100644
--- a/docs/docs/interfaces.md
+++ b/docs/docs/interfaces.md
@@ -35,7 +35,7 @@ These parameters cannot be changed by the running operating system and are there
 
 Other settings available for an inteface:
 
-- [*alt-names*](/schema/{{ ifstate_branch }}/#interfaces_pattern1_alt-names) - interface altnames
+- [*alt-names*](/schema/{{ ifstate_branch }}/#interfaces_pattern1_alt-names) - interface altnames, unconfigured altnames are removed unless disabled by *clear_altnames* in [defaults](defaults.md)
 - [*addresses*](/schema/{{ ifstate_branch }}/#interfaces_pattern1_addresses) - assigned ip addresses
 - [*flags*](/schema/{{ ifstate_branch }}/#interfaces_pattern1_flags) - device flags
 - [*token*](/schema/{{ ifstate_branch }}/#interfaces_pattern1_token) - IPv6 tokenized interface identifier
diff --git a/schema/2/ifstate.conf.schema.json b/schema/2/ifstate.conf.schema.json
index 877e869..2dd1e69 100644
--- a/schema/2/ifstate.conf.schema.json
+++ b/schema/2/ifstate.conf.schema.json
@@ -2182,6 +2182,11 @@
                                 "type": "boolean",
                                 "default": false
                             },
+                            "clear_altnames": {
+                                "description": "implicit remove all interface alternative names",
+                                "type": "boolean",
+                                "default": true
+                            },
                             "clear_fdb": {
                                 "description": "implicit remove all permanent fdb entries",
                                 "type": "boolean",
diff --git a/src/libifstate/__init__.py b/src/libifstate/__init__.py
index 5436bcc..208a3c8 100644
--- a/src/libifstate/__init__.py
+++ b/src/libifstate/__init__.py
@@ -266,6 +266,13 @@ class IfState:
             else:
                 ethtool = None
 
+            if "alt-names" in ifstate:
+                alt_names = ifstate["alt-names"]
+            elif defaults.get("clear_altnames", True):
+                alt_names = []
+            else:
+                alt_names = None
+
             link = {}
             if "link" in defaults:
                 link.update(defaults["link"])
@@ -281,7 +288,7 @@ class IfState:
                     ethtool,
                     ifstate.get("hooks", []),
                     ifstate.get("vrrp"),
-                    ifstate.get("alt-names"),
+                    alt_names,
                     ifstate.get("brport"),
                     ifstate.get("brvlan"),
                     ifstate.get("token"),
diff --git a/src/libifstate/link/base.py b/src/libifstate/link/base.py
index c7bdcb4..a1d86a8 100644
--- a/src/libifstate/link/base.py
+++ b/src/libifstate/link/base.py
@@ -191,10 +191,10 @@ class Link(ABC):
         self.ethtool = None
         self.hooks = hooks
         self.vrrp = vrrp
-        if altnames:
+        if altnames is not None:
             self.altnames = AltNames(netns, name, altnames)
         else:
-            self.altnames = AltNames(netns, name, [])
+            self.altnames = None
         if brport:
             self.brport = BRPort(netns, name, brport)
         else:
@@ -982,7 +982,8 @@ class Link(ABC):
                             excpts.add("set", err, master=master)
 
                     # configure altnames
-                    self.altnames.apply(do_apply, self.idx, excpts)
+                    if self.altnames is not None:
+                        self.altnames.apply(do_apply, self.idx, excpts)
 
                     # set brport settings if required
                     if self.brport and self.brport.has_changes(self.idx):
@@ -1162,7 +1163,8 @@ class Link(ABC):
                                 has_ethtool_changes.add(setting)
 
         # configure altnames
-        self.altnames.apply(do_apply, self.idx, excpts)
+        if self.altnames is not None:
+            self.altnames.apply(do_apply, self.idx, excpts)
 
         has_brport_changes = False
         if self.brport:
diff --git a/src/libifstate/parser/base.py b/src/libifstate/parser/base.py
index 3b1e6ae..e6fcc45 100644
--- a/src/libifstate/parser/base.py
+++ b/src/libifstate/parser/base.py
@@ -52,6 +52,7 @@ class Parser(ABC):
                 {
                     "match": [{"ifname": ""}],
                     "clear_addresses": True,
+                    "clear_altnames": True,
                     "clear_fdb": True,
                     "clear_neighbours": True,
                     "clear_tc": True,
-- 
2.55.0

