netplan.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. package netplan
  2. import (
  3. "bytes"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "nettool/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 + ".nettool.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. return s.WriteMany(path, []model.InterfaceConfig{input}, managementInterface, maintenanceCIDR)
  53. }
  54. func (s *Service) WriteMany(path string, inputs []model.InterfaceConfig, managementInterface string, maintenanceCIDR string) error {
  55. data, err := os.ReadFile(path)
  56. if err != nil {
  57. return err
  58. }
  59. var cfg fileConfig
  60. if err := yaml.Unmarshal(data, &cfg); err != nil {
  61. return err
  62. }
  63. if cfg.Network == nil {
  64. cfg.Network = make(map[string]any)
  65. }
  66. if _, ok := cfg.Network["version"]; !ok {
  67. cfg.Network["version"] = 2
  68. }
  69. ethernets := ensureMap(cfg.Network, "ethernets")
  70. for _, input := range inputs {
  71. targetInterface := strings.TrimSpace(input.Interface)
  72. if targetInterface == "" {
  73. return fmt.Errorf("目标接口不能为空")
  74. }
  75. target := ensureMap(ethernets, targetInterface)
  76. if input.Dhcp4 {
  77. target["dhcp4"] = true
  78. delete(target, "addresses")
  79. delete(target, "gateway4")
  80. delete(target, "routes")
  81. if len(input.DNS) > 0 {
  82. nameservers := ensureMap(target, "nameservers")
  83. nameservers["addresses"] = input.DNS
  84. } else {
  85. delete(target, "nameservers")
  86. }
  87. continue
  88. }
  89. addresses := normalizedAddresses(input)
  90. routes := normalizedRoutes(input)
  91. target["dhcp4"] = false
  92. addressItems := make([]string, 0, len(addresses))
  93. for _, address := range addresses {
  94. addressItems = append(addressItems, fmt.Sprintf("%s/%d", strings.TrimSpace(address.IP), address.Prefix))
  95. }
  96. target["addresses"] = addressItems
  97. delete(target, "gateway4")
  98. if len(routes) > 0 {
  99. routeItems := make([]map[string]string, 0, len(routes))
  100. for _, route := range routes {
  101. routeItems = append(routeItems, map[string]string{"to": strings.TrimSpace(route.To), "via": strings.TrimSpace(route.Via)})
  102. }
  103. target["routes"] = routeItems
  104. } else {
  105. delete(target, "routes")
  106. }
  107. if len(input.DNS) > 0 {
  108. nameservers := ensureMap(target, "nameservers")
  109. nameservers["addresses"] = input.DNS
  110. } else {
  111. delete(target, "nameservers")
  112. }
  113. }
  114. output, err := marshalYAML(&cfg)
  115. if err != nil {
  116. return err
  117. }
  118. return os.WriteFile(path, output, 0600)
  119. }
  120. func normalizedAddresses(input model.InterfaceConfig) []model.InterfaceAddressConfig {
  121. if len(input.Addresses) > 0 {
  122. return input.Addresses
  123. }
  124. if strings.TrimSpace(input.IP) == "" {
  125. return nil
  126. }
  127. return []model.InterfaceAddressConfig{{IP: strings.TrimSpace(input.IP), Prefix: input.Prefix}}
  128. }
  129. func normalizedRoutes(input model.InterfaceConfig) []model.InterfaceRouteConfig {
  130. if len(input.Routes) > 0 {
  131. return input.Routes
  132. }
  133. if strings.TrimSpace(input.Gateway) == "" {
  134. return nil
  135. }
  136. return []model.InterfaceRouteConfig{{To: "default", Via: strings.TrimSpace(input.Gateway)}}
  137. }
  138. func marshalYAML(value any) ([]byte, error) {
  139. var output bytes.Buffer
  140. encoder := yaml.NewEncoder(&output)
  141. encoder.SetIndent(2)
  142. if err := encoder.Encode(value); err != nil {
  143. _ = encoder.Close()
  144. return nil, err
  145. }
  146. if err := encoder.Close(); err != nil {
  147. return nil, err
  148. }
  149. return output.Bytes(), nil
  150. }
  151. func ensureMap(parent map[string]any, key string) map[string]any {
  152. if existing, ok := parent[key].(map[string]any); ok {
  153. return existing
  154. }
  155. child := make(map[string]any)
  156. parent[key] = child
  157. return child
  158. }
  159. func anyToStringSlice(value any) []string {
  160. switch typed := value.(type) {
  161. case []string:
  162. return append([]string(nil), typed...)
  163. case []any:
  164. result := make([]string, 0, len(typed))
  165. for _, item := range typed {
  166. text, ok := item.(string)
  167. if ok {
  168. result = append(result, text)
  169. }
  170. }
  171. return result
  172. default:
  173. return nil
  174. }
  175. }
  176. func contains(items []string, target string) bool {
  177. for _, item := range items {
  178. if item == target {
  179. return true
  180. }
  181. }
  182. return false
  183. }
  184. func uniqueNonEmpty(items []string) []string {
  185. seen := make(map[string]struct{})
  186. result := make([]string, 0, len(items))
  187. for _, item := range items {
  188. if item == "" {
  189. continue
  190. }
  191. if _, ok := seen[item]; ok {
  192. continue
  193. }
  194. seen[item] = struct{}{}
  195. result = append(result, item)
  196. }
  197. return result
  198. }