persistence.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. package network
  2. import (
  3. "fmt"
  4. "os"
  5. "path/filepath"
  6. "strings"
  7. "time"
  8. )
  9. const (
  10. DebianInterfacesPath = "/etc/network/interfaces"
  11. DebianVlansCfgPath = "/etc/network/interfaces.d/vlans.cfg"
  12. )
  13. // PersistenceManager handles writing Debian network configuration files.
  14. type PersistenceManager struct {
  15. netMgr *Manager
  16. }
  17. func NewPersistenceManager(netMgr *Manager) *PersistenceManager {
  18. return &PersistenceManager{netMgr: netMgr}
  19. }
  20. // GenerateDebianConfig creates the contents of the /etc/network/interfaces.d/vlans.cfg file.
  21. func (p *PersistenceManager) GenerateDebianConfig() (string, error) {
  22. vlans, err := p.netMgr.ListVLANs()
  23. if err != nil {
  24. return "", err
  25. }
  26. var sb strings.Builder
  27. sb.WriteString(fmt.Sprintf("# ====================================================\n"))
  28. sb.WriteString(fmt.Sprintf("# Proxmox Router Appliance - VLANs Configuration\n"))
  29. sb.WriteString(fmt.Sprintf("# Generated: %s\n", time.Now().Format(time.RFC1123)))
  30. sb.WriteString(fmt.Sprintf("# DO NOT EDIT MANUALLY - Managed via Router Web UI\n"))
  31. sb.WriteString(fmt.Sprintf("# ====================================================\n\n"))
  32. if len(vlans) == 0 {
  33. sb.WriteString("# No VLAN sub-interfaces configured.\n")
  34. return sb.String(), nil
  35. }
  36. for _, v := range vlans {
  37. sb.WriteString(fmt.Sprintf("# --- VLAN ID %d (%s) ---\n", v.VlanID, v.Name))
  38. if v.Description != "" {
  39. sb.WriteString(fmt.Sprintf("# Description: %s\n", v.Description))
  40. }
  41. if v.Role != "" {
  42. sb.WriteString(fmt.Sprintf("# Role: %s\n", v.Role))
  43. }
  44. sb.WriteString(fmt.Sprintf("auto %s\n", v.Name))
  45. if len(v.IPv4) > 0 {
  46. sb.WriteString(fmt.Sprintf("iface %s inet static\n", v.Name))
  47. sb.WriteString(fmt.Sprintf(" address %s\n", v.IPv4[0]))
  48. if v.ParentName != "" {
  49. sb.WriteString(fmt.Sprintf(" vlan-raw-device %s\n", v.ParentName))
  50. }
  51. if v.MTU > 0 && v.MTU != 1500 {
  52. sb.WriteString(fmt.Sprintf(" mtu %d\n", v.MTU))
  53. }
  54. // additional secondary IPv4 addresses
  55. for i := 1; i < len(v.IPv4); i++ {
  56. sb.WriteString(fmt.Sprintf(" up ip addr add %s dev %s\n", v.IPv4[i], v.Name))
  57. sb.WriteString(fmt.Sprintf(" down ip addr del %s dev %s\n", v.IPv4[i], v.Name))
  58. }
  59. } else {
  60. sb.WriteString(fmt.Sprintf("iface %s inet manual\n", v.Name))
  61. if v.ParentName != "" {
  62. sb.WriteString(fmt.Sprintf(" vlan-raw-device %s\n", v.ParentName))
  63. }
  64. }
  65. sb.WriteString("\n")
  66. }
  67. return sb.String(), nil
  68. }
  69. // EnsureSourceInclude ensures that /etc/network/interfaces contains "source /etc/network/interfaces.d/*".
  70. func (p *PersistenceManager) EnsureSourceInclude() error {
  71. content, err := os.ReadFile(DebianInterfacesPath)
  72. if err != nil {
  73. if os.IsNotExist(err) {
  74. return nil
  75. }
  76. return err
  77. }
  78. str := string(content)
  79. if strings.Contains(str, "source /etc/network/interfaces.d/*") ||
  80. strings.Contains(str, "source-directory /etc/network/interfaces.d") {
  81. return nil
  82. }
  83. // Append source directive
  84. newContent := str + "\n# Include extra interface configurations\nsource /etc/network/interfaces.d/*\n"
  85. return os.WriteFile(DebianInterfacesPath, []byte(newContent), 0644)
  86. }
  87. // SaveDebianConfig writes the configuration to disk.
  88. func (p *PersistenceManager) SaveDebianConfig() (string, error) {
  89. cfg, err := p.GenerateDebianConfig()
  90. if err != nil {
  91. return "", err
  92. }
  93. dir := filepath.Dir(DebianVlansCfgPath)
  94. if err := os.MkdirAll(dir, 0755); err != nil {
  95. return "", fmt.Errorf("failed to create directory %s: %w", dir, err)
  96. }
  97. if err := os.WriteFile(DebianVlansCfgPath, []byte(cfg), 0644); err != nil {
  98. return "", fmt.Errorf("failed to write %s: %w", DebianVlansCfgPath, err)
  99. }
  100. _ = p.EnsureSourceInclude()
  101. return cfg, nil
  102. }