netplan.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. package netplan
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "networktool/internal/model"
  9. "gopkg.in/yaml.v3"
  10. )
  11. type Service struct{}
  12. type fileConfig struct {
  13. Network map[string]any `yaml:"network"`
  14. }
  15. func New() *Service { return &Service{} }
  16. func (s *Service) FindSingleFile() (string, error) {
  17. files, err := filepath.Glob("/etc/netplan/*.yaml")
  18. if err != nil {
  19. return "", err
  20. }
  21. if len(files) == 0 {
  22. return "", fmt.Errorf("未找到 netplan 配置文件")
  23. }
  24. if len(files) > 1 {
  25. return "", fmt.Errorf("检测到多个 netplan 配置文件,首版暂不支持自动处理")
  26. }
  27. return files[0], nil
  28. }
  29. func (s *Service) Backup(path string) (string, error) {
  30. backupPath := path + ".networktool.bak"
  31. data, err := os.ReadFile(path)
  32. if err != nil {
  33. return "", err
  34. }
  35. if err := os.WriteFile(backupPath, data, 0600); err != nil {
  36. return "", err
  37. }
  38. return backupPath, nil
  39. }
  40. func (s *Service) Restore(path string, backupPath string) error {
  41. data, err := os.ReadFile(backupPath)
  42. if err != nil {
  43. return err
  44. }
  45. if err := os.WriteFile(path, data, 0600); err != nil {
  46. return err
  47. }
  48. _ = os.Remove(backupPath)
  49. return nil
  50. }
  51. func (s *Service) Write(path string, targetInterface string, input model.InterfaceConfig, managementInterface string, maintenanceCIDR string) error {
  52. data, err := os.ReadFile(path)
  53. if err != nil {
  54. return err
  55. }
  56. var cfg fileConfig
  57. if err := yaml.Unmarshal(data, &cfg); err != nil {
  58. return err
  59. }
  60. if cfg.Network == nil {
  61. cfg.Network = make(map[string]any)
  62. }
  63. if _, ok := cfg.Network["version"]; !ok {
  64. cfg.Network["version"] = 2
  65. }
  66. ethernets := ensureMap(cfg.Network, "ethernets")
  67. target := ensureMap(ethernets, targetInterface)
  68. if input.Dhcp4 {
  69. target["dhcp4"] = true
  70. delete(target, "addresses")
  71. delete(target, "gateway4")
  72. delete(target, "routes")
  73. if len(input.DNS) > 0 {
  74. nameservers := ensureMap(target, "nameservers")
  75. nameservers["addresses"] = input.DNS
  76. } else {
  77. delete(target, "nameservers")
  78. }
  79. output, err := marshalYAML(&cfg)
  80. if err != nil {
  81. return err
  82. }
  83. return os.WriteFile(path, output, 0600)
  84. }
  85. addresses := normalizedAddresses(input)
  86. routes := normalizedRoutes(input)
  87. target["dhcp4"] = false
  88. addressItems := make([]string, 0, len(addresses))
  89. for _, address := range addresses {
  90. addressItems = append(addressItems, fmt.Sprintf("%s/%d", strings.TrimSpace(address.IP), address.Prefix))
  91. }
  92. target["addresses"] = addressItems
  93. delete(target, "gateway4")
  94. if len(routes) > 0 {
  95. routeItems := make([]map[string]string, 0, len(routes))
  96. for _, route := range routes {
  97. routeItems = append(routeItems, map[string]string{"to": strings.TrimSpace(route.To), "via": strings.TrimSpace(route.Via)})
  98. }
  99. target["routes"] = routeItems
  100. } else {
  101. delete(target, "routes")
  102. }
  103. if len(input.DNS) > 0 {
  104. nameservers := ensureMap(target, "nameservers")
  105. nameservers["addresses"] = input.DNS
  106. } else {
  107. delete(target, "nameservers")
  108. }
  109. output, err := marshalYAML(&cfg)
  110. if err != nil {
  111. return err
  112. }
  113. return os.WriteFile(path, output, 0600)
  114. }
  115. func normalizedAddresses(input model.InterfaceConfig) []model.InterfaceAddressConfig {
  116. if len(input.Addresses) > 0 {
  117. return input.Addresses
  118. }
  119. if strings.TrimSpace(input.IP) == "" {
  120. return nil
  121. }
  122. return []model.InterfaceAddressConfig{{IP: strings.TrimSpace(input.IP), Prefix: input.Prefix}}
  123. }
  124. func normalizedRoutes(input model.InterfaceConfig) []model.InterfaceRouteConfig {
  125. if len(input.Routes) > 0 {
  126. return input.Routes
  127. }
  128. if strings.TrimSpace(input.Gateway) == "" {
  129. return nil
  130. }
  131. return []model.InterfaceRouteConfig{{To: "default", Via: strings.TrimSpace(input.Gateway)}}
  132. }
  133. func marshalYAML(value any) ([]byte, error) {
  134. var output bytes.Buffer
  135. encoder := yaml.NewEncoder(&output)
  136. encoder.SetIndent(2)
  137. if err := encoder.Encode(value); err != nil {
  138. _ = encoder.Close()
  139. return nil, err
  140. }
  141. if err := encoder.Close(); err != nil {
  142. return nil, err
  143. }
  144. return output.Bytes(), nil
  145. }
  146. func ensureMap(parent map[string]any, key string) map[string]any {
  147. if existing, ok := parent[key].(map[string]any); ok {
  148. return existing
  149. }
  150. child := make(map[string]any)
  151. parent[key] = child
  152. return child
  153. }
  154. func anyToStringSlice(value any) []string {
  155. switch typed := value.(type) {
  156. case []string:
  157. return append([]string(nil), typed...)
  158. case []any:
  159. result := make([]string, 0, len(typed))
  160. for _, item := range typed {
  161. text, ok := item.(string)
  162. if ok {
  163. result = append(result, text)
  164. }
  165. }
  166. return result
  167. default:
  168. return nil
  169. }
  170. }
  171. func contains(items []string, target string) bool {
  172. for _, item := range items {
  173. if item == target {
  174. return true
  175. }
  176. }
  177. return false
  178. }
  179. func uniqueNonEmpty(items []string) []string {
  180. seen := make(map[string]struct{})
  181. result := make([]string, 0, len(items))
  182. for _, item := range items {
  183. if item == "" {
  184. continue
  185. }
  186. if _, ok := seen[item]; ok {
  187. continue
  188. }
  189. seen[item] = struct{}{}
  190. result = append(result, item)
  191. }
  192. return result
  193. }