netplan.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  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. if len(addressItems) > 0 {
  97. target["addresses"] = addressItems
  98. } else {
  99. delete(target, "addresses")
  100. }
  101. delete(target, "gateway4")
  102. if len(routes) > 0 {
  103. routeItems := make([]map[string]string, 0, len(routes))
  104. for _, route := range routes {
  105. routeItems = append(routeItems, map[string]string{"to": strings.TrimSpace(route.To), "via": strings.TrimSpace(route.Via)})
  106. }
  107. target["routes"] = routeItems
  108. } else {
  109. delete(target, "routes")
  110. }
  111. if len(input.DNS) > 0 {
  112. nameservers := ensureMap(target, "nameservers")
  113. nameservers["addresses"] = input.DNS
  114. } else {
  115. delete(target, "nameservers")
  116. }
  117. }
  118. output, err := marshalYAML(&cfg)
  119. if err != nil {
  120. return err
  121. }
  122. return os.WriteFile(path, output, 0600)
  123. }
  124. func normalizedAddresses(input model.InterfaceConfig) []model.InterfaceAddressConfig {
  125. if len(input.Addresses) > 0 {
  126. return input.Addresses
  127. }
  128. if strings.TrimSpace(input.IP) == "" {
  129. return nil
  130. }
  131. return []model.InterfaceAddressConfig{{IP: strings.TrimSpace(input.IP), Prefix: input.Prefix}}
  132. }
  133. func normalizedRoutes(input model.InterfaceConfig) []model.InterfaceRouteConfig {
  134. if len(input.Routes) > 0 {
  135. return input.Routes
  136. }
  137. if strings.TrimSpace(input.Gateway) == "" {
  138. return nil
  139. }
  140. return []model.InterfaceRouteConfig{{To: "default", Via: strings.TrimSpace(input.Gateway)}}
  141. }
  142. func marshalYAML(value any) ([]byte, error) {
  143. var output bytes.Buffer
  144. encoder := yaml.NewEncoder(&output)
  145. encoder.SetIndent(2)
  146. if err := encoder.Encode(value); err != nil {
  147. _ = encoder.Close()
  148. return nil, err
  149. }
  150. if err := encoder.Close(); err != nil {
  151. return nil, err
  152. }
  153. return output.Bytes(), nil
  154. }
  155. func ensureMap(parent map[string]any, key string) map[string]any {
  156. if existing, ok := parent[key].(map[string]any); ok {
  157. return existing
  158. }
  159. child := make(map[string]any)
  160. parent[key] = child
  161. return child
  162. }
  163. func anyToStringSlice(value any) []string {
  164. switch typed := value.(type) {
  165. case []string:
  166. return append([]string(nil), typed...)
  167. case []any:
  168. result := make([]string, 0, len(typed))
  169. for _, item := range typed {
  170. text, ok := item.(string)
  171. if ok {
  172. result = append(result, text)
  173. }
  174. }
  175. return result
  176. default:
  177. return nil
  178. }
  179. }
  180. func contains(items []string, target string) bool {
  181. for _, item := range items {
  182. if item == target {
  183. return true
  184. }
  185. }
  186. return false
  187. }
  188. func uniqueNonEmpty(items []string) []string {
  189. seen := make(map[string]struct{})
  190. result := make([]string, 0, len(items))
  191. for _, item := range items {
  192. if item == "" {
  193. continue
  194. }
  195. if _, ok := seen[item]; ok {
  196. continue
  197. }
  198. seen[item] = struct{}{}
  199. result = append(result, item)
  200. }
  201. return result
  202. }