2025-03-11 14:07:01 +03:00

350 lines
9.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}