| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- #!/usr/bin/env python3
- """
- DebTik Network Interface Name Reconciler
- Runs before networking.service / network-pre.target on boot.
- Reads /etc/debtik/metadata.json (or legacy /etc/proxmox-router/metadata.json).
- Matches physical/veth devices by hardware MAC address and renames them to match the target interface name.
- Also brings UP interfaces that have configured IP addresses or auto_up set.
- """
- import json
- import os
- import subprocess
- import sys
- import time
- METADATA_PATHS = [
- "/etc/debtik/metadata.json",
- "/etc/proxmox-router/metadata.json"
- ]
- def log(msg):
- print(f"[debtik-ifnames] {msg}", file=sys.stderr)
- def get_current_interfaces():
- res = {}
- net_dir = "/sys/class/net"
- if not os.path.exists(net_dir):
- return res
- for dev in os.listdir(net_dir):
- if dev == "lo":
- continue
- addr_file = os.path.join(net_dir, dev, "address")
- if os.path.exists(addr_file):
- try:
- with open(addr_file, "r") as f:
- mac = f.read().strip().lower()
- if mac and mac != "00:00:00:00:00:00":
- res[dev] = mac
- except Exception:
- pass
- return res
- def rename_device(old_name, new_name):
- try:
- if os.path.exists(f"/sys/class/net/{new_name}"):
- tmp_name = f"tmp{int(time.time() * 1000) % 100000}"
- subprocess.run(["ip", "link", "set", "dev", new_name, "down"], check=False)
- subprocess.run(["ip", "link", "set", "dev", new_name, "name", tmp_name], check=False)
- log(f"Moved conflicting {new_name} -> {tmp_name}")
- subprocess.run(["ip", "link", "set", "dev", old_name, "down"], check=False)
- r = subprocess.run(["ip", "link", "set", "dev", old_name, "name", new_name], capture_output=True, text=True)
- if r.returncode == 0:
- log(f"Successfully renamed {old_name} -> {new_name}")
- return True
- else:
- log(f"Failed to rename {old_name} -> {new_name}: {r.stderr.strip()}")
- return False
- except Exception as e:
- log(f"Exception renaming {old_name} -> {new_name}: {e}")
- return False
- def main():
- metadata = None
- for p in METADATA_PATHS:
- if os.path.exists(p):
- try:
- with open(p, "r") as f:
- metadata = json.load(f)
- break
- except Exception as e:
- log(f"Error reading {p}: {e}")
- if not metadata or "interfaces" not in metadata:
- sys.exit(0)
- target_map = {}
- auto_up_names = set()
- for name, item in metadata["interfaces"].items():
- mac = item.get("mac", "").strip().lower()
- if mac:
- target_map[mac] = name
- if item.get("auto_up") or item.get("ip_addresses"):
- auto_up_names.add(name)
- if not target_map:
- sys.exit(0)
- current_devs = get_current_interfaces()
- for dev_name, mac in list(current_devs.items()):
- if mac in target_map:
- target_name = target_map[mac]
- if dev_name != target_name:
- log(f"Device {dev_name} has MAC {mac}, reconciling to {target_name}")
- if rename_device(dev_name, target_name):
- current_devs[target_name] = mac
- del current_devs[dev_name]
- if target_name in auto_up_names:
- subprocess.run(["ip", "link", "set", "dev", target_name, "up"], check=False)
- if __name__ == "__main__":
- main()
|