| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- package network
- import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
- "time"
- )
- const (
- DebianInterfacesPath = "/etc/network/interfaces"
- DebianVlansCfgPath = "/etc/network/interfaces.d/vlans.cfg"
- DebianIfacesCfgPath = "/etc/network/interfaces.d/interfaces.cfg"
- )
- // PersistenceManager handles writing Debian network configuration files.
- type PersistenceManager struct {
- netMgr *Manager
- }
- func NewPersistenceManager(netMgr *Manager) *PersistenceManager {
- return &PersistenceManager{netMgr: netMgr}
- }
- // GenerateDebianConfig creates the contents of the /etc/network/interfaces.d/vlans.cfg file.
- func (p *PersistenceManager) GenerateDebianConfig() (string, error) {
- vlans, err := p.netMgr.ListVLANs()
- if err != nil {
- return "", err
- }
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("# ====================================================\n"))
- sb.WriteString(fmt.Sprintf("# Proxmox Router Appliance - VLANs Configuration\n"))
- sb.WriteString(fmt.Sprintf("# Generated: %s\n", time.Now().Format(time.RFC1123)))
- sb.WriteString(fmt.Sprintf("# DO NOT EDIT MANUALLY - Managed via Router Web UI\n"))
- sb.WriteString(fmt.Sprintf("# ====================================================\n\n"))
- if len(vlans) == 0 {
- sb.WriteString("# No VLAN sub-interfaces configured.\n")
- return sb.String(), nil
- }
- for _, v := range vlans {
- sb.WriteString(fmt.Sprintf("# --- VLAN ID %d (%s) ---\n", v.VlanID, v.Name))
- if v.Description != "" {
- sb.WriteString(fmt.Sprintf("# Description: %s\n", v.Description))
- }
- if v.Role != "" {
- sb.WriteString(fmt.Sprintf("# Role: %s\n", v.Role))
- }
- sb.WriteString(fmt.Sprintf("auto %s\n", v.Name))
- if len(v.IPv4) > 0 {
- sb.WriteString(fmt.Sprintf("iface %s inet static\n", v.Name))
- sb.WriteString(fmt.Sprintf(" address %s\n", v.IPv4[0]))
- if v.ParentName != "" {
- sb.WriteString(fmt.Sprintf(" vlan-raw-device %s\n", v.ParentName))
- }
- if v.MTU > 0 && v.MTU != 1500 {
- sb.WriteString(fmt.Sprintf(" mtu %d\n", v.MTU))
- }
- // additional secondary IPv4 addresses
- for i := 1; i < len(v.IPv4); i++ {
- sb.WriteString(fmt.Sprintf(" up ip addr add %s dev %s\n", v.IPv4[i], v.Name))
- sb.WriteString(fmt.Sprintf(" down ip addr del %s dev %s\n", v.IPv4[i], v.Name))
- }
- } else {
- sb.WriteString(fmt.Sprintf("iface %s inet manual\n", v.Name))
- if v.ParentName != "" {
- sb.WriteString(fmt.Sprintf(" vlan-raw-device %s\n", v.ParentName))
- }
- }
- sb.WriteString("\n")
- }
- return sb.String(), nil
- }
- // EnsureSourceInclude ensures that /etc/network/interfaces contains loopback and "source /etc/network/interfaces.d/*"
- // while removing duplicate interface stanzas that are managed inside interfaces.d/.
- func (p *PersistenceManager) EnsureSourceInclude() error {
- cleanContent := `# The loopback network interface
- auto lo
- iface lo inet loopback
- # Include extra interface configurations managed by DebTik / Proxmox Router
- source /etc/network/interfaces.d/*
- `
- return os.WriteFile(DebianInterfacesPath, []byte(cleanContent), 0644)
- }
- // GenerateInterfacesConfig creates /etc/network/interfaces.d/interfaces.cfg for regular interfaces
- func (p *PersistenceManager) GenerateInterfacesConfig() (string, error) {
- ifacesWithIPs := p.netMgr.metadata.GetAllWithIPs()
- var sb strings.Builder
- sb.WriteString("# ====================================================\n")
- sb.WriteString("# Proxmox Router Appliance - Interface IP Configuration\n")
- sb.WriteString(fmt.Sprintf("# Generated: %s\n", time.Now().Format(time.RFC1123)))
- sb.WriteString("# DO NOT EDIT MANUALLY - Managed via Router Web UI\n")
- sb.WriteString("# ====================================================\n\n")
- if len(ifacesWithIPs) == 0 {
- sb.WriteString("# No persistent physical interface IPs configured.\n")
- return sb.String(), nil
- }
- for ifaceName, ips := range ifacesWithIPs {
- if len(ips) == 0 || strings.HasPrefix(ifaceName, "vlan") || strings.Contains(ifaceName, ".") {
- continue // Skip VLANs (handled in vlans.cfg)
- }
- sb.WriteString(fmt.Sprintf("# --- Interface %s ---\n", ifaceName))
- sb.WriteString(fmt.Sprintf("auto %s\n", ifaceName))
- sb.WriteString(fmt.Sprintf("iface %s inet static\n", ifaceName))
- sb.WriteString(fmt.Sprintf(" address %s\n", ips[0]))
- for i := 1; i < len(ips); i++ {
- sb.WriteString(fmt.Sprintf(" up ip addr add %s dev %s || true\n", ips[i], ifaceName))
- sb.WriteString(fmt.Sprintf(" down ip addr del %s dev %s || true\n", ips[i], ifaceName))
- }
- sb.WriteString("\n")
- }
- return sb.String(), nil
- }
- // SaveDebianConfig writes the configuration to disk.
- func (p *PersistenceManager) SaveDebianConfig() (string, error) {
- cfg, err := p.GenerateDebianConfig()
- if err != nil {
- return "", err
- }
- dir := filepath.Dir(DebianVlansCfgPath)
- if err := os.MkdirAll(dir, 0755); err != nil {
- return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
- }
- if err := os.WriteFile(DebianVlansCfgPath, []byte(cfg), 0644); err != nil {
- return "", fmt.Errorf("failed to write %s: %w", DebianVlansCfgPath, err)
- }
- ifaceCfg, err := p.GenerateInterfacesConfig()
- if err == nil {
- _ = os.WriteFile(DebianIfacesCfgPath, []byte(ifaceCfg), 0644)
- }
- _ = p.EnsureSourceInclude()
- return cfg, nil
- }
|