summaryrefslogtreecommitdiff
path: root/python
diff options
context:
space:
mode:
authorChristian Breunig <christian@breunig.cc>2026-10-07 19:56:49 +0200
committerChristian Breunig <christian@breunig.cc>2026-10-31 09:58:29 +0000
commit035d0260f65ad327c5a6a512a37157e96ca50fcf (patch)
tree85cde42c75f11685cb931344b2a0871b7f39ed5c /python
parent0b9903a85ccbef75b653a4d4349db5710ee9638d (diff)
downloadvyos-1x-035d0260f65ad327c5a6a512a37157e96ca50fcf.tar.gz
vyos-1x-035d0260f65ad327c5a6a512a37157e96ca50fcf.zip
interfaces: T3871: name a card by the card, not only by the slot it sits in
The map now records which card each name sat on. A card moved to another slot keeps its name rather than leaving it behind for whatever turns up next, and the slot it vacated does not lend that name out. Where the map and the hardware disagree, the map wins: a card the store already knows, found in a name's slot, takes that name. Only a card nothing knows about, sitting in a slot on record, is treated as a replacement - the one case which can quietly move a configured address onto another cable.
Diffstat (limited to 'python')
-rw-r--r--python/vyos/ifconfig/ifname_store.py51
1 files changed, 43 insertions, 8 deletions
diff --git a/python/vyos/ifconfig/ifname_store.py b/python/vyos/ifconfig/ifname_store.py
index 9c00810db..f2131a55a 100644
--- a/python/vyos/ifconfig/ifname_store.py
+++ b/python/vyos/ifconfig/ifname_store.py
@@ -314,7 +314,7 @@ def resolve(devices: list, store: dict) -> tuple:
seen = store.get('hardware', {})
assigned = {} # final name -> device
- report = {'matched': {}, 'bootstrapped': {}}
+ report = {'matched': {}, 'moved': {}, 'replaced': {}, 'bootstrapped': {}}
# A recorded name stays off-limits even while its slot is empty, so new
# hardware cannot inherit an absent interface's name and configuration.
@@ -326,13 +326,53 @@ def resolve(devices: list, store: dict) -> tuple:
report[bucket][name] = device['mac']
remaining.remove(device)
- # whatever card occupies the recorded slot is this interface
+ # the recorded card in its recorded slot. A name with no card on record
+ # counts too - a store written before addresses were kept has nothing to
+ # disagree with, and this is as certain as it gets.
for name, key in entries.items():
for device in remaining:
- if device_matches(device['properties'], key):
+ if (device_matches(device['properties'], key)
+ and seen.get(name, device['mac']) == device['mac']):
claim(device, name, 'matched')
break
+ # a card the store already knows about, sitting in this name's slot. The
+ # cards were rearranged among themselves, or the map was edited by hand -
+ # either way the map is what decides names, so the slot wins and the
+ # address on record simply follows.
+ known = {mac for mac in seen.values() if mac}
+ for name, key in entries.items():
+ if name in assigned:
+ continue
+ for device in remaining:
+ if (device_matches(device['properties'], key)
+ and device['mac'] in known):
+ claim(device, name, 'matched')
+ break
+
+ # the card moved, and nothing the store knows took its place. Its address
+ # identifies it, so the name follows the card rather than being handed to
+ # a stranger that happens to have landed in the slot it left.
+ for name in entries:
+ if name in assigned or not seen.get(name):
+ continue
+ for device in remaining:
+ if device['mac'] == seen[name]:
+ claim(device, name, 'moved')
+ break
+
+ # a different card in the recorded slot: an in-place replacement, or an
+ # unrelated card that happened to land there once the original was pulled.
+ # Nothing on the device tells those apart, so the slot decides and it is
+ # reported and warned about rather than taken silently.
+ for name, key in entries.items():
+ if name in assigned:
+ continue
+ for device in remaining:
+ if device_matches(device['properties'], key):
+ claim(device, name, 'replaced')
+ break
+
# unknown hardware: lowest free name of its type, in hardware order
for device in sorted(remaining, key=canonical_sort_key):
name = _next_free(reserved | set(assigned), _prefix_for(device))
@@ -351,11 +391,6 @@ def resolve(devices: list, store: dict) -> tuple:
if device['mac']:
new_hardware[name] = device['mac']
- # a slot which kept its name but changed card hands that interface's
- # addresses to different hardware - never silently
- report['replaced'] = {name: mac for name, mac in report['matched'].items()
- if seen.get(name) and seen[name] != mac}
-
plan = {device['name']: name for name, device in assigned.items()
if device['name'] != name}