package eth import ( "encoding/json" "fmt" "gopkg.in/yaml.v3" "io" "log" "net" "net/http" "os" "os/exec" ) // NetplanConfig структура для представления YAML файла type NetplanConfig struct { Network Network `yaml:"network" json:"network"` } type Network struct { Version int `yaml:"version" json:"version"` Renderer string `yaml:"renderer" json:"renderer"` Ethernets Ethernets `yaml:"ethernets" json:"ethernets"` } type Ethernets struct { Eth0 Eth0Config `yaml:"eth0" json:"eth0"` } type Eth0Config struct { DHCP4 *bool `yaml:"dhcp4,omitempty" json:"dhcp4"` // Используем указатель, чтобы различать "false" и отсутствие ключа Addresses []string `yaml:"addresses,omitempty" json:"addresses"` Gateway4 string `yaml:"gateway4,omitempty" json:"gateway4,omitempty"` Nameservers Nameservers `yaml:"nameservers,omitempty" json:"nameservers"` } type Nameservers struct { Addresses []string `yaml:"addresses,omitempty" json:"addresses"` } const netplanFilePath = "/etc/netplan/eth.yaml" // Путь к файлу netplan (для примера, лучше использовать /etc/netplan/eth.yaml) func NetplanHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: getNetplanConfig(w, r) case http.MethodPut: updateNetplanConfig(w, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func getNetplanConfig(w http.ResponseWriter, r *http.Request) { config, err := readNetplanConfig() if err != nil { http.Error(w, fmt.Sprintf("Failed to read config: %v", err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(config) } func updateNetplanConfig(w http.ResponseWriter, r *http.Request) { var updatedConfig NetplanConfig err := json.NewDecoder(r.Body).Decode(&updatedConfig) if err != nil { http.Error(w, fmt.Sprintf("Failed to decode request body: %v", err), http.StatusBadRequest) return } err = writeNetplanConfig(updatedConfig) if err != nil { http.Error(w, fmt.Sprintf("Failed to write config: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Netplan configuration updated successfully")) } func readNetplanConfig() (*NetplanConfig, error) { yamlFile, err := os.ReadFile(netplanFilePath) if err != nil { return nil, fmt.Errorf("failed to read netplan file: %w", err) } var config NetplanConfig err = yaml.Unmarshal(yamlFile, &config) if err != nil { return nil, fmt.Errorf("failed to unmarshal yaml: %w", err) } return &config, nil } func writeNetplanConfig(config NetplanConfig) error { savedConfig, err := readNetplanConfig() if err != nil { return fmt.Errorf("failed to read eth yaml: %w", err) } yamlData, err := yaml.Marshal(config) if err != nil { return fmt.Errorf("failed to marshal yaml: %w", err) } err = os.WriteFile(netplanFilePath, yamlData, 0644) if err != nil { return fmt.Errorf("failed to write netplan file: %w", err) } cmd := exec.Command("netplan", "apply") output, err := cmd.CombinedOutput() if err != nil { yamlData, err := yaml.Marshal(*savedConfig) if err != nil { return fmt.Errorf("failed to marshal yaml: %w", err) } err = os.WriteFile(netplanFilePath, yamlData, 0644) if err != nil { return fmt.Errorf("failed to write netplan file: %w", err) } return fmt.Errorf("Ошибка при выполнении команды: %s\n Вывод команды: %s\n", err, output) } return nil } func RawNetplanHandler(w http.ResponseWriter, r *http.Request) { switch r.Method { case http.MethodGet: getRawNetplanConfig(w, r) case http.MethodPut: updateRawNetplanConfig(w, r) default: http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) } } func getRawNetplanConfig(w http.ResponseWriter, r *http.Request) { content, err := readRawNetplanConfig() if err != nil { http.Error(w, fmt.Sprintf("Failed to read raw config: %v", err), http.StatusInternalServerError) return } w.Header().Set("Content-Type", "text/plain") w.Write([]byte(content)) } func updateRawNetplanConfig(w http.ResponseWriter, r *http.Request) { body, err := io.ReadAll(r.Body) if err != nil { http.Error(w, fmt.Sprintf("Failed to read request body: %v", err), http.StatusBadRequest) return } rawYamlContent := string(body) // Валидация YAML перед записью if !isValidYAML(rawYamlContent) { http.Error(w, "Invalid YAML content", http.StatusBadRequest) return } err = writeRawNetplanConfig(rawYamlContent) if err != nil { http.Error(w, fmt.Sprintf("Failed to write raw config: %v", err), http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK) w.Write([]byte("Raw Netplan configuration updated successfully")) } func readRawNetplanConfig() (string, error) { yamlFile, err := os.ReadFile(netplanFilePath) if err != nil { return "", fmt.Errorf("failed to read netplan file: %w", err) } return string(yamlFile), nil } func writeRawNetplanConfig(content string) error { err := os.WriteFile(netplanFilePath, []byte(content), 0644) if err != nil { return fmt.Errorf("failed to write netplan file: %w", err) } return nil } // isValidYAML проверяет, является ли строка валидным YAML func isValidYAML(yamlString string) bool { var temp map[interface{}]interface{} err := yaml.Unmarshal([]byte(yamlString), &temp) return err == nil } // Функция для создания тестового файла eth.yaml (для демонстрации) func createTestNetplanFile() { initialYaml := ` network: version: 2 renderer: networkd ethernets: eth0: dhcp4: yes addresses: [192.168.8.111/24] nameservers: addresses: [8.8.8.8, 8.8.4.4] ` err := os.WriteFile(netplanFilePath, []byte(initialYaml), 0644) if err != nil { log.Fatalf("Failed to create test netplan file: %v", err) } fmt.Println("Test netplan file created at:", netplanFilePath) } func init() { // Создаем тестовый файл при запуске сервера, если его нет (для демонстрации) if _, err := os.Stat(netplanFilePath); os.IsNotExist(err) { createTestNetplanFile() } } // MacAddressResponse структура для JSON ответа type MacAddressResponse struct { MacAddress string `json:"mac_address"` Interface string `json:"interface"` Error string `json:"error,omitempty"` } // MacAdres HTTP handler для получения MAC адреса интерфейса eth0 func MacAdres(w http.ResponseWriter, r *http.Request) { interfaces, err := net.Interfaces() if err != nil { response := MacAddressResponse{ Error: fmt.Sprintf("Failed to get network interfaces: %v", err), } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) // 500 Internal Server Error json.NewEncoder(w).Encode(response) return } var eth0Interface *net.Interface for _, iface := range interfaces { if iface.Name == "eth0" { eth0Interface = &iface break } } if eth0Interface == nil { response := MacAddressResponse{ Error: "Interface eth0 not found", Interface: "eth0", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) // 404 Not Found json.NewEncoder(w).Encode(response) return } macAddress := eth0Interface.HardwareAddr.String() response := MacAddressResponse{ MacAddress: macAddress, Interface: "eth0", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(response) } // InterfaceIPsResponse структура для JSON ответа со списком IP адресов интерфейса type InterfaceIPsResponse struct { Interface string `json:"interface"` IPAddresses []string `json:"ip_addresses"` Error string `json:"error,omitempty"` } // InterfaceIPs HTTP handler для получения списка IP адресов интерфейса eth0 func InterfaceIPs(w http.ResponseWriter, r *http.Request) { interfaces, err := net.Interfaces() if err != nil { response := InterfaceIPsResponse{ Error: fmt.Sprintf("Failed to get network interfaces: %v", err), } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) // 500 Internal Server Error json.NewEncoder(w).Encode(response) return } var eth0Interface *net.Interface for _, iface := range interfaces { if iface.Name == "eth0" { eth0Interface = &iface break } } if eth0Interface == nil { response := InterfaceIPsResponse{ Interface: "eth0", Error: "Interface eth0 not found", } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusNotFound) // 404 Not Found json.NewEncoder(w).Encode(response) return } addrs, err := eth0Interface.Addrs() if err != nil { response := InterfaceIPsResponse{ Interface: "eth0", Error: fmt.Sprintf("Failed to get addresses for eth0: %v", err), } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusInternalServerError) // 500 Internal Server Error json.NewEncoder(w).Encode(response) return } ipAddresses := make([]string, 0) for _, addr := range addrs { if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { // Проверяем, что это IPNet и не loopback if ipnet.IP.To4() != nil || ipnet.IP.To16() != nil { // Проверяем, что это IPv4 или IPv6 ipAddresses = append(ipAddresses, ipnet.IP.String()) } } } response := InterfaceIPsResponse{ Interface: "eth0", IPAddresses: ipAddresses, } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) // 200 OK json.NewEncoder(w).Encode(response) }