| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- package netplan
- import (
- "fmt"
- "os"
- "path/filepath"
- "quickip/internal/model"
- "gopkg.in/yaml.v3"
- )
- type Service struct{}
- type fileConfig struct {
- Network map[string]any `yaml:"network"`
- }
- func New() *Service { return &Service{} }
- func (s *Service) FindSingleFile() (string, error) {
- files, err := filepath.Glob("/etc/netplan/*.yaml")
- if err != nil {
- return "", err
- }
- if len(files) == 0 {
- return "", fmt.Errorf("未找到 netplan 配置文件")
- }
- if len(files) > 1 {
- return "", fmt.Errorf("检测到多个 netplan 配置文件,首版暂不支持自动处理")
- }
- return files[0], nil
- }
- func (s *Service) Backup(path string) (string, error) {
- backupPath := path + ".quickip.bak"
- data, err := os.ReadFile(path)
- if err != nil {
- return "", err
- }
- if err := os.WriteFile(backupPath, data, 0600); err != nil {
- return "", err
- }
- return backupPath, nil
- }
- func (s *Service) Restore(path string, backupPath string) error {
- data, err := os.ReadFile(backupPath)
- if err != nil {
- return err
- }
- if err := os.WriteFile(path, data, 0600); err != nil {
- return err
- }
- _ = os.Remove(backupPath)
- return nil
- }
- func (s *Service) Write(path string, targetInterface string, input model.InterfaceConfig, managementInterface string, maintenanceCIDR string) error {
- data, err := os.ReadFile(path)
- if err != nil {
- return err
- }
- var cfg fileConfig
- if err := yaml.Unmarshal(data, &cfg); err != nil {
- return err
- }
- if cfg.Network == nil {
- cfg.Network = make(map[string]any)
- }
- if _, ok := cfg.Network["version"]; !ok {
- cfg.Network["version"] = 2
- }
- ethernets := ensureMap(cfg.Network, "ethernets")
- target := ensureMap(ethernets, targetInterface)
- if input.Dhcp4 {
- target["dhcp4"] = true
- delete(target, "addresses")
- delete(target, "gateway4")
- delete(target, "routes")
- delete(target, "nameservers")
- output, err := yaml.Marshal(&cfg)
- if err != nil {
- return err
- }
- return os.WriteFile(path, output, 0600)
- }
- target["dhcp4"] = false
- target["addresses"] = []string{fmt.Sprintf("%s/%d", input.IP, input.Prefix)}
- delete(target, "gateway4")
- if input.Gateway != "" {
- target["routes"] = []map[string]string{{"to": "default", "via": input.Gateway}}
- } else {
- delete(target, "routes")
- }
- if len(input.DNS) > 0 {
- nameservers := ensureMap(target, "nameservers")
- nameservers["addresses"] = input.DNS
- } else {
- delete(target, "nameservers")
- }
- output, err := yaml.Marshal(&cfg)
- if err != nil {
- return err
- }
- return os.WriteFile(path, output, 0600)
- }
- func ensureMap(parent map[string]any, key string) map[string]any {
- if existing, ok := parent[key].(map[string]any); ok {
- return existing
- }
- child := make(map[string]any)
- parent[key] = child
- return child
- }
- func anyToStringSlice(value any) []string {
- switch typed := value.(type) {
- case []string:
- return append([]string(nil), typed...)
- case []any:
- result := make([]string, 0, len(typed))
- for _, item := range typed {
- text, ok := item.(string)
- if ok {
- result = append(result, text)
- }
- }
- return result
- default:
- return nil
- }
- }
- func contains(items []string, target string) bool {
- for _, item := range items {
- if item == target {
- return true
- }
- }
- return false
- }
- func uniqueNonEmpty(items []string) []string {
- seen := make(map[string]struct{})
- result := make([]string, 0, len(items))
- for _, item := range items {
- if item == "" {
- continue
- }
- if _, ok := seen[item]; ok {
- continue
- }
- seen[item] = struct{}{}
- result = append(result, item)
- }
- return result
- }
|